ソースを参照

touch : Add files via upload

Removed example files and move source to /pt/
Git-commit: e2e4bb9eacc2406cb0351d15e478e68249d5f625
Git-repo: https://github.com/Parade-Github/TTDL/tree/main

Change-Id: I07f3464718058d5bba76b7d5be486a79934db354
Signed-off-by: Surya Teja Kudiri <[email protected]>
Parade-Github 2 年 前
コミット
b0e4b1c194
16 ファイル変更41545 行追加0 行削除
  1. 57 0
      pt/Makefile
  2. 524 0
      pt/pt_btn.c
  3. 19501 0
      pt/pt_core.c
  4. 556 0
      pt/pt_debug.c
  5. 6830 0
      pt/pt_device_access.c
  6. 1128 0
      pt/pt_devtree.c
  7. 570 0
      pt/pt_i2c.c
  8. 5956 0
      pt/pt_loader.c
  9. 970 0
      pt/pt_mt_common.c
  10. 143 0
      pt/pt_mta.c
  11. 144 0
      pt/pt_mtb.c
  12. 572 0
      pt/pt_pen.c
  13. 1281 0
      pt/pt_platform.c
  14. 781 0
      pt/pt_proximity.c
  15. 1875 0
      pt/pt_regs.h
  16. 657 0
      pt/pt_spi.c

+ 57 - 0
pt/Makefile

@@ -0,0 +1,57 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Makefile for the touchscreen drivers.
+#
+
+# Each configuration option enables a list of files.
+
+obj-$(CONFIG_TOUCHSCREEN_PARADE)      += pt.o
+pt-y := pt_core.o pt_mt_common.o
+pt-$(CONFIG_TOUCHSCREEN_PARADE_MT_A) += pt_mta.o
+pt-$(CONFIG_TOUCHSCREEN_PARADE_MT_B) += pt_mtb.o
+pt-$(CONFIG_TOUCHSCREEN_PARADE_BUTTON) += pt_btn.o
+pt-$(CONFIG_TOUCHSCREEN_PARADE_PEN) += pt_pen.o
+pt-$(CONFIG_TOUCHSCREEN_PARADE_PROXIMITY) += pt_proximity.o
+obj-$(CONFIG_TOUCHSCREEN_PARADE_DEVICETREE_SUPPORT) += pt_devtree.o
+ifdef CONFIG_TOUCHSCREEN_PARADE
+obj-y += pt_platform.o
+endif
+obj-$(CONFIG_TOUCHSCREEN_PARADE_I2C)  += pt_i2c.o
+obj-$(CONFIG_TOUCHSCREEN_PARADE_SPI)  += pt_spi.o
+obj-$(CONFIG_TOUCHSCREEN_PARADE_DEBUG_MDL)    += pt_debug.o
+obj-$(CONFIG_TOUCHSCREEN_PARADE_LOADER)       += pt_loader.o
+obj-$(CONFIG_TOUCHSCREEN_PARADE_DEVICE_ACCESS)          += pt_device_access.o
+
+ifeq ($(CONFIG_TOUCHSCREEN_PARADE_DEBUG),y)
+CFLAGS_pt_core.o += -DDEBUG
+CFLAGS_pt_i2c.o += -DDEBUG
+CFLAGS_pt_spi.o += -DDEBUG
+CFLAGS_pt_mta.o += -DDEBUG
+CFLAGS_pt_mtb.o += -DDEBUG
+CFLAGS_pt_mt_common.o += -DDEBUG
+CFLAGS_pt_btn.o += -DDEBUG
+CFLAGS_pt_pen.o += -DDEBUG
+CFLAGS_pt_proximity.o += -DDEBUG
+CFLAGS_pt_device_access.o += -DDEBUG
+CFLAGS_pt_loader.o += -DDEBUG
+CFLAGS_pt_debug.o += -DDEBUG
+CFLAGS_pt_devtree.o += -DDEBUG
+CFLAGS_pt_platform.o += -DDEBUG
+endif
+
+ifeq ($(CONFIG_TOUCHSCREEN_PARADE_VDEBUG),y)
+CFLAGS_pt_core.o += -DVERBOSE_DEBUG
+CFLAGS_pt_i2c.o += -DVERBOSE_DEBUG
+CFLAGS_pt_spi.o += -DVERBOSE_DEBUG
+CFLAGS_pt_mta.o += -DVERBOSE_DEBUG
+CFLAGS_pt_mtb.o += -DVERBOSE_DEBUG
+CFLAGS_pt_mt_common.o += -DVERBOSE_DEBUG
+CFLAGS_pt_btn.o += -DVERBOSE_DEBUG
+CFLAGS_pt_pen.o += -DVERBOSE_DEBUG
+CFLAGS_pt_proximity.o += -DVERBOSE_DEBUG
+CFLAGS_pt_device_access.o += -DVERBOSE_DEBUG
+CFLAGS_pt_loader.o += -DVERBOSE_DEBUG
+CFLAGS_pt_debug.o += -DVERBOSE_DEBUG
+CFLAGS_pt_devtree.o += -DVERBOSE_DEBUG
+CFLAGS_pt_platform.o += -DVERBOSE_DEBUG
+endif

+ 524 - 0
pt/pt_btn.c

@@ -0,0 +1,524 @@
+#ifndef TTDL_KERNEL_SUBMISSION
+/*
+ * pt_btn.c
+ * Parade TrueTouch(TM) Standard Product CapSense Reports 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 <[email protected]>
+ */
+
+#include "pt_regs.h"
+
+/*******************************************************************************
+ * FUNCTION: pt_btn_key_action
+ *
+ * SUMMARY: Reports key event
+ *
+ * PARAMETERS:
+ *     *bd        - pointer to button data structure
+ *      btn_no    - number of button
+ *      btn_state - state of button
+ ******************************************************************************/
+static inline void pt_btn_key_action(struct pt_btn_data *bd,
+	int btn_no, int btn_state)
+{
+	struct device *dev = bd->dev;
+	struct pt_sysinfo *si = bd->si;
+
+	if (!si->btn[btn_no].enabled ||
+			si->btn[btn_no].state == btn_state)
+		return;
+
+	si->btn[btn_no].state = btn_state;
+	input_report_key(bd->input, si->btn[btn_no].key_code, btn_state);
+	input_sync(bd->input);
+
+	pt_debug(dev, DL_INFO, "%s: btn=%d key_code=%d %s\n",
+		__func__, btn_no, si->btn[btn_no].key_code,
+		btn_state == PT_BTN_PRESSED ?
+			"PRESSED" : "RELEASED");
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_get_btn_touches
+ *
+ * SUMMARY: Parse and report key event
+ *
+ * PARAMETERS:
+ *     *bd  - pointer to button data structure
+ ******************************************************************************/
+static void pt_get_btn_touches(struct pt_btn_data *bd)
+{
+	struct pt_sysinfo *si = bd->si;
+	int num_btns = si->num_btns;
+	int cur_btn;
+	int cur_btn_state;
+
+	for (cur_btn = 0; cur_btn < num_btns; cur_btn++) {
+		/* Get current button state */
+		cur_btn_state = (si->xy_data[0] >> (cur_btn * PT_BITS_PER_BTN))
+				& PT_NUM_BTN_EVENT_ID;
+
+		pt_btn_key_action(bd, cur_btn, cur_btn_state);
+	}
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_btn_lift_all
+ *
+ * SUMMARY: Reports button liftoff action
+ *
+ * PARAMETERS:
+ *     *bd  - pointer to button data structure
+ ******************************************************************************/
+static void pt_btn_lift_all(struct pt_btn_data *bd)
+{
+	struct pt_sysinfo *si = bd->si;
+	int i;
+
+	if (!si || si->num_btns == 0)
+		return;
+
+	for (i = 0; i < si->num_btns; i++)
+		pt_btn_key_action(bd, i, PT_BTN_RELEASED);
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_xy_worker
+ *
+ * SUMMARY: Read xy_data for all current CapSense button touches
+ *
+ * RETURN:
+ *	 0 = success
+ *
+ * PARAMETERS:
+ *     *bd  - pointer to button data structure
+ ******************************************************************************/
+static int pt_xy_worker(struct pt_btn_data *bd)
+{
+	struct pt_sysinfo *si = bd->si;
+
+	/* extract button press/release touch information */
+	if (si->num_btns > 0)
+		pt_get_btn_touches(bd);
+
+	return 0;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_btn_attention
+ *
+ * SUMMARY: Wrapper function for pt_xy_worker() that register to TTDL attention
+ *  list.
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *     *dev - pointer to device structure
+ ******************************************************************************/
+static int pt_btn_attention(struct device *dev)
+{
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+	struct pt_btn_data *bd = &cd->bd;
+	int rc;
+
+	if (bd->si->xy_mode[2] != bd->si->desc.btn_report_id)
+		return 0;
+
+	/* core handles handshake */
+	mutex_lock(&bd->btn_lock);
+	rc = pt_xy_worker(bd);
+	mutex_unlock(&bd->btn_lock);
+	if (rc < 0)
+		pt_debug(dev, DL_ERROR,
+			"%s: xy_worker error r=%d\n", __func__, rc);
+
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_startup_attention
+ *
+ * SUMMARY: Wrapper function for pt_btn_lift_all() that register to TTDL
+ *  attention list.
+ *
+ * RETURN:
+ *	 0 = success
+ *
+ * PARAMETERS:
+ *     *dev - pointer to device structure
+ ******************************************************************************/
+static int pt_startup_attention(struct device *dev)
+{
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+	struct pt_btn_data *bd = &cd->bd;
+
+	mutex_lock(&bd->btn_lock);
+	pt_btn_lift_all(bd);
+	mutex_unlock(&bd->btn_lock);
+
+	return 0;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_btn_suspend_attention
+ *
+ * SUMMARY: Function for button to enter suspend state that as following steps:
+ *          1) Lift all button
+ *          2) Set flag with suspend state
+ *          3) Decrese pm system count
+ *
+ * RETURN:
+ *	 0 = success
+ *
+ * PARAMETERS:
+ *     *dev - pointer to device structure
+ ******************************************************************************/
+static int pt_btn_suspend_attention(struct device *dev)
+{
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+	struct pt_btn_data *bd = &cd->bd;
+
+	mutex_lock(&bd->btn_lock);
+	pt_btn_lift_all(bd);
+	bd->is_suspended = true;
+	mutex_unlock(&bd->btn_lock);
+
+	pm_runtime_put(dev);
+
+	return 0;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_btn_resume_attention
+ *
+ * SUMMARY: Function for button to leave suspend state that as following steps:
+ *          1) Increse pm system count
+ *          2) Clear suspend state flag
+ *
+ * RETURN:
+ *	 0 = success
+ *
+ * PARAMETERS:
+ *     *dev - pointer to device structure
+ ******************************************************************************/
+static int pt_btn_resume_attention(struct device *dev)
+{
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+	struct pt_btn_data *bd = &cd->bd;
+
+	pm_runtime_get(dev);
+
+	mutex_lock(&bd->btn_lock);
+	bd->is_suspended = false;
+	mutex_unlock(&bd->btn_lock);
+
+	return 0;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_btn_open
+ *
+ * SUMMARY: Open method for input device(button) that sets up call back
+ *  functions to TTDL attention list
+ *
+ * RETURN:
+ *	 0 = success
+ *
+ * PARAMETERS:
+ *     *input - pointer to input_dev structure
+ ******************************************************************************/
+static int pt_btn_open(struct input_dev *input)
+{
+	struct device *dev = input->dev.parent;
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+	struct pt_btn_data *bd = &cd->bd;
+
+	pm_runtime_get_sync(dev);
+
+	mutex_lock(&bd->btn_lock);
+	bd->is_suspended = false;
+	mutex_unlock(&bd->btn_lock);
+
+	pt_debug(dev, DL_INFO, "%s: setup subscriptions\n", __func__);
+
+	/* set up touch call back */
+	_pt_subscribe_attention(dev, PT_ATTEN_IRQ, PT_BTN_NAME,
+		pt_btn_attention, PT_MODE_OPERATIONAL);
+
+	/* set up startup call back */
+	_pt_subscribe_attention(dev, PT_ATTEN_STARTUP, PT_BTN_NAME,
+		pt_startup_attention, 0);
+
+	/* set up suspend call back */
+	_pt_subscribe_attention(dev, PT_ATTEN_SUSPEND, PT_BTN_NAME,
+		pt_btn_suspend_attention, 0);
+
+	/* set up resume call back */
+	_pt_subscribe_attention(dev, PT_ATTEN_RESUME, PT_BTN_NAME,
+		pt_btn_resume_attention, 0);
+
+	return 0;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_btn_close
+ *
+ * SUMMARY: Close method for input device(button) that clears call back
+ *  functions from TTDL attention list.
+ *
+ * PARAMETERS:
+ *     *input - pointer to input_dev structure
+ ******************************************************************************/
+static void pt_btn_close(struct input_dev *input)
+{
+	struct device *dev = input->dev.parent;
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+	struct pt_btn_data *bd = &cd->bd;
+
+	_pt_unsubscribe_attention(dev, PT_ATTEN_IRQ, PT_BTN_NAME,
+		pt_btn_attention, PT_MODE_OPERATIONAL);
+
+	_pt_unsubscribe_attention(dev, PT_ATTEN_STARTUP, PT_BTN_NAME,
+		pt_startup_attention, 0);
+
+	_pt_unsubscribe_attention(dev, PT_ATTEN_SUSPEND, PT_BTN_NAME,
+		pt_btn_suspend_attention, 0);
+
+	_pt_unsubscribe_attention(dev, PT_ATTEN_RESUME, PT_BTN_NAME,
+		pt_btn_resume_attention, 0);
+
+	mutex_lock(&bd->btn_lock);
+	if (!bd->is_suspended) {
+		pm_runtime_put(dev);
+		bd->is_suspended = true;
+	}
+	mutex_unlock(&bd->btn_lock);
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_setup_input_device
+ *
+ * SUMMARY: Set up resolution, event signal capabilities and register input
+ *  device for button.
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *     *dev - pointer to device structure
+ ******************************************************************************/
+static int pt_setup_input_device(struct device *dev)
+{
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+	struct pt_btn_data *bd = &cd->bd;
+	int i;
+	int rc;
+
+	pt_debug(dev, DL_INFO, "%s: Initialize event signals\n",
+		__func__);
+	__set_bit(EV_KEY, bd->input->evbit);
+	pt_debug(dev, DL_INFO, "%s: Number of buttons %d\n",
+		__func__, bd->si->num_btns);
+	for (i = 0; i < bd->si->num_btns; i++) {
+		pt_debug(dev, DL_INFO, "%s: btn:%d keycode:%d\n",
+			__func__, i, bd->si->btn[i].key_code);
+		__set_bit(bd->si->btn[i].key_code, bd->input->keybit);
+	}
+
+	rc = input_register_device(bd->input);
+	if (rc < 0)
+		pt_debug(dev, DL_ERROR,
+			"%s: Error, failed register input device r=%d\n",
+			__func__, rc);
+	else
+		bd->input_device_registered = true;
+
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_setup_input_attention
+ *
+ * SUMMARY: Wrapper function for pt_setup_input_device() register to TTDL
+ *  attention list.
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *     *dev - pointer to device structure
+ ******************************************************************************/
+static int pt_setup_input_attention(struct device *dev)
+{
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+	struct pt_btn_data *bd = &cd->bd;
+	int rc;
+
+	bd->si = _pt_request_sysinfo(dev);
+	if (!bd->si)
+		return -1;
+
+	rc = pt_setup_input_device(dev);
+
+	_pt_unsubscribe_attention(dev, PT_ATTEN_STARTUP, PT_BTN_NAME,
+		pt_setup_input_attention, 0);
+
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_btn_probe
+ *
+ * SUMMARY: The probe function for button input device
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *	*dev   - pointer to device structure
+ ******************************************************************************/
+int pt_btn_probe(struct device *dev)
+{
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+	struct pt_btn_data *bd = &cd->bd;
+	struct pt_platform_data *pdata = dev_get_platdata(dev);
+	struct pt_btn_platform_data *btn_pdata;
+	int rc = 0;
+
+	if (!pdata || !pdata->btn_pdata) {
+		pt_debug(dev, DL_ERROR,
+			"%s: Missing platform data\n", __func__);
+		rc = -ENODEV;
+		goto error_no_pdata;
+	}
+	btn_pdata = pdata->btn_pdata;
+
+	mutex_init(&bd->btn_lock);
+	bd->dev = dev;
+	bd->pdata = btn_pdata;
+
+	/* Create the input device and register it. */
+	pt_debug(dev, DL_INFO,
+		"%s: Create the input device and register it\n", __func__);
+	bd->input = input_allocate_device();
+	if (!bd->input) {
+		pt_debug(dev, DL_ERROR,
+			"%s: Error, failed to allocate input device\n",
+			__func__);
+		rc = -ENODEV;
+		goto error_alloc_failed;
+	} else
+		bd->input_device_allocated = true;
+
+	if (bd->pdata->inp_dev_name)
+		bd->input->name = bd->pdata->inp_dev_name;
+	else
+		bd->input->name = PT_BTN_NAME;
+	scnprintf(bd->phys, sizeof(bd->phys), "%s/input%d", dev_name(dev),
+			cd->phys_num++);
+	bd->input->phys = bd->phys;
+	bd->input->dev.parent = bd->dev;
+	bd->input->open = pt_btn_open;
+	bd->input->close = pt_btn_close;
+	input_set_drvdata(bd->input, bd);
+
+	/* get sysinfo */
+	bd->si = _pt_request_sysinfo(dev);
+
+	if (bd->si) {
+		rc = pt_setup_input_device(dev);
+		if (rc)
+			goto error_init_input;
+	} else {
+		pt_debug(dev, DL_ERROR,
+			"%s: Fail get sysinfo pointer from core p=%p\n",
+			__func__, bd->si);
+		_pt_subscribe_attention(dev, PT_ATTEN_STARTUP,
+			PT_BTN_NAME, pt_setup_input_attention, 0);
+	}
+
+	return 0;
+
+error_init_input:
+	input_free_device(bd->input);
+	bd->input_device_allocated = false;
+error_alloc_failed:
+error_no_pdata:
+	pt_debug(dev, DL_ERROR, "%s failed.\n", __func__);
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_btn_release
+ *
+ * SUMMARY: The release function for button input device
+ *
+ * RETURN:
+ *	 0 = success
+ *
+ * PARAMETERS:
+ *	*dev   - pointer to device structure
+ ******************************************************************************/
+int pt_btn_release(struct device *dev)
+{
+	struct pt_core_data *cd;
+	struct pt_btn_data *bd;
+
+	/* Ensure valid pointers before de-referencing them */
+	if (dev) {
+		cd = dev_get_drvdata(dev);
+		if (cd)
+			bd = &cd->bd;
+		else
+			return 0;
+	} else {
+		return 0;
+	}
+
+	/*
+	 * Second call this function may cause kernel panic if probe fail.
+	 * Use input_device_registered & input_device_allocated variable to
+	 * avoid unregister or free unavailable devive.
+	 */
+	if (bd && bd->input_device_registered) {
+		bd->input_device_registered = false;
+		input_unregister_device(bd->input);
+		/* Unregistering device will free the device too */
+		bd->input_device_allocated = false;
+	} else if (bd && bd->input_device_allocated) {
+		bd->input_device_allocated = false;
+		input_free_device(bd->input);
+		_pt_unsubscribe_attention(dev, PT_ATTEN_STARTUP,
+			PT_BTN_NAME, pt_setup_input_attention, 0);
+	}
+
+	return 0;
+}
+#endif /*!TTDL_KERNEL_SUBMISSION */

+ 19501 - 0
pt/pt_core.c

@@ -0,0 +1,19501 @@
+/*
+ * 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 <[email protected]>
+ */
+
+#include "pt_regs.h"
+#include <linux/kthread.h>
+#include <linux/i2c.h>
+#include <linux/gpio.h>
+
+#ifdef PT_PTSBC_SUPPORT
+#define PT_CORE_PROBE_STARTUP_DELAY_MS		500
+#endif /* PT_PTSBC_SUPPORT */
+
+#define PT_CORE_STARTUP_RETRY_COUNT		3
+
+MODULE_FIRMWARE(PT_FW_FILE_NAME);
+
+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;
+
+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;
+	int log_collection_num;
+};
+
+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 {
+	__le16 descriptor;
+	u8 opcode;
+	u8 report_type;
+	union {
+		u8 report_id;
+		u8 power_state;
+	};
+	u8 has_data_register;
+	size_t write_length;
+	size_t read_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;
+	int 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 spaces each printed char takes 3 bytes, subtract
+		 * the length of the data_name and length prefix and divide 3
+		 */
+		pr_buf_index += scnprintf(pr_buf, PT_MAX_PR_BUF_SIZE, "%s [0..%d]: ",
+			data_name, buf_len);
+		max_size = (PT_MAX_PR_BUF_SIZE - pr_buf_index) / 3;
+		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;
+}
+
+#ifndef TTDL_KERNEL_SUBMISSION
+#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 */
+#endif /* !TTDL_KERNEL_SUBMISSION */
+
+/*******************************************************************************
+ * 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_create_cmd_and_send_
+ *
+ * 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_create_cmd_and_send_(struct pt_core_data *cd,
+		struct pt_hid_cmd *hid_cmd)
+{
+	int rc = 0;
+	u8 *cmd;
+	u16 cmd_length;
+	u8 cmd_offset = 0;
+
+	if (hid_cmd->descriptor) {
+		cmd_length = 2; /* hid or report register */
+	} else {
+		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 */
+	}
+
+	cmd = kzalloc(cmd_length, GFP_KERNEL);
+	if (!cmd)
+		return -ENOMEM;
+
+	/* hid & report descriptor doesn't require other field */
+	if (hid_cmd->descriptor) {
+		memcpy(&cmd[cmd_offset], &hid_cmd->descriptor, cmd_length);
+		goto skip_other_field;
+	}
+
+	/* 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);
+
+skip_other_field:
+	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,
+					 hid_cmd->read_length);
+	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;
+
+	mutex_lock(&cd->system_lock);
+	*cmd_state = 1;
+	mutex_unlock(&cd->system_lock);
+
+	rc = pt_hid_create_cmd_and_send_(cd, hid_cmd);
+	if (rc)
+		goto error;
+
+	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_exec_cmd_no_wait_
+ *
+ * SUMMARY: The function works to send HID command and can read response
+ *  directly instead of waiting it to be received in interrupt function. It
+ *  assgins the read buffer to receive response in following condition:
+ *   1) descriptor is assigned to get hid descriptor or report descripter
+ *   2) output register is assigned for vendor-defined commands (TBD)
+ *
+ * NOTE: If no read buffer is assigned, it only perform send action.
+ *
+ * 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_no_wait_(struct pt_core_data *cd,
+		struct pt_hid_cmd *hid_cmd)
+{
+	int rc = 0;
+
+	if (hid_cmd->descriptor)
+		hid_cmd->read_buf = cd->response_buf;
+
+	rc = pt_hid_create_cmd_and_send_(cd, hid_cmd);
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_hid_send_command
+ *
+ * SUMMARY: Wrapper function to call pt_hid_exec_cmd_no_wait_() for HID protocol
+ *  and pt_hid_exec_cmd_and_wait_() for PIP protocol.
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *	*cd      - pointer to core data
+ *	*hid_cmd - pointer to the HID command to send
+ ******************************************************************************/
+static int pt_hid_send_command(struct pt_core_data *cd,
+		struct pt_hid_cmd *hid_cmd)
+{
+	if (cd->dut_status.protocol_mode == PT_PROTOCOL_MODE_HID)
+		return pt_hid_exec_cmd_no_wait_(cd, hid_cmd);
+	else
+		return pt_hid_exec_cmd_and_wait_(cd, hid_cmd);
+}
+
+/*******************************************************************************
+ * 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,
+		.reset_cmd = 1,
+		.timeout_ms = PT_HID_CMD_DEFAULT_TIMEOUT,
+	};
+
+	return pt_hid_send_command(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,
+		.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_no_wait_(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_send_command(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;
+	}
+
+	/* HID COMMAND doesn't have a response */
+	if (cd->dut_status.protocol_mode == PT_PROTOCOL_MODE_HID)
+		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;
+}
+
+#ifndef TTDL_KERNEL_SUBMISSION
+/*******************************************************************************
+ * 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;
+}
+#endif /* !TTDL_KERNEL_SUBMISSION */
+
+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 -1;
+}
+
+/*******************************************************************************
+ * 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, 0);
+	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) {
+#ifdef TTDL_PTVIRTDUT_SUPPORT
+		if (cd->route_bus_virt_dut)
+			rc = pt_adap_read_default(cd, buf,
+				PT_MAX_PIP2_MSG_SIZE);
+		else
+			rc = pt_adap_read_default(cd, buf, 2);
+#else
+		rc = pt_adap_read_default(cd, buf, 2);
+#endif /* TTDL_PTVIRTDUT_SUPPORT */
+		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);
+
+#ifdef TTDL_PTVIRTDUT_SUPPORT
+			rc = 0;
+			if (cd->route_bus_virt_dut)
+				bytes_read = pip_len;
+			else
+				rc = pt_adap_read_default(cd, buf, pip_len);
+#else
+			rc = pt_adap_read_default(cd, buf, pip_len);
+#endif /* TTDL_PTVIRTDUT_SUPPORT */
+			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;
+
+#ifdef FUTURE
+	/*
+	 * *** TODO - Determine side effects of adding this safety net ***
+	 * If IRQ is already asserted due to a pending report, it must be
+	 * cleared before sending command.
+	 */
+	pt_flush_bus_if_irq_asserted(cd, PT_FLUSH_BUS_BASED_ON_LEN);
+#endif
+
+	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, 0);
+
+	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;
+	int i = 0;
+	int j = 0;
+	u16 write_len;
+	u8 *write_buf = NULL;
+	u16 read_len;
+	u8 extra_bytes;
+
+	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;
+	int i = 0;
+	int 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 (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, 0);
+	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);
+}
+
+#ifndef TTDL_KERNEL_SUBMISSION
+/*******************************************************************************
+ * 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;
+}
+#endif /*!TTDL_KERNEL_SUBMISSION */
+
+static void pt_stop_wd_timer(struct pt_core_data *cd);
+/*******************************************************************************
+ * FUNCTION: pt_pip_start_bootloader_
+ *
+ * SUMMARY: Sends the PIP 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;
+}
+
+#ifndef TTDL_KERNEL_SUBMISSION
+/*******************************************************************************
+ * 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;
+}
+#endif /*!TTDL_KERNEL_SUBMISSION */
+
+/*******************************************************************************
+ * 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)
+		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 */
+	int 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,
+	};
+
+	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 (*status == 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 "Initiate Bootload" (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 "Program and Verify" (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 "Verify Application Integrity" (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_status_
+ *
+ * SUMMARY: Sends a PIP2 STATUS command to the DUT and stores the data in
+ *	cd status_data.
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *	*cd       - pointer to core data structure
+ ******************************************************************************/
+static int pt_pip2_get_status_(struct pt_core_data *cd)
+{
+	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);
+	if (rc) {
+		pt_debug(cd->dev, DL_ERROR, "%s: PIP2 STATUS command rc = %d\n",
+		__func__, rc);
+		return 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;
+
+	cd->dut_status.fw_system_mode =
+		read_buf[PIP2_RESP_BODY_OFFSET + 1];
+
+	if (status == PIP2_RSP_ERR_NONE && boot == 0x00)
+		cd->dut_status.mode = PT_MODE_BOOTLOADER;
+	else if (status == PIP2_RSP_ERR_NONE && boot == 0x01) {
+		cd->dut_status.mode = PT_MODE_OPERATIONAL;
+		cd->dut_status.protocol_mode =
+			read_buf[PIP2_RESP_BODY_OFFSET + 2];
+	} else
+		cd->dut_status.mode = PT_MODE_UNKNOWN;
+
+	return rc;
+}
+
+/*******************************************************************************
+ * 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)
+{
+	int rc = 0;
+
+	rc = pt_pip2_get_status_(cd);
+	if (!rc) {
+		if (sys_mode)
+			*sys_mode = cd->dut_status.fw_system_mode;
+		if (mode)
+			*mode = cd->dut_status.mode;
+	} 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;
+	struct pt_hid_cmd hid_cmd = {
+		.descriptor = cd->hid_core.hid_desc_register,
+		.read_length = 0,
+	};
+	int rc = 0;
+	u16 hid_len;
+
+	/*
+	 * 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);
+
+	rc = pt_hid_send_command(cd, &hid_cmd);
+	if (rc) {
+		pt_debug(dev, DL_ERROR,
+			"%s: failed to get HID descriptor, rc=%d\n",
+			__func__, rc);
+		goto exit;
+	}
+
+	hid_len = get_unaligned_le16(&cd->response_buf[0]);
+	pt_pr_buf(cd->dev, DL_DEBUG, cd->response_buf, hid_len, "<<< HIDDesc");
+
+	/*
+	 * HID doesn't have packet_id and reserve_byte in struct struct
+	 * pt_hid_desc and assign fixed value to packet_id.
+	 */
+	if (cd->dut_status.protocol_mode == PT_PROTOCOL_MODE_HID) {
+		if ((hid_len + 2) != sizeof(*desc)) {
+			pt_debug(dev, DL_ERROR, "%s: Unsupported HID length: %X\n",
+				__func__, hid_len);
+			rc = -ENODEV;
+			goto exit;
+		}
+		/* Copy length filed */
+		memcpy((u8 *)desc, &cd->response_buf[0], 2);
+		/* Assign fixed value to packet_id */
+		desc->packet_id = HID_APP_REPORT_ID;
+		/* Skip 2 bytes in desc and Copy to left fields */
+		memcpy((u8 *)desc + 4, &cd->response_buf[2], hid_len - 2);
+	} else {
+		if (hid_len != sizeof(*desc)) {
+			pt_debug(dev, DL_ERROR, "%s: Unsupported HID length: %X\n",
+				__func__, hid_len);
+			rc = -ENODEV;
+			goto exit;
+		}
+
+		/* Load the HID descriptor including all registers */
+		memcpy((u8 *)desc, cd->response_buf, hid_len);
+	}
+
+	/* 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));
+
+	cd->hid_core.hid_report_desc_len =
+	    le16_to_cpu(desc->report_desc_len);
+
+	pt_debug(dev, DL_INFO, "%s: report descriptor len:%d\n", __func__,
+		cd->hid_core.hid_report_desc_len);
+
+	if (le16_to_cpu(desc->bcd_version) != HID_VERSION) {
+		pt_debug(dev, DL_ERROR, "%s: Unsupported HID version\n",
+			__func__);
+		rc = -ENODEV;
+		goto exit;
+	}
+
+#ifdef TTHE_TUNER_SUPPORT
+	tthe_print(cd, cd->response_buf, hid_len, "HIDDesc=");
+#endif
+
+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;
+
+	/* protocol_mode must be known before dut gen */
+	rc = pt_pip2_get_status_(cd);
+	if (rc) {
+		/* PIP prot assumed if error */
+		cd->dut_status.protocol_mode = PT_PROTOCOL_MODE_PIP;
+	}
+
+	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;
+	}
+
+	/* 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;
+	si->desc.pen_report_id = PT_HID_PEN_REPORT_ID;
+	si->desc.max_touch_num = MAX_TOUCH_NUM;
+
+	cd->features.easywake = 1;
+	cd->features.noise_metric = 1;
+	cd->features.tracking_heatmap = 1;
+	cd->features.sensor_data = 1;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_get_hid_report_
+ *
+ * SUMMARY: Get or create report. Must be called with cd->hid_report_lock
+ *   acquired.
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *  *cd          - pointer to core data structure
+ *  *index       - pointer to report index
+ *   report_type - type of hid report
+ *   report_id   - id of hid report
+ *   create      - true: will create a new report
+ *                 false: will not create a new report
+ ******************************************************************************/
+static int pt_get_hid_report_(struct pt_core_data *cd, u8 *index,
+		u8 report_type, u8 report_id, bool create)
+{
+	struct pt_hid_report *report = NULL;
+	int i;
+	int rc = 0;
+
+	/* Look for created reports */
+	for (i = 0; i < cd->num_hid_reports; i++) {
+		if (cd->hid_reports[i]->type == report_type &&
+		    cd->hid_reports[i]->id == report_id) {
+			*index = i;
+			goto exit;
+		}
+	}
+
+	if (create && cd->num_hid_reports >= PT_HID_MAX_REPORTS) {
+		pt_debug(cd->dev, DL_WARN,
+			"%s: num_hid_reports=%d max=%d\n", __func__,
+			cd->num_hid_reports, PT_HID_MAX_REPORTS);
+		rc = -EINVAL;
+	} else if (create && cd->num_hid_reports < PT_HID_MAX_REPORTS) {
+		/* Create a new report */
+		report = kzalloc(sizeof(struct pt_hid_report),
+				GFP_KERNEL);
+		if (!report)
+			rc = -ENOMEM;
+		else {
+			report->type = report_type;
+			report->id = report_id;
+			*index = cd->num_hid_reports;
+			cd->hid_reports[cd->num_hid_reports++] = report;
+		}
+	}
+
+exit:
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_free_hid_reports_
+ *
+ * SUMMARY: Free HID report. Must be called with cd->hid_report_lock acquired.
+ *
+ * PARAMETERS:
+ *  *cd  - pointer to core data structure
+ ******************************************************************************/
+static void pt_free_hid_reports_(struct pt_core_data *cd)
+{
+	struct pt_hid_report *report;
+	int i, j;
+
+	for (i = 0; i < cd->num_hid_reports; i++) {
+		report = cd->hid_reports[i];
+		for (j = 0; j < report->num_fields; j++)
+			kfree(report->fields[j]);
+		kfree(report);
+		cd->hid_reports[i] = NULL;
+	}
+
+	cd->num_hid_reports = 0;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_free_hid_reports
+ *
+ * SUMMARY: Protected call to pt_free_hid_reports_() by a mutex lock.
+ *
+ * PARAMETERS:
+ *  *cd  - pointer to core data structure
+ ******************************************************************************/
+static void pt_free_hid_reports(struct pt_core_data *cd)
+{
+	mutex_lock(&cd->hid_report_lock);
+	pt_free_hid_reports_(cd);
+	mutex_unlock(&cd->hid_report_lock);
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_create_hid_field_
+ *
+ * SUMMARY: Create field for HID report.Must be called with cd->hid_report_lock
+ *   acquired.
+ *
+ * RETURN:
+ *	 pointer to hid field structure
+ *
+ * PARAMETERS:
+ *  *report - pointer to hid report structure
+ ******************************************************************************/
+static struct pt_hid_field *pt_create_hid_field_(
+		struct pt_hid_report *report)
+{
+	struct pt_hid_field *field;
+
+	if (!report)
+		return NULL;
+
+	if (report->num_fields == PT_HID_MAX_FIELDS)
+		return NULL;
+
+	field = kzalloc(sizeof(struct pt_hid_field), GFP_KERNEL);
+	if (!field)
+		return NULL;
+
+	field->report = report;
+
+	report->fields[report->num_fields++] = field;
+
+	return field;
+}
+
+/*******************************************************************************
+ * FUNCTION: get_hid_item_data
+ *
+ * SUMMARY: Get hid item data according to the item size.
+ *
+ * RETURN:
+ *	 0 = no data
+ *	!0 = data
+ *
+ * PARAMETERS:
+ *  *data      - pointer to item data
+ *   item_size - the size of the item
+ ******************************************************************************/
+static inline int get_hid_item_data(u8 *data, int item_size)
+{
+	if (item_size == 1)
+		return (int)*data;
+	else if (item_size == 2)
+		return (int)get_unaligned_le16(data);
+	else if (item_size == 4)
+		return (int)get_unaligned_le32(data);
+	return 0;
+}
+
+/*******************************************************************************
+ * FUNCTION: parse_report_descriptor
+ *
+ * SUMMARY: Parse report descriptor.
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *  *cd           - pointer to core data structure
+ *  *report_desc  - pointer to report descriptor structure
+ *  len           - length of the data buffer to be parsed
+ ******************************************************************************/
+static int parse_report_descriptor(struct pt_core_data *cd,
+		u8 *report_desc, size_t len)
+{
+	struct pt_hid_report *report;
+	struct pt_hid_field *field;
+	u8 *buf = report_desc;
+	u8 *end = buf + len;
+	int rc = 0;
+	int offset = 0;
+	int i, j;
+	u8 report_type;
+	u32 up_usage;
+	/* Global items */
+	u8 report_id = 0;
+	u16 usage_page = 0;
+	int report_count = 0;
+	int report_size = 0;
+	int logical_min = 0;
+	int logical_max = 0;
+	/* Local items */
+	u16 usage = 0;
+	/* Main items - Collection stack */
+	u32 collection_usages[PT_HID_MAX_NESTED_COLLECTIONS] = {0};
+	u8 collection_types[PT_HID_MAX_NESTED_COLLECTIONS] = {0};
+	u32 usages_pen[PT_HID_MAX_CONTINUOUS_USAGES] = {0};
+	/* First collection for header, second for report */
+	int logical_collection_count = 0;
+	int app_collection_count = 0;
+	int collection_nest = 0;
+	int usage_cnt = 0;
+	u8 report_index = 0;
+
+	pt_debug(cd->dev, DL_DEBUG, "%s: Report descriptor length: %u\n",
+		__func__, (u32)len);
+
+	mutex_lock(&cd->hid_report_lock);
+	pt_free_hid_reports_(cd);
+
+	while (buf < end) {
+		int item_type;
+		int item_size;
+		int item_tag;
+		u8 *data;
+
+		/* Get Item */
+		item_size = HID_GET_ITEM_SIZE(buf[0]);
+		if (item_size == 3)
+			item_size = 4;
+		item_type = HID_GET_ITEM_TYPE(buf[0]);
+		item_tag = HID_GET_ITEM_TAG(buf[0]);
+
+		data = ++buf;
+		buf += item_size;
+
+		/* Process current item */
+		switch (item_type) {
+		case HID_ITEM_TYPE_GLOBAL:     /* 1 */
+			switch (item_tag) {
+			case HID_GLOBAL_ITEM_TAG_USAGE_PAGE:        /* 0 */
+				if (item_size == 0 || item_size == 4) {
+					rc = -EINVAL;
+					goto exit;
+				}
+				usage_page = (u16)get_hid_item_data(data,
+						item_size);
+				break;
+			case HID_GLOBAL_ITEM_TAG_LOGICAL_MINIMUM:   /* 1 */
+				if (item_size == 0) {
+					rc = -EINVAL;
+					goto exit;
+				}
+				logical_min = get_hid_item_data(data,
+						item_size);
+				break;
+			case HID_GLOBAL_ITEM_TAG_LOGICAL_MAXIMUM:   /* 2 */
+				if (item_size == 0) {
+					rc = -EINVAL;
+					goto exit;
+				}
+				logical_max = get_hid_item_data(data,
+						item_size);
+				break;
+			case HID_GLOBAL_ITEM_TAG_PHYSICAL_MINIMUM:  /* 3 */
+				pt_debug(cd->dev, DL_INFO,
+					"%s: TAG Ignored - Physical Min\n",
+					__func__);
+				break;
+			case HID_GLOBAL_ITEM_TAG_PHYSICAL_MAXIMUM:  /* 4 */
+				pt_debug(cd->dev, DL_INFO,
+					"%s: TAG Ignored - Physical Max\n",
+					__func__);
+				break;
+			case HID_GLOBAL_ITEM_TAG_UNIT_EXPONENT:     /* 5 */
+				pt_debug(cd->dev, DL_INFO,
+					"%s: TAG Ignored - Unit Exponent\n",
+					__func__);
+				break;
+			case HID_GLOBAL_ITEM_TAG_UNIT:              /* 6 */
+				pt_debug(cd->dev, DL_INFO,
+					"%s: TAG Ignored - Unit\n",
+					__func__);
+				break;
+			case HID_GLOBAL_ITEM_TAG_REPORT_SIZE:       /* 7 */
+				if (item_size == 0) {
+					rc = -EINVAL;
+					goto exit;
+				}
+				report_size = get_hid_item_data(data,
+						item_size);
+				break;
+			case HID_GLOBAL_ITEM_TAG_REPORT_ID:         /* 8 */
+				if (item_size != 1) {
+					rc = -EINVAL;
+					goto exit;
+				}
+				report_id = get_hid_item_data(data, item_size);
+				offset = 0;
+				logical_collection_count = 0;
+				break;
+			case HID_GLOBAL_ITEM_TAG_REPORT_COUNT:      /* 9 */
+				if (item_size == 0) {
+					rc = -EINVAL;
+					goto exit;
+				}
+				report_count = get_hid_item_data(data,
+						item_size);
+				break;
+			case HID_GLOBAL_ITEM_TAG_PUSH:              /* A */
+				pt_debug(cd->dev, DL_INFO,
+					"%s: TAG Ignored - Push\n",
+					__func__);
+				break;
+			case HID_GLOBAL_ITEM_TAG_POP:               /* B */
+				pt_debug(cd->dev, DL_INFO,
+					"%s: TAG Ignored - Pop\n",
+					__func__);
+				break;
+			default:
+				pt_debug(cd->dev, DL_INFO,
+					"%s: Unrecognized Global tag 0x%X\n",
+					__func__, item_tag);
+			}
+			break;
+		case HID_ITEM_TYPE_LOCAL:      /* 2 */
+			switch (item_tag) {
+			case HID_LOCAL_ITEM_TAG_USAGE:
+				if (item_size == 0 || item_size == 4) {
+					rc = -EINVAL;
+					goto exit;
+				}
+				usage = (u16)get_hid_item_data(data,
+						item_size);
+				if (report_id == PT_HID_PEN_REPORT_ID) {
+					usages_pen[usage_cnt++] =
+						usage_page << 16 | usage;
+				}
+				break;
+			case HID_LOCAL_ITEM_TAG_USAGE_MINIMUM:
+				if (item_size == 0) {
+					rc = -EINVAL;
+					goto exit;
+				}
+				/* usage_min = */
+				get_hid_item_data(data, item_size);
+				break;
+			case HID_LOCAL_ITEM_TAG_USAGE_MAXIMUM:
+				if (item_size == 0) {
+					rc = -EINVAL;
+					goto exit;
+				}
+				/* usage_max = */
+				get_hid_item_data(data, item_size);
+				break;
+			default:
+				pt_debug(cd->dev, DL_INFO,
+					"%s: Unrecognized Local tag %d\n",
+					__func__, item_tag);
+			}
+			break;
+		case HID_ITEM_TYPE_MAIN:       /* 0 */
+			switch (item_tag) {
+			case HID_MAIN_ITEM_TAG_BEGIN_COLLECTION:
+				usage_cnt = 0;
+
+				if (item_size != 1) {
+					rc = -EINVAL;
+					goto exit;
+				}
+				if (PT_HID_MAX_NESTED_COLLECTIONS ==
+						collection_nest) {
+					rc = -EINVAL;
+					goto exit;
+				}
+
+				up_usage = usage_page << 16 | usage;
+
+				/* Update collection stack */
+				collection_usages[collection_nest] = up_usage;
+				collection_types[collection_nest] =
+					get_hid_item_data(data, item_size);
+
+				if (collection_types[collection_nest] ==
+					HID_COLLECTION_LOGICAL) {
+					logical_collection_count++;
+					app_collection_count = 0;
+				}
+				if (collection_types[collection_nest] ==
+					HID_COLLECTION_APPLICATION)
+					app_collection_count++;
+
+				collection_nest++;
+				break;
+			case HID_MAIN_ITEM_TAG_END_COLLECTION:
+				if (item_size != 0) {
+					rc = -EINVAL;
+					goto exit;
+				}
+				if (--collection_nest < 0) {
+					rc = -EINVAL;
+					goto exit;
+				}
+				break;
+			case HID_MAIN_ITEM_TAG_INPUT:
+				report_type = HID_INPUT_REPORT;
+				goto continue_main_item;
+			case HID_MAIN_ITEM_TAG_OUTPUT:
+				report_type = HID_OUTPUT_REPORT;
+				goto continue_main_item;
+			case HID_MAIN_ITEM_TAG_FEATURE:
+				report_type = HID_FEATURE_REPORT;
+continue_main_item:
+				if (item_size != 1) {
+					pt_debug(cd->dev, DL_WARN,
+						"%s: %s=%d\n",
+						__func__,
+						"item_size", item_size);
+					rc = -EINVAL;
+					goto exit;
+				}
+
+				up_usage = usage_page << 16 | usage;
+
+				/* Get or create report */
+				rc = pt_get_hid_report_(cd, &report_index,
+					report_type, report_id, true);
+				if (rc) {
+					pt_debug(cd->dev, DL_WARN,
+						"%s: %s rc=%d\n",
+						__func__,
+						"get_hid_report failed", rc);
+					goto exit;
+				} else
+					report = cd->hid_reports[report_index];
+
+				if (!report->usage_page && collection_nest > 0)
+					report->usage_page =
+						collection_usages
+							[collection_nest - 1];
+
+				if (report->id == PT_HID_PEN_REPORT_ID
+					&& report_count > 1)
+					goto continue_pen_usages;
+
+				/* Create field */
+				field = pt_create_hid_field_(report);
+				if (!field) {
+					pt_debug(cd->dev, DL_WARN,
+						"%s: %s\n",
+						__func__,
+						"create field failed");
+					rc = -ENOMEM;
+					goto exit;
+				}
+
+				field->report_count = report_count;
+				field->report_size = report_size;
+				field->size = report_count
+					* report_size;
+				field->offset = offset;
+				field->data_type =
+					get_hid_item_data(data,
+						item_size);
+				field->logical_min = logical_min;
+				field->logical_max = logical_max;
+				field->usage_page = up_usage;
+				usages_pen[0] = 0;
+				for (j = 0; j < collection_nest; j++) {
+					field->collection_usage_pages
+						[collection_types[j]] =
+						collection_usages[j];
+				}
+
+				goto exit_main_item;
+continue_pen_usages:
+				for (i = 0; i < report_count; i++) {
+					/* Create field */
+					field = pt_create_hid_field_(
+						report);
+					if (!field) {
+						pt_debug(cd->dev, DL_WARN,
+							"%s: Pen - %s\n",
+							__func__,
+							"create field failed");
+						rc = -ENOMEM;
+						goto exit;
+					}
+
+					field->report_size = report_size;
+					field->size = report_count *
+						report_size;
+					field->report_count = 1;
+					field->offset = offset + i*report_size;
+					field->data_type =
+						get_hid_item_data(data,
+							item_size);
+					field->logical_min = logical_min;
+					field->logical_max = logical_max;
+					field->usage_page = usages_pen[i];
+					usages_pen[i] = 0;
+					for (j = 0; j < collection_nest; j++) {
+						field->collection_usage_pages
+						[collection_types[j]] =
+						collection_usages[j];
+					}
+				}
+
+exit_main_item:
+				/* Update report's header or record size */
+				if (app_collection_count == 1) {
+					report->header_size += field->size;
+				} else if (logical_collection_count == 1) {
+					field->record_field = true;
+					field->offset -= report->header_size;
+					/* Set record field index */
+					if (report->record_field_index == 0)
+						report->record_field_index =
+							report->num_fields - 1;
+					report->record_size += field->size;
+				}
+
+				report->size += field->size;
+				report->log_collection_num =
+					logical_collection_count;
+
+				offset += field->size;
+
+				usage_cnt = 0;
+				break;
+			default:
+				pt_debug(cd->dev, DL_INFO,
+					"%s: Unrecognized Main tag %d\n",
+					__func__, item_tag);
+			}
+
+			/* Reset all local items */
+			usage = 0;
+			break;
+		}
+	}
+
+	if (buf != end) {
+		pt_debug(cd->dev, DL_ERROR,
+			"%s: Report descriptor length invalid\n",
+			__func__);
+		rc = -EINVAL;
+		goto exit;
+	}
+
+	if (collection_nest) {
+		pt_debug(cd->dev, DL_ERROR,
+			"%s: Unbalanced collection items (%d)\n",
+			__func__, collection_nest);
+		rc = -EINVAL;
+		goto exit;
+	}
+
+exit:
+	if (rc)
+		pt_free_hid_reports_(cd);
+	mutex_unlock(&cd->hid_report_lock);
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: find_report_desc_field
+ *
+ * SUMMARY: Find the corresponding field from report according to the usage page
+ *    and collection usage page.
+ *
+ * RETURN:
+ *	 pointer to hid field structure
+ *
+ * PARAMETERS:
+ *  *cd                   - pointer to core data structure
+ *  usage_page            - hid usage page
+ *  collection_usage_page - hid collection usage page
+ *  collection_type       - hid collection type
+ ******************************************************************************/
+static struct pt_hid_field *find_report_desc_field(
+		struct pt_core_data *cd, u32 usage_page,
+		u32 collection_usage_page, u8 collection_type)
+{
+	struct pt_hid_report *report = NULL;
+	struct pt_hid_field *field = NULL;
+	int i;
+	int j;
+	u32 field_cup;
+	u32 field_up;
+
+	for (i = 0; i < cd->num_hid_reports; i++) {
+		report = cd->hid_reports[i];
+		for (j = 0; j < report->num_fields; j++) {
+			field = report->fields[j];
+			field_cup = field->collection_usage_pages
+				[collection_type];
+			field_up = field->usage_page;
+			if (field_cup == collection_usage_page
+					&& field_up == usage_page) {
+				return field;
+			}
+		}
+	}
+
+	return NULL;
+}
+
+/*******************************************************************************
+ * FUNCTION: fill_tch_abs
+ *
+ * SUMMARY: Fill touch abs with hid field.
+ *
+ * PARAMETERS:
+ *  *cd    - pointer to core data structure
+ *  *field - pointer to hid field structure
+ ******************************************************************************/
+static void fill_tch_abs(struct pt_tch_abs_params *tch_abs,
+		struct pt_hid_field *field)
+{
+	tch_abs->ofs = field->offset / 8;
+	tch_abs->size = field->report_size / 8;
+	if (field->report_size % 8)
+		tch_abs->size += 1;
+	tch_abs->min = 0;
+	tch_abs->max = 1 << field->report_size;
+	tch_abs->bofs = field->offset - (tch_abs->ofs << 3);
+	tch_abs->logical_max = field->logical_max;
+}
+
+/*******************************************************************************
+ * FUNCTION: find_report_desc
+ *
+ * SUMMARY: Find out the corresponding report based on the usage page.
+ *
+ * RETURN:
+ *	 pointer to hid report structure
+ *
+ * PARAMETERS:
+ *  *cd        - pointer to core data structure
+ *  id         - report id
+ *  usage_page - hid usage page
+ ******************************************************************************/
+static struct pt_hid_report *find_report_desc(struct pt_core_data *cd,
+		u8 id, u32 usage_page)
+{
+	struct pt_hid_report *report = NULL;
+	int i;
+
+	for (i = 0; i < cd->num_hid_reports; i++) {
+		if (cd->hid_reports[i]->usage_page == usage_page &&
+			cd->hid_reports[i]->id == id) {
+			report = cd->hid_reports[i];
+			break;
+		}
+	}
+
+	return report;
+}
+
+/*******************************************************************************
+ * FUNCTION: setup_pen_report_from_report_desc
+ *
+ * SUMMARY: Setup values for pen report according to report descriptor.
+ *
+ * PARAMETERS:
+ *  *cd  - pointer to core data structure
+ ******************************************************************************/
+static void setup_pen_report_from_report_desc(
+	struct pt_core_data *cd)
+{
+	struct pt_sysinfo *si = &cd->sysinfo;
+	struct pt_hid_report *report;
+	struct pt_hid_field *field;
+	int i;
+	u32 pen_collection_usage_page = HID_PT_PEN_COL_USAGE_PG;
+	u8 id = PT_HID_PEN_REPORT_ID;
+
+	/*
+	 * Search each pen abs field. If found, fill the values into the
+	 * pen struct.If not, mark this pen field as invalid (report = 0).
+	 */
+	for (i = PT_PEN_X; i < PT_PEN_NUM_ABS; i++) {
+		field = find_report_desc_field(cd,
+				pt_pen_abs_field_map[i],
+				pen_collection_usage_page,
+				HID_COLLECTION_PHYSICAL);
+		if (field) {
+			pt_debug(cd->dev, DL_DEBUG,
+				" Field %p: rep_cnt:%d rep_sz:%d off:%d data:%02X min:%d max:%d usage_page:%08X\n",
+				field, field->report_count, field->report_size,
+				field->offset, field->data_type,
+				field->logical_min, field->logical_max,
+				field->usage_page);
+			fill_tch_abs(&si->pen_abs[i], field);
+			si->pen_abs[i].report = 2;
+			pt_debug(cd->dev, DL_DEBUG, "%s: ofs:%u size:%u min:%u max:%u bofs:%u report:%d",
+				pt_pen_abs_string[i],
+				(u32)si->pen_abs[i].ofs,
+				(u32)si->pen_abs[i].size,
+				(u32)si->pen_abs[i].min,
+				(u32)si->pen_abs[i].max,
+				(u32)si->pen_abs[i].bofs,
+				si->pen_abs[i].report);
+
+		} else {
+			si->pen_abs[i].report = 0;
+			pt_debug(cd->dev, DL_DEBUG, "%s: report:%d\n",
+				pt_pen_abs_string[i], si->pen_abs[i].report);
+		}
+	}
+
+	report = find_report_desc(cd, id, pen_collection_usage_page);
+	if (report)
+		si->desc.pen_report_id = report->id;
+	else
+		si->desc.pen_report_id = PT_HID_PEN_REPORT_ID;
+}
+
+/*******************************************************************************
+ * FUNCTION: setup_finger_report_from_report_desc
+ *
+ * SUMMARY: Setup values for finger report according to report descriptor.
+ *
+ * PARAMETERS:
+ *  *cd  - pointer to core data structure
+ ******************************************************************************/
+static void setup_finger_report_from_report_desc(
+	struct pt_core_data *cd)
+{
+	struct pt_sysinfo *si = &cd->sysinfo;
+	struct pt_hid_report *report;
+	struct pt_hid_field *field;
+	u32 tch_collection_usage_page = HID_PT_TCH_COL_USAGE_PG;
+	u8 id = PT_HID_FINGER_REPORT_ID;
+	int i;
+
+	/*
+	 * Search each touch abs field. If found, fill the values into the
+	 * abs struct. If not, mark this abs field as invalid (report = 0).
+	 */
+	for (i = PT_TCH_X; i < PT_TCH_NUM_ABS; i++) {
+		field = find_report_desc_field(cd,
+				pt_tch_abs_field_map[i],
+				tch_collection_usage_page,
+				HID_COLLECTION_APPLICATION);
+		if (field) {
+			pt_debug(cd->dev, DL_DEBUG,
+				" Field %p: rep_cnt:%d rep_sz:%d off:%d data:%02X min:%d max:%d usage_page:%08X\n",
+				field, field->report_count, field->report_size,
+				field->offset, field->data_type,
+				field->logical_min, field->logical_max,
+				field->usage_page);
+			fill_tch_abs(&si->tch_abs[i], field);
+			si->tch_abs[i].report = 1;
+			pt_debug(cd->dev, DL_DEBUG, "%s: ofs:%u size:%u min:%u max:%u bofs:%u report:%d",
+				pt_tch_abs_string[i],
+				(u32)si->tch_abs[i].ofs,
+				(u32)si->tch_abs[i].size,
+				(u32)si->tch_abs[i].min,
+				(u32)si->tch_abs[i].max,
+				(u32)si->tch_abs[i].bofs,
+				si->tch_abs[i].report);
+		} else {
+			si->tch_abs[i].report = 0;
+		}
+	}
+
+	/*
+	 * Search each touch header field. If found, fill the values into
+	 * the header struct. If not, mark this header field as invalid
+	 * (report = 0).
+	 */
+	for (i = PT_TCH_TIME; i < PT_TCH_NUM_HDR; i++) {
+		field = find_report_desc_field(cd,
+				pt_tch_hdr_field_map[i],
+				tch_collection_usage_page,
+				HID_COLLECTION_APPLICATION);
+		if (field) {
+			pt_debug(cd->dev, DL_DEBUG,
+				" Field %p: rep_cnt:%d rep_sz:%d off:%d data:%02X min:%d max:%d usage_page:%08X\n",
+				field, field->report_count, field->report_size,
+				field->offset, field->data_type,
+				field->logical_min, field->logical_max,
+				field->usage_page);
+			fill_tch_abs(&si->tch_hdr[i], field);
+			si->tch_hdr[i].report = 1;
+			pt_debug(cd->dev, DL_DEBUG, "%s: ofs:%u size:%u min:%u max:%u bofs:%u report:%d",
+				pt_tch_hdr_string[i],
+				(u32)si->tch_hdr[i].ofs,
+				(u32)si->tch_hdr[i].size,
+				(u32)si->tch_hdr[i].min,
+				(u32)si->tch_hdr[i].max,
+				(u32)si->tch_hdr[i].bofs,
+				si->tch_hdr[i].report);
+		} else {
+			si->tch_hdr[i].report = 0;
+		}
+	}
+
+	si->desc.max_touch_num = si->tch_hdr[PT_TCH_NUM].max;
+
+	report = find_report_desc(cd, id, tch_collection_usage_page);
+	if (report) {
+		si->desc.tch_report_id = report->id;
+		/* First, report->record_size and report->header_size are based
+		 * on 'BIT', they need to be divided by 8 to get tch_record_size
+		 * and tch_header_size, which are based on 'BYTE'.
+		 * Second, tch_header_size needs to add 3 bytes: 2 bytes length,
+		 * 1 byte report id.
+		 */
+		si->desc.tch_record_size = report->record_size / 8;
+		si->desc.tch_header_size = (report->header_size / 8) + 3;
+		si->desc.max_tch_per_packet = report->log_collection_num;
+	} else {
+		si->desc.tch_report_id = PT_HID_FINGER_REPORT_ID;
+		si->desc.tch_record_size = TOUCH_REPORT_SIZE;
+		si->desc.tch_header_size = TOUCH_INPUT_HEADER_SIZE;
+		si->desc.max_tch_per_packet = MAX_TOUCH_NUM;
+	}
+
+	pt_debug(cd->dev, DL_DEBUG,
+		"%s: tch_record_size:%d, tch_header_size:%d, max_touch_num:%d\n",
+		__func__,
+		si->desc.tch_record_size,
+		si->desc.tch_header_size,
+		si->desc.max_touch_num);
+}
+
+/*******************************************************************************
+ * FUNCTION: publish_report_desc
+ *
+ * SUMMARY: If TTHE_TUNER_SUPPORT is defined print the report descriptor data
+ *	into the tthe_tuner sysfs node under the label "HIDRptDesc".
+ *
+ * PARAMETERS:
+ *	*cd  - pointer to core data
+ *	 len - Report descriptor length
+ ******************************************************************************/
+static void publish_report_desc(struct pt_core_data *cd, u16 len)
+{
+	int max_bytes = 300;
+	int pr_bytes = 0;
+	int size = len;
+
+#ifdef TTHE_TUNER_SUPPORT
+	tthe_print(cd, cd->response_buf, size,
+		"HIDRptDesc=");
+#endif
+
+	while (size > max_bytes) {
+		pt_pr_buf(cd->dev, DL_DEBUG,
+			cd->response_buf + pr_bytes,
+			max_bytes, "<<< HIDRptDesc");
+		pr_bytes += max_bytes;
+		size -= max_bytes;
+	}
+	if (size > 0)
+		pt_pr_buf(cd->dev, DL_DEBUG,
+			cd->response_buf + pr_bytes,
+			size, "<<< HIDRptDesc");
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_get_report_descriptor_
+ *
+ * SUMMARY: Get and parse report descriptor.
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *	*cd - pointer to core data
+ ******************************************************************************/
+static int pt_get_report_descriptor_(struct pt_core_data *cd)
+{
+	struct device *dev = cd->dev;
+	struct pt_hid_cmd hid_cmd = {
+		.descriptor = cd->hid_desc.report_desc_register,
+		.read_length = cd->hid_core.hid_report_desc_len,
+	};
+	int rc;
+	int t;
+	u8 *desc;
+	u16 desc_len;
+
+	rc = pt_hid_send_command(cd, &hid_cmd);
+	if (rc) {
+		pt_debug(dev, DL_ERROR,
+			"%s: failed to get HID report descriptor length, rc=%d\n",
+			__func__, rc);
+		goto exit;
+	}
+
+	desc = cd->response_buf;
+	desc_len = cd->hid_core.hid_report_desc_len;
+
+	/* Remove length field and report id to prepare parse work */
+	if (cd->dut_status.protocol_mode != PT_PROTOCOL_MODE_HID) {
+		desc = cd->response_buf + 3;
+		desc_len -= 3;
+	}
+
+	publish_report_desc(cd, cd->hid_core.hid_report_desc_len);
+
+	rc = parse_report_descriptor(cd, desc, desc_len);
+	if (rc) {
+		pt_debug(cd->dev, DL_ERROR,
+			"%s: Error parsing report descriptor rc=%d\n",
+			__func__, rc);
+	}
+
+	pt_debug(cd->dev, DL_INFO, "%s: %d reports found in descriptor\n",
+		__func__, cd->num_hid_reports);
+
+	for (t = 0; t < cd->num_hid_reports; t++) {
+		struct pt_hid_report *report = cd->hid_reports[t];
+		int j;
+
+		pt_debug(cd->dev, DL_DEBUG,
+			"Report %d: type:%d id:%02X size:%d fields:%d rec_fld_index:%d hdr_sz:%d rec_sz:%d usage_page:%08X\n",
+			t, report->type, report->id,
+			report->size, report->num_fields,
+			report->record_field_index, report->header_size,
+			report->record_size, report->usage_page);
+
+		if (report->id == PT_HID_FINGER_REPORT_ID)
+			pt_debug(cd->dev, DL_INFO, "%s: logical collection number: %d\n",
+				__func__, report->log_collection_num);
+
+		for (j = 0; j < report->num_fields; j++) {
+			struct pt_hid_field *field = report->fields[j];
+
+			pt_debug(cd->dev, DL_DEBUG,
+				" Field %d: rep_cnt:%d rep_sz:%d off:%d data:%02X min:%d max:%d usage_page:%08X\n",
+				j, field->report_count, field->report_size,
+				field->offset, field->data_type,
+				field->logical_min, field->logical_max,
+				field->usage_page);
+
+			pt_debug(cd->dev, DL_DEBUG, "  Collections Phys:%08X App:%08X Log:%08X\n",
+				field->collection_usage_pages
+					[HID_COLLECTION_PHYSICAL],
+				field->collection_usage_pages
+					[HID_COLLECTION_APPLICATION],
+				field->collection_usage_pages
+					[HID_COLLECTION_LOGICAL]);
+		}
+	}
+
+	setup_pen_report_from_report_desc(cd);
+	setup_finger_report_from_report_desc(cd);
+
+	/* Free it for now */
+	pt_free_hid_reports_(cd);
+
+	pt_debug(cd->dev, DL_INFO, "%s: %d reports found in descriptor\n",
+		__func__, cd->num_hid_reports);
+
+exit:
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_get_report_descriptor
+ *
+ * SUMMARY: Protected call to pt_get_report_descriptor_()
+ *
+ * RETURN:
+ *   0 = success
+ *  !0 = failure
+ *
+ * PARAMETERS:
+ *  *cd   - pointer to core data
+ ******************************************************************************/
+static int pt_get_report_descriptor(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_get_report_descriptor_(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_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;
+
+	mutex_lock(&cd->system_lock);
+	cd->wait_until_wake = 0;
+	mutex_unlock(&cd->system_lock);
+
+	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;
+
+	/*
+	 * Do nothing if system already sleeping or in progress of
+	 * entering sleep. Proceed if awake or waking.
+	 */
+	if (cd->sleep_state == SS_SLEEP_ON || cd->sleep_state == SS_SLEEPING)
+		goto exit;
+
+	mutex_lock(&cd->system_lock);
+	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 (IS_EASY_WAKE_CONFIGURED(cd->easy_wakeup_gesture))
+		rc = pt_put_device_into_easy_wakeup_(cd);
+	else if (cd->cpdata->flags & PT_CORE_FLAG_POWEROFF_ON_SLEEP)
+		rc = pt_core_poweroff_device_(cd);
+	else if (cd->cpdata->flags & PT_CORE_FLAG_DEEP_STANDBY)
+		rc = pt_put_device_into_deep_standby_(cd);
+	else
+		rc = pt_put_device_into_deep_sleep_(cd);
+
+	mutex_lock(&cd->system_lock);
+	if (rc == 0) {
+		cd->sleep_state = SS_SLEEP_ON;
+	} else {
+		cd->sleep_state = SS_SLEEP_OFF;
+		pt_start_wd_timer(cd);
+	}
+	mutex_unlock(&cd->system_lock);
+
+exit:
+	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_field
+ *
+ * SUMMARY: Function to calculate touch fields. The field refers to each element
+ *     in the input report, such as "Number of Records", "X-axis". This function
+ *     can calculate the value of element based on the bit offset, size and the
+ *     max value of the element.
+ *
+ * PARAMETERS:
+ *     *dev     - pointer to device structure
+ *     *field   - pointer to field calculation result
+ *      size    - size in bytes
+ *      max     - max value of result
+ *     *data    - pointer to input data to be parsed
+ *      bofs    - bit offset
+ ******************************************************************************/
+void pt_get_touch_field(struct device *dev,
+	int *field, int size, int max, u8 *data, int bofs)
+{
+	int nbyte;
+	int next;
+
+	for (nbyte = 0, *field = 0, next = 0; nbyte < size; nbyte++) {
+		pt_debug(dev, DL_DEBUG,
+			"%s: *field=%02X(%d) size=%d max=%08X data=%p data[%d]=%02X(%d) bofs=%d\n",
+			__func__, *field, *field, size, max, data, next,
+			data[next], data[next], bofs);
+		*field = *field + ((data[next] >> bofs) << (nbyte * 8));
+		next++;
+	}
+
+	if (max > 0)
+		*field &= max - 1;
+
+	pt_debug(dev, DL_DEBUG,
+		"%s: *field=%02X(%d) size=%d max=%08X data=%p data[%d]=%02X(%d)\n",
+		__func__, *field, *field, size, max, data, next,
+		data[next], data[next]);
+}
+
+/*******************************************************************************
+ * 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_pip
+ *
+ * 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_pip(struct pt_core_data *cd, struct pt_sysinfo *si)
+{
+	int max_tch = si->sensing_conf_data.max_tch;
+	int num_cur_tch = 0;
+	int length;
+	int actual_tch_num;
+	struct pt_tch_abs_params *tch = &si->tch_hdr[PT_TCH_NUM];
+
+	int size = get_unaligned_le16(&cd->input_buf[0]);
+#ifdef TTHE_TUNER_SUPPORT
+	if (size > 0)
+		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_field(cd->dev, &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;
+
+	actual_tch_num = (size - si->desc.tch_header_size)
+		/ si->desc.tch_record_size;
+
+	if (actual_tch_num < num_cur_tch) {
+		pt_debug(cd->dev, DL_ERROR,
+			"%s: ATM - Malformed touch packet. actual_tch_num=%d, num_cur_tch=%d\n",
+			__func__, actual_tch_num, num_cur_tch);
+		num_cur_tch = actual_tch_num;
+	}
+
+	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_touch_data_hid
+ *
+ * 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 "HID-USB", "HID-I2C" or
+ *  "OpModeData"
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *	*cd - pointer to core data
+ *	*si - pointer to the system information structure
+ ******************************************************************************/
+static int move_touch_data_hid(struct pt_core_data *cd,
+	struct pt_sysinfo *si)
+{
+	int max_tch = si->sensing_conf_data.max_tch;
+	int num_cur_tch = 0;
+	u16 hdr_sz;
+	u16 rec_sz;
+	int max_tch_per_packet;
+	static u8 remain_tch;
+	static u8 packet_no;
+	static u8 input_sz;
+	int length;
+	int rc = 0;
+	struct pt_tch_abs_params *tch = &si->tch_hdr[PT_TCH_NUM];
+	int size = get_unaligned_le16(&cd->input_buf[0]);
+
+	hdr_sz = si->desc.tch_header_size;
+	rec_sz = si->desc.tch_record_size;
+	max_tch_per_packet = si->desc.max_tch_per_packet;
+
+#ifdef TTHE_TUNER_SUPPORT
+	length = hdr_sz + rec_sz * max_tch_per_packet;
+	pt_debug(cd->dev, DL_DEBUG,
+		"%s: touch report size=%d, len=%d, record=%d, max_tch=%d\n",
+		__func__, size, length, rec_sz, max_tch_per_packet);
+	/*
+	 * 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_TTHE_TUNER_FORMAT_HID_USB)
+		tthe_print(cd, &(cd->input_buf[2]), length - 2,
+			"HID-USB=");
+	else if (cd->tthe_hid_usb_format ==
+		PT_TTHE_TUNER_FORMAT_HID_I2C)
+		tthe_print(cd, &(cd->input_buf[0]), length,
+			"HID-I2C=");
+#endif
+
+	memcpy(si->xy_mode, cd->input_buf, hdr_sz);
+	pt_pr_buf(cd->dev, DL_INFO, (u8 *)si->xy_mode,
+		hdr_sz, "xy_mode");
+
+	/*
+	 * The fifth parameter points to the location of "Number of Records":
+	 * Pointer "xy_mode" points to start address of touch report header.
+	 * The byte offset of "Number of Records" is 2 (tch->ofs, retrieved
+	 * from report descriptor). Then the pointer "xy_mode + 3 (skip the
+	 * two bytes length and 1 byte report ID) + tch->ofs" points to the
+	 * location of "Number of Records".
+	 */
+	pt_get_touch_field(cd->dev, &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;
+
+	/* According to Hybrid Mode touch report defined by Microsoft,
+	 * if the touch number exceeds the max touch per packet (defined
+	 * in report descriptor), the touch packet will be broken up into
+	 * to multiple reports. Timestamp will be consistent across all
+	 * touches in a single frame. The "Number of records" will have the
+	 * total in the first report and be 0 in all subsequent reports that
+	 * belong to the same frame.
+	 */
+	pt_debug(cd->dev, DL_INFO, "%s: max_tch=%d, packet_no=%d, num_cur_tch=%d\n",
+		__func__, max_tch, packet_no, num_cur_tch);
+
+	if (packet_no == 0) {
+		input_sz = hdr_sz + num_cur_tch * rec_sz;
+		memset(cd->touch_buf, 0, sizeof(cd->touch_buf));
+		memcpy(cd->touch_buf, cd->input_buf, input_sz);
+		if (num_cur_tch > max_tch_per_packet) {
+			remain_tch = num_cur_tch - max_tch_per_packet;
+			packet_no++;
+			rc = -EINVAL;
+			goto exit;
+		}
+	} else {
+		if (remain_tch <= max_tch_per_packet) {
+			memcpy(&cd->touch_buf[hdr_sz +
+				max_tch_per_packet * packet_no * rec_sz],
+				&(cd->input_buf[hdr_sz]),
+				rec_sz * remain_tch);
+			remain_tch = 0;
+			packet_no = 0;
+			rc = 0;
+		} else {
+			memcpy(&cd->touch_buf[hdr_sz +
+				max_tch_per_packet * packet_no * rec_sz],
+				&(cd->input_buf[hdr_sz]),
+				rec_sz * max_tch_per_packet);
+			remain_tch -= max_tch_per_packet;
+			packet_no++;
+			rc = -EINVAL;
+			goto exit;
+		}
+	}
+
+#ifdef TTHE_TUNER_SUPPORT
+	/* Update pip packet length */
+	cd->touch_buf[0] = input_sz & 0xff;
+	cd->touch_buf[1] = (input_sz & 0xff00) >> 8;
+
+	/*
+	 * For PT_TTHE_TUNER_FORMAT_HID_FINGER_TO_PIP and
+	 * PT_TTHE_TUNER_FORMAT_HID_FINGER_AND_PEN_TO_PIP mode, all touches
+	 * should be combined into a single row for the tthe_tuner node.
+	 */
+	if (cd->tthe_hid_usb_format ==
+		PT_TTHE_TUNER_FORMAT_HID_FINGER_TO_PIP ||
+		cd->tthe_hid_usb_format ==
+		PT_TTHE_TUNER_FORMAT_HID_FINGER_AND_PEN_TO_PIP)
+		tthe_print(cd, cd->touch_buf, input_sz,
+			"OpModeData=");
+#endif
+
+	memcpy(si->xy_data, &cd->touch_buf[hdr_sz], input_sz - hdr_sz);
+	pt_pr_buf(cd->dev, DL_INFO,
+		(u8 *)si->xy_data, input_sz - hdr_sz, "xy_data");
+
+	input_sz = 0;
+
+exit:
+	return rc;
+}
+
+/*******************************************************************************
+ * 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)
+{
+	int size = get_unaligned_le16(&cd->input_buf[0]);
+
+#ifdef TTHE_TUNER_SUPPORT
+	enum pt_pen_abs abs;
+	struct pt_pen pen;
+	int packet_len = 17;
+	int report_id = 1;
+	static int report_counter;
+	int event_id = 2;
+	int touch_id = 0;
+	int i = 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_TTHE_TUNER_FORMAT_HID_USB)
+			tthe_print(cd, &(cd->input_buf[2]), size - 2,
+				"HID-USB=");
+		else if (cd->tthe_hid_usb_format ==
+			PT_TTHE_TUNER_FORMAT_HID_I2C ||
+			cd->tthe_hid_usb_format ==
+			PT_TTHE_TUNER_FORMAT_HID_FINGER_TO_PIP)
+			tthe_print(cd, &(cd->input_buf[0]), size,
+				"HID-I2C=");
+		else if (cd->tthe_hid_usb_format ==
+			PT_TTHE_TUNER_FORMAT_HID_FINGER_AND_PEN_TO_PIP) {
+			memset(cd->touch_buf, 0, sizeof(cd->touch_buf));
+
+			for (abs = PT_PEN_X; abs < PT_PEN_NUM_ABS; abs++) {
+				if (!si->pen_abs[abs].report)
+					continue;
+				pt_get_touch_field(cd->dev, &pen.abs[abs],
+					si->pen_abs[abs].size,
+					si->pen_abs[abs].max,
+					cd->input_buf + 3 +
+						si->pen_abs[abs].ofs,
+					si->pen_abs[abs].bofs);
+				pt_debug(cd->dev, DL_DEBUG, "%s: get %s=%04X(%d)\n",
+					__func__, pt_pen_abs_string[abs],
+					pen.abs[abs], pen.abs[abs]);
+			}
+			/* pip packet length: 17  */
+			cd->touch_buf[i++] = packet_len & 0xff;
+			cd->touch_buf[i++] = (packet_len & 0xff00) >> 8;
+			/* pip finger report id: 1*/
+			cd->touch_buf[i++] = report_id;
+			/* Timestamp: 0 */
+			cd->touch_buf[i++] = 0;
+			cd->touch_buf[i++] = 0;
+			/* LO: 0; Number of Records: 1*/
+			cd->touch_buf[i++] = 1;
+			/* Report Counter:[0-3]; Noise Effects:0 */
+			cd->touch_buf[i++] = (report_counter & 0x03) << 6;
+			/* Touch Type: Stylus (2)*/
+			cd->touch_buf[i++] = PT_OBJ_STYLUS;
+			/* Tip: pen tip switch; Event ID: 2; Touch ID: 0 */
+			cd->touch_buf[i++] =
+				((pen.abs[PT_PEN_TS] & 0x01) << 7) |
+				((event_id & 0x03) << 5) |
+				(touch_id & 0x1f);
+			/* X: Pen X */
+			cd->touch_buf[i++] = pen.abs[PT_PEN_X] & 0xff;
+			cd->touch_buf[i++] = (pen.abs[PT_PEN_X] & 0xff00) >> 8;
+			/* Y: Pen Y */
+			cd->touch_buf[i++] = pen.abs[PT_PEN_Y] & 0xff;
+			cd->touch_buf[i++] = (pen.abs[PT_PEN_Y] & 0xff00) >> 8;
+			/* Pressure: Pen pressure drop the least 4 bits */
+			cd->touch_buf[i++] = (pen.abs[PT_PEN_P] & 0xff0) >> 4;
+			/* Major: Pen Tilt_X*/
+			cd->touch_buf[i++] = pen.abs[PT_PEN_X_TILT] & 0xff;
+			/* Minor: Pen Tilt_Y*/
+			cd->touch_buf[i++] = pen.abs[PT_PEN_Y_TILT] & 0xff;
+			/* Orientation: Btn2, Btn1*/
+			cd->touch_buf[i++] =
+				(pen.abs[PT_PEN_BS] & 0x01) |
+				((pen.abs[PT_PEN_2ND_BS] & 0x01) << 1);
+
+			if (++report_counter > 3)
+				report_counter = 0;
+
+			tthe_print(cd, cd->touch_buf, packet_len,
+				"OpModeData=");
+		}
+	}
+#endif
+	memcpy(si->xy_data, cd->input_buf, size);
+	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 ||
+	    report_id == PT_HID_VS_FINGER_REPORT_ID) {
+		if (cd->dut_status.protocol_mode == PT_PROTOCOL_MODE_PIP)
+			rc = move_touch_data_pip(cd, si);
+		else {
+			rc = move_touch_data_hid(cd, si);
+			if (rc) {
+				pt_debug(cd->dev, DL_INFO,
+					"%s: Skip report for the first touch packet\n",
+					__func__);
+				return 0;
+			}
+		}
+	} else if ((report_id == PT_HID_PEN_REPORT_ID ||
+		   report_id == PT_HID_VS_PEN_REPORT_ID) &&
+		   cd->dut_status.protocol_mode == PT_PROTOCOL_MODE_HID)
+		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_HID_VS_FINGER_REPORT_ID ||
+	    id == PT_HID_VS_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 = 0;
+	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) {
+		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))) {
+			report_id = cd->input_buf[PIP1_RESP_REPORT_ID_OFFSET];
+			cmd_id = cd->input_buf[PIP1_RESP_COMMAND_ID_OFFSET];
+			pt_debug(cd->dev, DL_INFO,
+				"%s: %s %d %s\n", __func__,
+				 "Received PIP1 report id =",
+				 report_id,
+				 "when expecting a PIP2 report");
+		} else {
+			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) {
+			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:
+	memset(cd->input_buf, 0, sizeof(cd->input_buf));
+	/* 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;
+
+#ifdef TTDL_PTVIRTDUT_SUPPORT
+	if (!cd->route_bus_virt_dut) {
+		if (!pt_check_irq_asserted(cd))
+			return IRQ_HANDLED;
+	}
+#else
+	if (!pt_check_irq_asserted(cd))
+		return IRQ_HANDLED;
+#endif /* TTDL_PTVIRTDUT_SUPPORT */
+
+	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;
+}
+
+#ifdef PT_AUX_BRIDGE_ENABLED
+/*******************************************************************************
+ * FUNCTION: pt_trigger_ttdl_irq
+ *
+ * SUMMARY: API for other kernel drivers to artificially trigger a TTDL IRQ
+ *
+ *	IMPROVE - dpmux should likely call pt_get_core_data once and make a
+ *		copy of cd so that the function only needs to be called once.
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS: none
+ *
+ ******************************************************************************/
+int pt_trigger_ttdl_irq(void)
+{
+	struct pt_core_data *cd;
+	char *core_name = PT_DEFAULT_CORE_ID;
+
+	cd = pt_get_core_data(core_name);
+	if (!cd) {
+		pr_err("%s: No Device\n", __func__);
+		return -ENODEV;
+	}
+	pt_irq(cd->irq, (void *)cd);
+	return 0;
+}
+EXPORT_SYMBOL_GPL(pt_trigger_ttdl_irq);
+#endif
+
+/*******************************************************************************
+ * 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);
+			kfree(atten);
+			pt_debug(cd->dev, DL_DEBUG, "%s: %s=%p %s=%d\n",
+				__func__,
+				"unsub for atten->dev", atten->dev,
+				"atten->mode", atten->mode);
+			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)
+{
+	mutex_lock(&cd->system_lock);
+	cd->wait_until_wake = 1;
+	mutex_unlock(&cd->system_lock);
+	wake_up(&cd->wait_q);
+	msleep(20);
+
+	return pt_core_wake_device_from_deep_sleep_(cd);
+}
+
+/*******************************************************************************
+ * 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_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;
+
+	/*
+	 * Do nothing if system already awake or in progress of waking.
+	 * Proceed if sleeping or entering sleep state.
+	 */
+	if (cd->sleep_state == SS_SLEEP_OFF || cd->sleep_state == SS_WAKING)
+		goto exit;
+
+	mutex_lock(&cd->system_lock);
+	cd->sleep_state = SS_WAKING;
+	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);
+		else if (cd->cpdata->flags & PT_CORE_FLAG_POWEROFF_ON_SLEEP)
+			rc = pt_core_poweron_device_(cd);
+		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);
+	if (rc == 0)
+		cd->sleep_state = SS_SLEEP_OFF;
+	else
+		cd->sleep_state = SS_SLEEP_ON;
+	mutex_unlock(&cd->system_lock);
+
+	pt_start_wd_timer(cd);
+
+exit:
+	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;
+
+	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 protocol_mode = PT_PROTOCOL_MODE_PIP;
+	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_status_(cd);
+		if (rc) {
+			pt_debug(cd->dev, DL_ERROR,
+				"%s: Get mode failed, mode unknown\n",
+				__func__);
+			mode = PT_MODE_UNKNOWN;
+		} else {
+			mode = cd->dut_status.mode;
+			detected = true;
+		}
+
+		cd->mode = mode;
+
+		switch (cd->mode) {
+		case PT_MODE_OPERATIONAL:
+			pt_debug(cd->dev, DL_INFO,
+				"%s: Operational mode\n", __func__);
+			protocol_mode = cd->dut_status.protocol_mode;
+			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_status_(cd);
+			if (rc) {
+				pt_debug(cd->dev, DL_ERROR,
+					"%s: Get mode failed, mode unknown\n",
+					__func__);
+				mode = PT_MODE_UNKNOWN;
+			} else
+				mode = cd->dut_status.mode;
+			cd->mode = mode;
+			if (cd->mode == PT_MODE_OPERATIONAL) {
+				pt_debug(cd->dev, DL_INFO,
+					 "%s: Launched to Operational mode\n",
+					 __func__);
+				protocol_mode = cd->dut_status.protocol_mode;
+			} 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;
+		}
+		if (cd->dut_status.protocol_mode == PT_PROTOCOL_MODE_HID) {
+			rc = pt_get_hid_descriptor_(cd, &hid_desc);
+			if (!rc)
+				*enum_status |= STARTUP_STATUS_GET_DESC;
+		} else {
+			*enum_status |= STARTUP_STATUS_GET_DESC;
+		}
+	}
+
+	pt_init_pip_report_fields(cd);
+	if (protocol_mode == PT_PROTOCOL_MODE_HID) {
+		rc = pt_get_report_descriptor_(cd);
+		if (rc < 0) {
+			pt_debug(cd->dev, DL_ERROR,
+				"%s: Error on getting report descriptor r=%d\n",
+				__func__, rc);
+			if (try++ < PT_CORE_STARTUP_RETRY_COUNT)
+				goto reset;
+			goto exit;
+		}
+	}
+
+	*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;
+}
+
+#ifndef TTDL_KERNEL_SUBMISSION
+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);
+#endif /*!TTDL_KERNEL_SUBMISSION */
+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);
+}
+
+#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);
+	int rc = 0;
+
+	if (cd->cpdata->flags & PT_CORE_FLAG_SKIP_RUNTIME)
+		return 0;
+
+	rc = pt_core_sleep(cd);
+	if (rc < 0) {
+		pt_debug(dev, DL_ERROR, "%s: Error on sleep\n", __func__);
+		return -EAGAIN;
+	}
+	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);
+	int rc = 0;
+
+	if (cd->cpdata->flags & PT_CORE_FLAG_SKIP_RUNTIME)
+		return 0;
+
+	rc = pt_core_wake(cd);
+	if (rc < 0) {
+		pt_debug(dev, DL_ERROR, "%s: Error on wake\n", __func__);
+		return -EAGAIN;
+	}
+
+	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)
+{
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+
+	pt_core_sleep(cd);
+
+	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__);
+	}
+
+	return 0;
+}
+
+/*******************************************************************************
+ * 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);
+
+	if (cd->cpdata->flags & PT_CORE_FLAG_SKIP_SYS_SLEEP)
+		return 0;
+
+	return pt_core_suspend_(dev);
+}
+
+/*******************************************************************************
+ * 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)
+{
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+
+	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:
+	pt_core_wake(cd);
+
+	return 0;
+}
+
+/*******************************************************************************
+ * 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);
+
+	if (cd->cpdata->flags & PT_CORE_FLAG_SKIP_SYS_SLEEP)
+		return 0;
+
+	return pt_core_resume_(dev);
+}
+#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)
+	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, 0);
+			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 rc = 0;
+	u16 status;
+	u16 actual_read_len;
+	u8  file_handle;
+	u8  data[2];
+	u8  read_buf[10];
+	u8  expected_len = pt_pip2_get_cmd_response_len(PIP2_CMD_ID_FILE_OPEN);
+
+	pt_debug(dev, DL_DEBUG, "%s: OPEN file %d\n", __func__, file_no);
+	data[0] = file_no;
+	rc = _pt_request_pip2_send_cmd(dev,
+		PT_CORE_CMD_UNPROTECTED, PIP2_CMD_ID_FILE_OPEN,
+		data, 1, read_buf, &actual_read_len);
+	if (rc) {
+		pt_debug(dev, DL_ERROR,
+			"%s: FILE_OPEN timeout for file=%d\n",
+			__func__, file_no);
+		return -PIP2_RSP_ERR_NOT_OPEN;
+	}
+
+	status = read_buf[PIP2_RESP_STATUS_OFFSET];
+	if (rc || ((status != PIP2_RSP_ERR_NONE) &&
+	    (status != PIP2_RSP_ERR_ALREADY_OPEN))) {
+		pt_debug(dev, DL_ERROR,
+			"%s: %s 0x%02X for file=%d\n",
+			__func__, "FILE_OPEN failure:", status, file_no);
+		return -status;
+	} else if (actual_read_len == expected_len) {
+		/* File_open returned a file handle */
+		file_handle = read_buf[PIP2_RESP_BODY_OFFSET];
+		if (file_handle != file_no) {
+			pt_debug(dev, DL_ERROR,
+				"%s: %s 0x%02X file=%d returned handle=%d\n",
+				__func__, "FILE_OPEN failure:",
+				status, file_no, file_handle);
+			return -status;
+		}
+	} else {
+		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_by_file_no
+ *
+ * SUMMARY: Using the BL PIP2 commands erase a file by file number only.
+ *
+ *  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
+ ******************************************************************************/
+int _pt_pip2_file_erase_by_file_no(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_erase_by_file_sector
+ *
+ * SUMMARY: Using the BL PIP2 commands erase a file sector by sector.
+ *
+ * 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
+ *   file_sector - Number of sectors to erase
+ *  *status      - PIP2 erase status code
+ ******************************************************************************/
+int _pt_pip2_file_erase_by_file_sector(struct device *dev, u8 file_handle,
+				       u16 file_sector, int *status)
+{
+	int ret = 0, index = 0;
+	int max_retry = 3;
+	int retry = 0;
+	int tmp_status = PIP2_RSP_ERR_NONE;
+	u16 sector_to_erase = 1;
+	u16 actual_read_len;
+	u8  data[6];
+	u8  read_buf[10];
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+
+	pt_debug(dev, DL_DEBUG, "%s: ERASE file=%d, sector=%d\n", __func__,
+		 file_handle, file_sector);
+	data[0] = file_handle;
+	data[1] = PIP2_FILE_IOCTL_CODE_ERASE_FILE;
+	/* Set how many sectors to erase*/
+	data[4] = LOW_BYTE(sector_to_erase);
+	data[5] = HI_BYTE(sector_to_erase);
+
+	/* Increase waiting time for large file erase */
+	mutex_lock(&cd->system_lock);
+	cd->pip_cmd_timeout = PT_PIP2_CMD_FILE_SECTOR_ERASE_TIMEOUT;
+	mutex_unlock(&cd->system_lock);
+	for (retry = 0; retry < max_retry; retry++) {
+		for (index = 0; index < file_sector; index++) {
+			/* Initialize status code */
+			tmp_status = PIP2_RSP_ERR_NONE;
+			/* Set which sector starts to erase */
+			data[2] = LOW_BYTE(index);
+			data[3] = HI_BYTE(index);
+			ret = _pt_request_pip2_send_cmd(dev,
+				PT_CORE_CMD_UNPROTECTED, PIP2_CMD_ID_FILE_IOCTL,
+				data, 6, read_buf, &actual_read_len);
+
+			tmp_status = read_buf[PIP2_RESP_STATUS_OFFSET];
+			if (ret || tmp_status != PIP2_RSP_ERR_NONE)
+				break;
+		}
+		/*
+		 * Only retry if response doesn't arrive or the status code is
+		 * PIP2_RSP_ERR_TIMEOUT.
+		 */
+		if ((ret != -ETIME) || (tmp_status != PIP2_RSP_ERR_TIMEOUT))
+			break;
+		else {
+#ifdef TTDL_DIAGNOSTICS
+			cd->file_erase_timeout_count++;
+#endif
+			pt_debug(dev, DL_WARN,
+			    "%s: ERASE timeout %d for file=%d, sector=%d\n",
+			    __func__, retry, file_handle, index);
+		}
+	}
+
+	mutex_lock(&cd->system_lock);
+	*status = tmp_status;
+	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, sector=%d\n",
+			__func__, ret, file_handle, index);
+		return -EIO;
+	}
+
+	if (*status != 0x00) {
+		pt_debug(dev, DL_ERROR,
+			"%s ROM FILE_ERASE failure: 0x%02X for file=%d, sector=%d\n",
+			__func__, *status, file_handle, index);
+		return -EIO;
+	}
+	return file_handle;
+}
+
+/*******************************************************************************
+ * FUNCTION: _pt_pip2_file_erase
+ *
+ * SUMMARY: Wrapper function to call _pt_pip2_file_erase_by_file_sector() and
+ *   _pt_pip2_file_erase_by_file_no() by checking file_sector.
+ *
+ * NOTE: If the file_sector is 0, it will erase the entire file.
+ *
+ * RETURNS:
+ *   <0 = Error
+ *   >0 = file handle closed
+ *
+ * PARAMETERS:
+ *  *dev         - pointer to device structure
+ *   file_handle - handle to the file to be erased
+ *   file_sector - Number of sectors to erase
+ *  *status      - PIP2 erase status code
+ ******************************************************************************/
+int _pt_pip2_file_erase(struct device *dev, u8 file_handle, u16 file_sector,
+			int *status)
+{
+	if (file_sector)
+		return _pt_pip2_file_erase_by_file_sector(dev, file_handle,
+							  file_sector, status);
+	else
+		return _pt_pip2_file_erase_by_file_no(dev, file_handle, status);
+}
+
+/*******************************************************************************
+ * 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 -1;
+	}
+	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 = get_fs();
+	set_fs(KERNEL_DS);
+	filp = filp_open(file_path, O_RDONLY, 0400);
+	if (IS_ERR(filp)) {
+		pt_debug(dev, DL_WARN, "%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 (kernel_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:
+	set_fs(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_WARN,
+				"%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;
+}
+
+#ifndef TTDL_KERNEL_SUBMISSION
+/*******************************************************************************
+ * 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;
+}
+#endif /* !TTDL_KERNEL_SUBMISSION */
+
+#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_store
+ *
+ * SUMMARY: Write method for tthe_tuner debugfs node. This function allows
+ *	user configuration of some output values via a bitmask.
+ *		0x01 = HID USB vs I2C output
+ *		0xFE = Reserved
+ *
+ * RETURN: Size of debugfs data write
+ *
+ * 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_store(struct file *filp, const char __user *buf,
+		size_t count, loff_t *ppos)
+{
+	struct pt_core_data *cd = filp->private_data;
+	ssize_t length;
+	u32 input_data[2];
+	u8 tmp_buf[4];    /* large enough for 1 32bit value */
+	int rc = 0;
+
+	mutex_lock(&cd->tthe_lock);
+
+	/* copy data from user space */
+	rc = simple_write_to_buffer(tmp_buf, sizeof(tmp_buf),
+		ppos, buf, count);
+	if (rc < 0)
+		goto exit;
+
+	length = _pt_ic_parse_input(cd->dev, tmp_buf, count,
+			input_data, ARRAY_SIZE(input_data));
+
+	if (length == 1) {
+		mutex_lock(&(cd->system_lock));
+		cd->tthe_hid_usb_format = input_data[0];
+		if (input_data[0] == PT_TTHE_TUNER_FORMAT_HID_USB)
+			pt_debug(cd->dev, DL_INFO,
+				"%s: Enable tthe_tuner HID-USB format\n",
+				__func__);
+		else if (input_data[0] == PT_TTHE_TUNER_FORMAT_HID_I2C)
+			pt_debug(cd->dev, DL_INFO,
+				"%s: Enable tthe_tuner HID-I2C format\n",
+				__func__);
+		else if (input_data[0] ==
+			PT_TTHE_TUNER_FORMAT_HID_FINGER_TO_PIP)
+			pt_debug(cd->dev, DL_INFO,
+				"%s: Enable tthe_tuner HID-FINGER-TO-PIP format\n",
+				__func__);
+		else if (input_data[0] ==
+			PT_TTHE_TUNER_FORMAT_HID_FINGER_AND_PEN_TO_PIP)
+			pt_debug(cd->dev, DL_INFO,
+				"%s: Enable tthe_tuner HID_FINGER_AND_PEN_TO_PIP format\n",
+				__func__);
+		else {
+			rc = -EINVAL;
+			pt_debug(cd->dev, DL_ERROR,
+				"%s: Invalid parameter: %d\n",
+				__func__, input_data[0]);
+		}
+		mutex_unlock(&(cd->system_lock));
+	} else {
+		rc = -EINVAL;
+		pt_debug(cd->dev, DL_ERROR,
+			"%s: Invalid number of parameters\n", __func__);
+	}
+
+exit:
+	mutex_unlock(&cd->tthe_lock);
+	if (rc)
+		return rc;
+	else
+		return count;
+}
+
+/*******************************************************************************
+ * 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);
+	}
+
+	/*
+	 * When size >= tthe_buf_len setting partial_read will cause NULL
+	 * characters to be printed in the output.
+	 */
+	if (size == count && size < cd->tthe_buf_len)
+		partial_read = count;
+
+	if (ret == size) {
+		mutex_unlock(&cd->tthe_lock);
+		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,
+	.write = tthe_debugfs_store,
+};
+#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,
+#ifndef TTDL_KERNEL_SUBMISSION
+	.parse_sysfs_input             = _pt_ic_parse_input,
+#endif
+#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_WARN, "%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_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_WARN,
+			"%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_WARN,
+				"%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_WARN,
+				"%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_WARN,
+					"%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_WARN,
+				"%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
+
+#ifdef PT_PTSBC_SUPPORT
+/* Required to support the Parade Techologies Development Platform */
+static int pt_probe_complete(struct pt_core_data *cd);
+
+/*******************************************************************************
+ * FUNCTION: pt_probe_work
+ *
+ * SUMMARY: For the PtSBC the probe functionality is split into two functions;
+ *	pt_probe() and pt_probe_complete() which is called from here.
+ *	This function is scheduled as a "work" task in order to launch after
+ *	I2C/SPI is up.
+ *
+ * RETURN: Void
+ *
+ * PARAMETERS:
+ *	*work - pointer to work structure
+ ******************************************************************************/
+static void pt_probe_work(struct work_struct *work)
+{
+	struct pt_core_data *cd =
+			container_of(work, struct pt_core_data,
+					probe_work);
+	int rc;
+
+	rc = pt_probe_complete(cd);
+
+	if (rc < 0)
+		pr_err("%s: Probe_complete returns rc=%d\n", __func__, rc);
+	else
+		pt_debug(cd->dev, DL_INFO,
+			"%s: Probe_complete returns rc=%d\n", __func__, rc);
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_probe_timer
+ *
+ * SUMMARY: For the PtSBC the probe functionality is split into two functions;
+ *	pt_probe() and pt_probe_complete(). This timer shedules the
+ *	probe_work function.
+ *
+ * RETURN: Void
+ *
+ * PARAMETERS:
+ *	handle - pointer to the core data
+ ******************************************************************************/
+static void pt_probe_timer(unsigned long handle)
+{
+	struct pt_core_data *cd = (struct pt_core_data *)handle;
+
+	if (!cd)
+		return;
+
+	pt_debug(cd->dev, DL_INFO, "%s: Watchdog timer triggered\n",
+		__func__);
+
+	if (!work_pending(&cd->probe_work))
+		schedule_work(&cd->probe_work);
+}
+#endif /* --- End PT_PTSBC_SUPPORT --- */
+
+
+
+/*******************************************************************************
+ * 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 snprintf(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 snprintf(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);
+
+#ifndef TTDL_KERNEL_SUBMISSION
+/*******************************************************************************
+ * 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_WARN,
+				"%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, 250,
+			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) {
+		pt_debug(dev, DL_WARN, "%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_WARN,
+				"%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);
+#ifdef TTDL_PTVIRTDUT_SUPPORT
+	struct pt_hid_output hid_output;
+#endif
+	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_WARN, "%s: Invalid number of arguments\n",
+			__func__);
+		rc = -EINVAL;
+		goto pt_command_store_exit;
+	}
+
+	/* Get HID Desc */
+	if (length == 2 && input_data[0] == 0x01 && input_data[1] == 0x00) {
+		pm_runtime_get_sync(dev);
+		rc = pt_get_hid_descriptor(cd, &cd->hid_desc);
+		mutex_lock(&cd->sysfs_lock);
+		if (!rc) {
+			cd->cmd_rsp_buf_len =
+			    get_unaligned_le16(&cd->response_buf[0]);
+			memcpy(cd->cmd_rsp_buf, cd->response_buf,
+			       cd->cmd_rsp_buf_len);
+		}
+		cd->raw_cmd_status = rc;
+		mutex_unlock(&cd->sysfs_lock);
+		pm_runtime_put(dev);
+		goto pt_command_store_exit;
+	}
+
+	/* Get Report Desc */
+	if (length == 2 && input_data[0] == 0x02 && input_data[1] == 0x00) {
+		pm_runtime_get_sync(dev);
+		rc = pt_get_report_descriptor(cd);
+		mutex_lock(&cd->sysfs_lock);
+		if (!rc) {
+			cd->cmd_rsp_buf_len = cd->hid_core.hid_report_desc_len;
+			memcpy(cd->cmd_rsp_buf, cd->response_buf,
+			       cd->cmd_rsp_buf_len);
+		}
+		cd->raw_cmd_status = rc;
+		mutex_unlock(&cd->sysfs_lock);
+		pm_runtime_put(dev);
+		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_WARN, "%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);
+
+#ifdef TTDL_PTVIRTDUT_SUPPORT
+	/* Special case for handling Virtual DUT exit */
+	if (length >= 3 &&
+	    input_data[0] == 0xFF &&
+	    input_data[1] == 0xFF &&
+	    input_data[2] == 0x00) {
+		hid_output.length = length,
+		hid_output.write_buf = input_data,
+		rc = pt_hid_send_output_user_(cd, &hid_output);
+		pm_runtime_put(dev);
+		goto pt_command_store_exit;
+	}
+#endif
+	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:
+ *  *filp     - pointer to file structure
+ *  *kobj     - pointer to kobject structure
+ *  *bin_attr - pointer to bin_attribute structure
+ *   buf      - pointer to cmd input buffer
+ *   offset   - offset index to store input buffer
+ *   count    - size of data in buffer
+ ******************************************************************************/
+static ssize_t pt_response_show(struct file *filp,
+		struct kobject *kobj, struct bin_attribute *bin_attr,
+		char *buf, loff_t offset, size_t count)
+{
+	struct device *dev = container_of(kobj, struct device, kobj);
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+	static int pr_left;
+	static int pr_index;
+	static u8 *pr_buf;
+	int i = 0;
+	int index = 0;
+	int rc = 0;
+	int print_len = 0;
+
+	if (pr_left) {
+		if (count < pr_left) {
+			index = count;
+			memcpy(buf, pr_buf + pr_index, index);
+			pr_left -= count;
+			pr_index += count;
+		} else {
+			index = pr_left;
+			memcpy(buf, pr_buf + pr_index, index);
+			pr_left = 0;
+			pr_index = 0;
+			kfree(pr_buf);
+		}
+		return index;
+	}
+
+	if (offset == 0) {
+		mutex_lock(&cd->sysfs_lock);
+		if (cd->raw_cmd_status) {
+			index = scnprintf(buf, PT_MAX_PRBUF_SIZE,
+					  "Status: %d\n", cd->raw_cmd_status);
+			mutex_unlock(&cd->sysfs_lock);
+			return index;
+		}
+
+		/*
+		 * Allocate memory according to the cost, each byte need 5
+		 * character, and extra 100 bytes for the header and tail.
+		 */
+		if (cd->cmd_rsp_buf_len > PT_MAX_INPUT) {
+			rc = -EINVAL;
+			goto exit;
+		}
+		print_len = cd->cmd_rsp_buf_len * 5 + 100;
+		pr_buf = kzalloc(print_len, GFP_KERNEL);
+		if (!pr_buf) {
+			rc = -ENOMEM;
+			goto exit;
+		}
+
+		/* Format all data to pr_buf */
+		index = scnprintf(pr_buf, print_len - index, "Status: %d\n",
+				cd->raw_cmd_status);
+		for (i = 0; i < cd->cmd_rsp_buf_len; i++)
+			index += scnprintf(pr_buf + index,
+				print_len - index, "0x%02X\n",
+				cd->cmd_rsp_buf[i]);
+		index += scnprintf(pr_buf + index, print_len - index,
+				   "(%zd bytes)\n", cd->cmd_rsp_buf_len);
+
+		mutex_unlock(&cd->sysfs_lock);
+
+		if (count < index) {
+			memcpy(buf, pr_buf, count);
+			pr_left = index - count;
+			pr_index = count;
+			index = count;
+		} else {
+			memcpy(buf, pr_buf, index);
+			pr_left = 0;
+			pr_index = 0;
+			kfree(pr_buf);
+		}
+		return index;
+	}
+
+exit:
+	mutex_unlock(&cd->sysfs_lock);
+	return rc;
+}
+
+static struct bin_attribute bin_attr_pt_response = {
+	.attr = {
+		.name = "response",
+		.mode = (0444),
+	},
+	.read = pt_response_show,
+};
+/*******************************************************************************
+ * 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"
+		"%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",
+		PT_DUT_DBG_REPORT_DESC, "", "Get HID Report 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"
+#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_NUM_DEVICES, "[1-255]", "Number of Devices",
+		PT_DRV_DBG_PIP_TIMEOUT, "[100-7000]", "PIP Resp Timeout (ms)"
+#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_WARN, "%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_sleep(cd);
+		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__);
+		break;
+
+	case PT_DRV_DBG_RESUME:				/* 5 */
+		pt_debug(dev, DL_INFO, "%s: TTDL: Wake\n", __func__);
+		rc = pt_core_wake(cd);
+		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__);
+		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_DUT_DBG_REPORT_DESC:			/* 113 */
+		rc = pt_get_report_descriptor(cd);
+		if (rc != 0) {
+			pt_debug(cd->dev, DL_ERROR,
+				"%s: Error on getting report descriptor r=%d\n",
+				__func__, rc);
+		}
+		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_WARN,
+				"%s: Force call pm_runtime_put()\n", __func__);
+		} else if (input_data[1] == 1) {
+			pm_runtime_get_sync(dev);
+			pt_debug(dev, DL_WARN,
+				"%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_NUM_DEVICES:			/* 217 */
+		mutex_lock(&cd->system_lock);
+		if (input_data[1] == 0) {
+			/* 0 is not supported */
+			rc = -EINVAL;
+			pt_debug(dev, DL_ERROR, "%s: Invalid parameter: %d\n",
+				__func__, input_data[1]);
+		} else if (input_data[1] == 1) {
+			cd->num_devices = input_data[1];
+			cd->ttdl_bist_select = 0x07;
+			pt_debug(dev, DL_INFO,
+				"%s: Multi-chip support Disabled\n", __func__);
+		} else if (input_data[1] > 1 &&
+			   input_data[1] <= PT_MAX_DEVICES) {
+			cd->num_devices = input_data[1];
+			cd->ttdl_bist_select = 0x3F;
+			pt_debug(dev, DL_INFO,
+				"%s: Multi-chip support Enabled with %d DUTs\n",
+				 __func__, cd->num_devices);
+		} 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: ATM - Set panel_id_support to %d\n",
+				__func__, cd->panel_id_support);
+		} else {
+			rc = -EINVAL;
+			pt_debug(dev, DL_ERROR,
+				"%s: ATM - 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: ATM - PIP Timeout = %d\n", __func__,
+				cd->pip_cmd_timeout_default);
+		} else {
+			rc = -EINVAL;
+			pt_debug(dev, DL_ERROR,
+				"%s: ATM - Invalid parameter: %d\n",
+				__func__, input_data[1]);
+		}
+		mutex_unlock(&(cd->system_lock));
+		break;
+	case PT_DRV_DBG_CORE_PLATFORM_FLAG:		/* 220 */
+		mutex_lock(&cd->system_lock);
+		if (cd->cpdata) {
+			cd->cpdata->flags = input_data[1];
+			pt_debug(dev, DL_INFO,
+				"%s: ATM - Set core platform flag to 0x%02X\n",
+				__func__, input_data[1]);
+		} else {
+			rc = -EINVAL;
+			pt_debug(dev, DL_ERROR, "%s: No platform data\n",
+				 __func__);
+		}
+		mutex_unlock(&cd->system_lock);
+		break;
+#ifdef TTDL_PTVIRTDUT_SUPPORT
+	case PT_DRV_DBG_SET_HW_DETECT:			/* 298 */
+		mutex_lock(&cd->system_lock);
+		if (input_data[1])
+			cd->hw_detect_enabled = true;
+		else
+			cd->hw_detect_enabled = false;
+		pt_debug(dev, DL_INFO,
+				"%s: Set hw_detect_enabled to %d\n",
+				__func__, cd->hw_detect_enabled);
+		mutex_unlock(&(cd->system_lock));
+		break;
+	case PT_DRV_DBG_VIRTUAL_I2C_DUT:		/* 299 */
+		if (input_data[1] == 0) {
+			/* Cancel all work threads when disabling */
+			pt_debug(dev, DL_WARN, "%s: Canceling Work", __func__);
+#ifdef PT_PTSBC_SUPPORT
+			cancel_work_sync(&cd->irq_work);
+			cancel_work_sync(&cd->probe_work);
+#endif
+			cancel_work_sync(&cd->ttdl_restart_work);
+			cancel_work_sync(&cd->enum_work);
+			pt_stop_wd_timer(cd);
+			call_atten_cb(cd, PT_ATTEN_CANCEL_LOADER, 0);
+
+			mutex_lock(&cd->system_lock);
+			cd->route_bus_virt_dut = 0;
+			mutex_unlock(&(cd->system_lock));
+			pt_debug(dev, DL_WARN, "%s: Enable Virtual DUT mode\n",
+				__func__);
+		} else if (input_data[1] == 1) {
+			mutex_lock(&cd->system_lock);
+			cd->route_bus_virt_dut = 1;
+			mutex_unlock(&(cd->system_lock));
+			pt_debug(dev, DL_WARN, "%s: Enable Virtual DUT mode\n",
+				__func__);
+		} else {
+			rc = -EINVAL;
+			pt_debug(dev, DL_ERROR, "%s: Invalid parameter: %d\n",
+				__func__, input_data[1]);
+		}
+		break;
+#endif /* TTDL_PTVIRTDUT_SUPPORT */
+#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 = snprintf(buf, PT_MAX_PRBUF_SIZE, "off\n");
+	else
+		ret = snprintf(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_WARN, "%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_WARN,
+					 "%s: Gen6 FW mode rc=%d PID=0x%02X\n",
+					 __func__, rc, pid);
+			}
+		} else {
+			pt_debug(dev, DL_WARN, "%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_WARN,
+					 "%s: TT/TC FW mode rc=%d PID=0x%02X\n",
+					 __func__, rc, pid);
+			}
+		} else {
+			pt_debug(dev, DL_WARN, "%s: Active mode unknown\n",
+				__func__);
+			rc = -EPERM;
+		}
+	} else {
+		pt_debug(dev, DL_WARN, "%s: Dut generation is unknown\n",
+				__func__);
+		rc = -EPERM;
+	}
+
+	ret = snprintf(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_WARN, "%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 snprintf(buf, PT_MAX_PRBUF_SIZE,
+					"Status: %d\n"
+					"DUT GPIO Reg: 0x%08X\n",
+					rc, gpio_value);
+		else
+			return snprintf(buf, PT_MAX_PRBUF_SIZE,
+					"Status: %d\n"
+					"DUT GPIO Reg: n/a\n",
+					status);
+	} else
+		return snprintf(buf, PT_MAX_PRBUF_SIZE,
+				"Status: %d\n"
+				"DUT GPIO Reg: n/a\n",
+				rc);
+}
+
+/*******************************************************************************
+ * 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 snprintf(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 snprintf(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: 0x%02X\n"
+		"%s: %s\n"
+		"%s: %s\n"
+		"%s: %d\n"
+		"%s: %s %s\n"
+		"%s: %s\n"
+		"%s: %s\n"
+		"%s: %s\n"
+		"%s: %s\n"
+		"%s: %s\n"
+		"%s: %s\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: %d\n"
+		"%s: %s\n"
+		"%s: %d\n"
+		"%s: 0x%04X\n"
+#endif /* TTDL_DIAGNOSTICS */
+		,
+		"Startup Status            ", cd->startup_status,
+		"TTDL Debug Level          ", cd->debug_level,
+		"Active Bus Module         ",
+			cd->bus_ops->bustype == BUS_I2C ? "I2C" : "SPI",
+		"I2C Address               ",
+			cd->bus_ops->bustype == BUS_I2C ? client->addr : 0,
+		"Exclusive Access Lock     ", cd->exclusive_dev ? "Set":"Free",
+		"HW Detected               ",
+			cd->hw_detected ? "True" : "False",
+		"Number of Devices         ", cd->num_devices,
+		"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)") : "",
+		"Protocol Mode             ",
+			cd->dut_status.protocol_mode == PT_PROTOCOL_MODE_HID ?
+			"Hybrid HID" : "PIP",
+		"Mode                      ",
+			cd->mode ? (cd->mode == PT_MODE_OPERATIONAL ?
+			"Operational" : "BL") : "Unknown",
+		"Flashless Mode            ",
+			cd->flashless_dut == 1 ? "Yes" : "No",
+		"Suppress No-Flash Auto BL ",
+			cd->flashless_auto_bl == PT_SUPPRESS_AUTO_BL ?
+			"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",
+		"Error GPIO trigger type   ", cd->err_gpio_type,
+		"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,
+		"Startup Retry Count       ", cd->startup_retry_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,
+		"RAM Parm Restore Count    ", pt_count_parameter_list(cd),
+		"Calibration Cache on host ",
+			cd->cal_cache_in_host == PT_FEATURE_ENABLE ?
+			"Yes" : "No",
+		"Calibration Cache size    ", cal_size,
+		"Calibration Cache chip ID ", crc
+#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);
+	bool current_bridge_mode = cd->bridge_mode;
+
+	/* 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 = snprintf(buf, PT_MAX_PRBUF_SIZE,
+			"Status: %d\nEntered BL\n", PT_ENTER_BL_PASS);
+		break;
+	case PT_ENTER_BL_ERROR:
+		ret = snprintf(buf, PT_MAX_PRBUF_SIZE, "Status: %d\n%s\n",
+			rc,
+			"  Unknown Error");
+		break;
+	case PT_ENTER_BL_RESET_FAIL:
+		ret = snprintf(buf, PT_MAX_PRBUF_SIZE, "Status: %d\n%s\n",
+			rc,
+			"  Soft Reset Failed");
+		break;
+	case PT_ENTER_BL_HID_START_BL_FAIL:
+		ret = snprintf(buf, PT_MAX_PRBUF_SIZE, "Status: %d\n%s\n",
+			rc,
+			"  PIP Start BL Cmd Failed");
+		break;
+	case PT_ENTER_BL_CONFIRM_FAIL:
+		ret = snprintf(buf, PT_MAX_PRBUF_SIZE, "Status: %d\n%s\n",
+			rc,
+			"  Error confirming DUT entered BL");
+		break;
+	default:
+		ret = snprintf(buf, PT_MAX_PRBUF_SIZE, "Status: %d\n%s\n",
+			rc, "  Unknown Error");
+		break;
+	};
+
+	/* Restore state of allowing enumeration work to be queued again */
+	mutex_lock(&cd->system_lock);
+	cd->bridge_mode = current_bridge_mode;
+	mutex_unlock(&cd->system_lock);
+
+	return ret;
+}
+static DEVICE_ATTR(pip2_enter_bl,  0444, pt_pip2_enter_bl_show, NULL);
+
+#define PT_STATUS_STR_LEN (50)
+/*******************************************************************************
+ * 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 = snprintf(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_WARN, "%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 = snprintf(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 += snprintf(buf + ret, PT_MAX_PRBUF_SIZE - ret, "Status: %d\n", 0);
+	for (i = 0; i < cd->gesture_data_length; i++)
+		ret += snprintf(buf + ret, PT_MAX_PRBUF_SIZE - ret,
+				"0x%02X\n", cd->gesture_data[i]);
+
+	ret += snprintf(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_WARN, "%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 += snprintf(buf, PT_MAX_PRBUF_SIZE, "Status: %d\n", 0);
+	if (cd->irq_enabled)
+		ret += snprintf(buf + ret, PT_MAX_PRBUF_SIZE - ret,
+			"Driver interrupt: ENABLED\n");
+	else
+		ret += snprintf(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_WARN, "%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, 0);
+	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)
+{
+#ifdef TTDL_PTVIRTDUT_SUPPORT
+	u8 release_irq[3] = {0xFF, 0xFF, 0x03};
+#endif /* TTDL_PTVIRTDUT_SUPPORT */
+	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);
+	}
+#ifdef TTDL_PTVIRTDUT_SUPPORT
+	if (pt_check_irq_asserted(cd) && cd->route_bus_virt_dut) {
+		/* Force virtual DUT to release IRQ */
+		pt_pr_buf(cd->dev, DL_DEBUG, release_irq,
+			(int)sizeof(release_irq), ">>> User CMD");
+		pt_adap_write_read_specific(cd, 3, release_irq, NULL, 0);
+	}
+#endif /* TTDL_PTVIRTDUT_SUPPORT */
+
+	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);
+#ifdef TTDL_PTVIRTDUT_SUPPORT
+	u8 release_irq[3] = {0xFF, 0xFF, 0x03};
+#endif /* TTDL_PTVIRTDUT_SUPPORT */
+	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);
+#ifdef TTDL_PTVIRTDUT_SUPPORT
+	if (pt_check_irq_asserted(cd) && cd->route_bus_virt_dut) {
+		/* Force virtual DUT to release IRQ */
+		pt_pr_buf(cd->dev, DL_DEBUG, release_irq,
+			(int)sizeof(release_irq), ">>> User CMD");
+		pt_adap_write_read_specific(cd, 3, release_irq, NULL, 0);
+	}
+#endif /* TTDL_PTVIRTDUT_SUPPORT */
+
+	/* 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    - pointer to one entry in the slave info array
+ ******************************************************************************/
+static int pt_bist_slave_irq_test(struct device *dev,
+	struct pt_bist_data *slave)
+{
+	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_WARN, "%s: Error entering BL rc=%d\n",
+			__func__, rc);
+		if (slave->irq_err_str)
+			strlcpy(slave->irq_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] & slave->mask;
+
+		/* 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 {
+			if (slave->irq_err_str)
+				strlcpy(slave->irq_err_str,
+					"- State could not be determined", PT_ERR_STR_SIZE);
+			rc = -EPERM;
+		}
+	} else {
+		pt_debug(cd->dev, DL_WARN, "%s: STATUS cmd failure\n",
+			__func__);
+		if (slave->irq_err_str)
+			strlcpy(slave->irq_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_WARN,
+				"%s: Master Boot Last Err = 0x%02X\n",
+				__func__, last_err);
+		}
+	} else {
+		pt_debug(cd->dev, DL_WARN,
+			"%s: GET_LAST_ERRNO cmd failure\n", __func__);
+		if (slave->irq_err_str)
+			strlcpy(slave->irq_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);
+
+	/*
+	 * Clear any possible false positives:
+	 * - An invalid image error as BIST doesn't need valid FW
+	 * - A Flash file too small as BIST doesn't need FLASH
+	 * - FLASH access errors when in no-flash mode
+	 */
+	if ((last_err == PIP2_RSP_ERR_INVALID_IMAGE) ||
+	    (last_err == PIP2_RSP_ERR_BUF_TOO_SMALL) ||
+	    (last_err == PIP2_RSP_ERR_BAD_ADDRESS && cd->flashless_dut) ||
+	    (last_err == PIP2_RSP_ERR_BAD_FRAME && cd->flashless_dut)) {
+		pt_debug(cd->dev, DL_INFO, "%s: Cleared boot error: 0x%02X\n",
+			__func__, last_err);
+		last_err = PIP2_RSP_ERR_NONE;
+	}
+
+	/* Attempt to add a hint based on boot error and detection */
+	if (slave->irq_err_str) {
+		if (last_err && detected)
+			scnprintf(slave->irq_err_str,PT_ERR_STR_SIZE, "%s 0x%02X", 
+				"- Likely stuck low. Boot Error:",
+				last_err);
+		else if (last_err && !detected)
+			scnprintf(slave->irq_err_str, PT_ERR_STR_SIZE, "%s 0x%02X",
+				"- Likely stuck high. Boot Error:",
+				last_err);
+		else if (detected)
+			strlcpy(slave->irq_err_str,
+				"- Likely stuck low. No Critical Boot Error", PT_ERR_STR_SIZE);
+		else if (!detected)
+			strlcpy(slave->irq_err_str,
+				"- Likely stuck high. No Critical Boot Error.", PT_ERR_STR_SIZE);
+	}
+
+	slave->irq_toggled = (detected && !last_err) ? true : false;
+	/* Leave as UNTEST if slave not detected */
+	if (detected)
+		slave->bus_toggled = !last_err ? true : false;
+	slave->detected = detected;
+	slave->boot_err = last_err;
+
+	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    - pointer to one entry in the slave info array
+ ******************************************************************************/
+static int pt_bist_slave_xres_test(struct device *dev,
+	struct pt_bist_data *slave)
+{
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+	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);
+	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->xres_err_str && !slave->detected) {
+			strlcpy(slave->xres_err_str, slave->irq_err_str, PT_ERR_STR_SIZE);
+			pt_debug(dev, DL_INFO,
+				"%s: detected=0 irq_err_str = %s\n",
+				__func__, slave->irq_err_str);
+		} else if (slave->xres_err_str && slave->boot_err > 0) {
+			scnprintf(slave->xres_err_str, PT_ERR_STR_SIZE, "%s 0x%02X",
+				"- likely open or an IRQ issue. Boot Error:",
+				slave->boot_err);
+			pt_debug(dev, DL_INFO,
+				"%s: boot_err=%d xres_err_str = %s\n",
+				__func__,
+				slave->boot_err, slave->xres_err_str);
+		} else {
+			pt_debug(dev, DL_WARN, "%s: No xres_err_str buffer\n",
+				__func__);
+		}
+	}
+
+	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    - pointer to one entry in the slave info array
+ ******************************************************************************/
+static int pt_bist_slave_bus_test(struct device *dev,
+	struct pt_bist_data *slave)
+{
+	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_WARN, "%s: Error entering BL rc=%d\n",
+			__func__, rc);
+		if (slave->bus_err_str)
+			strlcpy(slave->bus_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_WARN,
+			"%s Failed to open bin file\n", __func__);
+		if (slave->bus_err_str)
+			strlcpy(slave->bus_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_WARN,
+				"%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 */
+	slave->irq_toggled = bus_toggled;
+	slave->bus_toggled = bus_toggled;
+
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_bist_slave_test
+ *
+ * SUMMARY: Tests XRES, SPI BUS and IRQ for the slave DUT
+ *
+ * RETURN:
+ *	 0 = Success
+ *	!0 = Failure
+ *
+ * PARAMETERS:
+ *	*dev      - pointer to device structure
+ *	*slave    - pointer to one entry in the bist info array
+ *	slave_id  - id of the slave chip
+ ******************************************************************************/
+static int pt_bist_slave_test(struct device *dev,
+	struct pt_bist_data *slave, int slave_id)
+{
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+	char *pr_buf;
+	int rc = 0;
+
+	slave->mask = 0x01 << slave_id;
+	slave->bus_toggled  = 0x0F; /* untested */
+	slave->irq_toggled  = 0x0F; /* untested */
+	slave->xres_toggled = 0x0F; /* untested */
+
+	pr_buf = slave->print_buf;
+	if (!pr_buf) {
+		rc = -ENOMEM;
+		goto print_results;
+	}
+
+	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);
+
+	/* ensure no malloc failure */
+	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);
+
+	/* --------------- SLAVE XRES BIST TEST --------------- */
+	if ((cd->ttdl_bist_select & PT_BIST_SLAVE_XRES_TEST) != 0) {
+		pt_debug(dev, DL_INFO,
+			"%s: ----- Start Slave %d XRES BIST -----",
+			__func__, slave_id);
+		slave->xres_toggled = 0xFF;
+		rc = pt_bist_slave_xres_test(dev, slave);
+		if (slave->bus_toggled == 1 &&
+		    slave->irq_toggled == 1 &&
+		    slave->xres_toggled == 1)
+			goto print_results;
+	}
+
+	/* --------------- SLAVE IRQ BIST TEST --------------- */
+	if ((cd->ttdl_bist_select & PT_BIST_SLAVE_IRQ_TEST) != 0) {
+		pt_debug(dev, DL_INFO,
+			"%s: ----- Start Slave %d IRQ BIST -----",
+			__func__, slave_id);
+		slave->irq_toggled = 0xFF;
+		rc = pt_bist_slave_irq_test(dev, slave);
+		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->ttdl_bist_select & PT_BIST_SLAVE_BUS_TEST) != 0) {
+		pt_debug(dev, DL_INFO,
+			"%s: ----- Start Slave %d BUS BIST -----",
+			__func__, slave_id);
+		slave->bus_toggled = 0xFF;
+		rc = pt_bist_slave_bus_test(dev, slave);
+	}
+
+print_results:
+	/* --------------- PRINT OUT BIST RESULTS ---------------*/
+	pt_debug(dev, DL_INFO, "%s: ----- BIST Print Results ----", __func__);
+	if (!slave->bus_err_str ||
+		!slave->irq_err_str ||
+		!slave->xres_err_str) {
+		slave->pr_index = scnprintf(pr_buf, PT_MAX_PR_BUF_SIZE,
+				"M/S%d SPI (MISO,MOSI,CS,CLK): [UNTEST]\n"
+				"M/S%d IRQ connection:         [UNTEST]\n"
+				"M/S%d TP_XRES connection:     [UNTEST]\n",
+				slave_id, slave_id, slave_id);
+	} else {
+		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);
+
+		slave->status = 0;
+		if (cd->ttdl_bist_select & PT_BIST_SLAVE_BUS_TEST)
+			slave->status += slave->bus_toggled;
+		if (cd->ttdl_bist_select & PT_BIST_SLAVE_IRQ_TEST)
+			slave->status += slave->irq_toggled;
+		if (cd->ttdl_bist_select & PT_BIST_SLAVE_XRES_TEST)
+			slave->status += slave->xres_toggled;
+		pt_debug(dev, DL_WARN,
+			"%s: status = %d (Slave %d: %d,%d,%d)\n",
+			__func__, slave->status, slave_id,
+			slave->bus_toggled,
+			slave->irq_toggled,
+			slave->xres_toggled);
+
+		slave->pr_index = scnprintf(pr_buf, PT_MAX_PR_BUF_SIZE,
+			"M/S%d SPI (MISO,MOSI,CS,CLK): %s %s\n"
+			"M/S%d IRQ connection:         %s %s\n"
+			"M/S%d TP_XRES connection:     %s %s\n",
+			slave_id,
+			slave->bus_toggled == 0x0F ?
+				"[UNTEST]" :
+				slave->bus_toggled == 1 ?
+				"[  OK  ]" :
+				"[FAILED]",
+				slave->bus_err_str,
+			slave_id,
+			slave->irq_toggled == 0x0F ?
+				"[UNTEST]" :
+				slave->irq_toggled == 1 ?
+				"[  OK  ]" :
+				"[FAILED]",
+				slave->irq_err_str,
+			slave_id,
+			slave->xres_toggled == 0x0F ?
+				"[UNTEST]" :
+				slave->xres_toggled == 1 ?
+				"[  OK  ]" :
+				"[FAILED]",
+				slave->xres_err_str);
+	}
+
+	kfree(slave->bus_err_str);
+	kfree(slave->irq_err_str);
+	kfree(slave->xres_err_str);
+
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_bist_host_test
+ *
+ * SUMMARY: Tests XRES, SPI BUS and IRQ for the host DUT
+ *
+ * RETURN:
+ *	 0 = Success
+ *	!0 = Failure
+ *
+ * PARAMETERS:
+ *	*dev      - pointer to device structure
+ *	*host     - pointer to the entry in the bist info array
+ ******************************************************************************/
+static int pt_bist_host_test(struct device *dev,
+	struct pt_bist_data *host)
+{
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+	char *pr_buf;
+	int rc = 0;
+
+	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 */
+
+	pr_buf = host->print_buf;
+	if (!pr_buf) {
+		rc = -ENOMEM;
+		goto print_results;
+	}
+
+	host->bus_err_str = kzalloc(PT_ERR_STR_SIZE, GFP_KERNEL);
+	host->irq_err_str = kzalloc(PT_ERR_STR_SIZE, GFP_KERNEL);
+	host->xres_err_str = kzalloc(PT_ERR_STR_SIZE, GFP_KERNEL);
+	if (!host->bus_err_str ||
+		!host->irq_err_str ||
+		!host->xres_err_str)
+		goto print_results;
+
+	memset(host->xres_err_str, 0, PT_ERR_STR_SIZE);
+	memset(host->irq_err_str, 0, PT_ERR_STR_SIZE);
+	memset(host->bus_err_str, 0, PT_ERR_STR_SIZE);
+
+	/* --------------- TP_XRES BIST TEST --------------- */
+	if ((cd->ttdl_bist_select & PT_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, host->xres_err_str);
+		/* Done if the rest of all nets toggled */
+		if (bus_toggled == 1 && irq_toggled == 1 && xres_toggled == 1)
+			goto print_results;
+	}
+
+	/* 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_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, host->irq_err_str);
+		/* If this net failed clear results from previous net */
+		if (irq_toggled != 1) {
+			xres_toggled = 0x0F;
+			memset(host->xres_err_str, 0, PT_ERR_STR_SIZE);
+		}
+		if (bus_toggled == 1 && irq_toggled == 1)
+			goto print_results;
+	}
+
+	/* 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_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, host->bus_err_str);
+		/* If this net failed clear results from previous net */
+		if (bus_toggled == 0) {
+			irq_toggled = 0x0F;
+			memset(host->irq_err_str, 0, PT_ERR_STR_SIZE);
+		}
+	}
+
+print_results:
+	/* --------------- PRINT OUT BIST RESULTS ---------------*/
+	pt_debug(dev, DL_INFO, "%s: ----- BIST Print Results ----", __func__);
+
+	/* Cannot print if any memory allocation issues */
+	if (!host->bus_err_str || !host->irq_err_str || !host->xres_err_str) {
+		host->pr_index = scnprintf(pr_buf, PT_MAX_PR_BUF_SIZE,
+			"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);
+	} else {
+		host->status = 0;
+		if (bus_toggled == 1)
+			memset(host->bus_err_str, 0, PT_ERR_STR_SIZE);
+		if (irq_toggled == 1)
+			memset(host->irq_err_str, 0, PT_ERR_STR_SIZE);
+		if (xres_toggled == 1)
+			memset(host->xres_err_str, 0, PT_ERR_STR_SIZE);
+
+		if (cd->ttdl_bist_select & PT_BIST_BUS_TEST)
+			host->status += bus_toggled;
+		if (cd->ttdl_bist_select & PT_BIST_IRQ_TEST)
+			host->status += irq_toggled;
+		if (cd->ttdl_bist_select & PT_BIST_TP_XRES_TEST)
+			host->status += xres_toggled;
+		pt_debug(dev, DL_WARN, "%s: status = %d (%d,%d,%d)\n",
+			__func__, host->status, bus_toggled, irq_toggled,
+			xres_toggled);
+
+		if (cd->bus_ops->bustype == BUS_I2C)
+			i2c_toggled = bus_toggled;
+		else
+			spi_toggled = bus_toggled;
+
+		host->pr_index = scnprintf(pr_buf, PT_MAX_PR_BUF_SIZE,
+			"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",
+			i2c_toggled == 0x0F ? "[UNTEST]" :
+				i2c_toggled == 1 ? "[  OK  ]" : "[FAILED]",
+			i2c_toggled == 0x0F ? "" : host->bus_err_str,
+			spi_toggled == 0x0F ? "[UNTEST]" :
+				spi_toggled == 1 ? "[  OK  ]" : "[FAILED]",
+			spi_toggled == 0x0F ? "" : host->bus_err_str,
+			irq_toggled == 0x0F ? "[UNTEST]" :
+				irq_toggled == 1 ? "[  OK  ]" : "[FAILED]",
+			host->irq_err_str,
+			xres_toggled == 0x0F ? "[UNTEST]" :
+				xres_toggled == 1 ? "[  OK  ]" : "[FAILED]",
+			host->xres_err_str);
+	}
+
+	kfree(host->bus_err_str);
+	kfree(host->irq_err_str);
+	kfree(host->xres_err_str);
+
+	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 = 0;
+	u8 tests;
+	int slave_id          = 0;
+	int num_slaves        = 0;
+	int rc                = 0;
+	int num_tests         = 0;
+	int status            = 1;    /* 0 = Pass, !0 = fail */
+	struct pt_bist_data host;
+	struct pt_bist_data slaves[PT_MAX_DEVICES];
+	int idx_status = 0;
+
+	host.print_buf = kzalloc(PT_MAX_PR_BUF_SIZE, GFP_KERNEL);
+	if (!host.print_buf) {
+		ret = scnprintf(buf, strlen(buf),
+			"Status: 1\n"
+			"Failed to alloc memory");
+		return ret;
+	}
+
+	/* Load up slave info array when slaves present */
+	num_slaves = cd->num_devices - 1;
+
+	/* 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_WARN, "%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 HOST BIST TEST --------------- */
+	host.status = 0;
+	host.pr_index = 0;
+	rc = pt_bist_host_test(dev, &host);
+	pt_debug(dev, DL_INFO, "%s print_idx = %d\n",
+			__func__, host.pr_index);
+
+	status = host.status;
+
+	/* --------------- TP SLAVE BIST TEST --------------- */
+	for (slave_id = 0; slave_id < num_slaves; slave_id++) {
+		slaves[slave_id].print_buf = kzalloc(PT_MAX_PR_BUF_SIZE,
+					GFP_KERNEL);
+		if (!slaves[slave_id].print_buf) {
+			ret = scnprintf(buf, strlen(buf),
+				"Status: 1\n"
+				"Failed to alloc memory");
+			goto exit;
+		}
+		slaves[slave_id].status = 0;
+		slaves[slave_id].pr_index = 0;
+		pt_bist_slave_test(dev, &slaves[slave_id], slave_id);
+		status += slaves[slave_id].status;
+	}
+
+	idx_status = scnprintf(buf, strlen(buf), "Status: %d\n",
+			status == num_tests ? 0 : 1);
+	memcpy(buf + idx_status, host.print_buf, host.pr_index);
+	ret = idx_status + host.pr_index;
+
+	for (slave_id = 0; slave_id < num_slaves; slave_id++) {
+		memcpy(buf + ret, slaves[slave_id].print_buf,
+			slaves[slave_id].pr_index);
+		ret += slaves[slave_id].pr_index;
+	}
+
+exit:
+	/* 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);
+
+	/* 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(host.print_buf);
+	for (slave_id = 0; slave_id < num_slaves; slave_id++)
+		kfree(slaves[slave_id].print_buf);
+
+	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_WARN, "%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_WARN, "%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_WARN, "%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_WARN, "%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_WARN, "%s: Invalid value\n", __func__);
+		rc = -EINVAL;
+	}
+	mutex_unlock(&cd->system_lock);
+
+exit:
+	pt_debug(dev, DL_WARN, "%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 = snprintf(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 = snprintf(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 = snprintf(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,
+#ifndef TTDL_KERNEL_SUBMISSION
+	&dev_attr_pip2_cmd_rsp.attr,
+	&dev_attr_command.attr,
+	&dev_attr_drv_debug.attr,
+	&dev_attr_hw_reset.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 */
+#endif /* !TTDL_KERNEL_SUBMISSION */
+	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),
+#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);
+}
+#endif /* !TTDL_KERNEL_SUBMISSION */
+
+
+/*******************************************************************************
+ *******************************************************************************
+ * 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;
+	int rc = 0;
+#ifndef PT_PTSBC_SUPPORT
+	u8 pip_ver_major;
+	u8 pip_ver_minor;
+	u32 status = STARTUP_STATUS_START;
+#endif
+#ifdef TTDL_PTVIRTDUT_SUPPORT
+	int retry = 3;
+#endif
+
+	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_alloc_data;
+	}
+
+	/* 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->num_devices                = 1;
+	cd->tthe_hid_usb_format        = PT_TTHE_TUNER_FORMAT_HID_I2C;
+
+	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;
+#ifdef PT_PTSBC_SUPPORT
+	/* Extend first WD to allow DDI to complete configuration */
+	cd->watchdog_interval          = PT_PTSBC_INIT_WATCHDOG_TIMEOUT;
+#else
+	cd->watchdog_interval          = PT_WATCHDOG_TIMEOUT;
+#endif
+	cd->hid_cmd_state                 = 1;
+	cd->fw_updating                   = false;
+
+#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 */
+
+#ifdef TTDL_PTVIRTDUT_SUPPORT
+	/*
+	 * This variable is only used for BATS test. The
+	 * default value "true" has no effect on whether
+	 * the PT_DETECT_HW build flag is enabled or not.
+	 */
+	cd->hw_detect_enabled             = true;
+	cd->route_bus_virt_dut            = 0;
+#endif /* TTDL_PTVIRTDUT_SUPPORT */
+
+	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);
+	mutex_init(&cd->hid_report_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);
+
+	/* 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;
+
+	/* 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);
+#ifndef PT_PTSBC_SUPPORT
+	/* PtSBC builds will call this function in pt_probe_complete() */
+	pt_add_core(dev);
+#endif
+
+	rc = sysfs_create_group(&dev->kobj, &early_attr_group);
+	if (rc)
+		pt_debug(cd->dev, DL_WARN, "%s:create early attrs failed\n",
+			__func__);
+#ifndef TTDL_KERNEL_SUBMISSION
+	rc = device_create_bin_file(dev, &bin_attr_pt_response);
+	if (rc) {
+		pt_debug(dev, DL_ERROR,
+			"%s: Error, could not create node response\n",
+			__func__);
+	}
+#endif /* !TTDL_KERNEL_SUBMISSION */
+
+	/*
+	 * Save the pointer to a global value, which will be used
+	 * in ttdl_restart function
+	 */
+	cd->bus_ops = ops;
+
+	/*
+	 * 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_WARN, "%s:No irq_stat, Set cd->irq = %d\n",
+			__func__, cd->irq);
+	}
+
+#ifdef PT_PTSBC_SUPPORT
+	/*
+	 * For the PtSBC, on the first bring up, I2C/SPI will not be ready
+	 * in time so complete probe with pt_probe_complete() after work
+	 * probe timer expires.
+	 */
+	INIT_WORK(&cd->probe_work, pt_probe_work);
+#if (KERNEL_VERSION(4, 14, 0) > LINUX_VERSION_CODE)
+	setup_timer(&cd->probe_timer, pt_probe_timer, (unsigned long)cd);
+#else
+	timer_setup(&cd->probe_timer, pt_probe_timer, 0);
+#endif
+
+	/* Some host i2c/spi busses start late and then run too slow */
+	pt_debug(cd->dev, DL_INFO, "%s:start wait for probe timer\n",
+		__func__);
+	mod_timer(&cd->probe_timer, jiffies +
+			msecs_to_jiffies(PT_CORE_PROBE_STARTUP_DELAY_MS));
+	return rc;
+
+error_alloc_data:
+error_no_pdata:
+	pt_debug(dev, DL_ERROR, "%s failed.\n", __func__);
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_probe_complete
+ *
+ * SUMMARY: This function is only needed when PT_PTSBC_SUPPORT is enabled.
+ *	For the PtSBC, the probe functionality is split into two functions;
+ *	pt_probe() and pt_probe_complete(). The initial setup is done
+ *	in pt_probe() and the rest is done here after I2C/SPI is up. This
+ *	function also configures all voltage regulators for the PtSBC.
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *	*cd - pointer to the core data structure
+ ******************************************************************************/
+static int pt_probe_complete(struct pt_core_data *cd)
+{
+	int rc = -1;
+	u32 status = STARTUP_STATUS_START;
+	struct device *dev = cd->dev;
+	u8 pip_ver_major;
+	u8 pip_ver_minor;
+#ifdef TTDL_PTVIRTDUT_SUPPORT
+	int retry = 3;
+#endif
+
+	pt_debug(cd->dev, DL_DEBUG,
+		"%s: PARADE Entering Probe complete function\n", __func__);
+	pt_add_core(cd->dev);
+#endif /* --- End PT_PTSBC_SUPPORT --- */
+
+	/* 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_WARN, "%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_WARN, "%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 */
+
+#ifdef TTDL_PTVIRTDUT_SUPPORT
+	/*
+	 * In the case that the variable hw_detect_enabled needs to be set to
+	 * false as the default value, it needs to be set to true through
+	 * drv_debug sysfs node when testing HW detect function. But the probe()
+	 * function runs fast, and drv_debug may not set this variable to
+	 * true in time. This "retry" is to avoid this issue.
+	 */
+retry_hw_detect:
+	if (cd->hw_detect_enabled) {
+#endif
+		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_WARN,
+				"%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__);
+		}
+#ifdef TTDL_PTVIRTDUT_SUPPORT
+	} else {
+		if (retry--) {
+			msleep(50);
+			goto retry_hw_detect;
+		}
+	}
+#endif
+
+	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);
+
+	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_WARN,
+			" === 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_WARN,
+			" === 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_WARN, "%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);
+#ifndef PT_PTSBC_SUPPORT
+		/* For PtSBC don't error out, allow TTDL to stay up */
+		goto error_after_startup;
+#endif
+	}
+
+	/* Suspend scanning until probe is complete to avoid asyc touches */
+	pt_pip_suspend_scanning_(cd);
+
+#ifndef TTDL_KERNEL_SUBMISSION
+	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_WARN,
+			"%s: No HW detected, sysfs interfaces not added\n",
+			__func__);
+	}
+#endif /* !TTDL_KERNEL_SUBMISSION */
+
+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_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_debug(dev, DL_WARN, "%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);
+#ifndef TTDL_KERNEL_SUBMISSION
+	remove_sysfs_interfaces(dev);
+#endif /* !TTDL_KERNEL_SUBMISSION */
+
+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:
+#ifdef PT_PTSBC_SUPPORT
+	del_timer(&cd->probe_timer);
+#endif
+	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);
+	pt_del_core(dev);
+	dev_set_drvdata(dev, NULL);
+	kfree(cd);
+#ifndef PT_PTSBC_SUPPORT
+error_alloc_data:
+error_no_pdata:
+#endif
+	pr_err("%s failed.\n", __func__);
+	return rc;
+}
+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);
+#ifdef PT_PTSBC_SUPPORT
+	cancel_work_sync(&cd->probe_work);
+#endif
+	call_atten_cb(cd, PT_ATTEN_CANCEL_LOADER, 0);
+	cancel_work_sync(&cd->ttdl_restart_work);
+	cancel_work_sync(&cd->enum_work);
+	pt_stop_wd_timer(cd);
+#ifdef PT_PTSBC_SUPPORT
+	cancel_work_sync(&cd->irq_work);
+#endif
+
+	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_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);
+
+#ifndef TTDL_KERNEL_SUBMISSION
+	remove_sysfs_interfaces(dev);
+#endif /* !TTDL_KERNEL_SUBMISSION */
+
+	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);
+	pt_free_si_ptrs(cd);
+	pt_free_hid_reports(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 <[email protected]>");

+ 556 - 0
pt/pt_debug.c

@@ -0,0 +1,556 @@
+/*
+ * pt_debug.c
+ * Parade TrueTouch(TM) Standard Product Debug 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 <[email protected]>
+ */
+
+#include "pt_regs.h"
+
+#define PT_DEBUG_NAME "pt_debug"
+
+struct pt_debug_data {
+	struct device *dev;
+	struct pt_sysinfo *si;
+	uint32_t interrupt_count;
+	uint32_t formated_output;
+	struct mutex sysfs_lock;
+	u8 pr_buf[PT_MAX_PRBUF_SIZE];
+};
+
+static struct pt_core_commands *cmd;
+
+static struct pt_module debug_module;
+
+/*******************************************************************************
+ * FUNCTION: pt_get_debug_data
+ *
+ * SUMMARY: Inline function to get pt_debug_data pointer from debug module.
+ *
+ * RETURN:
+ *	 pointer to pt_debug_data structure
+ *
+ * PARAMETERS:
+ *	*dev          - pointer to device structure
+ ******************************************************************************/
+static inline struct pt_debug_data *pt_get_debug_data(
+		struct device *dev)
+{
+	return pt_get_module_data(dev, &debug_module);
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_pr_buf_op_mode
+ *
+ * SUMMARY: Formats touch/button report to pr_buf that combined xy_mode and
+ *  xy_data. The feature is required by TTHE.
+ *
+ * PARAMETERS:
+ *  *dd         - pointer to pt_debug_data structure
+ *  *pr_buf     - pointer to print buffer
+ *  *si         - pointer to sysinfo structure
+ *   cur_touch  - number of current touch
+ ******************************************************************************/
+static void pt_pr_buf_op_mode(struct pt_debug_data *dd, u8 *pr_buf,
+		struct pt_sysinfo *si, u8 cur_touch)
+{
+	const char fmt[] = "%02X ";
+	int max = (PT_MAX_PRBUF_SIZE - 1) - sizeof(PT_PR_TRUNCATED);
+	u8 report_id = si->xy_mode[2];
+	int header_size = 0;
+	int report_size = 0;
+	int total_size = 0;
+	int i, k;
+
+	if (report_id == si->desc.tch_report_id) {
+		header_size = si->desc.tch_header_size;
+		report_size = cur_touch * si->desc.tch_record_size;
+	} else if (report_id == si->desc.btn_report_id) {
+		header_size = BTN_INPUT_HEADER_SIZE;
+		report_size = BTN_REPORT_SIZE;
+	}
+	total_size = header_size + report_size;
+
+	pr_buf[0] = 0;
+	for (i = k = 0; i < header_size && i < max; i++, k += 3)
+		scnprintf(pr_buf + k, PT_MAX_PRBUF_SIZE, fmt, si->xy_mode[i]);
+
+	for (i = 0; i < report_size && i < max; i++, k += 3)
+		scnprintf(pr_buf + k, PT_MAX_PRBUF_SIZE, fmt, si->xy_data[i]);
+
+	pr_info("%s=%s%s\n", "pt_OpModeData", pr_buf,
+			total_size <= max ? "" : PT_PR_TRUNCATED);
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_debug_print
+ *
+ * SUMMARY: This function prints header to show data size and data_name and
+ *  content of "pr_buf" with hex base.
+ *
+ * PARAMETERS:
+ *  *dev        - pointer to device structure
+ *  *pr_buf     - pointer to input buffer which stores the formated data
+ *  *sptr       - pointer to the buffer to print
+ *   size       - size of data elements
+ *  *data_name  - data name to print
+ ******************************************************************************/
+static void pt_debug_print(struct device *dev, u8 *pr_buf, u8 *sptr,
+		int size, const char *data_name)
+{
+	int i, j;
+	int elem_size = sizeof("XX ") - 1;
+	int max = (PT_MAX_PRBUF_SIZE - 1) / elem_size;
+	int limit = size < max ? size : max;
+
+	if (limit < 0)
+		limit = 0;
+
+	pr_buf[0] = 0;
+	for (i = j = 0; i < limit; i++, j += elem_size)
+		scnprintf(pr_buf + j, PT_MAX_PRBUF_SIZE - j, "%02X ", sptr[i]);
+
+	if (size)
+		pr_info("%s[0..%d]=%s%s\n", data_name, size - 1, pr_buf,
+			size <= max ? "" : PT_PR_TRUNCATED);
+	else
+		pr_info("%s[]\n", data_name);
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_debug_formated
+ *
+ * SUMMARY: Formats and prints touch & button report.
+ *
+ * PARAMETERS:
+ *  *dev        - pointer to device structure
+ *  *pr_buf     - pointer to print buffer
+ *  *si         - pointer to sysinfo structure
+ *   cur_touch  - number of current touch
+ ******************************************************************************/
+static void pt_debug_formated(struct device *dev, u8 *pr_buf,
+		struct pt_sysinfo *si, u8 num_cur_tch)
+{
+	u8 report_id = si->xy_mode[2];
+	int header_size = 0;
+	int report_size = 0;
+	u8 data_name[] = "touch[99]";
+	int max_print_length = 20;
+	int i;
+
+	if (report_id == si->desc.tch_report_id) {
+		header_size = si->desc.tch_header_size;
+		report_size = num_cur_tch * si->desc.tch_record_size;
+	} else if (report_id == si->desc.btn_report_id) {
+		header_size = BTN_INPUT_HEADER_SIZE;
+		report_size = BTN_REPORT_SIZE;
+	}
+
+	/* xy_mode */
+	pt_debug_print(dev, pr_buf, si->xy_mode, header_size, "xy_mode");
+
+	/* xy_data */
+	if (report_size > max_print_length) {
+		pr_info("xy_data[0..%d]:\n", report_size);
+		for (i = 0; i < report_size - max_print_length;
+				i += max_print_length) {
+			pt_debug_print(dev, pr_buf, si->xy_data + i,
+					max_print_length, " ");
+		}
+		if (report_size - i)
+			pt_debug_print(dev, pr_buf, si->xy_data + i,
+					report_size - i, " ");
+	} else {
+		pt_debug_print(dev, pr_buf, si->xy_data, report_size,
+				"xy_data");
+	}
+
+	/* touches */
+	if (report_id == si->desc.tch_report_id) {
+		for (i = 0; i < num_cur_tch; i++) {
+			scnprintf(data_name, sizeof(data_name) - 1,
+					"touch[%u]", i);
+			pt_debug_print(dev, pr_buf,
+				si->xy_data + (i * si->desc.tch_record_size),
+				si->desc.tch_record_size, data_name);
+		}
+	}
+
+	/* buttons */
+	if (report_id == si->desc.btn_report_id)
+		pt_debug_print(dev, pr_buf, si->xy_data, report_size,
+				"button");
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_xy_worker
+ *
+ * SUMMARY: Read xy_data for all touches for debug.
+ *
+ * RETURN:
+ *   0 = success
+ *
+ * PARAMETERS:
+ *  *dd - pointer to pt_debug_data structure
+ ******************************************************************************/
+static int pt_xy_worker(struct pt_debug_data *dd)
+{
+	struct device *dev = dd->dev;
+	struct pt_sysinfo *si = dd->si;
+	u8 report_reg = si->xy_mode[TOUCH_COUNT_BYTE_OFFSET];
+	u8 num_cur_tch = GET_NUM_TOUCHES(report_reg);
+	uint32_t formated_output;
+
+	mutex_lock(&dd->sysfs_lock);
+	dd->interrupt_count++;
+	formated_output = dd->formated_output;
+	mutex_unlock(&dd->sysfs_lock);
+
+	/* Interrupt */
+	pr_info("Interrupt(%u)\n", dd->interrupt_count);
+
+	if (formated_output)
+		pt_debug_formated(dev, dd->pr_buf, si, num_cur_tch);
+	else
+		/* print data for TTHE */
+		pt_pr_buf_op_mode(dd, dd->pr_buf, si, num_cur_tch);
+
+	pr_info("\n");
+
+	return 0;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_debug_attention
+ *
+ * SUMMARY: Wrapper function for pt_xy_worker() to subcribe into TTDL attention
+ *  list.
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *	*dev  - pointer to device structure
+ ******************************************************************************/
+static int pt_debug_attention(struct device *dev)
+{
+	struct pt_debug_data *dd = pt_get_debug_data(dev);
+	struct pt_sysinfo *si = dd->si;
+	u8 report_id = si->xy_mode[2];
+	int rc = 0;
+
+	if (report_id != si->desc.tch_report_id
+			&& report_id != si->desc.btn_report_id)
+		return 0;
+
+	/* core handles handshake */
+	rc = pt_xy_worker(dd);
+	if (rc < 0)
+		pt_debug(dev, DL_ERROR, "%s: xy_worker error r=%d\n",
+			__func__, rc);
+
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_status_show
+ *
+ * SUMMARY: The show method for the int_count sysfs node. This node displays
+ *	the count of interrupt.
+ *
+ * PARAMETERS:
+ *      *dev  - pointer to Device structure
+ *      *attr - pointer to the device attribute structure
+ *      *buf  - pointer to buffer to print
+ ******************************************************************************/
+static ssize_t pt_interrupt_count_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct pt_debug_data *dd = pt_get_debug_data(dev);
+	int val;
+
+	mutex_lock(&dd->sysfs_lock);
+	val = dd->interrupt_count;
+	mutex_unlock(&dd->sysfs_lock);
+
+	return scnprintf(buf, PT_MAX_PRBUF_SIZE, "Interrupt Count: %d\n", val);
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_interrupt_count_store
+ *
+ * SUMMARY: The store method for the int_count sysfs node that allows the count
+ *	of interrput to be cleared.
+ *
+ * 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_interrupt_count_store(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t size)
+{
+	struct pt_debug_data *dd = pt_get_debug_data(dev);
+
+	mutex_lock(&dd->sysfs_lock);
+	dd->interrupt_count = 0;
+	mutex_unlock(&dd->sysfs_lock);
+	return size;
+}
+
+static DEVICE_ATTR(int_count, 0600,
+	pt_interrupt_count_show, pt_interrupt_count_store);
+
+/*******************************************************************************
+ * FUNCTION: pt_formated_output_show
+ *
+ * SUMMARY: Show method for the formated_output sysfs node that will show
+ *  whether to format data to buffer or print directly.
+ *
+ * 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_formated_output_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct pt_debug_data *dd = pt_get_debug_data(dev);
+	int val;
+
+	mutex_lock(&dd->sysfs_lock);
+	val = dd->formated_output;
+	mutex_unlock(&dd->sysfs_lock);
+
+	return scnprintf(buf, PT_MAX_PRBUF_SIZE,
+			"Formated debug output: %x\n", val);
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_formated_output_store
+ *
+ * SUMMARY: The store method for the formated_output sysfs node. Allows the
+ *  setting to format data to buffer or print directly.
+ *
+ * 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_formated_output_store(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t size)
+{
+	struct pt_debug_data *dd = pt_get_debug_data(dev);
+	unsigned long value;
+	int rc;
+
+	rc = kstrtoul(buf, 10, &value);
+	if (rc < 0) {
+		pt_debug(dev, DL_ERROR, "%s: Invalid value\n", __func__);
+		return size;
+	}
+
+	/* Expecting only 0 or 1 */
+	if (value != 0 && value != 1) {
+		pt_debug(dev, DL_ERROR, "%s: Invalid value %lu\n",
+					__func__, value);
+		return size;
+	}
+
+	mutex_lock(&dd->sysfs_lock);
+	dd->formated_output = value;
+	mutex_unlock(&dd->sysfs_lock);
+	return size;
+}
+
+static DEVICE_ATTR(formated_output, 0600,
+	pt_formated_output_show, pt_formated_output_store);
+
+/*******************************************************************************
+ * FUNCTION: pt_mt_probe
+ *
+ * SUMMARY: The probe function for debug module to create sysfs nodes and
+ *  subscribe attention list.
+ *
+ * RETURN:
+ *	 0 = success
+ *
+ * PARAMETERS:
+ *   *dev   - pointer to device structure
+ *  **data  - double pointer tothe pt_debug_data structure to be created here
+ ******************************************************************************/
+static int pt_debug_probe(struct device *dev, void **data)
+{
+	struct pt_debug_data *dd;
+	int rc;
+
+	/* get context and debug print buffers */
+	dd = kzalloc(sizeof(*dd), GFP_KERNEL);
+	if (!dd) {
+		rc = -ENOMEM;
+		goto pt_debug_probe_alloc_failed;
+	}
+
+	rc = device_create_file(dev, &dev_attr_int_count);
+	if (rc) {
+		pt_debug(dev, DL_ERROR, "%s: Error, could not create int_count\n",
+					__func__);
+		goto pt_debug_probe_create_int_count_failed;
+	}
+
+	rc = device_create_file(dev, &dev_attr_formated_output);
+	if (rc) {
+		pt_debug(dev, DL_ERROR, "%s: Error, could not create formated_output\n",
+					__func__);
+		goto pt_debug_probe_create_formated_failed;
+	}
+
+	mutex_init(&dd->sysfs_lock);
+	dd->dev = dev;
+	*data = dd;
+
+	dd->si = cmd->request_sysinfo(dev);
+	if (!dd->si) {
+		pt_debug(dev, DL_ERROR, "%s: Fail get sysinfo pointer from core\n",
+					__func__);
+		rc = -ENODEV;
+		goto pt_debug_probe_sysinfo_failed;
+	}
+
+	rc = cmd->subscribe_attention(dev, PT_ATTEN_IRQ, PT_DEBUG_NAME,
+		pt_debug_attention, PT_MODE_OPERATIONAL);
+	if (rc < 0) {
+		pt_debug(dev, DL_ERROR, "%s: Error, could not subscribe attention cb\n",
+					__func__);
+		goto pt_debug_probe_subscribe_failed;
+	}
+
+	return 0;
+
+pt_debug_probe_subscribe_failed:
+pt_debug_probe_sysinfo_failed:
+	device_remove_file(dev, &dev_attr_formated_output);
+pt_debug_probe_create_formated_failed:
+	device_remove_file(dev, &dev_attr_int_count);
+pt_debug_probe_create_int_count_failed:
+	kfree(dd);
+pt_debug_probe_alloc_failed:
+	pt_debug(dev, DL_ERROR, "%s failed.\n", __func__);
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_debug_release
+ *
+ * SUMMARY: Remove function for debug module that does following cleanup:
+ *	- Unsubscibe all registered attention tasks
+ *	- Removes all created sysfs nodes
+ *
+ * PARAMETERS:
+ *	*dev   - pointer to device structure
+ *	*data  - pointer to the pt_debug_data structure
+ ******************************************************************************/
+static void pt_debug_release(struct device *dev, void *data)
+{
+	struct pt_debug_data *dd = data;
+	int rc;
+
+	rc = cmd->unsubscribe_attention(dev, PT_ATTEN_IRQ, PT_DEBUG_NAME,
+		pt_debug_attention, PT_MODE_OPERATIONAL);
+	if (rc < 0) {
+		pt_debug(dev, DL_ERROR, "%s: Error, could not un-subscribe attention\n",
+					__func__);
+		goto pt_debug_release_exit;
+	}
+
+pt_debug_release_exit:
+	device_remove_file(dev, &dev_attr_formated_output);
+	device_remove_file(dev, &dev_attr_int_count);
+	kfree(dd);
+}
+
+static struct pt_module debug_module = {
+	.name = PT_DEBUG_NAME,
+	.probe = pt_debug_probe,
+	.release = pt_debug_release,
+};
+
+/*******************************************************************************
+ * FUNCTION: pt_debug_init
+ *
+ * SUMMARY: Initialize function for debug module which to register
+ *  debug_module into TTDL module list.
+ *
+ * RETURN:
+ *	 0 = success
+ ******************************************************************************/
+static int __init pt_debug_init(void)
+{
+	int rc;
+
+	cmd = pt_get_commands();
+	if (!cmd)
+		return -EINVAL;
+
+	rc = pt_register_module(&debug_module);
+	if (rc < 0) {
+		pr_err("%s: Error, failed registering module\n",
+			__func__);
+			return rc;
+	}
+
+	pr_info("%s: Parade TTSP Debug Driver (Built %s) rc=%d\n",
+		 __func__, PT_DRIVER_VERSION, rc);
+	return 0;
+}
+module_init(pt_debug_init);
+
+/*******************************************************************************
+ * FUNCTION: pt_debug_exit
+ *
+ * SUMMARY: Exit function for debug module which to unregister debug_module
+ * from TTDL module list.
+ *
+ ******************************************************************************/
+static void __exit pt_debug_exit(void)
+{
+	pt_unregister_module(&debug_module);
+}
+module_exit(pt_debug_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Parade TrueTouch(R) Standard Product Debug Driver");
+MODULE_AUTHOR("Parade Technologies <[email protected]>");

+ 6830 - 0
pt/pt_device_access.c

@@ -0,0 +1,6830 @@
+#ifndef TTDL_KERNEL_SUBMISSION
+/*
+ * pt_device_access.c
+ * Parade TrueTouch(TM) Standard Product Device Access Module.
+ * Configuration and Test command/status user interface.
+ * 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 <[email protected]>
+ */
+
+#include "pt_regs.h"
+#include <linux/firmware.h>
+
+#include <linux/timer.h>
+#include <linux/timex.h>
+#include <linux/rtc.h>
+
+#define PT_CMCP_THRESHOLD_FILE_NAME "pt_thresholdfile.csv"
+#define CMCP_THRESHOLD_FILE_NAME "ttdl_cmcp_thresholdfile.csv"
+
+/* Max test case number */
+#define MAX_CASE_NUM            (23)
+
+/* ASCII */
+#define ASCII_LF                (0x0A)
+#define ASCII_CR                (0x0D)
+#define ASCII_COMMA             (0x2C)
+#define ASCII_ZERO              (0x30)
+#define ASCII_NINE              (0x39)
+
+/* Max characters of test case name */
+#define NAME_SIZE_MAX           (50)
+
+/* Max characters of project information */
+#define NAME_PROJECT_INFO_MAX   (128)
+
+/* Max sensor and button number */
+#define MAX_BUTTONS             (PIP1_SYSINFO_MAX_BTN)
+#define MAX_SENSORS             (5120)
+#define MAX_TX_SENSORS          (128)
+#define MAX_RX_SENSORS          (128)
+
+/* Multiply by 2 for double (min, max) values */
+#define TABLE_BUTTON_MAX_SIZE   (MAX_BUTTONS * 2)
+#define TABLE_SENSOR_MAX_SIZE   (MAX_SENSORS * 2)
+#define TABLE_TX_MAX_SIZE       (MAX_TX_SENSORS*2)
+#define TABLE_RX_MAX_SIZE       (MAX_RX_SENSORS*2)
+
+#define CM_PANEL_DATA_OFFSET    (6)
+#define CM_BTN_DATA_OFFSET      (6)
+#define CP_PANEL_DATA_OFFSET    (6)
+#define CP_BTN_DATA_OFFSET      (6)
+#define MAX_BUF_LEN             (100000)
+#define RETRIEVE_PANEL_SCAN_HDR (10)
+
+enum print_buffer_format {
+	PT_PR_FORMAT_DEFAULT        = 0,
+	PT_PR_FORMAT_U8_SPACE       = 1,
+	PT_PR_FORMAT_U16_SPACE      = 2,
+	PT_PR_FORMAT_U8_NO_SPACE    = 3,
+	PT_PR_FORMAT_U32_SPACE      = 4,
+	PT_PR_FORMAT_UNDEFINE
+};
+
+/* cmcp csv file information */
+struct configuration {
+	/* One more space in the arrary below is left for null character */
+	char proj_info[NAME_PROJECT_INFO_MAX + 1];
+	u32 cm_range_limit_row;
+	u32 cm_range_limit_col;
+	u32 cm_min_limit_cal;
+	u32 cm_max_limit_cal;
+	u32 cm_max_delta_sensor_percent;
+	u32 cm_max_delta_button_percent;
+	u32 min_button;
+	u32 max_button;
+	u32 cp_max_delta_sensor_rx_percent;
+	u32 cp_max_delta_sensor_tx_percent;
+	u32 cm_min_max_table_btn[TABLE_BUTTON_MAX_SIZE];
+	u32 cp_min_max_table_btn[TABLE_BUTTON_MAX_SIZE];
+	u32 cm_min_max_table_sensor[TABLE_SENSOR_MAX_SIZE];
+	u32 cp_min_max_table_rx[TABLE_RX_MAX_SIZE];
+	u32 cp_min_max_table_tx[TABLE_TX_MAX_SIZE];
+	u32 cm_min_max_table_btn_size;
+	u32 cp_min_max_table_btn_size;
+	u32 cm_min_max_table_sensor_size;
+	u32 cp_min_max_table_rx_size;
+	u32 cp_min_max_table_tx_size;
+	u32 cp_max_delta_button_percent;
+	u32 cm_max_table_gradient_cols_percent[TABLE_TX_MAX_SIZE];
+	u32 cm_max_table_gradient_cols_percent_size;
+	u32 cm_max_table_gradient_rows_percent[TABLE_RX_MAX_SIZE];
+	u32 cm_max_table_gradient_rows_percent_size;
+	u32 cm_excluding_row_edge;
+	u32 cm_excluding_col_edge;
+	u32 rx_num;
+	u32 tx_num;
+	u32 btn_num;
+	u32 cm_enabled;
+	u32 cp_enabled;
+	u32 is_valid_or_not;
+};
+
+/* Test case search definition */
+struct test_case_search {
+	char name[NAME_SIZE_MAX]; /* Test case name */
+	u32 name_size;            /* Test case name size */
+	u32 offset;               /* Test case offset */
+};
+
+/* Test case field definition */
+struct test_case_field {
+	char *name;     /* Test case name */
+	u32 name_size;  /* Test case name size */
+	u32 type;       /* Test case type */
+	u32 *bufptr;    /* Buffer to store value information */
+	u32 exist_or_not;/* Test case exist or not */
+	u32 data_num;   /* Buffer data number */
+	u32 line_num;   /* Buffer line number */
+};
+
+/* Test case type */
+enum test_case_type {
+	TEST_CASE_TYPE_NO,
+	TEST_CASE_TYPE_ONE,
+	TEST_CASE_TYPE_MUL,
+	TEST_CASE_TYPE_MUL_LINES,
+	TEST_CASE_TYPE_STRING,
+};
+
+/* Test case order in test_case_field_array */
+enum case_order {
+	PROJ_VERSION,
+	CM_TEST_INPUTS,
+	CM_EXCLUDING_COL_EDGE,
+	CM_EXCLUDING_ROW_EDGE,
+	CM_GRADIENT_CHECK_COL,
+	CM_GRADIENT_CHECK_ROW,
+	CM_RANGE_LIMIT_ROW,
+	CM_RANGE_LIMIT_COL,
+	CM_MIN_LIMIT_CAL,
+	CM_MAX_LIMIT_CAL,
+	CM_MAX_DELTA_SENSOR_PERCENT,
+	CM_MAX_DELTA_BUTTON_PERCENT,
+	PER_ELEMENT_MIN_MAX_TABLE_BUTTON,
+	PER_ELEMENT_MIN_MAX_TABLE_SENSOR,
+	CP_TEST_INPUTS,
+	CP_MAX_DELTA_SENSOR_RX_PERCENT,
+	CP_MAX_DELTA_SENSOR_TX_PERCENT,
+	CP_MAX_DELTA_BUTTON_PERCENT,
+	CP_PER_ELEMENT_MIN_MAX_BUTTON,
+	MIN_BUTTON,
+	MAX_BUTTON,
+	PER_ELEMENT_MIN_MAX_RX,
+	PER_ELEMENT_MIN_MAX_TX,
+	CASE_ORDER_MAX,
+};
+
+enum cmcp_test_item {
+	CMCP_FULL = 0,
+	CMCP_CM_PANEL,
+	CMCP_CP_PANEL,
+	CMCP_CM_BTN,
+	CMCP_CP_BTN,
+};
+
+#define CM_ENABLED 0x10
+#define CP_ENABLED 0x20
+#define CM_PANEL (0x01 | CM_ENABLED)
+#define CP_PANEL (0x02 | CP_ENABLED)
+#define CM_BTN (0x04 | CM_ENABLED)
+#define CP_BTN (0x08 | CP_ENABLED)
+#define CMCP_FULL_CASE\
+	(CM_PANEL | CP_PANEL | CM_BTN | CP_BTN | CM_ENABLED | CP_ENABLED)
+
+#define PT_DEVICE_ACCESS_NAME "pt_device_access"
+#define PT_INPUT_ELEM_SZ (sizeof("0xHH") + 1)
+
+#define PIP_CMD_MAX_LENGTH ((1 << 16) - 1)
+
+#ifdef TTHE_TUNER_SUPPORT
+struct heatmap_param {
+	bool scan_start;
+	enum scan_data_type_list data_type; /* raw, base, diff */
+	int num_element;
+};
+#endif
+#define ABS(x)			(((x) < 0) ? -(x) : (x))
+
+#ifndef MIN
+#define MIN(a, b)  (((a) < (b)) ? (a) : (b))
+#endif
+
+#define PT_MAX_CONFIG_BYTES    256
+#define PT_TTHE_TUNER_GET_PANEL_DATA_FILE_NAME "get_panel_data"
+#define TTHE_TUNER_MAX_BUF	(PT_MAX_PRBUF_SIZE * 8)
+
+struct pt_device_access_data {
+	struct device *dev;
+	struct pt_sysinfo *si;
+	struct mutex sysfs_lock;
+	bool sysfs_nodes_created;
+	struct kobject mfg_test;
+	u8 panel_scan_retrieve_id;
+	u8 panel_scan_type_id;
+	u8 get_idac_data_id;
+	u8 calibrate_sensing_mode;
+	u8 calibrate_initialize_baselines;
+	u8 baseline_sensing_mode;
+	u8 fw_self_test_id;
+	u8 fw_self_test_format;
+	u16 fw_self_test_param_len;
+	u8 fw_self_test_param[PT_FW_SELF_TEST_MAX_PARM];
+	struct pt_cal_ext_data cal_ext_data;
+	struct dentry *panel_scan_debugfs;
+	int panel_scan_size;
+	u8 panel_scan_data_buf[TTHE_TUNER_MAX_BUF];
+	struct mutex debugfs_lock;
+#ifdef TTHE_TUNER_SUPPORT
+	struct heatmap_param heatmap;
+	struct dentry *tthe_get_panel_data_debugfs;
+	u8 tthe_get_panel_data_is_open;
+#endif
+	struct dentry *cmcp_results_debugfs;
+	struct dentry *base_dentry;
+	struct dentry *mfg_test_dentry;
+	u8 ic_buf[10 * PT_MAX_PRBUF_SIZE];
+	u8 response_buf[PT_MAX_PRBUF_SIZE];
+	struct mutex cmcp_threshold_lock;
+	u8 *cmcp_threshold_data;
+	int cmcp_threshold_size;
+	bool cmcp_threshold_loading;
+	struct work_struct cmcp_threshold_update;
+	int builtin_cmcp_threshold_status;
+	bool is_manual_upgrade_enabled;
+	struct configuration *configs;
+	struct cmcp_data *cmcp_info;
+	struct result *result;
+	struct test_case_search *test_search_array;
+	struct test_case_field *test_field_array;
+	int cmcp_test_items;
+	int test_executed;
+	int cmcp_range_check;
+	int cmcp_force_calibrate;
+	int cmcp_test_in_progress;
+};
+
+struct cmcp_data {
+	struct gd_sensor *gd_sensor_col;
+	struct gd_sensor *gd_sensor_row;
+	int32_t *cm_data_panel;
+	int32_t *cp_tx_data_panel;
+	int32_t *cp_rx_data_panel;
+	int32_t *cp_tx_cal_data_panel;
+	int32_t *cp_rx_cal_data_panel;
+	int32_t cp_sensor_rx_delta;
+	int32_t cp_sensor_tx_delta;
+	int32_t cp_button_delta;
+	int32_t *cm_btn_data;
+	int32_t *cp_btn_data;
+	int32_t *cm_sensor_column_delta;
+	int32_t *cm_sensor_row_delta;
+	int32_t cp_btn_cal;
+	int32_t cm_btn_cal;
+	int32_t cp_button_ave;
+	int32_t cm_ave_data_panel;
+	int32_t cp_tx_ave_data_panel;
+	int32_t cp_rx_ave_data_panel;
+	int32_t cm_cal_data_panel;
+	int32_t cm_ave_data_btn;
+	int32_t cm_cal_data_btn;
+	int32_t cm_delta_data_btn;
+	int32_t cm_sensor_delta;
+
+	int32_t tx_num;
+	int32_t rx_num;
+	int32_t btn_num;
+};
+
+struct result {
+	int32_t config_ver;
+	int32_t revision_ctrl;
+	int32_t device_id_high;
+	int32_t device_id_low;
+	/* Sensor Cm validation */
+	bool cm_test_pass;
+	bool cm_sensor_validation_pass;
+	bool cm_sensor_row_delta_pass;
+	bool cm_sensor_col_delta_pass;
+	bool cm_sensor_gd_row_pass;
+	bool cm_sensor_gd_col_pass;
+	bool cm_sensor_calibration_pass;
+	bool cm_sensor_delta_pass;
+	bool cm_button_validation_pass;
+	bool cm_button_delta_pass;
+
+	int32_t *cm_sensor_raw_data;
+	int32_t cm_sensor_calibration;
+	int32_t cm_sensor_delta;
+	int32_t *cm_button_raw_data;
+	int32_t cm_button_delta;
+
+	/* Sensor Cp validation */
+	bool cp_test_pass;
+	bool cp_sensor_delta_pass;
+	bool cp_sensor_rx_delta_pass;
+	bool cp_sensor_tx_delta_pass;
+	bool cp_sensor_average_pass;
+	bool cp_button_delta_pass;
+	bool cp_button_average_pass;
+	bool cp_rx_validation_pass;
+	bool cp_tx_validation_pass;
+	bool cp_button_validation_pass;
+
+	int32_t *cp_sensor_rx_raw_data;
+	int32_t *cp_sensor_tx_raw_data;
+	int32_t cp_sensor_rx_delta;
+	int32_t cp_sensor_tx_delta;
+	int32_t cp_sensor_rx_calibration;
+	int32_t cp_sensor_tx_calibration;
+	int32_t *cp_button_raw_data;
+	int32_t cp_button_delta;
+
+	/*other validation*/
+	bool short_test_pass;
+	bool test_summary;
+};
+
+static struct pt_core_commands *cmd;
+
+static struct pt_module device_access_module;
+
+static ssize_t pt_run_and_get_selftest_result(struct device *dev,
+		int protect, char *buf, size_t buf_len, u8 test_id,
+		u16 read_length, u8 get_result_on_pass,
+		bool print_results, u8 print_format);
+
+static int _pt_calibrate_idacs_cmd(struct device *dev,
+		u8 sensing_mode, u8 *status);
+
+static int pt_perform_calibration(struct device *dev);
+
+/*******************************************************************************
+ * FUNCTION: pt_get_device_access_data
+ *
+ * SUMMARY: Inline function to get pt_device_access_data.
+ *
+ * RETURN:
+ *	 pointer to pt_device_access_data structure
+ *
+ * PARAMETERS:
+ *	*dev          - pointer to device structure
+ ******************************************************************************/
+static inline struct pt_device_access_data *pt_get_device_access_data(
+		struct device *dev)
+{
+	return pt_get_module_data(dev, &device_access_module);
+}
+
+/*******************************************************************************
+ * FUNCTION: cmcp_check_config_fw_match
+ *
+ * SUMMARY: Checks if tx,rx and btn num of firmware match with configuration.
+ *
+ * RETURN:
+ *	 0 = match
+ *	!0 = doesn't match
+ *
+ * PARAMETERS:
+ *	*dev           - pointer to device structure
+ *	*configuration - pointer to configuration structure
+ ******************************************************************************/
+static int cmcp_check_config_fw_match(struct device *dev,
+	struct configuration *configuration)
+{
+	struct pt_device_access_data *dad
+		= pt_get_device_access_data(dev);
+	int32_t tx_num = dad->configs->tx_num;
+	int32_t rx_num = dad->configs->rx_num;
+	int32_t button_num = dad->configs->btn_num;
+	int ret = 0;
+
+	if (tx_num != dad->si->sensing_conf_data.tx_num) {
+		pt_debug(dev, DL_ERROR,
+			"%s: TX number mismatch! CSV=%d DUT=%d\n",
+			__func__, tx_num, dad->si->sensing_conf_data.tx_num);
+		ret = -EINVAL;
+	}
+
+	if (rx_num != dad->si->sensing_conf_data.rx_num) {
+		pt_debug(dev, DL_ERROR,
+			"%s: RX number mismatch! CSV=%d DUT=%d\n",
+			__func__, rx_num, dad->si->sensing_conf_data.rx_num);
+		ret = -EINVAL;
+	}
+
+	if (button_num != dad->si->num_btns) {
+		pt_debug(dev, DL_ERROR,
+			"%s: Button number mismatch! CSV=%d DUT=%d\n",
+			__func__, button_num, dad->si->num_btns);
+		ret = -EINVAL;
+	}
+
+	return ret;
+}
+
+/*******************************************************************************
+ * FUNCTION: validate_cm_test_results
+ *
+ * SUMMARY: Checks cm test results and outputs each test and a summary result
+ *
+ * RETURN:
+ *   0 = success
+ *  !0 = failure
+ *
+ * PARAMETERS:
+ *  *dev           - pointer to device structure
+ *  *configuration - pointer to configuration structure
+ *  *cmcp_info     - pointer to cmcp_data structure to store cmcp data from fw
+ *  *result        - pointer to result structure
+ *  *pass          - pointer to bool value
+ *   test_item     - flag to store all test item are requested
+ ******************************************************************************/
+static int validate_cm_test_results(struct device *dev,
+	struct configuration *configuration, struct cmcp_data *cmcp_info,
+	struct result *result, bool *pass, int test_item)
+{
+	int32_t tx_num = cmcp_info->tx_num;
+	int32_t rx_num = cmcp_info->rx_num;
+	int32_t button_num =  cmcp_info->btn_num;
+	uint32_t sensor_num = tx_num * rx_num;
+	int32_t *cm_sensor_data = cmcp_info->cm_data_panel;
+	int32_t cm_button_delta;
+	int32_t cm_sensor_calibration;
+	int32_t *cm_button_data = cmcp_info->cm_btn_data;
+	struct gd_sensor *gd_sensor_col = cmcp_info->gd_sensor_col;
+	struct gd_sensor *gd_sensor_row = cmcp_info->gd_sensor_row;
+	int32_t *cm_sensor_column_delta = cmcp_info->cm_sensor_column_delta;
+	int32_t *cm_sensor_row_delta = cmcp_info->cm_sensor_row_delta;
+	int ret = 0;
+	int i, j;
+
+	pt_debug(dev, DL_INFO, "%s: start\n", __func__);
+
+	if ((test_item & CM_PANEL) == CM_PANEL) {
+		pt_debug(dev, DL_INFO,
+			"Check each sensor Cm data for min max value\n ");
+
+		/* Check each sensor Cm data for min/max values */
+		result->cm_sensor_validation_pass = true;
+
+	for (i = 0; i < sensor_num; i++) {
+		int row = i % rx_num;
+		int col = i / rx_num;
+		int32_t cm_sensor_min =
+		  configuration->cm_min_max_table_sensor[(row*tx_num+col)*2];
+		int32_t cm_sensor_max =
+		  configuration->cm_min_max_table_sensor[(row*tx_num+col)*2+1];
+		if ((cm_sensor_data[i] < cm_sensor_min) ||
+		    (cm_sensor_data[i] > cm_sensor_max)) {
+			pt_debug(dev, DL_WARN,
+					"%s: Sensor[%d,%d]:%d (%d,%d)\n",
+					"Cm sensor min/max test",
+					row, col,
+					cm_sensor_data[i],
+					cm_sensor_min, cm_sensor_max);
+			result->cm_sensor_validation_pass = false;
+		}
+	}
+
+	/*check cm gradient column data*/
+	result->cm_sensor_gd_col_pass = true;
+	for (i = 0; i < configuration->cm_max_table_gradient_cols_percent_size;
+	     i++) {
+		if ((gd_sensor_col + i)->gradient_val >
+		    10 * configuration->cm_max_table_gradient_cols_percent[i]) {
+			pt_debug(dev, DL_WARN,
+			"%s: cm_max_table_gradient_cols_percent[%d]:%d, gradient_val:%d\n",
+			__func__, i,
+			configuration->cm_max_table_gradient_cols_percent[i],
+			(gd_sensor_col + i)->gradient_val);
+			result->cm_sensor_gd_col_pass = false;
+		}
+	}
+
+	/*check cm gradient row data*/
+	result->cm_sensor_gd_row_pass = true;
+	for (j = 0; j < configuration->cm_max_table_gradient_rows_percent_size;
+	     j++) {
+		if ((gd_sensor_row + j)->gradient_val >
+		    10 * configuration->cm_max_table_gradient_rows_percent[j]) {
+			pt_debug(dev, DL_WARN,
+			"%s: cm_max_table_gradient_rows_percent[%d]:%d, gradient_val:%d\n",
+			__func__, j,
+			configuration->cm_max_table_gradient_rows_percent[j],
+			(gd_sensor_row + j)->gradient_val);
+			result->cm_sensor_gd_row_pass = false;
+		}
+	}
+
+	result->cm_sensor_row_delta_pass = true;
+	result->cm_sensor_col_delta_pass = true;
+	result->cm_sensor_calibration_pass = true;
+	result->cm_sensor_delta_pass = true;
+
+	/* Check each row Cm data with neighbor for difference */
+	for (i = 0; i < tx_num; i++) {
+		for (j = 1; j < rx_num; j++) {
+			int32_t cm_sensor_row_diff =
+				ABS(cm_sensor_data[i * rx_num + j] -
+				cm_sensor_data[i * rx_num + j - 1]);
+			cm_sensor_row_delta[i * rx_num + j - 1] =
+				cm_sensor_row_diff;
+			if (cm_sensor_row_diff >
+			    configuration->cm_range_limit_row) {
+				pt_debug(dev, DL_DEBUG,
+					"%s: Sensor[%d,%d]:%d (%d)\n",
+					"Cm sensor row range limit test",
+					j, i, cm_sensor_row_diff,
+					configuration->cm_range_limit_row);
+				result->cm_sensor_row_delta_pass = false;
+			}
+		}
+	}
+
+	/* Check each column Cm data with neighbor for difference */
+	for (i = 1; i < tx_num; i++) {
+		for (j = 0; j < rx_num; j++) {
+			int32_t cm_sensor_col_diff =
+				ABS((int)cm_sensor_data[i * rx_num + j] -
+				(int)cm_sensor_data[(i - 1) * rx_num + j]);
+			cm_sensor_column_delta[(i - 1) * rx_num + j] =
+				cm_sensor_col_diff;
+			if (cm_sensor_col_diff >
+			    configuration->cm_range_limit_col) {
+				pt_debug(dev, DL_DEBUG,
+					"%s: Sensor[%d,%d]:%d (%d)\n",
+					"Cm sensor column range limit test",
+					j, i, cm_sensor_col_diff,
+					configuration->cm_range_limit_col);
+				result->cm_sensor_col_delta_pass = false;
+			}
+		}
+	}
+
+	/* Check sensor calculated Cm for min/max values */
+	cm_sensor_calibration = cmcp_info->cm_cal_data_panel;
+	if (cm_sensor_calibration <
+	    configuration->cm_min_limit_cal ||
+	    cm_sensor_calibration > configuration->cm_max_limit_cal) {
+		pt_debug(dev, DL_DEBUG, "%s: Cm_cal:%d (%d,%d)\n",
+			"Cm sensor Cm_cal min/max test",
+			cm_sensor_calibration,
+			configuration->cm_min_limit_cal,
+			configuration->cm_max_limit_cal);
+		result->cm_sensor_calibration_pass = false;
+	}
+
+	/* Check sensor Cm delta for range limit */
+	if (cmcp_info->cm_sensor_delta >
+	    (10 * configuration->cm_max_delta_sensor_percent)) {
+		pt_debug(dev, DL_DEBUG,
+			"%s: Cm_sensor_delta:%d (%d)\n",
+			"Cm sensor delta range limit test",
+			cmcp_info->cm_sensor_delta,
+			configuration->cm_max_delta_sensor_percent);
+		result->cm_sensor_delta_pass = false;
+	}
+
+	result->cm_test_pass = result->cm_sensor_gd_col_pass
+			&& result->cm_sensor_gd_row_pass
+			&& result->cm_sensor_validation_pass
+			&& result->cm_sensor_row_delta_pass
+			&& result->cm_sensor_col_delta_pass
+			&& result->cm_sensor_calibration_pass
+			&& result->cm_sensor_delta_pass;
+	}
+
+	if (((test_item & CM_BTN) == CM_BTN) && (cmcp_info->btn_num)) {
+		/* Check each button Cm data for min/max values */
+		result->cm_button_validation_pass = true;
+		for (i = 0; i < button_num; i++) {
+			int32_t  cm_button_min =
+				configuration->cm_min_max_table_btn[i * 2];
+			int32_t  cm_button_max =
+				configuration->cm_min_max_table_btn[i * 2 + 1];
+			if ((cm_button_data[i] <= cm_button_min) ||
+			    (cm_button_data[i] >= cm_button_max)) {
+				pt_debug(dev, DL_DEBUG,
+					"%s: Button[%d]:%d (%d,%d)\n",
+					"Cm button min/max test",
+					i, cm_button_data[i],
+					cm_button_min, cm_button_max);
+				result->cm_button_validation_pass = false;
+			}
+		}
+
+		/* Check button Cm delta for range limit */
+		result->cm_button_delta_pass = true;
+
+		cm_button_delta = ABS((cmcp_info->cm_ave_data_btn -
+			cmcp_info->cm_cal_data_btn) * 100 /
+			cmcp_info->cm_ave_data_btn);
+		if (cm_button_delta >
+		    configuration->cm_max_delta_button_percent) {
+			pt_debug(dev, DL_INFO,
+				"%s: Cm_button_delta:%d (%d)\n",
+				"Cm button delta range limit test",
+				cm_button_delta,
+				configuration->cm_max_delta_button_percent);
+			result->cm_button_delta_pass = false;
+		}
+
+		result->cm_test_pass = result->cm_test_pass &&
+				       result->cm_button_validation_pass &&
+				       result->cm_button_delta_pass;
+	}
+
+	if (pass)
+		*pass = result->cm_test_pass;
+
+	return ret;
+}
+
+/*******************************************************************************
+ * FUNCTION: validate_cp_test_results
+ *
+ * SUMMARY: Checks cp test results and outputs each test and a summary result.
+ *
+ * RETURN:
+ *   0 = success
+ *  !0 = failure
+ *
+ * PARAMETERS:
+ *  *dev           - pointer to device structure
+ *  *configuration - pointer to configuration structure
+ *  *cmcp_info     - pointer to cmcp_data structure to store cmcp data from fw
+ *  *result        - pointer to result structure
+ *  *pass          - pointer to bool value
+ *   test_item     - flag to store all test item are requested
+ ******************************************************************************/
+static int validate_cp_test_results(struct device *dev,
+	struct configuration *configuration, struct cmcp_data *cmcp_info,
+	struct result *result, bool *pass, int test_item)
+{
+	int i = 0;
+	uint32_t configuration_rx_num;
+	uint32_t configuration_tx_num;
+	int32_t *cp_sensor_tx_data = cmcp_info->cp_tx_data_panel;
+	int32_t *cp_sensor_rx_data = cmcp_info->cp_rx_data_panel;
+	int32_t cp_button_delta;
+	int32_t cp_button_average;
+
+	result->cp_test_pass = true;
+	configuration_rx_num = configuration->cp_min_max_table_rx_size/2;
+	configuration_tx_num = configuration->cp_min_max_table_tx_size/2;
+
+	pt_debug(dev, DL_INFO, "%s start\n", __func__);
+
+	if ((test_item & CP_PANEL) == CP_PANEL) {
+		int32_t cp_sensor_tx_delta;
+		int32_t cp_sensor_rx_delta;
+
+		/* Check Sensor Cp delta for range limit */
+		result->cp_sensor_delta_pass = true;
+		/*check cp_sensor_tx_delta */
+		for (i = 0; i < configuration_tx_num; i++) {
+			cp_sensor_tx_delta =
+				ABS((cmcp_info->cp_tx_cal_data_panel[i]-
+				cmcp_info->cp_tx_data_panel[i]) * 100 /
+				cmcp_info->cp_tx_data_panel[i]);
+
+			if (cp_sensor_tx_delta >
+			    configuration->cp_max_delta_sensor_tx_percent) {
+				pt_debug(dev, DL_DEBUG,
+				"%s: Cp_sensor_tx_delta:%d (%d)\n",
+				"Cp sensor delta range limit test",
+				cp_sensor_tx_delta,
+				configuration->cp_max_delta_sensor_tx_percent);
+				result->cp_sensor_delta_pass = false;
+			}
+		}
+
+		/*check cp_sensor_rx_delta */
+		for (i = 0; i < configuration_rx_num; i++) {
+			cp_sensor_rx_delta =
+				ABS((cmcp_info->cp_rx_cal_data_panel[i] -
+				cmcp_info->cp_rx_data_panel[i]) * 100 /
+				cmcp_info->cp_rx_data_panel[i]);
+			if (cp_sensor_rx_delta >
+			    configuration->cp_max_delta_sensor_rx_percent) {
+				pt_debug(dev, DL_DEBUG,
+				"%s: Cp_sensor_rx_delta:%d(%d)\n",
+				"Cp sensor delta range limit test",
+				cp_sensor_rx_delta,
+				configuration->cp_max_delta_sensor_rx_percent);
+				result->cp_sensor_delta_pass = false;
+			}
+		}
+
+		/* Check sensor Cp rx for min/max values */
+		result->cp_rx_validation_pass = true;
+		for (i = 0; i < configuration_rx_num; i++) {
+			int32_t cp_rx_min =
+				configuration->cp_min_max_table_rx[i * 2];
+			int32_t cp_rx_max =
+				configuration->cp_min_max_table_rx[i * 2 + 1];
+			if ((cp_sensor_rx_data[i] <= cp_rx_min) ||
+			    (cp_sensor_rx_data[i] >= cp_rx_max)) {
+				pt_debug(dev, DL_DEBUG,
+					"%s: Cp Rx[%d]:%d (%d,%d)\n",
+					"Cp Rx min/max test",
+					i, (int)cp_sensor_rx_data[i],
+					cp_rx_min, cp_rx_max);
+				result->cp_rx_validation_pass = false;
+			}
+		}
+
+		/* Check sensor Cp tx for min/max values */
+		result->cp_tx_validation_pass = true;
+		for (i = 0; i < configuration_tx_num; i++) {
+			int32_t cp_tx_min =
+				configuration->cp_min_max_table_tx[i * 2];
+			int32_t cp_tx_max =
+				configuration->cp_min_max_table_tx[i * 2 + 1];
+			if ((cp_sensor_tx_data[i] <= cp_tx_min) ||
+			    (cp_sensor_tx_data[i] >= cp_tx_max)) {
+				pt_debug(dev, DL_DEBUG,
+					"%s: Cp Tx[%d]:%d(%d,%d)\n",
+					"Cp Tx min/max test",
+					i, cp_sensor_tx_data[i],
+					cp_tx_min, cp_tx_max);
+				result->cp_tx_validation_pass = false;
+			}
+		}
+
+		result->cp_test_pass = result->cp_test_pass
+				&& result->cp_sensor_delta_pass
+				&& result->cp_rx_validation_pass
+				&& result->cp_tx_validation_pass;
+	}
+
+	if (((test_item & CP_BTN) == CP_BTN) && (cmcp_info->btn_num)) {
+		result->cp_button_delta_pass = true;
+
+		/* Check button Cp delta for range limit */
+		cp_button_delta = ABS((cmcp_info->cp_btn_cal
+		- cmcp_info->cp_button_ave) * 100 /
+		cmcp_info->cp_button_ave);
+		if (cp_button_delta >
+		    configuration->cp_max_delta_button_percent) {
+			pt_debug(dev, DL_INFO,
+				"%s: Cp_button_delta:%d (%d)\n",
+				"Cp button delta range limit test",
+				cp_button_delta,
+				configuration->cp_max_delta_button_percent);
+			result->cp_button_delta_pass = false;
+		}
+
+		/* Check button Cp average for min/max values */
+		result->cp_button_average_pass = true;
+		cp_button_average = cmcp_info->cp_button_ave;
+		if (cp_button_average < configuration->min_button ||
+		    cp_button_average > configuration->max_button) {
+			pt_debug(dev, DL_INFO,
+				"%s: Button Cp average fails min/max test\n",
+				__func__);
+			pt_debug(dev, DL_INFO,
+				"%s: Cp_button_average:%d (%d,%d)\n",
+				"Cp button average min/max test",
+				cp_button_average,
+				configuration->min_button,
+				configuration->max_button);
+			result->cp_button_average_pass = false;
+		}
+
+		/* Check each button Cp data for min/max values */
+		result->cp_button_validation_pass = true;
+		for (i = 0; i < cmcp_info->btn_num; i++) {
+			int32_t  cp_button_min =
+				configuration->cp_min_max_table_btn[i * 2];
+			int32_t  cp_button_max =
+				configuration->cp_min_max_table_btn[i * 2 + 1];
+			if ((cmcp_info->cp_btn_data[i] <= cp_button_min) ||
+			    (cmcp_info->cp_btn_data[i] >= cp_button_max)) {
+				pt_debug(dev, DL_DEBUG,
+					"%s: Button[%d]:%d (%d,%d)\n",
+					"Cp button min/max test",
+					i, cmcp_info->cp_btn_data[i],
+					cp_button_min, cp_button_max);
+				result->cp_button_validation_pass = false;
+			}
+		}
+
+		result->cp_test_pass = result->cp_test_pass
+				&& result->cp_button_delta_pass
+				&& result->cp_button_average_pass
+				&& result->cp_button_validation_pass;
+	}
+
+	if (pass)
+		*pass = result->cp_test_pass;
+
+	return 0;
+}
+
+/*******************************************************************************
+ * FUNCTION: calculate_gradient_row
+ *
+ * SUMMARY: Calculates gradient value for rows.
+ *
+ * PARAMETERS:
+ *  *gd_sensor_row_head - pointer to gd_sensor structure
+ *   row_num            - number of row
+ *   exclude_row_edge   - flag to exclude row edge(1:exclude; 0:include)
+ *   exclude_col_edge   - flag to exclude column edge(1:exclude; 0:include)
+ ******************************************************************************/
+static void calculate_gradient_row(struct gd_sensor *gd_sensor_row_head,
+		 uint16_t row_num, int exclude_row_edge, int exclude_col_edge)
+{
+	int i = 0;
+	uint16_t cm_min_cur = 0;
+	uint16_t cm_max_cur = 0;
+	uint16_t cm_ave_cur = 0;
+	uint16_t cm_ave_next = 0;
+	uint16_t cm_ave_prev = 0;
+	struct gd_sensor *p = gd_sensor_row_head;
+
+	if (exclude_row_edge) {
+		for (i = 0; i < row_num; i++) {
+			if (!exclude_col_edge) {
+				cm_ave_cur = (p + i)->cm_ave;
+				cm_min_cur = (p + i)->cm_min;
+				cm_max_cur = (p + i)->cm_max;
+				if (i < (row_num-1))
+					cm_ave_next = (p + i+1)->cm_ave;
+				if (i > 0)
+					cm_ave_prev = (p + i-1)->cm_ave;
+			} else {
+				cm_ave_cur = (p + i)->cm_ave_exclude_edge;
+				cm_min_cur = (p + i)->cm_min_exclude_edge;
+				cm_max_cur = (p + i)->cm_max_exclude_edge;
+				if (i < (row_num-1))
+					cm_ave_next =
+					(p + i+1)->cm_ave_exclude_edge;
+				if (i > 0)
+					cm_ave_prev =
+					(p + i-1)->cm_ave_exclude_edge;
+			}
+
+			if (cm_ave_cur == 0)
+				cm_ave_cur = 1;
+
+			/*multiple 1000 to increate accuracy*/
+			if ((i == 0) || (i == (row_num-1))) {
+				(p + i)->gradient_val =
+				(cm_max_cur - cm_min_cur) * 1000 /
+				cm_ave_cur;
+			} else if (i == 1) {
+				(p + i)->gradient_val = (cm_max_cur - cm_min_cur
+				+ ABS(cm_ave_cur - cm_ave_next)) * 1000 /
+				cm_ave_cur;
+			} else {
+				(p + i)->gradient_val = (cm_max_cur - cm_min_cur
+				+ ABS(cm_ave_cur - cm_ave_prev)) * 1000 /
+				cm_ave_cur;
+			}
+		}
+	} else if (!exclude_row_edge) {
+		for (i = 0; i < row_num; i++) {
+			if (!exclude_col_edge) {
+				cm_ave_cur = (p + i)->cm_ave;
+				cm_min_cur = (p + i)->cm_min;
+				cm_max_cur = (p + i)->cm_max;
+				if (i < (row_num-1))
+					cm_ave_next = (p + i+1)->cm_ave;
+				if (i > 0)
+					cm_ave_prev = (p + i-1)->cm_ave;
+			} else {
+				cm_ave_cur = (p + i)->cm_ave_exclude_edge;
+				cm_min_cur = (p + i)->cm_min_exclude_edge;
+				cm_max_cur = (p + i)->cm_max_exclude_edge;
+				if (i < (row_num-1))
+					cm_ave_next =
+					(p + i+1)->cm_ave_exclude_edge;
+				if (i > 0)
+					cm_ave_prev =
+					(p + i-1)->cm_ave_exclude_edge;
+			}
+
+			if (cm_ave_cur == 0)
+				cm_ave_cur = 1;
+			/*multiple 1000 to increate accuracy*/
+			if (i <= 1)
+				(p + i)->gradient_val = (cm_max_cur - cm_min_cur
+				+ ABS(cm_ave_cur - cm_ave_next)) * 1000 /
+				cm_ave_cur;
+			else
+				(p + i)->gradient_val = (cm_max_cur - cm_min_cur
+				+ ABS(cm_ave_cur - cm_ave_prev)) * 1000 /
+				cm_ave_cur;
+		}
+	}
+}
+
+/*******************************************************************************
+ * FUNCTION: calculate_gradient_col
+ *
+ * SUMMARY: Calculates gradient value for columns.
+ *
+ * PARAMETERS:
+ *  *gd_sensor_row_head - pointer to gd_sensor structure
+ *   col_num            - number of column
+ *   exclude_row_edge   - flag to exclude row edge(1:exclude; 0:include)
+ *   exclude_col_edge   - flag to exclude column edge(1:exclude; 0:include)
+ ******************************************************************************/
+static void calculate_gradient_col(struct gd_sensor *gd_sensor_row_head,
+	uint16_t col_num, int exclude_row_edge, int exclude_col_edge)
+{
+	int i = 0;
+	int32_t cm_min_cur = 0;
+	int32_t cm_max_cur = 0;
+	int32_t cm_ave_cur = 0;
+	int32_t cm_ave_next = 0;
+	int32_t cm_ave_prev = 0;
+	struct gd_sensor *p = gd_sensor_row_head;
+
+	if (!exclude_col_edge) {
+		for (i = 0; i < col_num; i++) {
+			if (!exclude_row_edge) {
+				cm_ave_cur = (p + i)->cm_ave;
+				cm_min_cur = (p + i)->cm_min;
+				cm_max_cur = (p + i)->cm_max;
+				if (i < (col_num-1))
+					cm_ave_next = (p + i+1)->cm_ave;
+				if (i > 0)
+					cm_ave_prev = (p + i-1)->cm_ave;
+			} else {
+				cm_ave_cur = (p + i)->cm_ave_exclude_edge;
+				cm_min_cur = (p + i)->cm_min_exclude_edge;
+				cm_max_cur = (p + i)->cm_max_exclude_edge;
+				if (i < (col_num-1))
+					cm_ave_next =
+					(p + i+1)->cm_ave_exclude_edge;
+				if (i > 0)
+					cm_ave_prev =
+					(p + i-1)->cm_ave_exclude_edge;
+			}
+			if (cm_ave_cur == 0)
+				cm_ave_cur = 1;
+			/*multiple 1000 to increate accuracy*/
+			if (i <= 1)
+				(p + i)->gradient_val = (cm_max_cur - cm_min_cur
+				+ ABS(cm_ave_cur - cm_ave_next)) * 1000 /
+				cm_ave_cur;
+			else
+				(p + i)->gradient_val = (cm_max_cur - cm_min_cur
+				+ ABS(cm_ave_cur - cm_ave_prev)) * 1000 /
+				cm_ave_cur;
+		}
+	} else if (exclude_col_edge) {
+		for (i = 0; i < col_num; i++) {
+			if (!exclude_row_edge) {
+				cm_ave_cur = (p + i)->cm_ave;
+				cm_min_cur = (p + i)->cm_min;
+				cm_max_cur = (p + i)->cm_max;
+				if (i < (col_num-1))
+					cm_ave_next = (p + i+1)->cm_ave;
+				if (i > 0)
+					cm_ave_prev = (p + i-1)->cm_ave;
+			} else {
+				cm_ave_cur = (p + i)->cm_ave_exclude_edge;
+				cm_min_cur = (p + i)->cm_min_exclude_edge;
+				cm_max_cur = (p + i)->cm_max_exclude_edge;
+				if (i < (col_num-1))
+					cm_ave_next =
+					(p + i+1)->cm_ave_exclude_edge;
+				if (i > 0)
+					cm_ave_prev =
+					(p + i-1)->cm_ave_exclude_edge;
+			}
+
+			if (cm_ave_cur == 0)
+				cm_ave_cur = 1;
+			/*multiple 1000 to increate accuracy*/
+			if ((i == 0) || (i == (col_num - 1)))
+				(p + i)->gradient_val =
+					 (cm_max_cur - cm_min_cur) * 1000 /
+					 cm_ave_cur;
+			else if (i == 1)
+				(p + i)->gradient_val =
+					(cm_max_cur - cm_min_cur +
+					ABS(cm_ave_cur - cm_ave_next))
+					 * 1000 / cm_ave_cur;
+			else
+				(p + i)->gradient_val =
+					(cm_max_cur - cm_min_cur +
+					ABS(cm_ave_cur - cm_ave_prev))
+					* 1000 / cm_ave_cur;
+			}
+	}
+}
+
+/*******************************************************************************
+ * FUNCTION: fill_gd_sensor_table
+ *
+ * SUMMARY: Fills cm calculation result and exclude parameter to gd_sensor
+ *  structure.
+ *
+ * PARAMETERS:
+ *  *head                - pointer to gd_sensor structure
+ *   index               - index of row or column
+ *   cm_max              - maximum of cm
+ *   cm_min              - minmum of cm
+ *   cm_ave              - average of cm
+ *   cm_max_exclude_edge - maximum of cm without edge data
+ *   cm_min_exclude_edge - minmum of cm without edge data
+ *   cm_ave_exclude_edge - average of cm without edge data
+ ******************************************************************************/
+static void fill_gd_sensor_table(struct gd_sensor *head, int32_t index,
+	int32_t cm_max, int32_t cm_min,	int32_t cm_ave,
+	int32_t cm_max_exclude_edge, int32_t cm_min_exclude_edge,
+	int32_t cm_ave_exclude_edge)
+{
+	(head + index)->cm_max = cm_max;
+	(head + index)->cm_min = cm_min;
+	(head + index)->cm_ave = cm_ave;
+	(head + index)->cm_ave_exclude_edge = cm_ave_exclude_edge;
+	(head + index)->cm_max_exclude_edge = cm_max_exclude_edge;
+	(head + index)->cm_min_exclude_edge = cm_min_exclude_edge;
+}
+
+/*******************************************************************************
+ * FUNCTION: calculate_gd_info
+ *
+ * SUMMARY: Calculates gradient panel sensor column and row by calling
+ *  function calculate_gradient_col() & calculate_gradient_row().
+ *
+ * PARAMETERS:
+ *  *head                - pointer to gd_sensor structure
+ *   index               - index of row or column
+ *   cm_max              - maximum of cm
+ *   cm_min              - minmum of cm
+ *   cm_ave              - average of cm
+ *   cm_max_exclude_edge - maximum of cm without edge data
+ *   cm_min_exclude_edge - minmum of cm without edge data
+ *   cm_ave_exclude_edge - average of cm without edge data
+ ******************************************************************************/
+static void calculate_gd_info(struct gd_sensor *gd_sensor_col,
+	struct gd_sensor *gd_sensor_row, int tx_num, int rx_num,
+	int32_t *cm_sensor_data, int cm_excluding_row_edge,
+	int cm_excluding_col_edge)
+{
+	int32_t cm_max;
+	int32_t cm_min;
+	int32_t cm_ave;
+	int32_t cm_max_exclude_edge;
+	int32_t cm_min_exclude_edge;
+	int32_t cm_ave_exclude_edge;
+	int32_t cm_data;
+	int i;
+	int j;
+
+	/*calculate all the gradient related info for column*/
+	for (i = 0; i < tx_num; i++) {
+		/*re-initialize for a new col*/
+		cm_max = cm_sensor_data[i * rx_num];
+		cm_min = cm_max;
+		cm_ave = 0;
+		cm_max_exclude_edge = cm_sensor_data[i * rx_num + 1];
+		cm_min_exclude_edge = cm_max_exclude_edge;
+		cm_ave_exclude_edge = 0;
+
+		for (j = 0; j < rx_num; j++) {
+			cm_data = cm_sensor_data[i * rx_num + j];
+			if (cm_data > cm_max)
+				cm_max = cm_data;
+			if (cm_data < cm_min)
+				cm_min = cm_data;
+			cm_ave += cm_data;
+			/*calculate exclude edge data*/
+			if ((j > 0) && (j < (rx_num-1))) {
+				if (cm_data > cm_max_exclude_edge)
+					cm_max_exclude_edge = cm_data;
+				if (cm_data < cm_min_exclude_edge)
+					cm_min_exclude_edge = cm_data;
+				cm_ave_exclude_edge += cm_data;
+			}
+		}
+		cm_ave /= rx_num;
+		cm_ave_exclude_edge /= (rx_num-2);
+		fill_gd_sensor_table(gd_sensor_col, i, cm_max, cm_min, cm_ave,
+		cm_max_exclude_edge, cm_min_exclude_edge, cm_ave_exclude_edge);
+	}
+
+	calculate_gradient_col(gd_sensor_col, tx_num, cm_excluding_row_edge,
+		 cm_excluding_col_edge);
+
+	/*calculate all the gradient related info for row*/
+	for (j = 0; j < rx_num; j++) {
+		/*re-initialize for a new row*/
+		cm_max = cm_sensor_data[j];
+		cm_min = cm_max;
+		cm_ave = 0;
+		cm_max_exclude_edge = cm_sensor_data[rx_num + j];
+		cm_min_exclude_edge = cm_max_exclude_edge;
+		cm_ave_exclude_edge = 0;
+		for (i = 0; i < tx_num; i++) {
+			cm_data = cm_sensor_data[i * rx_num + j];
+			if (cm_data > cm_max)
+				cm_max = cm_data;
+			if (cm_data < cm_min)
+				cm_min = cm_data;
+			cm_ave += cm_data;
+			/*calculate exclude edge data*/
+			if ((i >  0) && (i < (tx_num-1))) {
+				if (cm_data > cm_max_exclude_edge)
+					cm_max_exclude_edge = cm_data;
+				if (cm_data < cm_min_exclude_edge)
+					cm_min_exclude_edge = cm_data;
+				cm_ave_exclude_edge += cm_data;
+			}
+		}
+		cm_ave /= tx_num;
+		cm_ave_exclude_edge /= (tx_num-2);
+		fill_gd_sensor_table(gd_sensor_row, j, cm_max, cm_min, cm_ave,
+		cm_max_exclude_edge, cm_min_exclude_edge, cm_ave_exclude_edge);
+	}
+	calculate_gradient_row(gd_sensor_row, rx_num, cm_excluding_row_edge,
+		 cm_excluding_col_edge);
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_get_cmcp_info
+ *
+ * SUMMARY: Function to include following work:
+ *  1) run short test and get result
+ *  2) run selftest to get cm_panel data, cm_cal_data_panel data, calculate
+ *     cm_ave_data_panel, cm_sensor_delta and gradient by column and row.
+ *  3) run selftest to get cp_panel data, cp_cal_data_panel data, cacluate
+ *     cp_ave_data_panel, cp_sensor_delta for tx and rx.
+ *  4) run selftest to get cm_btn data, cm_cal_data_btn data, cacluate
+ *     cm_delta_data_btn data, cm_ave_data_btn data.
+ *  5) run selftest to get cp_btn data, cp_btn_cal data, cacluate
+ *     cp_button_delta data, cp_button_ave data.
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *   *dad       - pointer to pt_device_access_data structure
+ *   *cmcp_info - pointer to cmcp_data structure
+ ******************************************************************************/
+static int  pt_get_cmcp_info(struct pt_device_access_data *dad,
+	struct cmcp_data *cmcp_info)
+{
+	struct device *dev;
+	int32_t *cm_data_panel = cmcp_info->cm_data_panel;
+	int32_t *cp_tx_data_panel = cmcp_info->cp_tx_data_panel;
+	int32_t *cp_rx_data_panel = cmcp_info->cp_rx_data_panel;
+	int32_t *cp_tx_cal_data_panel = cmcp_info->cp_tx_cal_data_panel;
+	int32_t *cp_rx_cal_data_panel = cmcp_info->cp_rx_cal_data_panel;
+	int32_t *cm_btn_data = cmcp_info->cm_btn_data;
+	int32_t *cp_btn_data = cmcp_info->cp_btn_data;
+	struct gd_sensor *gd_sensor_col = cmcp_info->gd_sensor_col;
+	struct gd_sensor *gd_sensor_row = cmcp_info->gd_sensor_row;
+	struct result *result = dad->result;
+	int32_t cp_btn_cal = 0;
+	int32_t cp_btn_ave = 0;
+	int32_t cm_ave_data_panel = 0;
+	int32_t cm_ave_data_btn = 0;
+	int32_t cp_tx_ave_data_panel = 0;
+	int32_t cp_rx_ave_data_panel = 0;
+	u8 tmp_buf[3];
+	int tx_num;
+	int rx_num;
+	int btn_num;
+	int rc = 0;
+	int i;
+
+	dev = dad->dev;
+	cmcp_info->tx_num = dad->si->sensing_conf_data.tx_num;
+	cmcp_info->rx_num = dad->si->sensing_conf_data.rx_num;
+	cmcp_info->btn_num = dad->si->num_btns;
+
+	tx_num = cmcp_info->tx_num;
+	rx_num = cmcp_info->rx_num;
+	btn_num = cmcp_info->btn_num;
+	pt_debug(dev, DL_INFO, "%s tx_num=%d", __func__, tx_num);
+	pt_debug(dev, DL_INFO, "%s rx_num=%d", __func__, rx_num);
+	pt_debug(dev, DL_INFO, "%s btn_num=%d", __func__, btn_num);
+
+	/*short test*/
+	result->short_test_pass = true;
+	rc = pt_run_and_get_selftest_result(dev, PT_CORE_CMD_UNPROTECTED,
+		tmp_buf, sizeof(tmp_buf),
+		PT_ST_ID_AUTOSHORTS, PIP_CMD_MAX_LENGTH,
+		PT_ST_DONT_GET_RESULTS, PT_ST_NOPRINT, PT_PR_FORMAT_DEFAULT);
+	if (rc) {
+		pt_debug(dev, DL_ERROR, "short test not supported");
+		goto exit;
+	}
+	if (dad->ic_buf[1] != 0)
+		result->short_test_pass = false;
+
+	/*Get cm_panel data*/
+	rc = pt_run_and_get_selftest_result(dev, PT_CORE_CMD_UNPROTECTED,
+		tmp_buf, sizeof(tmp_buf),
+		PT_ST_ID_CM_PANEL, PIP_CMD_MAX_LENGTH,
+		PT_ST_GET_RESULTS, PT_ST_NOPRINT, PT_PR_FORMAT_DEFAULT);
+	if (rc) {
+		pt_debug(dev, DL_ERROR, "Get CM Panel not supported");
+		goto exit;
+	}
+	if (cm_data_panel != NULL) {
+		for (i = 0; i < tx_num * rx_num;  i++) {
+			cm_data_panel[i] =
+			10*(dad->ic_buf[CM_PANEL_DATA_OFFSET+i*2] + 256
+			* dad->ic_buf[CM_PANEL_DATA_OFFSET+i*2+1]);
+			pt_debug(dev, DL_DEBUG,
+				"cm_data_panel[%d]=%d\n",
+				i, cm_data_panel[i]);
+			cm_ave_data_panel += cm_data_panel[i];
+		}
+		cm_ave_data_panel /= (tx_num * rx_num);
+		cmcp_info->cm_ave_data_panel = cm_ave_data_panel;
+	}
+
+	/* Calculate gradient panel sensor column/row here */
+	calculate_gd_info(gd_sensor_col, gd_sensor_row, tx_num, rx_num,
+		cm_data_panel, 1, 1);
+	for (i = 0; i < tx_num; i++) {
+		pt_debug(dev, DL_DEBUG,
+			"i=%d max=%d,min=%d,ave=%d, gradient=%d", i,
+			gd_sensor_col[i].cm_max,
+			gd_sensor_col[i].cm_min,
+			gd_sensor_col[i].cm_ave,
+			gd_sensor_col[i].gradient_val);
+	}
+
+	for (i = 0; i < rx_num; i++) {
+		pt_debug(dev, DL_DEBUG,
+			"i=%d max=%d,min=%d,ave=%d, gradient=%d", i,
+			gd_sensor_row[i].cm_max,
+			gd_sensor_row[i].cm_min,
+			gd_sensor_row[i].cm_ave,
+			gd_sensor_row[i].gradient_val);
+	}
+
+	/*Get cp data*/
+	rc = pt_run_and_get_selftest_result(dev, PT_CORE_CMD_UNPROTECTED,
+		tmp_buf, sizeof(tmp_buf),
+		PT_ST_ID_CP_PANEL, PIP_CMD_MAX_LENGTH,
+		PT_ST_GET_RESULTS, PT_ST_NOPRINT, PT_PR_FORMAT_DEFAULT);
+	if (rc) {
+		pt_debug(dev, DL_ERROR, "Get CP Panel not supported");
+		goto exit;
+	}
+	/*Get cp_tx_data_panel*/
+	if (cp_tx_data_panel != NULL) {
+		for (i = 0; i < tx_num; i++) {
+			cp_tx_data_panel[i] =
+			10*(dad->ic_buf[CP_PANEL_DATA_OFFSET+i*2]
+			+ 256 * dad->ic_buf[CP_PANEL_DATA_OFFSET+i*2+1]);
+			pt_debug(dev, DL_DEBUG,
+				"cp_tx_data_panel[%d]=%d\n",
+				i, cp_tx_data_panel[i]);
+			cp_tx_ave_data_panel += cp_tx_data_panel[i];
+		}
+		cp_tx_ave_data_panel /= tx_num;
+		cmcp_info->cp_tx_ave_data_panel = cp_tx_ave_data_panel;
+	}
+
+	/*Get cp_tx_cal_data_panel*/
+	if (cp_tx_cal_data_panel != NULL) {
+		for (i = 0; i < tx_num; i++) {
+			cp_tx_cal_data_panel[i] =
+			10*(dad->ic_buf[CP_PANEL_DATA_OFFSET+tx_num*2+i*2]
+		+ 256 * dad->ic_buf[CP_PANEL_DATA_OFFSET+tx_num*2+i*2+1]);
+			pt_debug(dev, DL_DEBUG, "cp_tx_cal_data_panel[%d]=%d\n",
+				i, cp_tx_cal_data_panel[i]);
+		}
+	}
+
+	/*get cp_sensor_tx_delta,using the first sensor cal value for temp */
+	/*multiple 1000 to increase accuracy*/
+	cmcp_info->cp_sensor_tx_delta = ABS((cp_tx_cal_data_panel[0]
+		- cp_tx_ave_data_panel) * 1000 / cp_tx_ave_data_panel);
+
+	/*Get cp_rx_data_panel*/
+	if (cp_rx_data_panel != NULL) {
+		for (i = 0; i < rx_num;  i++) {
+			cp_rx_data_panel[i] =
+			10*(dad->ic_buf[CP_PANEL_DATA_OFFSET+tx_num*4+i*2] +
+			256 * dad->ic_buf[CP_PANEL_DATA_OFFSET+tx_num*4+i*2+1]);
+			pt_debug(dev, DL_DEBUG,
+				"cp_rx_data_panel[%d]=%d\n", i,
+				cp_rx_data_panel[i]);
+			cp_rx_ave_data_panel += cp_rx_data_panel[i];
+		}
+		cp_rx_ave_data_panel /= rx_num;
+		cmcp_info->cp_rx_ave_data_panel = cp_rx_ave_data_panel;
+	}
+
+	/*Get cp_rx_cal_data_panel*/
+	if (cp_rx_cal_data_panel != NULL) {
+		for (i = 0; i < rx_num; i++) {
+			cp_rx_cal_data_panel[i] =
+		10 * (dad->ic_buf[CP_PANEL_DATA_OFFSET+tx_num*4+rx_num*2+i*2] +
+		256 *
+		dad->ic_buf[CP_PANEL_DATA_OFFSET+tx_num*4+rx_num*2+i*2+1]);
+			pt_debug(dev, DL_DEBUG,
+				"cp_rx_cal_data_panel[%d]=%d\n", i,
+				cp_rx_cal_data_panel[i]);
+		}
+	}
+
+	/*get cp_sensor_rx_delta,using the first sensor cal value for temp */
+	/*multiple 1000 to increase accuracy*/
+	cmcp_info->cp_sensor_rx_delta = ABS((cp_rx_cal_data_panel[0]
+		- cp_rx_ave_data_panel) * 1000 / cp_rx_ave_data_panel);
+
+	if (btn_num == 0) {
+		pt_debug(dev, DL_INFO, "%s: Skip Button Test\n", __func__);
+		goto skip_button_test;
+	}
+
+	/*get cm btn data*/
+	rc = pt_run_and_get_selftest_result(dev, PT_CORE_CMD_UNPROTECTED,
+		tmp_buf, sizeof(tmp_buf),
+		PT_ST_ID_CM_BUTTON, PIP_CMD_MAX_LENGTH,
+		PT_ST_GET_RESULTS, PT_ST_NOPRINT, PT_PR_FORMAT_DEFAULT);
+	if (rc) {
+		pt_debug(dev, DL_ERROR, "Get CM BTN not supported");
+		goto exit;
+	}
+	if (cm_btn_data != NULL) {
+		for (i = 0; i < btn_num; i++) {
+			cm_btn_data[i] =
+			10 * (dad->ic_buf[CM_BTN_DATA_OFFSET+i*2] +
+			256 * dad->ic_buf[CM_BTN_DATA_OFFSET+i*2+1]);
+			pt_debug(dev, DL_DEBUG,
+				" cm_btn_data[%d]=%d\n",
+				i, cm_btn_data[i]);
+			cm_ave_data_btn += cm_btn_data[i];
+		}
+		cm_ave_data_btn /= btn_num;
+		cmcp_info->cm_ave_data_btn = cm_ave_data_btn;
+	}
+
+	/*get cp btn data*/
+	rc = pt_run_and_get_selftest_result(dev, PT_CORE_CMD_UNPROTECTED,
+		tmp_buf, sizeof(tmp_buf),
+		PT_ST_ID_CP_BUTTON, PIP_CMD_MAX_LENGTH,
+		PT_ST_GET_RESULTS, PT_ST_NOPRINT, PT_PR_FORMAT_DEFAULT);
+	if (rc) {
+		pt_debug(dev, DL_ERROR, "Get CP BTN not supported");
+		goto exit;
+	}
+	if (cp_btn_data != NULL) {
+		for (i = 0; i < btn_num; i++) {
+			cp_btn_data[i] =
+			10 * (dad->ic_buf[CP_BTN_DATA_OFFSET+i*2] +
+			256 * dad->ic_buf[CP_BTN_DATA_OFFSET+i*2+1]);
+			cp_btn_ave += cp_btn_data[i];
+			pt_debug(dev, DL_DEBUG,
+				"cp_btn_data[%d]=%d\n",
+				i, cp_btn_data[i]);
+		}
+		cp_btn_ave /= btn_num;
+		cp_btn_cal = 10*(dad->ic_buf[CP_BTN_DATA_OFFSET+i*2]
+			 + 256 * dad->ic_buf[CP_BTN_DATA_OFFSET+i*2+1]);
+		cmcp_info->cp_button_ave = cp_btn_ave;
+		cmcp_info->cp_btn_cal = cp_btn_cal;
+		/*multiple 1000 to increase accuracy*/
+		cmcp_info->cp_button_delta = ABS((cp_btn_cal
+		- cp_btn_ave) * 1000 / cp_btn_ave);
+		pt_debug(dev, DL_INFO, " cp_btn_cal=%d\n",
+			cp_btn_cal);
+		pt_debug(dev, DL_INFO, " cp_btn_ave=%d\n",
+			cp_btn_ave);
+	}
+
+skip_button_test:
+exit:
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_get_cm_cal
+ *
+ * SUMMARY: Function to include following work:
+ *  1) run selftest to get cm_cal_data_panel, cm_sensor_delta
+ *  2) run selftest to get cm_cal_data_btn, cm_delta_data_btn
+ *
+ * NOTE:
+ *  This function depends on the calculation result of pt_get_cmcp_info()
+ *
+ * RETURN:
+ *   0 = success
+ *  !0 = failure
+ *
+ * PARAMETERS:
+ *   *dad       - pointer to pt_device_access_data structure
+ *   *cmcp_info - pointer to cmcp_data structure
+ ******************************************************************************/
+static int pt_get_cm_cal(struct pt_device_access_data *dad,
+	struct cmcp_data *cmcp_info)
+{
+	struct device *dev;
+	int32_t *cm_data_panel = cmcp_info->cm_data_panel;
+	int32_t *cm_btn_data = cmcp_info->cm_btn_data;
+	u8 tmp_buf[3];
+	int rc = 0;
+	int i;
+
+	dev = dad->dev;
+
+	/*Get cm_cal data*/
+	rc = pt_run_and_get_selftest_result(dev, PT_CORE_CMD_UNPROTECTED,
+			tmp_buf, sizeof(tmp_buf),
+			PT_ST_ID_CM_PANEL, PIP_CMD_MAX_LENGTH,
+			PT_ST_GET_RESULTS, PT_ST_NOPRINT, PT_PR_FORMAT_DEFAULT);
+	if (rc) {
+		pt_debug(dev, DL_ERROR, "Get CM Panel not supported");
+		goto exit;
+	}
+	if (cm_data_panel != NULL) {
+		i = cmcp_info->tx_num * cmcp_info->rx_num;
+		cmcp_info->cm_cal_data_panel =
+		    10 * (dad->ic_buf[CM_PANEL_DATA_OFFSET + i * 2] +
+			  256 * dad->ic_buf[CM_PANEL_DATA_OFFSET + i * 2 + 1]);
+		/*multiple 1000 to increase accuracy*/
+		cmcp_info->cm_sensor_delta =
+		    ABS((cmcp_info->cm_ave_data_panel -
+			 cmcp_info->cm_cal_data_panel) *
+			1000 / cmcp_info->cm_ave_data_panel);
+	}
+
+	if (cmcp_info->btn_num == 0) {
+		pt_debug(dev, DL_INFO, "%s: Skip Button Test\n", __func__);
+		goto skip_button_test;
+	}
+
+	/*get cm_btn_cal data*/
+	rc = pt_run_and_get_selftest_result(dev, PT_CORE_CMD_UNPROTECTED,
+			tmp_buf, sizeof(tmp_buf),
+			PT_ST_ID_CM_BUTTON, PIP_CMD_MAX_LENGTH,
+			PT_ST_GET_RESULTS, PT_ST_NOPRINT, PT_PR_FORMAT_DEFAULT);
+	if (rc) {
+		pt_debug(dev, DL_ERROR, "Get CM BTN not supported");
+		goto exit;
+	}
+	if (cm_btn_data != NULL) {
+		i = cmcp_info->btn_num;
+		cmcp_info->cm_cal_data_btn =
+		    10 * (dad->ic_buf[CM_BTN_DATA_OFFSET + i * 2] +
+			  256 * dad->ic_buf[CM_BTN_DATA_OFFSET + i * 2 + 1]);
+		/*multiple 1000 to increase accuracy*/
+		cmcp_info->cm_delta_data_btn = ABS(
+		    (cmcp_info->cm_ave_data_btn - cmcp_info->cm_cal_data_btn) *
+		    1000 / cmcp_info->cm_ave_data_btn);
+		pt_debug(dev, DL_INFO, " cm_btn_cal=%d\n",
+			 cmcp_info->cm_cal_data_btn);
+	}
+
+skip_button_test:
+exit:
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_free_cmcp_buf
+ *
+ * SUMMARY: Free pointers in cmcp_data structure
+ *
+ * PARAMETERS:
+ *  *cmcp_info  - pointer to cmcp_data structure
+ ******************************************************************************/
+static void pt_free_cmcp_buf(struct cmcp_data *cmcp_info)
+{
+	if (cmcp_info->gd_sensor_col != NULL)
+		kfree(cmcp_info->gd_sensor_col);
+	if (cmcp_info->gd_sensor_row != NULL)
+		kfree(cmcp_info->gd_sensor_row);
+	if (cmcp_info->cm_data_panel != NULL)
+		kfree(cmcp_info->cm_data_panel);
+	if (cmcp_info->cp_tx_data_panel != NULL)
+		kfree(cmcp_info->cp_tx_data_panel);
+	if (cmcp_info->cp_rx_data_panel != NULL)
+		kfree(cmcp_info->cp_rx_data_panel);
+	if (cmcp_info->cp_tx_cal_data_panel != NULL)
+		kfree(cmcp_info->cp_tx_cal_data_panel);
+	if (cmcp_info->cp_rx_cal_data_panel != NULL)
+		kfree(cmcp_info->cp_rx_cal_data_panel);
+	if (cmcp_info->cm_btn_data != NULL)
+		kfree(cmcp_info->cm_btn_data);
+	if (cmcp_info->cp_btn_data != NULL)
+		kfree(cmcp_info->cp_btn_data);
+	if (cmcp_info->cm_sensor_column_delta != NULL)
+		kfree(cmcp_info->cm_sensor_column_delta);
+	if (cmcp_info->cm_sensor_row_delta != NULL)
+		kfree(cmcp_info->cm_sensor_row_delta);
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_cmcp_get_test_item
+ *
+ * SUMMARY: Parses enum cmcp_test_item to integer value test_item as bitwise
+ *  type.
+ *
+ * RETURN: integer value to indidate available test item with bitwise type
+ *
+ * PARAMETERS:
+ *	 item_input  - enum cmcp_test_item
+ ******************************************************************************/
+static int pt_cmcp_get_test_item(int item_input)
+{
+	int test_item = 0;
+
+	switch (item_input) {
+	case CMCP_FULL:
+		test_item = CMCP_FULL_CASE;
+		break;
+	case CMCP_CM_PANEL:
+		test_item = CM_PANEL;
+		break;
+	case CMCP_CP_PANEL:
+		test_item = CP_PANEL;
+		break;
+	case CMCP_CM_BTN:
+		test_item = CM_BTN;
+		break;
+	case CMCP_CP_BTN:
+		test_item = CP_BTN;
+		break;
+	}
+	return test_item;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_cmcp_test_show
+ *
+ * SUMMARY: Show method for cmcp_test sysfs node. Allows to perform cmcp test
+ *  with following steps:
+ *   1) Get cmcp test items which from threhold file
+ *   2) Check whether cmcp test items match with firmware
+ *   3) Set parameter to force single TX
+ *   4) Do calibration if requested
+ *   5) Get all cmcp data from FW and do calculation
+ *   6) Set parameter to restore to multi tx
+ *   7) Do calibration if requested
+ *   8) Check scan state,try to fix if it is not right
+ *   9) Start watchdog
+ *  10) Validate cm and cp test results if requested
+ *  11) Fill the test result
+ *
+ * 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_cmcp_test_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct pt_device_access_data *dad
+		= pt_get_device_access_data(dev);
+	struct cmcp_data *cmcp_info = dad->cmcp_info;
+	struct result *result = dad->result;
+	struct configuration *configuration = dad->configs;
+	bool final_pass = true;
+	static const char * const cmcp_test_case_array[] = {"Full Cm/Cp test",
+		"Cm panel test", "Cp panel test",
+		"Cm button test", "Cp button test"};
+	int index = 0;
+	int test_item = 0;
+	int no_builtin_file = 0;
+	int rc = 0;
+	int self_test_result_1 = 0;
+	int self_test_result_2 = 0;
+	u8 sys_mode = FW_SYS_MODE_UNDEFINED;
+	u8 retry = 3;
+
+	dev = dad->dev;
+	if ((configuration == NULL) || (cmcp_info == NULL))
+		goto exit;
+
+	mutex_lock(&dad->sysfs_lock);
+
+	if (dad->cmcp_test_in_progress) {
+		mutex_unlock(&dad->sysfs_lock);
+		goto cmcp_not_ready;
+	}
+	dad->cmcp_test_in_progress = 1;
+
+	dad->test_executed = 0;
+	test_item = pt_cmcp_get_test_item(dad->cmcp_test_items);
+
+	if (dad->builtin_cmcp_threshold_status < 0) {
+		pt_debug(dev, DL_WARN, "%s: No cmcp threshold file.\n",
+			__func__);
+		no_builtin_file = 1;
+		mutex_unlock(&dad->sysfs_lock);
+		goto start_testing;
+	}
+
+	if (dad->cmcp_test_items < 0) {
+		pt_debug(dev, DL_ERROR,
+			"%s: Invalid test item! Should be 0~4!\n", __func__);
+		mutex_unlock(&dad->sysfs_lock);
+		goto invalid_item;
+	}
+
+	pt_debug(dev, DL_INFO, "%s: Test item is %s, %d\n",
+		__func__, cmcp_test_case_array[dad->cmcp_test_items],
+		test_item);
+
+	if ((dad->si->num_btns == 0)
+		&& ((dad->cmcp_test_items == CMCP_CM_BTN)
+			|| (dad->cmcp_test_items == CMCP_CP_BTN))) {
+		pt_debug(dev, DL_WARN,
+			"%s: FW doesn't support button!\n", __func__);
+		mutex_unlock(&dad->sysfs_lock);
+		goto invalid_item_btn;
+	}
+
+	mutex_unlock(&dad->sysfs_lock);
+
+	if (cmcp_check_config_fw_match(dev, configuration))
+		goto mismatch;
+
+start_testing:
+	pt_debug(dev, DL_INFO, "%s: Start Cm/Cp test!\n", __func__);
+	result->cm_test_pass = true;
+	result->cp_test_pass = true;
+
+	/*stop watchdog*/
+	rc = cmd->request_stop_wd(dev);
+	if (rc)
+		pt_debug(dev, DL_ERROR, "stop watchdog failed");
+
+	/* Make sure the device is awake */
+	pm_runtime_get_sync(dev);
+	/* Resource protect */
+	rc = cmd->request_exclusive(dev, PT_REQUEST_EXCLUSIVE_TIMEOUT);
+	if (rc) {
+		pt_debug(dev, DL_ERROR,
+			"%s: Error on request exclusive rc = %d\n",
+			__func__, rc);
+	}
+
+	/*force single tx*/
+	rc = cmd->nonhid_cmd->set_param(dev,
+			PT_CORE_CMD_UNPROTECTED, 0x1F, 1, 1);
+	if (rc)
+		pt_debug(dev, DL_ERROR, "force single tx failed");
+
+	/*suspend_scanning */
+	rc = cmd->nonhid_cmd->suspend_scanning(dev, PT_CORE_CMD_UNPROTECTED);
+	if (rc)
+		pt_debug(dev, DL_ERROR, "suspend_scanning failed");
+
+	/* Do calibration if requested */
+	if (!dad->cmcp_force_calibrate) {
+		pt_debug(dev, DL_INFO, "do calibration in single tx mode");
+		rc = pt_perform_calibration(dev);
+		if (rc)
+			pt_debug(dev, DL_ERROR, "calibration failed");
+	}
+	/*resume_scanning */
+	rc = cmd->nonhid_cmd->resume_scanning(dev, PT_CORE_CMD_UNPROTECTED);
+	if (rc)
+		pt_debug(dev, DL_ERROR, "resume_scanning failed");
+
+	/*get all cmcp data from FW*/
+	self_test_result_1 = pt_get_cmcp_info(dad, cmcp_info);
+	if (self_test_result_1)
+		pt_debug(dev, DL_ERROR, "pt_get_cmcp_info failed");
+
+	/*restore to multi tx*/
+	rc = cmd->nonhid_cmd->set_param(dev,
+			PT_CORE_CMD_UNPROTECTED, 0x1F, 0, 1);
+	if (rc)
+		pt_debug(dev, DL_ERROR, "restore multi tx failed");
+
+	/*suspend_scanning */
+	rc = cmd->nonhid_cmd->suspend_scanning(dev, 0);
+	if (rc)
+		pt_debug(dev, DL_ERROR, "suspend_scanning failed");
+
+	/* Do calibration if requested */
+	if (!dad->cmcp_force_calibrate) {
+		pt_debug(dev, DL_INFO, "do calibration in multi tx mode");
+		rc = pt_perform_calibration(dev);
+		if (rc)
+			pt_debug(dev, DL_ERROR, "calibration failed");
+	}
+	/*resume_scanning */
+	rc = cmd->nonhid_cmd->resume_scanning(dev, PT_CORE_CMD_UNPROTECTED);
+	if (rc)
+		pt_debug(dev, DL_ERROR, "resume_scanning failed");
+
+	/*get cm cal data from FW*/
+	self_test_result_2 = pt_get_cm_cal(dad, cmcp_info);
+	if (self_test_result_2)
+		pt_debug(dev, DL_ERROR, "pt_get_cm_cal failed");
+
+	/* check scan state,try to fix if it is not right*/
+	while (retry--) {
+		rc = cmd->request_get_fw_mode(dev, PT_CORE_CMD_UNPROTECTED,
+			&sys_mode, NULL);
+
+		if (sys_mode != FW_SYS_MODE_SCANNING) {
+			pt_debug(dev, DL_ERROR,
+				"%s: fw mode: %d, retry: %d, rc = %d\n",
+				__func__, sys_mode, retry, rc);
+			rc = cmd->nonhid_cmd->resume_scanning(dev,
+						PT_CORE_CMD_UNPROTECTED);
+		}
+	}
+
+	rc = cmd->release_exclusive(dev);
+	if (rc) {
+		pt_debug(dev, DL_ERROR,
+			"%s: Error on release exclusive rc = %d\n",
+			__func__, rc);
+	}
+	pm_runtime_put(dev);
+
+	/*start  watchdog*/
+	rc = cmd->request_start_wd(dev);
+	if (rc)
+		pt_debug(dev, DL_ERROR, "start watchdog failed");
+
+	if (self_test_result_1 || self_test_result_2)
+		goto self_test_failed;
+
+	/* The tests are finished without failure */
+	mutex_lock(&dad->sysfs_lock);
+	dad->test_executed = 1;
+	mutex_unlock(&dad->sysfs_lock);
+
+	if (no_builtin_file)
+		goto no_builtin;
+
+	if ((test_item) & (CM_ENABLED))
+		validate_cm_test_results(dev, configuration, cmcp_info,
+		result, &final_pass, test_item);
+
+	if ((test_item) & (CP_ENABLED))
+		validate_cp_test_results(dev, configuration, cmcp_info,
+		result, &final_pass, test_item);
+
+	if ((dad->cmcp_test_items == CMCP_FULL)
+	&& (dad->cmcp_range_check == 0)) {
+		/*full test and full check*/
+		result->test_summary = result->cm_test_pass
+			&& result->cp_test_pass
+			&& result->short_test_pass;
+	} else if ((dad->cmcp_test_items == CMCP_FULL)
+		&& (dad->cmcp_range_check == 1)) {
+		/*full test and basic check*/
+		result->test_summary = result->cm_sensor_gd_col_pass
+			&& result->cm_sensor_gd_row_pass
+			&& result->cm_sensor_validation_pass
+			&& result->cp_rx_validation_pass
+			&& result->cp_tx_validation_pass
+			&& result->short_test_pass;
+	} else if (dad->cmcp_test_items == CMCP_CM_PANEL) {
+		/*cm panel test result only*/
+		result->test_summary = result->cm_sensor_gd_col_pass
+			&& result->cm_sensor_gd_row_pass
+			&& result->cm_sensor_validation_pass
+			&& result->cm_sensor_row_delta_pass
+			&& result->cm_sensor_col_delta_pass
+			&& result->cm_sensor_calibration_pass
+			&& result->cm_sensor_delta_pass;
+	} else if (dad->cmcp_test_items == CMCP_CP_PANEL) {
+		/*cp panel test result only*/
+		result->test_summary = result->cp_sensor_delta_pass
+			&& result->cp_rx_validation_pass
+			&& result->cp_tx_validation_pass;
+	} else if (dad->cmcp_test_items == CMCP_CM_BTN) {
+		/*cm button test result only*/
+		result->test_summary = result->cm_button_validation_pass
+			&& result->cm_button_delta_pass;
+	} else if (dad->cmcp_test_items == CMCP_CP_BTN) {
+		/*cp button test result only*/
+		result->test_summary = result->cp_button_delta_pass
+			&& result->cp_button_average_pass
+			&& result->cp_button_validation_pass;
+	}
+
+	if (result->test_summary) {
+		pt_debug(dev, DL_INFO,
+			"%s: Finish Cm/Cp test! All Test Passed\n", __func__);
+		index = snprintf(buf, PT_MAX_PRBUF_SIZE, "Status: 1\n");
+	} else {
+		pt_debug(dev, DL_INFO,
+			"%s: Finish Cm/Cp test! Range Check Failure\n",
+			__func__);
+		index = snprintf(buf, PT_MAX_PRBUF_SIZE, "Status: 6\n");
+	}
+	goto cmcp_ready;
+
+mismatch:
+	index = snprintf(buf, PT_MAX_PRBUF_SIZE,
+		 "Status: 2\nInput cmcp threshold file mismatches with FW\n");
+	goto cmcp_ready;
+invalid_item_btn:
+	index = snprintf(buf, PT_MAX_PRBUF_SIZE,
+		"Status: 3\nFW doesn't support button!\n");
+	goto cmcp_ready;
+invalid_item:
+	index = snprintf(buf, PT_MAX_PRBUF_SIZE,
+		"Status: 4\nWrong test item or range check input!\nOnly support below items:\n0 - Cm/Cp Panel & Button with Gradient (Typical)\n1 - Cm Panel with Gradient\n2 - Cp Panel\n3 - Cm Button\n4 - Cp Button\nOnly support below range check:\n0 - Full Range Checking (default)\n1 - Basic Range Checking(TSG5 style)\n");
+	goto cmcp_ready;
+self_test_failed:
+	index = snprintf(buf, PT_MAX_PRBUF_SIZE,
+		"Status: 5\nget self test ID not supported!\n");
+	goto cmcp_ready;
+cmcp_not_ready:
+	index = snprintf(buf, PT_MAX_PRBUF_SIZE, "Status: 0\n");
+	goto cmcp_ready;
+no_builtin:
+	index = snprintf(buf, PT_MAX_PRBUF_SIZE,
+		"Status: 7\nNo cmcp threshold file!\n");
+cmcp_ready:
+	mutex_lock(&dad->sysfs_lock);
+	dad->cmcp_test_in_progress = 0;
+	mutex_unlock(&dad->sysfs_lock);
+exit:
+	return index;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_cmcp_test_store
+ *
+ * SUMMARY: The store method for cmcp_test sysfs node.Allows the user to
+ *  configure which cm/cp tests will be executed on the "cat" of this node.
+ *
+ * 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_cmcp_test_store(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t size)
+{
+	struct pt_device_access_data *dad
+		= pt_get_device_access_data(dev);
+	u8 test_item = 0;
+	u8 range_check = 0;
+	u8 force_calibrate = 0;
+	u32 input_data[4];
+	int ret = 0;
+	static const char * const cmcp_test_case_array[] = {"Full Cm/Cp test",
+		"Cm panel test", "Cp panel test",
+		"Cm button test", "Cp button test"};
+	static const char * const cmcp_test_range_check_array[] = {
+		"Full (default)", "Basic"};
+	static const char * const cmcp_test_force_cal_array[] =	{
+		"Calibrate When Testing (default)", "No Calibration"};
+	ssize_t length = 0;
+
+	pm_runtime_get_sync(dev);
+	mutex_lock(&dad->sysfs_lock);
+
+	length = cmd->parse_sysfs_input(dev, buf, size, input_data,
+			ARRAY_SIZE(input_data));
+	if (length <= 0 || length > 3) {
+		pt_debug(dev, DL_ERROR, "%s: Input format error!\n",
+					__func__);
+		dad->cmcp_test_items = -EINVAL;
+		ret = -EINVAL;
+		goto error;
+	}
+
+	/* Get test item  */
+	test_item = input_data[0];
+	/* Get range check */
+	if (length >= 2)
+		range_check = input_data[1];
+	/* Get force calibration */
+	if (length == 3)
+		force_calibrate = input_data[2];
+
+	/*
+	 * Test item limitation:
+	 *	 0: Perform all Tests
+	 *	 1: CM Panel with Gradient
+	 *	 2: CP Panel
+	 *	 3: CM Button
+	 *	 4: CP Button
+	 * Ranage check limitation:
+	 *	 0: full check
+	 *	 1: basic check
+	 * Force calibrate limitation:
+	 *	 0: do calibration
+	 *	 1: don't do calibration
+	 */
+	if ((test_item < 0) || (test_item > 4) || (range_check > 1)
+		|| (force_calibrate > 1)) {
+		pt_debug(dev, DL_ERROR,
+		"%s: Test item should be 0~4; Range check should be 0~1; Force calibrate should be 0~1\n",
+		__func__);
+		dad->cmcp_test_items = -EINVAL;
+		ret = -EINVAL;
+		goto error;
+	}
+	/*
+	 * If it is not all Test, then range_check should be 0
+	 * because other test does not has concept of basic check
+	 */
+	if (test_item > 0 && test_item < 5)
+		range_check = 0;
+
+	dad->cmcp_test_items = test_item;
+	dad->cmcp_range_check = range_check;
+	dad->cmcp_force_calibrate = force_calibrate;
+	pt_debug(dev, DL_INFO,
+		"%s: Test item=%s; Range check=%s; Force cal=%s.\n",
+		__func__,
+		cmcp_test_case_array[test_item],
+		cmcp_test_range_check_array[range_check],
+		cmcp_test_force_cal_array[force_calibrate]);
+
+error:
+	mutex_unlock(&dad->sysfs_lock);
+	pm_runtime_put(dev);
+
+	if (ret)
+		return ret;
+
+	return size;
+}
+
+static DEVICE_ATTR(cmcp_test, 0600,
+	pt_cmcp_test_show, pt_cmcp_test_store);
+
+/*******************************************************************************
+ * FUNCTION: prepare_print_string
+ *
+ * SUMMARY: Formats input buffer to out buffer with string type,and increases
+ *  the index by size of formated data.
+ *
+ * RETURN:
+ *	 index plus with size of formated data
+ *
+ * PARAMETERS:
+ *  *out_buf  - output buffer to store formated data
+ *  *in_buf   - input buffer to be formated
+ *   index    - index in output buffer for appending content
+ ******************************************************************************/
+int prepare_print_string(char *out_buf, char *in_buf, int index)
+{
+	if ((out_buf == NULL) || (in_buf == NULL))
+		return index;
+	index += scnprintf(&out_buf[index], MAX_BUF_LEN - index,
+			"%s", in_buf);
+	return index;
+}
+
+/*******************************************************************************
+ * FUNCTION: prepare_print_string
+ *
+ * SUMMARY: Formats input buffer to out buffer with decimal base,and increases
+ *  the index by size of formated data.
+ *
+ * RETURN:
+ *	 index plus with size of formated data
+ *
+ * PARAMETERS:
+ *  *out_buf  - output buffer to store formated data
+ *  *in_buf   - input buffer to be formated
+ *   index    - index in output buffer for appending content
+ *   data_num - data number in input buffer
+ ******************************************************************************/
+int prepare_print_data(char *out_buf, int32_t *in_buf, int index, int data_num)
+{
+	int i;
+
+	if ((out_buf == NULL) || (in_buf == NULL))
+		return index;
+	for (i = 0; i < data_num; i++)
+		index += scnprintf(&out_buf[index], MAX_BUF_LEN - index,
+				"%d,", in_buf[i]);
+	return index;
+}
+
+/*******************************************************************************
+ * FUNCTION: save_header
+ *
+ * SUMMARY: Appends "header" for cmcp test result to output buffer.
+ *
+ * RETURN:
+ *	 index plus with size of formated data
+ *
+ * PARAMETERS:
+ *  *out_buf  - output buffer to store formated data
+ *   index    - index in output buffer for appending content
+ *  *result   - pointer to result structure
+ ******************************************************************************/
+int save_header(char *out_buf, int index, struct configuration *config,
+		struct result *result)
+{
+	struct rtc_time tm;
+	char time_buf[100] = {0};
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 0, 0))
+	struct timespec64 ts;
+
+	ktime_get_real_ts64(&ts);
+	rtc_time64_to_tm(ts.tv_sec, &tm);
+#else
+	struct timex txc;
+
+	do_gettimeofday(&(txc.time));
+	rtc_time_to_tm(txc.time.tv_sec, &tm);
+#endif
+
+	scnprintf(time_buf, 100, "%d/%d/%d,TIME,%d:%d:%d,", tm.tm_year+1900,
+		 tm.tm_mon, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec);
+
+	index = prepare_print_string(out_buf, ",.header,\n", index);
+	index = prepare_print_string(out_buf, ",DATE,", index);
+	index = prepare_print_string(out_buf, &time_buf[0], index);
+	index = prepare_print_string(out_buf, ",\n", index);
+	index = prepare_print_string(out_buf, ",SW_VERSION,", index);
+	index = prepare_print_string(out_buf, PT_DRIVER_VERSION, index);
+	index = prepare_print_string(out_buf, ",\n", index);
+	if (config) {
+		index = prepare_print_string(out_buf, ",PROJ_VERSION,", index);
+		index = prepare_print_string(out_buf, config->proj_info, index);
+		index = prepare_print_string(out_buf, ",\n", index);
+	}
+
+	index = prepare_print_string(out_buf, ",.end,\n", index);
+	index = prepare_print_string(out_buf, ",.engineering data,\n", index);
+
+	return index;
+}
+
+/*******************************************************************************
+ * FUNCTION: print_silicon_id
+ *
+ * SUMMARY: Formats input buffer(silicon id) to out buffer with
+ *  string type,and increases the index by size of formated data.
+ *
+ * RETURN:
+ *	 index plus with size of formated data
+ *
+ * PARAMETERS:
+ *  *out_buf  - output buffer to store formated data
+ *  *in_buf   - input buffer to be formated
+ *   index    - index in output buffer for appending content
+ ******************************************************************************/
+static int print_silicon_id(char *out_buf, char *in_buf, int index)
+{
+	index = prepare_print_string(out_buf, ",1,", index);
+	index = prepare_print_string(out_buf, &in_buf[0], index);
+	return index;
+}
+
+/*******************************************************************************
+ * FUNCTION: save_engineering_data
+ *
+ * SUMMARY: Generates cmcp test result with *.csv format to output buffer, but
+ *  it doesn't include the header.
+ *
+ * RETURN:
+ *	 index plus with size of formated data
+ *
+ * PARAMETERS:
+ *  *dev             - pointer to device structure
+ *  *out_buf         - output buffer to store formated data
+ *   index           - index in output buffer for appending content
+ *  *cmcp_info       - pointer to cmcp_data structure
+ *  *configuration   - pointer to configuration structure
+ *  *result          - pointer to result structure
+ *   test_item       - test control in bitwise
+ *   no_builtin_file - flag to determin if builtin-file exist
+ ******************************************************************************/
+int save_engineering_data(struct device *dev, char *out_buf, int index,
+	struct cmcp_data *cmcp_info, struct configuration *configuration,
+	struct result *result, int test_item, int no_builtin_file)
+{
+	int i;
+	int j;
+	int tx_num = cmcp_info->tx_num;
+	int rx_num = cmcp_info->rx_num;
+	int btn_num = cmcp_info->btn_num;
+	int tmp = 0;
+	uint32_t fw_revision_control;
+	uint32_t fw_config_ver;
+	char device_id[20] = {0};
+	struct pt_device_access_data *dad
+		= pt_get_device_access_data(dev);
+
+	fw_revision_control = dad->si->ttdata.revctrl;
+	fw_config_ver = dad->si->ttdata.fw_ver_conf;
+	/*calculate silicon id*/
+	result->device_id_low = 0;
+	result->device_id_high = 0;
+
+	for (i = 0; i < 4; i++)
+		result->device_id_low =
+		(result->device_id_low << 8) + dad->si->ttdata.mfg_id[i];
+
+	for (i = 4; i < 8; i++)
+		result->device_id_high =
+		(result->device_id_high << 8) + dad->si->ttdata.mfg_id[i];
+
+	scnprintf(device_id, 20, "%x%x",
+		result->device_id_high, result->device_id_low);
+
+	/*print test summary*/
+	index = print_silicon_id(out_buf, &device_id[0], index);
+	if (result->test_summary)
+		index = prepare_print_string(out_buf, ",PASS,\n", index);
+	else
+		index = prepare_print_string(out_buf, ",FAIL,\n", index);
+
+	/*revision ctrl number*/
+	index = print_silicon_id(out_buf, &device_id[0], index);
+	index = prepare_print_string(out_buf, ",FW revision Control,", index);
+	index = prepare_print_data(out_buf, &fw_revision_control, index, 1);
+	index = prepare_print_string(out_buf, "\n", index);
+
+	/*config version*/
+	index = print_silicon_id(out_buf, &device_id[0], index);
+	index = prepare_print_string(out_buf, ",CONFIG_VER,", index);
+	index = prepare_print_data(out_buf, &fw_config_ver, index, 1);
+	index = prepare_print_string(out_buf, "\n", index);
+
+	/* Shorts test */
+	index = print_silicon_id(out_buf, &device_id[0], index);
+	if (result->short_test_pass)
+		index = prepare_print_string(out_buf, ",Shorts,PASS,\n", index);
+	else
+		index = prepare_print_string(out_buf, ",Shorts,FAIL,\n", index);
+
+	if ((test_item & CM_ENABLED) == CM_ENABLED) {
+		/*print BUTNS_CM_DATA_ROW00*/
+		if (((test_item & CM_BTN) == CM_BTN) && (btn_num > 0)) {
+			index = print_silicon_id(out_buf, &device_id[0], index);
+			index = prepare_print_string(out_buf,
+				",Sensor Cm Validation,BUTNS_CM_DATA_ROW00,",
+				index);
+			index = prepare_print_data(out_buf,
+				&cmcp_info->cm_btn_data[0],
+				index,
+				btn_num);
+			index = prepare_print_string(out_buf, "\n", index);
+		}
+
+		if ((test_item & CM_PANEL) == CM_PANEL) {
+			/*print CM_DATA_ROW*/
+			for (i = 0; i < rx_num; i++) {
+				index = print_silicon_id(out_buf, &device_id[0],
+							index);
+				index = prepare_print_string(out_buf,
+							",Sensor Cm Validation,CM_DATA_ROW",
+							index);
+				index = prepare_print_data(out_buf, &i,
+							index, 1);
+				for (j = 0; j < tx_num; j++)
+					index = prepare_print_data(out_buf,
+					&cmcp_info->cm_data_panel[j*rx_num+i],
+					index, 1);
+				index = prepare_print_string(out_buf,
+					"\n", index);
+			}
+
+			if (!no_builtin_file) {
+				/*print CM_MAX_GRADIENT_COLS_PERCENT*/
+				index = print_silicon_id(out_buf,
+							&device_id[0], index);
+				index = prepare_print_string(out_buf,
+					 ",Sensor Cm Validation,CM_MAX_GRADIENT_COLS_PERCENT,",
+					index);
+				for (i = 0; i < tx_num; i++) {
+					char tmp_buf[10] = {0};
+
+					scnprintf(tmp_buf, 10, "%d.%d,",
+				cmcp_info->gd_sensor_col[i].gradient_val / 10,
+				cmcp_info->gd_sensor_col[i].gradient_val % 10);
+					index = prepare_print_string(out_buf,
+						&tmp_buf[0], index);
+				}
+				index = prepare_print_string(out_buf,
+								"\n", index);
+
+				/*print CM_MAX_GRADIENT_ROWS_PERCENT*/
+				index = print_silicon_id(out_buf,
+							&device_id[0], index);
+				index = prepare_print_string(out_buf,
+			",Sensor Cm Validation,CM_MAX_GRADIENT_ROWS_PERCENT,",
+					index);
+				for (i = 0; i < rx_num; i++) {
+					char tmp_buf[10] = {0};
+
+					scnprintf(tmp_buf, 10, "%d.%d,",
+				cmcp_info->gd_sensor_row[i].gradient_val / 10,
+				cmcp_info->gd_sensor_row[i].gradient_val % 10);
+					index = prepare_print_string(out_buf,
+						&tmp_buf[0], index);
+				}
+				index = prepare_print_string(out_buf,
+								"\n", index);
+
+				if (!dad->cmcp_range_check) {
+					/*print CM_DELTA_COLUMN*/
+					for (i = 0; i < rx_num; i++) {
+						index = print_silicon_id(
+							out_buf,
+							&device_id[0], index);
+						index = prepare_print_string(
+							out_buf,
+							",Sensor Cm Validation,DELTA_COLUMNS_ROW",
+							index);
+						index = prepare_print_data(
+							out_buf,
+							&i, index, 1);
+						index = prepare_print_data(
+							out_buf,
+							&tmp, index, 1);
+					for (j = 1; j < tx_num; j++)
+						index = prepare_print_data(
+						out_buf,
+			&cmcp_info->cm_sensor_column_delta[(j-1)*rx_num+i],
+						index, 1);
+						index = prepare_print_string(
+								out_buf,
+								"\n", index);
+					}
+
+					/*print CM_DELTA_ROW*/
+					index = print_silicon_id(out_buf,
+								&device_id[0],
+								index);
+					index = prepare_print_string(out_buf,
+						 ",Sensor Cm Validation,DELTA_ROWS_ROW",
+								index);
+					index = prepare_print_data(out_buf,
+								&tmp, index, 1);
+					for (j = 0; j < tx_num; j++)
+						index = prepare_print_data(
+								out_buf,
+								&tmp, index, 1);
+					index = prepare_print_string(out_buf,
+								"\n", index);
+
+					for (i = 1; i < rx_num; i++) {
+						index = print_silicon_id(
+								out_buf,
+								&device_id[0],
+								index);
+						index = prepare_print_string(
+								out_buf,
+						",Sensor Cm Validation,DELTA_ROWS_ROW",
+								index);
+						index = prepare_print_data(
+								out_buf, &i,
+								index, 1);
+					for (j = 0; j < tx_num; j++)
+						index = prepare_print_data(
+							out_buf,
+				&cmcp_info->cm_sensor_row_delta[j*rx_num+i-1],
+							index, 1);
+						index = prepare_print_string(
+							out_buf,
+							"\n", index);
+					}
+
+				/*print pass/fail Sensor Cm Validation*/
+				index = print_silicon_id(out_buf, &device_id[0],
+							index);
+				if (result->cm_test_pass)
+					index = prepare_print_string(out_buf,
+						",Sensor Cm Validation,PASS,\n",
+						index);
+				else
+					index = prepare_print_string(out_buf,
+						",Sensor Cm Validation,FAIL,\n",
+						index);
+			}
+			}
+		}
+
+		if (!no_builtin_file) {
+			if (((test_item & CM_BTN) == CM_BTN) && (btn_num > 0)
+				&& (!dad->cmcp_range_check)) {
+				char tmp_buf[10] = {0};
+				/*print Button Element by Element */
+				index = print_silicon_id(out_buf, &device_id[0],
+					index);
+				if (result->cm_button_validation_pass)
+					index = prepare_print_string(out_buf,
+					",Sensor Cm Validation - Button Element by Element,PASS\n",
+					index);
+				else
+					index = prepare_print_string(out_buf,
+					",Sensor Cm Validation - Button Element by Element,FAIL\n",
+					index);
+
+				/*
+				 *print  Sensor Cm Validation
+				 *- Buttons Range Buttons Range
+				 */
+				index = print_silicon_id(out_buf,
+					&device_id[0], index);
+				index = prepare_print_string(out_buf,
+					 ",Sensor Cm Validation - Buttons Range,Buttons Range,",
+					 index);
+				scnprintf(tmp_buf, 10, "%d.%d,",
+					 cmcp_info->cm_delta_data_btn / 10,
+					 cmcp_info->cm_delta_data_btn % 10);
+				index = prepare_print_string(out_buf,
+					&tmp_buf[0], index);
+				index = prepare_print_string(out_buf,
+					"\n", index);
+
+				/*print  Sensor Cm Validation
+				 *-Buttons Range Cm_button_avg
+				 */
+				index = print_silicon_id(out_buf,
+					&device_id[0], index);
+				index = prepare_print_string(out_buf,
+					 ",Sensor Cm Validation - Buttons Range,Cm_button_avg,",
+					 index);
+				index = prepare_print_data(out_buf,
+					&cmcp_info->cm_ave_data_btn,
+					 index, 1);
+				index = prepare_print_string(out_buf,
+					"\n", index);
+
+				/*print  Sensor Cm Validation
+				 * -Buttons Range Cm_button_avg
+				 */
+				index = print_silicon_id(out_buf,
+					&device_id[0], index);
+				index = prepare_print_string(out_buf,
+					",Sensor Cm Validation - Buttons Range,Cm_button_cal,",
+					index);
+				index = prepare_print_data(out_buf,
+					&cmcp_info->cm_cal_data_btn,
+					index, 1);
+				index = prepare_print_string(out_buf,
+					"\n", index);
+
+				/*print  Sensor Cm Validation
+				 *-Buttons Range pass/fail
+				 */
+				index = print_silicon_id(out_buf,
+					&device_id[0], index);
+				if (result->cm_button_delta_pass)
+					index = prepare_print_string(out_buf,
+						",Sensor Cm Validation - Buttons Range,PASS,LIMITS,",
+						index);
+				else
+					index = prepare_print_string(out_buf,
+						",Sensor Cm Validation - Buttons Range,FAIL,LIMITS,",
+						index);
+				index = prepare_print_data(out_buf,
+				&configuration->cm_max_delta_button_percent,
+				index, 1);
+				index = prepare_print_string(out_buf,
+				"\n", index);
+			}
+
+			if ((test_item & CM_PANEL) == CM_PANEL &&
+				!dad->cmcp_range_check) {
+				char tmp_buf[10] = {0};
+				/*print Cm_sensor_cal */
+				index = print_silicon_id(out_buf,
+					&device_id[0], index);
+				index = prepare_print_string(out_buf,
+					",Sensor Cm Validation - Calibration,Cm_sensor_cal,",
+					index);
+				index = prepare_print_data(out_buf,
+					&cmcp_info->cm_cal_data_panel,
+					index, 1);
+				index = prepare_print_string(out_buf,
+					"\n", index);
+
+				/*print Cm_sensor_cal limit*/
+				index = print_silicon_id(out_buf,
+					&device_id[0], index);
+				if (result->cm_sensor_calibration_pass)
+					index = prepare_print_string(out_buf,
+						",Sensor Cm Validation - Calibration,PASS,LIMITS,",
+						index);
+				else
+					index = prepare_print_string(out_buf,
+						",Sensor Cm Validation - Calibration,FAIL,LIMITS,",
+						index);
+				index = prepare_print_data(out_buf,
+					&configuration->cm_min_limit_cal,
+					index, 1);
+				index = prepare_print_data(out_buf,
+					&configuration->cm_max_limit_cal,
+					index, 1);
+				index = prepare_print_string(out_buf,
+					"\n", index);
+
+				/*print Columns Delta Matrix*/
+				index = print_silicon_id(out_buf,
+					&device_id[0], index);
+				if (result->cm_sensor_col_delta_pass)
+					index = prepare_print_string(out_buf,
+					",Sensor Cm Validation - Columns Delta Matrix,PASS,LIMITS,",
+					index);
+				else
+					index = prepare_print_string(out_buf,
+					",Sensor Cm Validation - Columns Delta Matrix,FAIL,LIMITS,",
+					index);
+				index = prepare_print_data(out_buf,
+					&configuration->cm_range_limit_col,
+					index, 1);
+				index = prepare_print_string(out_buf,
+					"\n", index);
+
+				/*print Cm Validation - Element by Element*/
+				index = print_silicon_id(out_buf,
+					&device_id[0], index);
+				if (result->cm_sensor_validation_pass)
+					index = prepare_print_string(out_buf,
+						",Sensor Cm Validation - Element by Element,PASS,",
+						index);
+				else
+					index = prepare_print_string(out_buf,
+						",Sensor Cm Validation - Element by Element,FAIL,",
+						index);
+				index = prepare_print_string(out_buf,
+					"\n", index);
+
+				/*print Cm Validation -Gradient Cols*/
+				index = print_silicon_id(out_buf,
+					&device_id[0], index);
+				if (result->cm_sensor_gd_col_pass)
+					index = prepare_print_string(out_buf,
+						",Sensor Cm Validation - Gradient Cols,PASS,",
+						index);
+				else
+					index = prepare_print_string(out_buf,
+						",Sensor Cm Validation - Gradient Cols,FAIL,",
+						index);
+				index = prepare_print_string(out_buf,
+					"\n", index);
+
+				/*print Cm Validation -Gradient Rows*/
+				index = print_silicon_id(out_buf,
+					&device_id[0], index);
+				if (result->cm_sensor_gd_row_pass)
+					index = prepare_print_string(out_buf,
+						",Sensor Cm Validation - Gradient Rows,PASS,",
+						index);
+				else
+					index = prepare_print_string(out_buf,
+						",Sensor Cm Validation - Gradient Rows,FAIL,",
+						index);
+				index = prepare_print_string(out_buf,
+					"\n", index);
+
+
+				/*
+				 * Print Sensor Cm Validation
+				 * -Rows Delta Matrix
+				 */
+				index = print_silicon_id(out_buf,
+					&device_id[0], index);
+				if (result->cm_sensor_row_delta_pass)
+					index = prepare_print_string(out_buf,
+					",Sensor Cm Validation - Rows Delta Matrix,PASS,LIMITS,",
+					index);
+				else
+					index = prepare_print_string(out_buf,
+					",Sensor Cm Validation - Rows Delta Matrix,FAIL,LIMITS,",
+					index);
+				index = prepare_print_data(out_buf,
+					&configuration->cm_range_limit_row,
+					index, 1);
+				index = prepare_print_string(out_buf,
+					"\n", index);
+
+				/*print Cm_sensor_avg */
+				index = print_silicon_id(out_buf,
+					&device_id[0], index);
+				index = prepare_print_string(out_buf,
+					",Sensor Cm Validation - Sensor Range,Cm_sensor_avg,",
+					index);
+				index = prepare_print_data(out_buf,
+					&cmcp_info->cm_ave_data_panel,
+					index, 1);
+				index = prepare_print_string(out_buf,
+					"\n", index);
+
+				/*printSensor Cm Validation -
+				 * Sensor Range,   Sensor Range
+				 */
+				index = print_silicon_id(out_buf,
+					&device_id[0], index);
+				index = prepare_print_string(out_buf,
+					",Sensor Cm Validation - Sensor Range,Sensor Range,",
+					index);
+				scnprintf(tmp_buf, 10, "%d.%d,",
+					cmcp_info->cm_sensor_delta / 10,
+					cmcp_info->cm_sensor_delta % 10);
+				index = prepare_print_string(out_buf,
+					&tmp_buf[0], index);
+				index = prepare_print_string(out_buf,
+					"\n", index);
+
+				/*print Sensor Cm Validation - Sensor Range*/
+				index = print_silicon_id(out_buf,
+					&device_id[0], index);
+				if (result->cm_sensor_delta_pass)
+					index = prepare_print_string(out_buf,
+						",Sensor Cm Validation - Sensor Range,PASS,LIMITS,",
+						index);
+				else
+					index = prepare_print_string(out_buf,
+						",Sensor Cm Validation - Sensor Range,FAIL,LIMITS,",
+						index);
+				index = prepare_print_data(out_buf,
+				&configuration->cm_max_delta_sensor_percent,
+						index, 1);
+				index = prepare_print_string(out_buf,
+					"\n", index);
+			}
+		}
+	}
+
+	if ((test_item & CP_ENABLED) == CP_ENABLED) {
+		if (((test_item & CP_BTN) == CP_BTN) && (btn_num > 0)) {
+			/*print   BUTNS_CP_DATA_ROW00  */
+			index = print_silicon_id(out_buf, &device_id[0], index);
+			index = prepare_print_string(out_buf,
+			",Self-cap Calibration Check,BUTNS_CP_DATA_ROW00,",
+				index);
+			index = prepare_print_data(out_buf,
+				&cmcp_info->cp_btn_data[0],
+				index, btn_num);
+			index = prepare_print_string(out_buf,
+				"\n", index);
+
+			if (!no_builtin_file && !dad->cmcp_range_check) {
+				/*print Cp Button Element by Element */
+				index = print_silicon_id(out_buf, &device_id[0],
+					index);
+				if (result->cp_button_validation_pass)
+					index = prepare_print_string(out_buf,
+					",Self-cap Calibration Check - Button Element by Element,PASS\n",
+					index);
+				else
+					index = prepare_print_string(out_buf,
+					",Self-cap Calibration Check - Button Element by Element,FAIL\n",
+					index);
+
+				/*print   cp_button_ave  */
+				index = print_silicon_id(out_buf,
+					&device_id[0], index);
+				index = prepare_print_string(out_buf,
+				",Self-cap Calibration Check,Cp_button_avg,",
+					index);
+				index = prepare_print_data(out_buf,
+					&cmcp_info->cp_button_ave,
+					index, 1);
+				index = prepare_print_string(out_buf,
+					"\n", index);
+
+				/*print   Cp_button_cal  */
+				index = print_silicon_id(out_buf,
+					&device_id[0], index);
+				index = prepare_print_string(out_buf,
+				",Self-cap Calibration Check,Cp_button_cal,",
+					index);
+				index = prepare_print_data(out_buf,
+					&cmcp_info->cp_btn_cal,
+					index, 1);
+				index = prepare_print_string(out_buf,
+					"\n", index);
+			}
+		}
+
+		if ((test_item & CP_PANEL) == CP_PANEL) {
+			/*print CP_DATA_RX */
+			index = print_silicon_id(out_buf, &device_id[0], index);
+			index = prepare_print_string(out_buf,
+			",Self-cap Calibration Check,CP_DATA_RX,", index);
+			index = prepare_print_data(out_buf,
+				&cmcp_info->cp_rx_data_panel[0], index, rx_num);
+			index = prepare_print_string(out_buf, "\n", index);
+
+			/*print CP_DATA_TX */
+			index = print_silicon_id(out_buf, &device_id[0], index);
+			index = prepare_print_string(out_buf,
+			",Self-cap Calibration Check,CP_DATA_TX,", index);
+			index = prepare_print_data(out_buf,
+				&cmcp_info->cp_tx_data_panel[0], index, tx_num);
+			index = prepare_print_string(out_buf, "\n", index);
+		}
+		if (((test_item & CP_BTN) == CP_BTN) && (btn_num > 0)
+			&& !dad->cmcp_range_check) {
+			if (!no_builtin_file) {
+				char tmp_buf[10] = {0};
+				/*print  Cp_delta_button */
+				index = print_silicon_id(out_buf, &device_id[0],
+					index);
+				index = prepare_print_string(out_buf,
+				",Self-cap Calibration Check,Cp_delta_button,",
+				index);
+				scnprintf(tmp_buf, 10, "%d.%d,",
+				cmcp_info->cp_button_delta / 10,
+				cmcp_info->cp_button_delta % 10);
+				index = prepare_print_string(out_buf,
+						&tmp_buf[0], index);
+				index = prepare_print_string(out_buf, "\n",
+					index);
+			}
+		}
+		if ((test_item & CP_PANEL) == CP_PANEL &&
+			!dad->cmcp_range_check) {
+			if (!no_builtin_file) {
+				char tmp_buf[10] = {0};
+				/*print Cp_delta_rx */
+				index = print_silicon_id(out_buf, &device_id[0],
+					index);
+				index = prepare_print_string(out_buf,
+				",Self-cap Calibration Check,Cp_delta_rx,",
+				index);
+				scnprintf(tmp_buf, 10, "%d.%d,",
+				cmcp_info->cp_sensor_rx_delta / 10,
+				cmcp_info->cp_sensor_rx_delta % 10);
+				index = prepare_print_string(out_buf,
+						&tmp_buf[0], index);
+				index = prepare_print_string(out_buf, "\n",
+					index);
+
+				/*print Cp_delta_tx */
+				index = print_silicon_id(out_buf, &device_id[0],
+					index);
+				index = prepare_print_string(out_buf,
+				",Self-cap Calibration Check,Cp_delta_tx,",
+					index);
+				scnprintf(tmp_buf, 10, "%d.%d,",
+				cmcp_info->cp_sensor_tx_delta / 10,
+				cmcp_info->cp_sensor_tx_delta % 10);
+				index = prepare_print_string(out_buf,
+						&tmp_buf[0], index);
+				index = prepare_print_string(out_buf, "\n",
+					index);
+
+				/*print Cp_sensor_avg_rx */
+				index = print_silicon_id(out_buf, &device_id[0],
+					index);
+				index = prepare_print_string(out_buf,
+				",Self-cap Calibration Check,Cp_sensor_avg_rx,",
+					index);
+				index = prepare_print_data(out_buf,
+					&cmcp_info->cp_rx_ave_data_panel,
+					index, 1);
+				index = prepare_print_string(out_buf,
+					"\n", index);
+
+				/*print Cp_sensor_avg_tx */
+				index = print_silicon_id(out_buf,
+					&device_id[0], index);
+				index = prepare_print_string(out_buf,
+				",Self-cap Calibration Check,Cp_sensor_avg_tx,",
+					index);
+				index = prepare_print_data(out_buf,
+					&cmcp_info->cp_tx_ave_data_panel,
+					index, 1);
+				index = prepare_print_string(out_buf,
+					"\n", index);
+
+				/*print Cp_sensor_cal_rx */
+				index = print_silicon_id(out_buf,
+					&device_id[0], index);
+				index = prepare_print_string(out_buf,
+				",Self-cap Calibration Check,Cp_sensor_cal_rx,",
+					index);
+				index = prepare_print_data(out_buf,
+					&cmcp_info->cp_rx_cal_data_panel[0],
+					index, rx_num);
+				index = prepare_print_string(out_buf,
+					"\n", index);
+
+				/*print Cp_sensor_cal_tx */
+				index = print_silicon_id(out_buf,
+					&device_id[0], index);
+				index = prepare_print_string(out_buf,
+				",Self-cap Calibration Check,Cp_sensor_cal_tx,",
+					index);
+				index = prepare_print_data(out_buf,
+					&cmcp_info->cp_tx_cal_data_panel[0],
+					index, tx_num);
+				index = prepare_print_string(out_buf,
+					"\n", index);
+			}
+		}
+
+		if (!no_builtin_file && !dad->cmcp_range_check) {
+			/*print  cp test limits  */
+			index = print_silicon_id(out_buf, &device_id[0], index);
+			if (result->cp_test_pass)
+				index = prepare_print_string(out_buf,
+				",Self-cap Calibration Check,PASS, LIMITS,",
+				index);
+			else
+				index = prepare_print_string(out_buf,
+				",Self-cap Calibration Check,FAIL, LIMITS,",
+				index);
+
+			index = prepare_print_string(out_buf,
+				"CP_MAX_DELTA_SENSOR_RX_PERCENT,", index);
+			index = prepare_print_data(out_buf,
+				&configuration->cp_max_delta_sensor_rx_percent,
+				index, 1);
+			index = prepare_print_string(out_buf,
+				"CP_MAX_DELTA_SENSOR_TX_PERCENT,", index);
+			index = prepare_print_data(out_buf,
+				&configuration->cp_max_delta_sensor_tx_percent,
+				index, 1);
+			index = prepare_print_string(out_buf,
+				"CP_MAX_DELTA_BUTTON_PERCENT,", index);
+			index = prepare_print_data(out_buf,
+				&configuration->cp_max_delta_button_percent,
+				index, 1);
+			index = prepare_print_string(out_buf, "\n", index);
+		}
+	}
+
+	if (!no_builtin_file) {
+		if ((test_item & CM_ENABLED) == CM_ENABLED) {
+			if ((test_item & CM_PANEL) == CM_PANEL) {
+				/*print columns gradient limit*/
+				index = prepare_print_string(out_buf,
+				",Sensor Cm Validation,MAX_LIMITS,CM_MAX_GRADIENT_COLS_PERCENT,",
+				index);
+				index = prepare_print_data(out_buf,
+			&configuration->cm_max_table_gradient_cols_percent[0],
+					index,
+			configuration->cm_max_table_gradient_cols_percent_size);
+				index = prepare_print_string(out_buf,
+					"\n", index);
+				/*print rows gradient limit*/
+				index = prepare_print_string(out_buf,
+				",Sensor Cm Validation,MAX_LIMITS,CM_MAX_GRADIENT_ROWS_PERCENT,",
+				index);
+				index = prepare_print_data(out_buf,
+			&configuration->cm_max_table_gradient_rows_percent[0],
+					index,
+			configuration->cm_max_table_gradient_rows_percent_size);
+				index = prepare_print_string(out_buf,
+					"\n", index);
+
+				/*print cm max limit*/
+				for (i = 0; i < rx_num; i++) {
+					index = prepare_print_string(out_buf,
+						",Sensor Cm Validation,MAX_LIMITS,CM_DATA_ROW",
+						index);
+					index = prepare_print_data(out_buf,
+						&i, index, 1);
+					for (j = 0; j < tx_num; j++)
+						index = prepare_print_data(
+							out_buf,
+		&configuration->cm_min_max_table_sensor[i*tx_num*2+j*2+1],
+							index, 1);
+					index = prepare_print_string(out_buf,
+						"\n", index);
+				}
+			}
+
+			if (((test_item & CM_BTN) == CM_BTN) && (btn_num > 0)) {
+				index = prepare_print_string(out_buf,
+					",Sensor Cm Validation,MAX LIMITS,M_BUTNS,",
+					index);
+				for (j = 0; j < btn_num; j++) {
+					index = prepare_print_data(out_buf,
+				&configuration->cm_min_max_table_btn[2*j+1],
+						index, 1);
+				}
+				index = prepare_print_string(out_buf,
+					"\n", index);
+			}
+
+			index = prepare_print_string(out_buf,
+				 ",Sensor Cm Validation MAX LIMITS\n", index);
+
+			if ((test_item & CM_PANEL) == CM_PANEL) {
+				/*print cm min limit*/
+				for (i = 0; i < rx_num; i++) {
+					index = prepare_print_string(out_buf,
+						",Sensor Cm Validation,MIN_LIMITS,CM_DATA_ROW",
+						index);
+					index = prepare_print_data(out_buf, &i,
+						index, 1);
+					for (j = 0; j < tx_num; j++)
+						index = prepare_print_data(
+							out_buf,
+		&configuration->cm_min_max_table_sensor[i*tx_num*2 + j*2],
+							index, 1);
+					index = prepare_print_string(out_buf,
+						"\n", index);
+				}
+			}
+
+			if (((test_item & CM_BTN) == CM_BTN) && (btn_num > 0)) {
+				index = prepare_print_string(out_buf,
+					",Sensor Cm Validation,MIN LIMITS,M_BUTNS,",
+					index);
+				for (j = 0; j < btn_num; j++) {
+					index = prepare_print_data(out_buf,
+				&configuration->cm_min_max_table_btn[2*j],
+						 index, 1);
+				}
+				index = prepare_print_string(out_buf,
+					"\n", index);
+			}
+			index = prepare_print_string(out_buf,
+				",Sensor Cm Validation MIN LIMITS\n", index);
+		}
+
+		if ((test_item & CP_ENABLED) == CP_ENABLED) {
+			if ((test_item & CP_PANEL) == CP_PANEL) {
+				/*print cp tx max limit*/
+				index = prepare_print_string(out_buf,
+				",Self-cap Calibration Check,MAX_LIMITS,TX,",
+					index);
+				for (i = 0; i < tx_num; i++)
+					index = prepare_print_data(out_buf,
+				&configuration->cp_min_max_table_tx[i*2+1],
+						index, 1);
+				index = prepare_print_string(out_buf,
+					"\n", index);
+
+				/*print cp rx max limit*/
+				index = prepare_print_string(out_buf,
+				",Self-cap Calibration Check,MAX_LIMITS,RX,",
+					index);
+				for (i = 0; i < rx_num; i++)
+					index = prepare_print_data(out_buf,
+				&configuration->cp_min_max_table_rx[i*2+1],
+						index, 1);
+				index = prepare_print_string(out_buf,
+					"\n", index);
+			}
+
+			/*print cp btn max limit*/
+			if (((test_item & CP_BTN) == CP_BTN) && (btn_num > 0)) {
+				index = prepare_print_string(out_buf,
+			",Self-cap Calibration Check,MAX_LIMITS,S_BUTNS,",
+					index);
+				for (i = 0; i < btn_num; i++)
+					index = prepare_print_data(out_buf,
+				&configuration->cp_min_max_table_btn[i*2+1],
+						index, 1);
+				index = prepare_print_string(out_buf,
+						"\n", index);
+			}
+
+			if ((test_item & CP_PANEL) == CP_PANEL) {
+				/*print cp tx min limit*/
+				index = prepare_print_string(out_buf,
+				",Self-cap Calibration Check,MIN_LIMITS,TX,",
+					index);
+				for (i = 0; i < tx_num; i++)
+					index = prepare_print_data(out_buf,
+				&configuration->cp_min_max_table_tx[i*2],
+						index, 1);
+				index = prepare_print_string(out_buf,
+					"\n", index);
+
+				/*print cp rx min limit*/
+				index = prepare_print_string(out_buf,
+				",Self-cap Calibration Check,MIN_LIMITS,RX,",
+					index);
+				for (i = 0; i < rx_num; i++)
+					index = prepare_print_data(out_buf,
+				&configuration->cp_min_max_table_rx[i*2],
+						index, 1);
+				index = prepare_print_string(out_buf,
+					"\n", index);
+			}
+
+			/*print cp btn min limit*/
+			if (((test_item & CP_BTN) == CP_BTN) && (btn_num > 0)) {
+				index = prepare_print_string(out_buf,
+			",Self-cap Calibration Check,MIN_LIMITS,S_BUTNS,",
+					index);
+				for (i = 0; i < btn_num; i++)
+					index = prepare_print_data(out_buf,
+				&configuration->cp_min_max_table_btn[i*2],
+						index, 1);
+				index = prepare_print_string(out_buf,
+						"\n", index);
+			}
+		}
+	}
+	return index;
+}
+
+/*******************************************************************************
+ * FUNCTION: result_save
+ *
+ * SUMMARY: Malloc memory for output buffer and populate with the cmcp test
+ *  header and results in the csv file format.
+ *
+ * NOTE: It supports simple_read_from_buffer() to read data multiple times to
+ *  the buffer.
+ *
+ * RETURN:
+ *	 Size of data printed to "buf"
+ *
+ * PARAMETERS:
+ *  *dev             - pointer to device structure
+ *  *buf             - the user space buffer to read to
+ *  *configuration   - pointer to configuration structure
+ *  *result          - pointer to result structure
+ *  *cmcp_info       - pointer to cmcp_data structure
+ *  *ppos            - the current position in the buffer
+ *   count           - the maximum number of bytes to read
+ *   test_item       - test control in bitwise
+ *   no_builtin_file - flag to determine if builtin-file exist
+ ******************************************************************************/
+int result_save(struct device *dev, char *buf,
+	struct configuration *configuration, struct result *result,
+	struct cmcp_data *cmcp_info, loff_t *ppos, size_t count, int test_item,
+	int no_builtin_file)
+{
+	u8 *out_buf = NULL;
+	int index = 0;
+	int byte_left;
+
+	out_buf = kzalloc(MAX_BUF_LEN, GFP_KERNEL);
+	if (configuration == NULL)
+		pt_debug(dev, DL_WARN, "config is NULL");
+	if (result == NULL)
+		pt_debug(dev, DL_WARN, "result is NULL");
+	if (cmcp_info == NULL)
+		pt_debug(dev, DL_WARN, "cmcp_info is NULL");
+
+	index = save_header(out_buf, index, configuration, result);
+	index = save_engineering_data(dev, out_buf, index,
+		cmcp_info, configuration, result,
+		test_item, no_builtin_file);
+	byte_left = simple_read_from_buffer(buf, count, ppos, out_buf, index);
+
+	kfree(out_buf);
+	return byte_left;
+}
+
+/*******************************************************************************
+ * FUNCTION: cmcp_results_debugfs_open
+ *
+ * SUMMARY: Open method for cmcp_results debugfs node.
+ *
+ * RETURN: 0 = success
+ *
+ * PARAMETERS:
+ *      *inode - file inode number
+ *      *filp  - file pointer to debugfs file
+ ******************************************************************************/
+static int cmcp_results_debugfs_open(struct inode *inode,
+		struct file *filp)
+{
+	filp->private_data = inode->i_private;
+	return 0;
+}
+
+/*******************************************************************************
+ * FUNCTION: cmcp_results_debugfs_close
+ *
+ * SUMMARY: Close method for cmcp_results debugfs node.
+ *
+ * RETURN: 0 = success
+ *
+ * PARAMETERS:
+ *      *inode - file inode number
+ *      *filp  - file pointer to debugfs file
+ ******************************************************************************/
+static int cmcp_results_debugfs_close(struct inode *inode,
+		struct file *filp)
+{
+	filp->private_data = NULL;
+	return 0;
+}
+
+/*******************************************************************************
+ * FUNCTION: cmcp_results_debugfs_read
+ *
+ * SUMMARY: Read method for cmcp_results debugfs node. This function prints
+ *	cmcp test results 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 cmcp_results_debugfs_read(struct file *filp,
+		char __user *buf, size_t count, loff_t *ppos)
+{
+	struct pt_device_access_data *dad = filp->private_data;
+	struct device *dev;
+	struct cmcp_data *cmcp_info = dad->cmcp_info;
+	struct result *result = dad->result;
+	struct configuration *configuration = dad->configs;
+	int ret = 0;
+	int test_item;
+	int no_builtin_file = 0;
+	int test_executed = 0;
+
+	dev = dad->dev;
+
+	mutex_lock(&dad->sysfs_lock);
+	test_executed = dad->test_executed;
+	test_item = pt_cmcp_get_test_item(dad->cmcp_test_items);
+	if (dad->builtin_cmcp_threshold_status < 0) {
+		pt_debug(dev, DL_WARN,
+				"%s: No cmcp threshold file.\n", __func__);
+		no_builtin_file = 1;
+	}
+	mutex_unlock(&dad->sysfs_lock);
+
+	if (test_executed)
+		/*save result to buf*/
+		ret = result_save(dev, buf, configuration, result, cmcp_info,
+			ppos, count, test_item, no_builtin_file);
+	else {
+		char warning_info[] =
+		"No test result available!\n";
+		pt_debug(dev, DL_ERROR,
+				"%s: No test result available!\n", __func__);
+
+		return simple_read_from_buffer(buf, count, ppos, warning_info,
+			strlen(warning_info));
+	}
+
+	return ret;
+}
+
+static const struct file_operations cmcp_results_debugfs_fops = {
+	.open = cmcp_results_debugfs_open,
+	.release = cmcp_results_debugfs_close,
+	.read = cmcp_results_debugfs_read,
+	.write = NULL,
+};
+
+/*******************************************************************************
+ * FUNCTION: cmcp_return_offset_of_new_case
+ *
+ * SUMMARY: Returns the buffer offset of new test case
+ *
+ * NOTE: There are two static variable inside this function.
+ *
+ * RETURN: offset index for new case
+ *
+ * PARAMETERS:
+ *   *bufPtr      - pointer to input buffer
+ *    first_time  - flag to initialize some static variable
+ *                  (0:init; 1:don't init)
+ *   *pFileEnd    - pointer to the end of file for safe check
+ ******************************************************************************/
+u32 cmcp_return_offset_of_new_case(const char *bufPtr, u32 first_time,
+		const char *pFileEnd)
+{
+	static u32 offset, first_search;
+
+	if (first_time == 0) {
+		first_search = 0;
+		offset = 0;
+	}
+
+	if (first_search != 0) {
+		/* Search one case */
+		for (;;) {
+			/* Search ASCII_LF */
+			while (bufPtr < pFileEnd) {
+				if (*bufPtr++ != ASCII_LF)
+					offset++;
+				else
+					break;
+			}
+			if (bufPtr >= pFileEnd)
+				break;
+			offset++;
+			/*
+			 * Single line: end loop
+			 * Multiple lines: continue loop
+			 */
+			if (*bufPtr != ASCII_COMMA)
+				break;
+		}
+	} else
+		first_search = 1;
+
+	return offset;
+}
+
+/*******************************************************************************
+ * FUNCTION: cmcp_get_case_info_from_threshold_file
+ *
+ * SUMMARY: Gets test case information from cmcp threshold file
+ *
+ * RETURN:
+ *   Number of test cases
+ *
+ * PARAMETERS:
+ *  *dev           - pointer to Device structure
+ *  *buf           - pointer to input file
+ *  *search_array  - pointer to test_case_search structure
+ *   file_size     - size of input file for safe check
+ ******************************************************************************/
+u32 cmcp_get_case_info_from_threshold_file(struct device *dev, const char *buf,
+		struct test_case_search *search_array, u32 file_size)
+{
+	u32 case_num = 0, buffer_offset = 0, name_count = 0, first_search = 0;
+	const char *pFileEnd = buf + file_size;
+
+	pt_debug(dev, DL_INFO, "%s: Search cmcp threshold file\n",
+		__func__);
+
+	/* Get all the test cases */
+	for (case_num = 0; case_num < MAX_CASE_NUM; case_num++) {
+		buffer_offset =
+			cmcp_return_offset_of_new_case(&buf[buffer_offset],
+			first_search, pFileEnd);
+		first_search = 1;
+
+		if (buf[buffer_offset] == 0)
+			break;
+
+		for (name_count = 0; name_count < NAME_SIZE_MAX; name_count++) {
+			/* File end */
+			if (buf[buffer_offset + name_count] == ASCII_COMMA)
+				break;
+
+			search_array[case_num].name[name_count] =
+					buf[buffer_offset + name_count];
+		}
+
+		/* Exit when buffer offset is larger than file size */
+		if (buffer_offset >= file_size)
+			break;
+
+		search_array[case_num].name_size = name_count;
+		search_array[case_num].offset = buffer_offset;
+		/*
+		 *  pt_debug(dev, DL_INFO, "Find case %d: Name is %s;
+		 *  Name size is %d; Case offset is %d\n",
+		 *	case_num,
+		 *	search_array[case_num].name,
+		 *	search_array[case_num].name_size,
+		 *	search_array[case_num].offset);
+		 */
+	}
+
+	return case_num;
+}
+
+/*******************************************************************************
+ * FUNCTION: cmcp_compose_data
+ *
+ * SUMMARY: Composes one value based on data of each bit
+ *
+ * RETURN:
+ *   Value that composed from buffer
+ *
+ * PARAMETERS:
+ *  *buf     - pointer to input file
+ *   count   - number of data elements in *buf in decimal
+ ******************************************************************************/
+int cmcp_compose_data(char *buf, u32 count)
+{
+	u32 base_array[] = {1, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9};
+	int value = 0;
+	u32 index = 0;
+
+	for (index = 0; index < count; index++)
+		value += buf[index] * base_array[count - 1 - index];
+
+	return value;
+}
+
+/*******************************************************************************
+ * FUNCTION: cmcp_return_one_value
+ *
+ * SUMMARY: Parses csv file at a given row and offset and combines multiple
+ *  "bits' as a single value. Handles data over multiple lines and various
+ *  end-of-line characters.
+ *
+ * NOTE: There is a static value to calculate line count inside this function.
+ *
+ * RETURN:
+ *   Value that parsed from buffer
+ *
+ * PARAMETERS:
+ *  *dev       - pointer to devices structure
+ *  *buf       - pointer to input buffer
+ *  *offset    - offset index of input buffer
+ *  *line_num  - store line count
+ *   pFileEnd -  pointer to the end of threshold file
+ ******************************************************************************/
+int cmcp_return_one_value(struct device *dev, const char *buf, u32 *offset,
+			  u32 *line_num, const char *pFileEnd)
+{
+	int value = -1;
+	char tmp_buffer[10];
+	u32 count = 0;
+	u32 tmp_offset = *offset;
+	static u32 line_count = 1;
+
+	/* Bypass extra commas */
+	while (((buf + tmp_offset + 1) < pFileEnd)
+			&& buf[tmp_offset] == ASCII_COMMA
+			&& buf[tmp_offset + 1] == ASCII_COMMA)
+		tmp_offset++;
+
+	if ((buf + tmp_offset + 1) >= pFileEnd)
+		goto exit;
+
+	/* Windows and Linux difference at the end of one line */
+	if (buf[tmp_offset] == ASCII_COMMA && buf[tmp_offset + 1] == ASCII_CR) {
+		if ((buf + tmp_offset + 2) < pFileEnd) {
+			if (buf[tmp_offset + 2] == ASCII_LF)
+				tmp_offset += 2;
+		} else
+			goto exit;
+	} else if (buf[tmp_offset] == ASCII_COMMA &&
+		 buf[tmp_offset + 1] == ASCII_LF)
+		tmp_offset += 1;
+	else if (buf[tmp_offset] == ASCII_COMMA
+			&& buf[tmp_offset + 1] == ASCII_CR)
+		tmp_offset += 1;
+
+	if ((buf + tmp_offset + 1) >= pFileEnd)
+		goto exit;
+
+	/* New line for multiple lines */
+	if ((buf[tmp_offset] == ASCII_LF || buf[tmp_offset] == ASCII_CR) &&
+	     buf[tmp_offset + 1] == ASCII_COMMA) {
+		tmp_offset++;
+		line_count++;
+		pt_debug(dev, DL_DEBUG, "%s: Line Count = %d\n",
+			__func__, line_count);
+	}
+
+	/* Beginning */
+	if (buf[tmp_offset] == ASCII_COMMA) {
+		tmp_offset++;
+		for (;;) {
+			if ((buf + tmp_offset) >= pFileEnd)
+				break;
+
+			if ((buf[tmp_offset] >= ASCII_ZERO)
+			&& (buf[tmp_offset] <= ASCII_NINE)) {
+				tmp_buffer[count++] =
+				buf[tmp_offset] - ASCII_ZERO;
+				tmp_offset++;
+			} else {
+				if (count != 0) {
+					value = cmcp_compose_data(tmp_buffer,
+					count);
+					/*pt_debug(dev, DL_DEBUG, */
+					/* ",%d", value);*/
+				} else {
+					/* 0 indicates no data available */
+					value = -1;
+				}
+				break;
+			}
+		}
+	} else {
+		/* Multiple line: line count */
+		*line_num = line_count;
+		/* Reset for next case */
+		line_count = 1;
+	}
+
+exit:
+	*offset = tmp_offset;
+
+	return value;
+}
+
+/*******************************************************************************
+ * FUNCTION: cmcp_get_one_string
+ *
+ * SUMMARY: Parses csv file at a given row and offset and find out one string
+ *  which must start with a comma and should end with a comma. The character
+ *  LF/CR will also be taken as the symbol to end the search.
+ *
+ * NOTE: There is a static value to calculate line count inside this function.
+ *
+ * RETURN:
+ *   0 = success
+ *  !0 = failure
+ *
+ * PARAMETERS:
+ *  *dev         - pointer to devices structure
+ *  *input_buf   - pointer to input buffer
+ *  *offset      - offset index of input buffer
+ *  *out_buf     - pointer to store string
+ *   out_buf_len - size of out_buf
+ *   pFileEnd    - pointer to the end of threshold file
+ ******************************************************************************/
+static int cmcp_get_one_string(struct device *dev, const char *input_buf,
+			       u32 *offset, char *out_buf, u32 out_buf_len,
+			       const char *pFileEnd)
+{
+	u8 token = ASCII_COMMA;
+	u32 i;
+	u32 temp_offset = *offset + 1; /* skip first comma */
+	u32 pre_index; /* to store the start index of string */
+	u32 next_index; /* to store the index of comma after string */
+	u32 total_len = pFileEnd - input_buf; /* valid range of index */
+
+	memset(out_buf, 0, out_buf_len);
+
+	pre_index = next_index = temp_offset;
+	for (i = temp_offset; i < total_len; i++) {
+		if (input_buf[i] == token || input_buf[i] == ASCII_LF ||
+		    input_buf[i] == ASCII_CR) {
+			next_index = i;
+			/*
+			 * next_index could be equal to pre_index if string is
+			 * null. The max size of string is restricted by macro
+			 * NAME_PROJECT_INFO_MAX.
+			 */
+			if (pre_index < next_index)
+				memcpy(out_buf, input_buf + pre_index,
+						MIN(next_index - pre_index,
+						NAME_PROJECT_INFO_MAX));
+			*offset = next_index;
+			return 0;
+		}
+
+		if (input_buf[i] == ASCII_LF || input_buf[i] == ASCII_CR)
+			break;
+	}
+
+	return -ENODATA;
+}
+
+/*******************************************************************************
+ * FUNCTION: cmcp_get_configuration_info
+ *
+ * SUMMARY: Gets cmcp configuration information.
+ *
+ * PARAMETERS:
+ *  *dev          - pointer to devices structure
+ *  *buf          - pointer to input buffer
+ *  *search_array - pointer to test_case_search structure
+ *   case_count   - number of test cases
+ *  *field_array  - pointer to test_case_field structure
+ *  *config       - pointer to configuration structure
+ *   file_size    - file size of threshold file
+ ******************************************************************************/
+void cmcp_get_configuration_info(struct device *dev,
+		const char *buf, struct test_case_search *search_array,
+		u32 case_count, struct test_case_field *field_array,
+		struct configuration *config, u32 file_size)
+{
+	u32 count = 0, sub_count = 0;
+	u32 exist_or_not = 0;
+	u32 value_offset = 0;
+	int retval = 0;
+	u32 data_num = 0;
+	u32 line_num = 1;
+	const char *pFileEnd = buf + file_size;
+
+	pt_debug(dev, DL_INFO,
+		"%s: Fill configuration struct per cmcp threshold file\n",
+		__func__);
+
+	/* Search cases */
+	for (count = 0; count < MAX_CASE_NUM; count++) {
+		exist_or_not = 0;
+		for (sub_count = 0; sub_count < case_count; sub_count++) {
+			if (!strncmp(field_array[count].name,
+					search_array[sub_count].name,
+					field_array[count].name_size)) {
+				exist_or_not = 1;
+				break;
+			}
+		}
+
+		field_array[count].exist_or_not = exist_or_not;
+
+		pt_debug(dev, DL_DEBUG,
+			"%s: Field Array[%d] exists: %d, type: %d\n",
+			__func__, count, exist_or_not, field_array[count].type);
+
+		/* Clear data number */
+		data_num = 0;
+
+		if (exist_or_not == 1) {
+			switch (field_array[count].type) {
+			case TEST_CASE_TYPE_NO:
+				field_array[count].data_num = 0;
+				field_array[count].line_num = 1;
+				break;
+			case TEST_CASE_TYPE_ONE:
+				value_offset = search_array[sub_count].offset
+					+ search_array[sub_count].name_size;
+				*field_array[count].bufptr =
+						cmcp_return_one_value(dev, buf,
+						&value_offset, 0, pFileEnd);
+				field_array[count].data_num = 1;
+				field_array[count].line_num = 1;
+				break;
+			case TEST_CASE_TYPE_STRING:
+				value_offset = search_array[sub_count].offset
+					+ search_array[sub_count].name_size;
+				cmcp_get_one_string(
+				    dev, buf, &value_offset,
+				    (char *)field_array[count].bufptr,
+				    NAME_PROJECT_INFO_MAX, pFileEnd);
+				field_array[count].data_num = 1;
+				field_array[count].line_num = 1;
+				break;
+			case TEST_CASE_TYPE_MUL:
+			case TEST_CASE_TYPE_MUL_LINES:
+				line_num = 1;
+				value_offset = search_array[sub_count].offset
+					+ search_array[sub_count].name_size;
+				for (;;) {
+					retval = cmcp_return_one_value(
+					    dev, buf, &value_offset, &line_num,
+					    pFileEnd);
+					if (retval >= 0) {
+						*field_array[count].bufptr++ =
+						retval;
+						data_num++;
+					} else
+						break;
+				}
+
+				field_array[count].data_num = data_num;
+				field_array[count].line_num = line_num;
+				break;
+			default:
+				break;
+			}
+			pt_debug(dev, DL_DEBUG,
+				"%s: %s: Data count is %d, line number is %d\n",
+				__func__,
+				field_array[count].name,
+				field_array[count].data_num,
+				field_array[count].line_num);
+		} else
+			pt_debug(dev, DL_ERROR, "%s: !!! %s doesn't exist\n",
+				__func__, field_array[count].name);
+	}
+}
+
+/*******************************************************************************
+ * FUNCTION: cmcp_get_basic_info
+ *
+ * SUMMARY: Gets basic information for cmcp test, such as available test item,
+ *  number of tx, rx, button.
+ *
+ * PARAMETERS:
+ *  *dev          - pointer to devices structure
+ *  *field_array  - pointer to test_case_field structure
+ *  *config       - pointer to configuration structure
+ ******************************************************************************/
+void cmcp_get_basic_info(struct device *dev,
+	struct test_case_field *field_array, struct configuration *config)
+{
+	u32 tx_num = 0;
+	u32 index = 0;
+
+	config->is_valid_or_not = 1; /* Set to valid by default */
+	config->cm_enabled = 0;
+	config->cp_enabled = 0;
+
+	if (field_array[CM_TEST_INPUTS].exist_or_not)
+		config->cm_enabled = 1;
+	if (field_array[CP_TEST_INPUTS].exist_or_not)
+		config->cp_enabled = 1;
+
+	/* Get basic information only when CM and CP are enabled */
+	if (config->cm_enabled && config->cp_enabled) {
+		pt_debug(dev, DL_INFO,
+			"%s: Find CM and CP thresholds\n", __func__);
+
+		config->rx_num =
+			field_array[PER_ELEMENT_MIN_MAX_TABLE_SENSOR].line_num;
+		tx_num =
+		(field_array[PER_ELEMENT_MIN_MAX_TABLE_SENSOR].data_num >> 1)
+			/field_array[PER_ELEMENT_MIN_MAX_TABLE_SENSOR].line_num;
+		config->tx_num = tx_num;
+
+		config->btn_num =
+		field_array[PER_ELEMENT_MIN_MAX_TABLE_BUTTON].data_num >> 1;
+
+		config->cm_min_max_table_btn_size =
+			field_array[PER_ELEMENT_MIN_MAX_TABLE_BUTTON].data_num;
+		config->cm_min_max_table_sensor_size =
+			field_array[PER_ELEMENT_MIN_MAX_TABLE_SENSOR].data_num;
+		config->cp_min_max_table_rx_size =
+			field_array[PER_ELEMENT_MIN_MAX_RX].data_num;
+		config->cp_min_max_table_tx_size =
+			field_array[PER_ELEMENT_MIN_MAX_TX].data_num;
+		config->cm_max_table_gradient_cols_percent_size =
+			field_array[CM_GRADIENT_CHECK_COL].data_num;
+		config->cm_max_table_gradient_rows_percent_size =
+			field_array[CM_GRADIENT_CHECK_ROW].data_num;
+		config->cp_min_max_table_btn_size =
+			field_array[CP_PER_ELEMENT_MIN_MAX_BUTTON].data_num;
+
+		/* *** Detailed Debug Information *** */
+		pt_debug(dev, DL_DEBUG, "%d\n",
+					config->cm_excluding_col_edge);
+		pt_debug(dev, DL_DEBUG, "%d\n",
+					config->cm_excluding_row_edge);
+		for (index = 0;
+		index < config->cm_max_table_gradient_cols_percent_size;
+		index++)
+			pt_debug(dev, DL_DEBUG, "%d\n",
+			config->cm_max_table_gradient_cols_percent[index]);
+		for (index = 0;
+		index < config->cm_max_table_gradient_rows_percent_size;
+		index++)
+			pt_debug(dev, DL_DEBUG, "%d\n",
+			config->cm_max_table_gradient_rows_percent[index]);
+			pt_debug(dev, DL_DEBUG, "%d\n",
+				config->cm_range_limit_row);
+			pt_debug(dev, DL_DEBUG, "%d\n",
+				config->cm_range_limit_col);
+			pt_debug(dev, DL_DEBUG, "%d\n",
+				config->cm_min_limit_cal);
+			pt_debug(dev, DL_DEBUG, "%d\n",
+				config->cm_max_limit_cal);
+			pt_debug(dev, DL_DEBUG, "%d\n",
+				config->cm_max_delta_sensor_percent);
+			pt_debug(dev, DL_DEBUG, "%d\n",
+				config->cm_max_delta_button_percent);
+		for (index = 0;
+		index < config->cm_min_max_table_btn_size; index++)
+			pt_debug(dev, DL_DEBUG, "%d\n",
+				config->cm_min_max_table_btn[index]);
+		for (index = 0;
+			index < config->cm_min_max_table_sensor_size; index++)
+			pt_debug(dev, DL_DEBUG, "%d\n",
+				config->cm_min_max_table_sensor[index]);
+			pt_debug(dev, DL_DEBUG, "%d\n",
+				config->cp_max_delta_sensor_rx_percent);
+			pt_debug(dev, DL_DEBUG, "%d\n",
+				config->cp_max_delta_sensor_tx_percent);
+			pt_debug(dev, DL_DEBUG, "%d\n",
+				config->cp_max_delta_button_percent);
+			pt_debug(dev, DL_DEBUG, "%d\n",
+				config->min_button);
+			pt_debug(dev, DL_DEBUG, "%d\n",
+				config->max_button);
+
+		for (index = 0;
+		index < config->cp_min_max_table_btn_size; index++)
+			pt_debug(dev, DL_DEBUG, "%d\n",
+				config->cp_min_max_table_btn[index]);
+		for (index = 0;
+		index < config->cp_min_max_table_rx_size; index++)
+			pt_debug(dev, DL_DEBUG, "%d\n",
+				config->cp_min_max_table_rx[index]);
+		for (index = 0;
+		index < config->cp_min_max_table_tx_size; index++)
+			pt_debug(dev, DL_DEBUG, "%d\n",
+				config->cp_min_max_table_tx[index]);
+		/* *** End of Detailed Debug Information *** */
+
+		/* Invalid mutual data length */
+		if ((field_array[PER_ELEMENT_MIN_MAX_TABLE_SENSOR].data_num >>
+		1) % field_array[PER_ELEMENT_MIN_MAX_TABLE_SENSOR].line_num) {
+			config->is_valid_or_not = 0;
+			pt_debug(dev, DL_ERROR, "Invalid mutual data length\n");
+		}
+	} else {
+		if (!config->cm_enabled)
+			pt_debug(dev, DL_ERROR,
+				"%s: Miss CM thresholds or CM data format is wrong!\n",
+				__func__);
+
+		if (!config->cp_enabled)
+			pt_debug(dev, DL_ERROR,
+				"%s: Miss CP thresholds or CP data format is wrong!\n",
+				__func__);
+
+		config->rx_num = 0;
+		config->tx_num = 0;
+		config->btn_num = 0;
+		config->is_valid_or_not = 0;
+	}
+
+	pt_debug(dev, DL_DEBUG,
+		"%s:\n"
+		"Input file is %s!\n"
+		"CM test: %s\n"
+		"CP test: %s\n"
+		"rx_num is %d\n"
+		"tx_num is %d\n"
+		"btn_num is %d\n",
+		__func__,
+		config->is_valid_or_not == 1 ? "VALID" : "!!! INVALID !!!",
+		config->cm_enabled == 1 ? "Found" : "Not found",
+		config->cp_enabled == 1 ? "Found" : "Not found",
+		config->rx_num,
+		config->tx_num,
+		config->btn_num);
+}
+
+/*******************************************************************************
+ * FUNCTION: cmcp_test_case_field_init
+ *
+ * SUMMARY: Initialize the structure test_field_array.
+ *
+ * PARAMETERS:
+ *  *test_field_array  - pointer to test_case_field structure
+ *  *configuration     - pointer to configuration structure
+ ******************************************************************************/
+void cmcp_test_case_field_init(struct test_case_field *test_field_array,
+	struct configuration *configs)
+{
+	struct test_case_field test_case_field_array[MAX_CASE_NUM] = {
+		{"PROJ_VERSION", 12, TEST_CASE_TYPE_STRING,
+				(void *)&configs->proj_info, 0, 0, 0},
+		{"CM TEST INPUTS", 14, TEST_CASE_TYPE_NO,
+				NULL, 0, 0, 0},
+		{"CM_EXCLUDING_COL_EDGE", 21, TEST_CASE_TYPE_ONE,
+				&configs->cm_excluding_col_edge, 0, 0, 0},
+		{"CM_EXCLUDING_ROW_EDGE", 21, TEST_CASE_TYPE_ONE,
+				&configs->cm_excluding_row_edge, 0, 0, 0},
+		{"CM_GRADIENT_CHECK_COL", 21, TEST_CASE_TYPE_MUL,
+				&configs->cm_max_table_gradient_cols_percent[0],
+				0, 0, 0},
+		{"CM_GRADIENT_CHECK_ROW", 21, TEST_CASE_TYPE_MUL,
+				&configs->cm_max_table_gradient_rows_percent[0],
+				0, 0, 0},
+		{"CM_RANGE_LIMIT_ROW", 18, TEST_CASE_TYPE_ONE,
+				&configs->cm_range_limit_row, 0, 0, 0},
+		{"CM_RANGE_LIMIT_COL", 18, TEST_CASE_TYPE_ONE,
+				&configs->cm_range_limit_col, 0, 0, 0},
+		{"CM_MIN_LIMIT_CAL", 16, TEST_CASE_TYPE_ONE,
+				&configs->cm_min_limit_cal, 0, 0, 0},
+		{"CM_MAX_LIMIT_CAL", 16, TEST_CASE_TYPE_ONE,
+				&configs->cm_max_limit_cal, 0, 0, 0},
+		{"CM_MAX_DELTA_SENSOR_PERCENT", 27, TEST_CASE_TYPE_ONE,
+				&configs->cm_max_delta_sensor_percent, 0, 0, 0},
+		{"CM_MAX_DELTA_BUTTON_PERCENT", 27, TEST_CASE_TYPE_ONE,
+				&configs->cm_max_delta_button_percent, 0, 0, 0},
+		{"PER_ELEMENT_MIN_MAX_TABLE_BUTTON", 32, TEST_CASE_TYPE_MUL,
+				&configs->cm_min_max_table_btn[0], 0, 0, 0},
+		{"PER_ELEMENT_MIN_MAX_TABLE_SENSOR", 32,
+				TEST_CASE_TYPE_MUL_LINES,
+				&configs->cm_min_max_table_sensor[0], 0, 0, 0},
+		{"CP TEST INPUTS", 14, TEST_CASE_TYPE_NO,
+				NULL, 0, 0, 0},
+		{"CP_PER_ELEMENT_MIN_MAX_BUTTON", 29, TEST_CASE_TYPE_MUL,
+				&configs->cp_min_max_table_btn[0], 0, 0, 0},
+		{"CP_MAX_DELTA_SENSOR_RX_PERCENT", 30, TEST_CASE_TYPE_ONE,
+				&configs->cp_max_delta_sensor_rx_percent,
+				0, 0, 0},
+		{"CP_MAX_DELTA_SENSOR_TX_PERCENT", 30, TEST_CASE_TYPE_ONE,
+				&configs->cp_max_delta_sensor_tx_percent,
+				0, 0, 0},
+		{"CP_MAX_DELTA_BUTTON_PERCENT", 27, TEST_CASE_TYPE_ONE,
+				&configs->cp_max_delta_button_percent, 0, 0, 0},
+		{"MIN_BUTTON", 10, TEST_CASE_TYPE_ONE,
+				&configs->min_button, 0, 0, 0},
+		{"MAX_BUTTON", 10, TEST_CASE_TYPE_ONE,
+				&configs->max_button, 0, 0, 0},
+		{"PER_ELEMENT_MIN_MAX_RX", 22, TEST_CASE_TYPE_MUL,
+				&configs->cp_min_max_table_rx[0], 0, 0, 0},
+		{"PER_ELEMENT_MIN_MAX_TX", 22, TEST_CASE_TYPE_MUL,
+				&configs->cp_min_max_table_tx[0], 0, 0, 0},
+	};
+
+	memcpy(test_field_array, test_case_field_array,
+		sizeof(struct test_case_field) * MAX_CASE_NUM);
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_parse_cmcp_threshold_file_common
+ *
+ * SUMMARY: Parses cmcp threshold file and stores to the data structure.
+ *
+ * PARAMETERS:
+ *  *dev       - pointer to devices structure
+ *  *buf       - pointer to input buffer
+ *   file_size - file size
+ ******************************************************************************/
+static ssize_t pt_parse_cmcp_threshold_file_common(
+	struct device *dev, const char *buf, u32 file_size)
+{
+	struct pt_device_access_data *dad
+		= pt_get_device_access_data(dev);
+	ssize_t rc = 0;
+	u32 case_count = 0;
+
+	pt_debug(dev, DL_INFO,
+		"%s: Start parsing cmcp threshold file. File size is %d\n",
+		__func__, file_size);
+
+	cmcp_test_case_field_init(dad->test_field_array, dad->configs);
+
+	/* Get all the cases from .csv file */
+	case_count = cmcp_get_case_info_from_threshold_file(dev,
+		buf, dad->test_search_array, file_size);
+
+	pt_debug(dev, DL_INFO,
+		"%s: Number of cases found in CSV file: %d\n",
+		__func__, case_count);
+
+	/* Search cases */
+	cmcp_get_configuration_info(dev,
+		buf,
+		dad->test_search_array, case_count, dad->test_field_array,
+		dad->configs, file_size);
+
+	/* Get basic information */
+	cmcp_get_basic_info(dev, dad->test_field_array, dad->configs);
+
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_cmcp_threshold_loading_store
+ *
+ * SUMMARY: The store method for the cmcp_threshold_loading sysfs node. The
+ *  passed in value controls if threshold loading is performed.
+ *
+ * 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_cmcp_threshold_loading_store(struct device *dev,
+	struct device_attribute *attr, const char *buf, size_t size)
+{
+	struct pt_device_access_data *dad = pt_get_device_access_data(dev);
+	ssize_t length;
+	u32 input_data[3];
+	int rc = 0;
+
+	length = cmd->parse_sysfs_input(dev, buf, size, input_data,
+			ARRAY_SIZE(input_data));
+
+	if (length != 1) {
+		pt_debug(dev, DL_WARN, "%s: Invalid number of arguments\n",
+			__func__);
+		rc = -EINVAL;
+		goto exit;
+	}
+
+	mutex_lock(&dad->cmcp_threshold_lock);
+
+	if (input_data[0] == 1)
+		dad->cmcp_threshold_loading = true;
+	else if (input_data[0] == -1)
+		dad->cmcp_threshold_loading = false;
+	else if (input_data[0] == 0 && dad->cmcp_threshold_loading) {
+		dad->cmcp_threshold_loading = false;
+
+		if (dad->cmcp_threshold_size == 0) {
+			pt_debug(dev, DL_ERROR, "%s: No cmcp threshold data\n",
+					__func__);
+			goto exit_free;
+		}
+
+		/* Clear test executed flag */
+		dad->test_executed = 0;
+
+		pt_parse_cmcp_threshold_file_common(dev,
+			&dad->cmcp_threshold_data[0], dad->cmcp_threshold_size);
+
+		/* Mark valid */
+		dad->builtin_cmcp_threshold_status = 0;
+		/* Restore test item to default value when new file input */
+		dad->cmcp_test_items = 0;
+	} else {
+		pt_debug(dev, DL_WARN, "%s: Invalid value\n", __func__);
+		rc = -EINVAL;
+		mutex_unlock(&dad->cmcp_threshold_lock);
+		goto exit;
+	}
+
+exit_free:
+	kfree(dad->cmcp_threshold_data);
+	dad->cmcp_threshold_data = NULL;
+	dad->cmcp_threshold_size = 0;
+	mutex_unlock(&dad->cmcp_threshold_lock);
+
+exit:
+	if (rc)
+		return rc;
+	return size;
+}
+
+static DEVICE_ATTR(cmcp_threshold_loading, 0200,
+	NULL, pt_cmcp_threshold_loading_store);
+
+/*******************************************************************************
+ * FUNCTION: pt_cmcp_threshold_data_write
+ *
+ * SUMMARY: The write method for the cmcp_threshold_data_sysfs node. The passed
+ *  in data (threshold file) is written to the threshold buffer.
+ *
+ * RETURN: Size of passed in buffer is success
+ *
+ * PARAMETERS:
+ *  *filp     - pointer to file structure
+ *  *kobj     - pointer to kobject structure
+ *  *bin_attr - pointer to bin_attribute structure
+ *   buf      - pointer to cmd input buffer
+ *   offset   - offset index to store input buffer
+ *   count    - size of data in buffer
+ ******************************************************************************/
+static ssize_t pt_cmcp_threshold_data_write(struct file *filp,
+		struct kobject *kobj, struct bin_attribute *bin_attr,
+		char *buf, loff_t offset, size_t count)
+{
+	struct device *dev = container_of(kobj, struct device, kobj);
+	struct pt_device_access_data *dad
+		= pt_get_device_access_data(dev);
+	u8 *p;
+
+	pt_debug(dev, DL_INFO, "%s: offset:%lld count:%zu\n",
+		__func__, offset, count);
+
+	mutex_lock(&dad->cmcp_threshold_lock);
+
+	if (!dad->cmcp_threshold_loading) {
+		mutex_unlock(&dad->cmcp_threshold_lock);
+		return -ENODEV;
+	}
+
+	p = krealloc(dad->cmcp_threshold_data, offset + count, GFP_KERNEL);
+	if (!p) {
+		kfree(dad->cmcp_threshold_data);
+		dad->cmcp_threshold_data = NULL;
+		mutex_unlock(&dad->cmcp_threshold_lock);
+		return -ENOMEM;
+	}
+	dad->cmcp_threshold_data = p;
+
+	memcpy(&dad->cmcp_threshold_data[offset], buf, count);
+	dad->cmcp_threshold_size += count;
+
+	mutex_unlock(&dad->cmcp_threshold_lock);
+
+	return count;
+}
+
+static struct bin_attribute bin_attr_cmcp_threshold_data = {
+	.attr = {
+		.name = "cmcp_threshold_data",
+		.mode = 0200,
+	},
+	.size = 0,
+	.write = pt_cmcp_threshold_data_write,
+};
+
+
+/*******************************************************************************
+ * FUNCTION: pt_suspend_scan_cmd_
+ *
+ * SUMMARY: Non-protected wrapper function for suspend scan command
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *	*dev - pointer to devices structure
+ ******************************************************************************/
+static int pt_suspend_scan_cmd_(struct device *dev)
+{
+	int rc;
+
+	rc = cmd->nonhid_cmd->suspend_scanning(dev, 0);
+	if (rc)
+		pt_debug(dev, DL_ERROR, "%s: Suspend scan failed rc = %d\n",
+			__func__, rc);
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_resume_scan_cmd_
+ *
+ * SUMMARY: Non-protected wrapper function for resume scan command
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *	*dev - pointer to devices structure
+ ******************************************************************************/
+static int pt_resume_scan_cmd_(struct device *dev)
+{
+	int rc;
+
+	rc = cmd->nonhid_cmd->resume_scanning(dev, 0);
+	if (rc)
+		pt_debug(dev, DL_ERROR, "%s: Resume scan failed rc = %d\n",
+			__func__, rc);
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_exec_scan_cmd_
+ *
+ * SUMMARY: Non-protected wrapper function for execute scan command
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *	*dev       - pointer to devices structure
+ *	 scan_type - type of panel scan to perform (PIP2 only)
+ ******************************************************************************/
+static int pt_exec_scan_cmd_(struct device *dev, u8 scan_type)
+{
+	int rc;
+
+	rc = cmd->nonhid_cmd->exec_panel_scan(dev, PT_CORE_CMD_UNPROTECTED,
+		scan_type);
+	if (rc)
+		pt_debug(dev, DL_ERROR,
+			"%s: Heatmap start scan failed rc = %d\n",
+			__func__, rc);
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_ret_scan_data_cmd_
+ *
+ * SUMMARY: Non-protected wrapper function for retrieve panel data command
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *	*dev             - pointer to device structure
+ *	 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
+ *	*return_buf      - pointer to the read buffer
+ ******************************************************************************/
+static int pt_ret_scan_data_cmd_(struct device *dev, u16 read_offset,
+		u16 read_count, u8 data_id, u8 *response, u8 *config,
+		u16 *actual_read_len, u8 *return_buf)
+{
+	int rc;
+
+	rc = cmd->nonhid_cmd->retrieve_panel_scan(dev, 0, read_offset,
+			read_count, data_id, response, config, actual_read_len,
+			return_buf);
+	if (rc)
+		pt_debug(dev, DL_ERROR,
+				"%s: Retrieve scan data failed rc = %d\n",
+				__func__, rc);
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_get_data_structure_cmd_
+ *
+ * SUMMARY: Non-protected wrapper function for get data structure command
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *	*dev             - pointer to device structure
+ *	 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_get_data_structure_cmd_(struct device *dev, u16 read_offset,
+		u16 read_length, u8 data_id, u8 *status, u8 *data_format,
+		u16 *actual_read_len, u8 *data)
+{
+	int rc;
+
+	rc = cmd->nonhid_cmd->get_data_structure(dev, 0, read_offset,
+			read_length, data_id, status, data_format,
+			actual_read_len, data);
+	if (rc)
+		pt_debug(dev, DL_ERROR,
+				"%s: Get data structure failed rc = %d\n",
+				__func__, rc);
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_run_selftest_cmd_
+ *
+ * SUMMARY: Non-protected wrapper function for run self test command
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *	*dev                  - pointer to device structure
+ *	 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_run_selftest_cmd_(struct device *dev, u8 test_id,
+		u8 write_idacs_to_flash, u8 *status, u8 *summary_result,
+		u8 *results_available)
+{
+	int rc;
+
+	rc = cmd->nonhid_cmd->run_selftest(dev, 0, test_id,
+			write_idacs_to_flash, status, summary_result,
+			results_available);
+	if (rc)
+		pt_debug(dev, DL_ERROR, "%s: Run self test failed rc = %d\n",
+				__func__, rc);
+	return rc;
+}
+
+
+/*******************************************************************************
+ * FUNCTION: pt_get_selftest_result_cmd_
+ *
+ * SUMMARY: Non-protected wrapper function for get self test result command
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *	*dev             - pointer to device structure
+ *	 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_get_selftest_result_cmd_(struct device *dev,
+		u16 read_offset, u16 read_length, u8 test_id, u8 *status,
+		u16 *actual_read_len, u8 *data)
+{
+	int rc;
+
+	rc = cmd->nonhid_cmd->get_selftest_result(dev, 0, read_offset,
+			read_length, test_id, status, actual_read_len, data);
+	if (rc)
+		pt_debug(dev, DL_ERROR,
+			"%s: Get self test result failed rc = %d\n",
+			__func__, rc);
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: _pt_calibrate_ext_cmd
+ *
+ * SUMMARY: Wrapper function to function calibrate_ext() in pt_core_commands
+ *  structure
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *  *dev          - pointer to device structure
+ *  *cal_data     - pointer to extended calibration data structure
+ *  *status       - pointer to where the command response status is stored
+ ******************************************************************************/
+static int _pt_calibrate_ext_cmd(struct device *dev,
+		struct pt_cal_ext_data *cal_data, u8 *status)
+{
+	int rc;
+
+	rc = cmd->nonhid_cmd->calibrate_ext(dev,
+			PT_CORE_CMD_UNPROTECTED, cal_data, status);
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: _pt_calibrate_idacs_cmd
+ *
+ * SUMMARY: Wrapper function to function calibrate_idacs() in pt_core_commands
+ *  structure
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *  *dev          - pointer to device structure
+ *   sensing_mode - sense mode to calibrate (0-5)
+ *  *status       - pointer to where the command response status is stored
+ ******************************************************************************/
+static int _pt_calibrate_idacs_cmd(struct device *dev,
+		u8 sensing_mode, u8 *status)
+{
+	int rc;
+
+	rc = cmd->nonhid_cmd->calibrate_idacs(dev, 0, sensing_mode, status);
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: _pt_initialize_baselines_cmd
+ *
+ * SUMMARY: Wrapper function to call initialize_baselines() in pt_core_commands
+ *  structure
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *  *dev          - pointer to device structure
+ *   sensing_mode - enumerated ID against which to initialize the baseline
+ *  *status       - pointer to where the command response statas is stored
+ ******************************************************************************/
+static int _pt_initialize_baselines_cmd(struct device *dev,
+		u8 sensing_mode, u8 *status)
+{
+	int rc;
+
+	rc = cmd->nonhid_cmd->initialize_baselines(dev, 0, sensing_mode,
+			status);
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_perform_calibration
+ *
+ * SUMMARY: For Gen5/6, Send the PIP1 Calibrate IDACs command (0x28). For TT/TC,
+ *  send PIP1 Extended Calibrate command (0x30).
+ *
+ * NOTE: Panel scan must be suspended prior to calling this function.
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *	*dev - pointer to device structure
+ ******************************************************************************/
+static int pt_perform_calibration(struct device *dev)
+{
+	struct pt_cal_ext_data cal_data = {0};
+	u8 dut_gen = cmd->request_dut_generation(dev);
+	u8 mode;
+	u8 status;
+	int rc;
+
+	if (dut_gen == DUT_PIP1_ONLY) {
+		for (mode = 0; mode < 3; mode++) {
+			rc = _pt_calibrate_idacs_cmd(dev, mode, &status);
+			if (rc < 0) {
+				pt_debug(dev, DL_ERROR,
+					"%s: calibrate idac error, mode= %d, rc = %d\n",
+					__func__, mode, rc);
+				break;
+			}
+		}
+	} else {
+		memset(&cal_data, 0, sizeof(struct pt_cal_ext_data));
+		rc = _pt_calibrate_ext_cmd(dev, &cal_data, &status);
+		if (rc < 0)
+			pt_debug(dev, DL_ERROR,
+				"%s: extended calibrate error, rc = %d\n",
+				__func__, rc);
+	}
+
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: prepare_print_buffer
+ *
+ * SUMMARY: Format input buffer to out buffer with Hex base,and format "status"
+ *  to decimal base.
+ *
+ * RETURN:
+ *	 size of formated data in output buffer
+ *
+ * PARAMETERS:
+ *   status       - Indicate test result:0(STATUS_SUCCESS),-1(STATUS_FAIL)
+ *  *in_buf       - input buffer to be formated
+ *   length       - length of input buffer
+ *  *out_buf      - output buffer to store formated data
+ *   out_buf_size - length of output buffer
+ *   out_format   - format of output data (5 supported formats):
+ *    PT_PR_FORMAT_DEFAULT      : format all data as a column
+ *    PT_PR_FORMAT_U8_SPACE     : sort status bytes and self test results,
+ *      and format the results as a row, each element include 1 byte
+ *    PT_PR_FORMAT_U16_SPACE    : sort status bytes and self test results,
+ *      and format the results as a row, each element include 2 byte
+ *    PT_PR_FORMAT_U8_NO_SPACE  : sort status bytes and self test results,
+ *      and format the results as a row, no space between the elements
+ *    PT_PR_FORMAT_U32_SPACE    : sort status bytes and self test results,
+ *      and format the results as a row, each element include 4 byte
+ ******************************************************************************/
+static int prepare_print_buffer(int status, u8 *in_buf, int length,
+		u8 *out_buf, size_t out_buf_size, u8 out_format)
+{
+	int index = 0;
+	int data_length;
+	int i;
+
+	index += scnprintf(out_buf, out_buf_size, "Status: %d\n", status);
+
+	if (out_format == PT_PR_FORMAT_DEFAULT) {
+		for (i = 0; i < length; i++)
+			index += scnprintf(&out_buf[index],
+				out_buf_size - index,
+				"%02X\n", in_buf[i]);
+	} else {
+		index += scnprintf(&out_buf[index],
+			out_buf_size - index,
+			"Response Status[1-%d]: ", MIN(length, 3));
+		for (i = 0; i < MIN(length, 3); i++)
+			index += scnprintf(&out_buf[index],
+				out_buf_size - index,
+				"%02X ", in_buf[i]);
+		index += scnprintf(&out_buf[index], out_buf_size - index, "\n");
+		if (length <= 6) {
+			goto exit;
+		} else {
+			data_length = get_unaligned_le16(&in_buf[4]);
+			index += scnprintf(&out_buf[index],
+				out_buf_size - index, "RAW_DATA: ");
+		}
+
+		if (out_format == PT_PR_FORMAT_U8_SPACE) {
+			for (i = 6; i < length; i++)
+				index += scnprintf(&out_buf[index],
+					out_buf_size - index,
+					"%02X ", in_buf[i]);
+			index += scnprintf(&out_buf[index],
+				out_buf_size - index,
+				":(%d bytes)\n", data_length);
+		} else if (out_format == PT_PR_FORMAT_U16_SPACE) {
+			for (i = 6; (i + 1) < length; i += 2)
+				index += scnprintf(&out_buf[index],
+					out_buf_size - index, "%04X ",
+					get_unaligned_le16(&in_buf[i]));
+			index += scnprintf(&out_buf[index],
+				out_buf_size - index,
+				":(%d words)\n", (length-6)/2);
+		} else if (out_format == PT_PR_FORMAT_U8_NO_SPACE) {
+			for (i = 6; i < length; i++)
+				index += scnprintf(&out_buf[index],
+					out_buf_size - index,
+					"%02X", in_buf[i]);
+			index += scnprintf(&out_buf[index],
+				out_buf_size - index,
+				":(%d bytes)\n", data_length);
+		} else if (out_format == PT_PR_FORMAT_U32_SPACE) {
+			for (i = 6; (i + 1) < length; i += 4)
+				index += scnprintf(&out_buf[index],
+					out_buf_size - index, "%08X ",
+					get_unaligned_le32(&in_buf[i]));
+			index += scnprintf(&out_buf[index],
+				out_buf_size - index,
+				":(%d 32bit values)\n", (length-6)/4);
+		}
+	}
+
+exit:
+	return index;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_run_and_get_selftest_result
+ *
+ * SUMMARY: Run the selftest and store the test result in the
+ *  pt_device_access_data struct.
+ *
+ * RETURN:
+ *	 >0 : Size of debugfs data to print
+ *	 <0 : failure
+ *	  0 : success
+ *
+ * NOTE: "Status: x" - x will contain the first error code if any
+ *
+ * PARAMETERS:
+ *  *dev                - pointer to device structure
+ *   protect            - flag to call protected or non-protected
+ *  *buf                - pointer to print buf of return data
+ *   buf_len            - length of print buf of return data
+ *   test_id            - selftest id
+ *   read_length        - max length to stor return data
+ *   get_result_on_pass - indicate whether to get result when finish test
+ *   print_results      - print results to log
+ * (true:get result;false:don't get result )
+ *   print_format       - format of print results
+ ******************************************************************************/
+static ssize_t pt_run_and_get_selftest_result(struct device *dev,
+		int protect, char *buf, size_t buf_len, u8 test_id,
+		u16 read_length, u8 get_result_on_pass, bool print_results,
+		u8 print_format)
+{
+	struct pt_device_access_data *dad = pt_get_device_access_data(dev);
+	int status = STATUS_SUCCESS;
+	u8 cmd_status = STATUS_SUCCESS;
+	u8 summary_result = 0;
+	u8 sys_mode = FW_SYS_MODE_UNDEFINED;
+	u16 act_length = 0;
+	int length = 0;
+	int size = 0;
+	int rc;
+
+	mutex_lock(&dad->sysfs_lock);
+
+	pm_runtime_get_sync(dev);
+
+	if (protect == PT_CORE_CMD_PROTECTED) {
+		rc = cmd->request_exclusive(dev, PT_REQUEST_EXCLUSIVE_TIMEOUT);
+		if (rc) {
+			pt_debug(dev, DL_ERROR,
+				"%s: Error on request exclusive rc = %d\n",
+				__func__, rc);
+			status = -EPERM;
+			goto put_pm_runtime;
+		}
+	}
+
+	/* Get the current scan state so we restore to the same at the end */
+	rc = cmd->request_get_fw_mode(dev, PT_CORE_CMD_UNPROTECTED, &sys_mode,
+		NULL);
+	if (rc) {
+		status = rc;
+		goto release_exclusive;
+	}
+
+	if (sys_mode != FW_SYS_MODE_TEST) {
+		rc = pt_suspend_scan_cmd_(dev);
+		if (rc) {
+			pt_debug(dev, DL_ERROR,
+					"%s: Error on suspend scan rc = %d\n",
+					__func__, rc);
+			status = -EPERM;
+			goto release_exclusive;
+		}
+	}
+
+	/* Sleep for 20ms to allow the last scan to be available in FW */
+	msleep(20);
+
+	rc = pt_run_selftest_cmd_(dev, test_id, 0,
+		&cmd_status, &summary_result, NULL);
+	if (rc) {
+		/* Error sending self test */
+		pt_debug(dev, DL_ERROR,
+			"%s: Error on run self test for test_id:%d rc = %d\n",
+			__func__, test_id, rc);
+		status = rc;
+		goto resume_scan;
+	}
+	if (cmd_status) {
+		/* Self test response status failure */
+		pt_debug(dev, DL_WARN,
+			"%s: Test ID: 0x%02X resulted in status: 0x%02X\n",
+			__func__, test_id, cmd_status);
+		status = cmd_status;
+	}
+
+	dad->si = cmd->request_sysinfo(dad->dev);
+	if (!dad->si) {
+		pt_debug(dad->dev, DL_ERROR,
+			"%s: Fail get sysinfo pointer from core\n", __func__);
+		if (status == STATUS_SUCCESS)
+			status = -EINVAL;
+		goto resume_scan;
+	}
+	if (IS_PIP_VER_GE(dad->si, 1, 11)) {
+		/* PIP1.11+ does not report the summary_result in byte 6 */
+		summary_result = cmd_status;
+	}
+
+	/* Form response buffer */
+	dad->ic_buf[0] = cmd_status;
+	dad->ic_buf[1] = summary_result;
+
+	pt_debug(dev, DL_INFO, "%s: Run Self Test cmd status = %d\n",
+		__func__, cmd_status);
+	pt_debug(dev, DL_INFO, "%s: Run Self Test result summary = %d\n",
+		__func__, summary_result);
+
+	length = 2;
+
+	/*
+	 * For "auto_shorts" sysfs node, detailed data is
+	 * retrieved only when the summary result is fail.
+	 */
+	if (get_result_on_pass == PT_ST_GET_RESULTS_BASED_ON_DATA &&
+		test_id == PT_ST_ID_AUTOSHORTS &&
+		summary_result == PT_ST_RESULT_PASS)
+		goto resume_scan;
+
+	/*
+	 * Get data if requested and the cmd status indicates that the test
+	 * completed with either a pass or a fail. All other status codes
+	 * indicate the test itself was not run so there is no data to retrieve
+	 */
+	if ((cmd_status == PT_ST_RESULT_PASS ||
+	     cmd_status == PT_ST_RESULT_FAIL)  &&
+	     get_result_on_pass != PT_ST_DONT_GET_RESULTS) {
+		rc = pt_get_selftest_result_cmd_(dev, 0, read_length,
+			test_id, &cmd_status, &act_length, &dad->ic_buf[6]);
+		if (rc) {
+			pt_debug(dev, DL_ERROR,
+				"%s: Error on get self test result rc = %d\n",
+				__func__, rc);
+			if (status == STATUS_SUCCESS)
+				status = rc;
+			goto resume_scan;
+		}
+
+		pt_debug(dev, DL_INFO, "%s: Get Self Test result status = %d\n",
+			__func__, cmd_status);
+
+		/* Only store new status if no error on running self test */
+		if (status == STATUS_SUCCESS)
+			status = cmd_status;
+
+		dad->ic_buf[2] = cmd_status;
+		dad->ic_buf[3] = test_id;
+		dad->ic_buf[4] = LOW_BYTE(act_length);
+		dad->ic_buf[5] = HI_BYTE(act_length);
+
+		length = 6 + act_length;
+	}
+
+resume_scan:
+	/* Only resume scanning if we suspended it */
+	if (sys_mode == FW_SYS_MODE_SCANNING)
+		pt_resume_scan_cmd_(dev);
+
+release_exclusive:
+	if (protect == PT_CORE_CMD_PROTECTED)
+		cmd->release_exclusive(dev);
+
+put_pm_runtime:
+	pm_runtime_put(dev);
+
+	/* Communication error */
+	if (status < 0)
+		length = 0;
+
+	if (print_results) {
+		size = prepare_print_buffer(status, dad->ic_buf, length,
+			buf, buf_len, print_format);
+		rc = size;
+	}
+
+	mutex_unlock(&dad->sysfs_lock);
+
+	return rc;
+}
+
+struct pt_device_access_debugfs_data {
+	struct pt_device_access_data *dad;
+	ssize_t pr_buf_len;
+	u8 pr_buf[10 * PT_MAX_PRBUF_SIZE];
+};
+
+/*******************************************************************************
+ * FUNCTION: pt_device_access_debugfs_open
+ *
+ * SUMMARY: Open the device_access debugfs node to initialize.
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *  *inode - pointer to inode structure
+ *  *filp  - pointer to file structure
+ ******************************************************************************/
+static int pt_device_access_debugfs_open(struct inode *inode,
+		struct file *filp)
+{
+	struct pt_device_access_data *dad = inode->i_private;
+	struct pt_device_access_debugfs_data *data;
+
+	data = kzalloc(sizeof(*data), GFP_KERNEL);
+	if (!data)
+		return -ENOMEM;
+
+	data->dad = dad;
+
+	filp->private_data = data;
+
+	return nonseekable_open(inode, filp);
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_device_access_debugfs_release
+ *
+ * SUMMARY: Close the device_access debugfs node to free pointer.
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *  *inode - pointer to inode structure
+ *  *filp  - pointer to file structure
+ ******************************************************************************/
+static int pt_device_access_debugfs_release(struct inode *inode,
+		struct file *filp)
+{
+	kfree(filp->private_data);
+
+	return 0;
+}
+
+#define PT_DEBUGFS_FOPS(_name, _read, _write) \
+static const struct file_operations _name##_debugfs_fops = { \
+	.open = pt_device_access_debugfs_open, \
+	.release = pt_device_access_debugfs_release, \
+	.read = _read, \
+	.write = _write, \
+}
+
+/*******************************************************************************
+ * FUNCTION: panel_scan_debugfs_read
+ *
+ * SUMMARY: This function retrieves a full panel scan by sending the following
+ *	PIP commands:
+ *		1) Suspend Scanning
+ *		2) Execute Panel Scan
+ *		3) Retrieve Panel Scan (n times to retrieve full scan)
+ *		4) Resume Scanning
+ *
+ * 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 panel_scan_debugfs_read(struct file *filp, char __user *buf,
+		size_t count, loff_t *ppos)
+{
+	struct pt_device_access_debugfs_data *data = filp->private_data;
+	struct pt_device_access_data *dad = data->dad;
+	struct device *dev = dad->dev;
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+	int status = STATUS_FAIL;
+	u8 config;
+	u16 num_elem_read;
+	int length = 0;
+	u8 element_size = 0;
+	u8 *buf_out;
+	u8 *buf_offset;
+	u8 sys_mode = FW_SYS_MODE_UNDEFINED;
+	int elem_offset = 0;
+	int rc;
+	int print_idx = 0;
+	int i;
+
+	mutex_lock(&dad->debugfs_lock);
+	buf_out = dad->panel_scan_data_buf;
+	if (!buf_out)
+		goto release_mutex;
+
+	pm_runtime_get_sync(dev);
+
+	/*
+	 * This function will re-enter if the panel_scan_size is greater than
+	 * count (count is the kernel page size which is typically 4096), on
+	 * re-entry, *ppos will retain how far the last copy to user space
+	 * completed
+	 */
+	if (*ppos) {
+		if (*ppos >= dad->panel_scan_size)
+			goto release_mutex;
+
+		print_idx = simple_read_from_buffer(buf, count, ppos,
+			buf_out, dad->panel_scan_size);
+
+		pt_debug(dev, DL_DEBUG, "%s: Sent %d bytes to user space\n",
+			__func__, print_idx);
+
+		goto release_mutex;
+	}
+
+	rc = cmd->request_exclusive(dev, PT_REQUEST_EXCLUSIVE_TIMEOUT);
+	if (rc) {
+		pt_debug(dev, DL_ERROR,
+			"%s: Error on request exclusive rc = %d\n",
+			__func__, rc);
+		goto put_pm_runtime;
+	}
+
+	/* Get the current scan state so we restore to the same at the end */
+	rc = cmd->request_get_fw_mode(dev, PT_CORE_CMD_UNPROTECTED, &sys_mode,
+		NULL);
+	if (rc) {
+		status = rc;
+		goto release_exclusive;
+	}
+
+	if (sys_mode != FW_SYS_MODE_TEST) {
+		rc = pt_suspend_scan_cmd_(dev);
+		if (rc) {
+			pt_debug(dev, DL_ERROR,
+					"%s: Error on suspend scan rc = %d\n",
+					__func__, rc);
+			goto release_exclusive;
+		}
+	}
+
+	rc = pt_exec_scan_cmd_(dev, dad->panel_scan_type_id);
+	if (rc) {
+		pt_debug(dev, DL_ERROR,
+				"%s: Error on execute panel scan rc = %d\n",
+				__func__, rc);
+		goto resume_scan;
+	}
+
+	/* Set length to max to read all */
+	rc = pt_ret_scan_data_cmd_(dev, 0, 0xFFFF,
+			dad->panel_scan_retrieve_id, dad->ic_buf, &config,
+			&num_elem_read, NULL);
+	if (rc) {
+		pt_debug(dev, DL_ERROR,
+				"%s: Error on retrieve panel scan rc = %d\n",
+				__func__, rc);
+		goto resume_scan;
+	}
+
+	length = get_unaligned_le16(&dad->ic_buf[0]);
+	buf_offset = dad->ic_buf + length;
+	element_size = config & 0x07;
+	elem_offset = num_elem_read;
+	while (num_elem_read > 0) {
+		rc = pt_ret_scan_data_cmd_(dev, elem_offset, 0xFFFF,
+				dad->panel_scan_retrieve_id, NULL, &config,
+				&num_elem_read, buf_offset);
+		if (rc)
+			goto resume_scan;
+
+		length += num_elem_read * element_size;
+		buf_offset = dad->ic_buf + length;
+		elem_offset += num_elem_read;
+		if (num_elem_read < 0x7A)
+			break;
+	}
+	/* Reconstruct cmd header */
+	put_unaligned_le16(length, &dad->ic_buf[0]);
+	put_unaligned_le16(elem_offset, &dad->ic_buf[7]);
+
+	status = STATUS_SUCCESS;
+
+resume_scan:
+	/* Only resume scanning if we suspended it */
+	if (sys_mode == FW_SYS_MODE_SCANNING)
+		pt_resume_scan_cmd_(dev);
+
+release_exclusive:
+	cmd->release_exclusive(dev);
+
+put_pm_runtime:
+	pm_runtime_put(dev);
+
+	if (status == STATUS_FAIL)
+		length = 0;
+	if (cd->show_timestamp)
+		print_idx += scnprintf(buf_out + print_idx, TTHE_TUNER_MAX_BUF,
+			"[%u] SCAN_DATA:", pt_get_time_stamp());
+	else
+		print_idx += scnprintf(buf_out + print_idx, TTHE_TUNER_MAX_BUF,
+			"SCAN_DATA:");
+
+	for (i = 0; i < length; i++)
+		print_idx += scnprintf(buf_out + print_idx,
+			TTHE_TUNER_MAX_BUF - print_idx,
+			"%02X ", dad->ic_buf[i]);
+	print_idx += scnprintf(buf_out + print_idx,
+			TTHE_TUNER_MAX_BUF - print_idx,
+			":(%d bytes)\n", length);
+
+	/*
+	 * Save the size of the full scan which this function uses on re-entry
+	 * to send the data back to user space in 'count' size chuncks
+	 */
+	dad->panel_scan_size = print_idx;
+	print_idx = simple_read_from_buffer(buf, count, ppos, buf_out,
+		print_idx);
+	pt_debug(dev, DL_DEBUG, "%s: Sent %d bytes to user space\n",
+		__func__, print_idx);
+
+release_mutex:
+	mutex_unlock(&dad->debugfs_lock);
+	return print_idx;
+}
+
+/*******************************************************************************
+ * FUNCTION: panel_scan_debugfs_write
+ *
+ * SUMMARY: Store the type of panel scan the read method will perform.
+ *
+ * RETURN: Size of debugfs data write
+ *
+ * PARAMETERS:
+ *      *filp   - file pointer to debugfs file
+ *      *buf    - the user space buffer to write to
+ *       count  - the maximum number of bytes to write
+ *      *ppos   - the current position in the buffer
+ ******************************************************************************/
+static ssize_t panel_scan_debugfs_write(struct file *filp,
+		const char __user *buf, size_t count, loff_t *ppos)
+{
+	struct pt_device_access_debugfs_data *data = filp->private_data;
+	struct pt_device_access_data *dad = data->dad;
+	ssize_t length;
+	u32 input_data[3];
+	int rc = 0;
+
+	rc = simple_write_to_buffer(data->pr_buf, sizeof(data->pr_buf), ppos,
+			buf, count);
+	if (rc < 0)
+		return rc;
+
+	count = rc;
+
+	mutex_lock(&dad->debugfs_lock);
+	length = cmd->parse_sysfs_input(dad->dev, data->pr_buf, count,
+			input_data, ARRAY_SIZE(input_data));
+	switch (length) {
+	case 1:
+		dad->panel_scan_retrieve_id = input_data[0];
+		dad->panel_scan_type_id = 0;
+		break;
+	case 2:
+		dad->panel_scan_retrieve_id = input_data[0];
+		dad->panel_scan_type_id = input_data[1];
+		break;
+	default:
+		pt_debug(dad->dev, DL_ERROR,
+				"%s: Malformed input\n", __func__);
+		rc = -EINVAL;
+	}
+	mutex_unlock(&dad->debugfs_lock);
+
+	if (rc)
+		return rc;
+	return count;
+}
+
+/*******************************************************************************
+ * FUNCTION: panel_scan_debugfs_open
+ *
+ * SUMMARY: Open the panel_scan debugfs node to initialize.
+ *
+ * RETURN: 0 = success
+ *        !0 = failure
+ *
+ * PARAMETERS:
+ *      *inode - file inode number
+ *      *filp  - file pointer to debugfs file
+ ******************************************************************************/
+static int panel_scan_debugfs_open(struct inode *inode,
+		struct file *filp)
+{
+	struct pt_device_access_data *dad = inode->i_private;
+	struct pt_device_access_debugfs_data *data;
+
+	data = kzalloc(sizeof(*data), GFP_KERNEL);
+	if (!data)
+		return -ENOMEM;
+
+	data->dad = dad;
+	data->pr_buf_len = 4 * PT_MAX_PRBUF_SIZE;
+
+	filp->private_data = data;
+
+	return nonseekable_open(inode, filp);
+}
+
+/*******************************************************************************
+ * FUNCTION: panel_scan_debugfs_close
+ *
+ * SUMMARY: Close the panel_scan debugfs node to free pointer.
+ *
+ * RETURN: 0 = success
+ *
+ * PARAMETERS:
+ *      *inode - file inode number
+ *      *filp  - file pointer to debugfs file
+ ******************************************************************************/
+static int panel_scan_debugfs_close(struct inode *inode,
+		struct file *filp)
+{
+	kfree(filp->private_data);
+	filp->private_data = NULL;
+	return 0;
+}
+
+static const struct file_operations panel_scan_fops = {
+	.open = panel_scan_debugfs_open,
+	.release = panel_scan_debugfs_close,
+	.read = panel_scan_debugfs_read,
+	.write = panel_scan_debugfs_write,
+};
+
+/*******************************************************************************
+ * FUNCTION: get_idac_debugfs_read
+ *
+ * SUMMARY: Retrieve data structure with idac data id by sending the following
+ *	PIP commands:
+ *		1) Suspend Scanning
+ *		2) Retrieve data structure
+ *		3) Resume Scanning
+ *	The "Status: n" this node prints, 'n' will be:
+ *		- zero for a full pass
+ *		- negative for TTDL communication errors
+ *		- positive for any FW status errors
+ *
+ * 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 get_idac_debugfs_read(struct file *filp, char __user *buf,
+		size_t count, loff_t *ppos)
+{
+	struct pt_device_access_debugfs_data *data = filp->private_data;
+	struct pt_device_access_data *dad = data->dad;
+	struct device *dev = dad->dev;
+	int status = STATUS_FAIL;
+	u8 cmd_status = 0;
+	u8 data_format = 0;
+	u16 act_length = 0;
+	int length = 0;
+	int rc;
+
+	if (*ppos)
+		goto exit;
+
+	mutex_lock(&dad->sysfs_lock);
+
+	pm_runtime_get_sync(dev);
+
+	rc = cmd->request_exclusive(dev, PT_REQUEST_EXCLUSIVE_TIMEOUT);
+	if (rc) {
+		status = rc;
+		pt_debug(dev, DL_ERROR,
+			"%s: Error on request exclusive rc = %d\n",
+			__func__, rc);
+		goto put_pm_runtime;
+	}
+
+	rc = pt_suspend_scan_cmd_(dev);
+	if (rc) {
+		status = rc;
+		pt_debug(dev, DL_ERROR, "%s: Error on suspend scan rc = %d\n",
+			__func__, rc);
+		goto release_exclusive;
+	}
+
+	rc = pt_get_data_structure_cmd_(dev, 0, PIP_CMD_MAX_LENGTH,
+		dad->get_idac_data_id, &cmd_status, &data_format,
+		&act_length, &dad->ic_buf[5]);
+	if (rc) {
+		status = rc;
+		pt_debug(dev, DL_ERROR,
+			"%s: Error on get data structure rc = %d\n",
+			__func__, rc);
+		goto resume_scan;
+	}
+
+	dad->ic_buf[0] = cmd_status;
+	dad->ic_buf[1] = dad->get_idac_data_id;
+	dad->ic_buf[2] = LOW_BYTE(act_length);
+	dad->ic_buf[3] = HI_BYTE(act_length);
+	dad->ic_buf[4] = data_format;
+
+	length = 5 + act_length;
+
+	status = cmd_status;
+
+resume_scan:
+	pt_resume_scan_cmd_(dev);
+
+release_exclusive:
+	cmd->release_exclusive(dev);
+
+put_pm_runtime:
+	pm_runtime_put(dev);
+
+	if (status == STATUS_FAIL)
+		length = 0;
+
+	data->pr_buf_len = prepare_print_buffer(status, dad->ic_buf, length,
+		data->pr_buf, sizeof(data->pr_buf), PT_PR_FORMAT_DEFAULT);
+
+	mutex_unlock(&dad->sysfs_lock);
+
+exit:
+	return simple_read_from_buffer(buf, count, ppos, data->pr_buf,
+			data->pr_buf_len);
+}
+
+/*******************************************************************************
+ * FUNCTION: get_idac_debugfs_write
+ *
+ * SUMMARY: Store the data id of idac,the read method will perform.
+ *
+ * RETURN: Size of debugfs data write
+ *
+ * PARAMETERS:
+ *      *filp   - file pointer to debugfs file
+ *      *buf    - the user space buffer to write to
+ *       count  - the maximum number of bytes to write
+ *      *ppos   - the current position in the buffer
+ ******************************************************************************/
+static ssize_t get_idac_debugfs_write(struct file *filp,
+		const char __user *buf, size_t count, loff_t *ppos)
+{
+	struct pt_device_access_debugfs_data *data = filp->private_data;
+	struct pt_device_access_data *dad = data->dad;
+	ssize_t length;
+	u32 input_data[2];
+	int rc = 0;
+
+	rc = simple_write_to_buffer(data->pr_buf, sizeof(data->pr_buf), ppos,
+			buf, count);
+	if (rc < 0)
+		return rc;
+
+	count = rc;
+
+	mutex_lock(&dad->sysfs_lock);
+
+	length = cmd->parse_sysfs_input(dad->dev, data->pr_buf, count,
+			input_data, ARRAY_SIZE(input_data));
+	if (length != 1) {
+		pt_debug(dad->dev, DL_ERROR,
+				"%s: Malformed input\n", __func__);
+		rc = -EINVAL;
+		goto exit_unlock;
+	}
+
+	dad->get_idac_data_id = input_data[0];
+
+exit_unlock:
+	mutex_unlock(&dad->sysfs_lock);
+
+	if (rc)
+		return rc;
+
+	return count;
+}
+
+PT_DEBUGFS_FOPS(get_idac, get_idac_debugfs_read, get_idac_debugfs_write);
+
+/*******************************************************************************
+ * FUNCTION: calibrate_ext_debugfs_read
+ *
+ * SUMMARY: Perform extended calibration command(0x30) which is flexible to
+ *  calibrate each individual feature by adding extra parameter for calibration
+ *  mode.
+ *
+ * NOTE:
+ *	- This calibrate command requires the DUT to support PIP version >= 1.10
+ *	- The "Status:" included in the printout will be one of the following:
+ *		<0 - Linux error code (PIP transmission error)
+ *		 0 - Full pass
+ *		>0 - PIP error status
+ *
+ * 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 calibrate_ext_debugfs_read(struct file *filp, char __user *buf,
+		size_t count, loff_t *ppos)
+{
+	struct pt_device_access_debugfs_data *data = filp->private_data;
+	struct pt_device_access_data *dad = data->dad;
+	struct device *dev = dad->dev;
+	int status = STATUS_FAIL;
+	int length = 0;
+	int rc;
+
+	if (*ppos)
+		goto exit;
+
+	dad->si = cmd->request_sysinfo(dad->dev);
+	if (!dad->si) {
+		pt_debug(dad->dev, DL_ERROR,
+			"%s: Fail get sysinfo pointer from core\n",
+			__func__);
+		status = -EIO;
+		data->pr_buf_len = prepare_print_buffer(status, dad->ic_buf, 0,
+			data->pr_buf, sizeof(data->pr_buf),
+			PT_PR_FORMAT_DEFAULT);
+		goto exit;
+	}
+
+	if (!IS_PIP_VER_GE(dad->si, 1, 10)) {
+		pt_debug(dad->dev, DL_ERROR,
+			"%s: extended calibration command is not supported\n",
+			__func__);
+		status = -EPROTONOSUPPORT;
+		data->pr_buf_len = prepare_print_buffer(status, dad->ic_buf, 0,
+			data->pr_buf, sizeof(data->pr_buf),
+			PT_PR_FORMAT_DEFAULT);
+		goto exit;
+	}
+
+	if (dad->cal_ext_data.mode == PT_CAL_EXT_MODE_UNDEFINED) {
+		pt_debug(dad->dev, DL_ERROR,
+			"%s: No parameters provided for calibration command\n",
+			__func__);
+		status = -EINVAL;
+		data->pr_buf_len = prepare_print_buffer(status, dad->ic_buf, 0,
+			data->pr_buf, sizeof(data->pr_buf),
+			PT_PR_FORMAT_DEFAULT);
+		goto exit;
+	}
+
+	mutex_lock(&dad->sysfs_lock);
+
+	pm_runtime_get_sync(dev);
+
+	rc = cmd->request_exclusive(dev, PT_REQUEST_EXCLUSIVE_TIMEOUT);
+	if (rc) {
+		status = rc;
+		pt_debug(dev, DL_ERROR,
+				"%s: Error on request exclusive rc = %d\n",
+				__func__, rc);
+		goto put_pm_runtime;
+	}
+
+	rc = pt_suspend_scan_cmd_(dev);
+	if (rc) {
+		status = rc;
+		pt_debug(dev, DL_ERROR, "%s: Error on suspend scan rc = %d\n",
+				__func__, rc);
+		goto release_exclusive;
+	}
+
+	rc = _pt_calibrate_ext_cmd(dev, &dad->cal_ext_data, &dad->ic_buf[0]);
+	if (rc) {
+		status = rc;
+		pt_debug(dev, DL_ERROR,
+				"%s: Error on calibrate_ext rc = %d\n",
+				__func__, rc);
+		goto resume_scan;
+	}
+
+	/*
+	 * Include PIP errors as positive status codes and report the data.
+	 * No PIP error "0x00" in the response indicates full success
+	 */
+	length = 1;
+	status = dad->ic_buf[0];
+
+resume_scan:
+	pt_resume_scan_cmd_(dev);
+
+release_exclusive:
+	cmd->release_exclusive(dev);
+
+put_pm_runtime:
+	pm_runtime_put(dev);
+
+	/* Negative status codes are bus transmission errors and have no data */
+	if (status < 0)
+		length = 0;
+
+	data->pr_buf_len = prepare_print_buffer(status, dad->ic_buf, length,
+		data->pr_buf, sizeof(data->pr_buf), PT_PR_FORMAT_DEFAULT);
+
+	mutex_unlock(&dad->sysfs_lock);
+
+exit:
+	return simple_read_from_buffer(buf, count, ppos, data->pr_buf,
+			data->pr_buf_len);
+}
+
+/*******************************************************************************
+ * FUNCTION: calibrate_ext_debugfs_write
+ *
+ * SUMMARY: Stores the calibration mode and up to three parameters to perform
+ * individual features.
+ *
+ * RETURN: Size of debugfs data write
+ *
+ * PARAMETERS:
+ *	   *filp   - file pointer to debugfs file
+ *	   *buf    - the user space buffer to write to
+ *		count  - the maximum number of bytes to write
+ *	   *ppos   - the current position in the buffer
+ ******************************************************************************/
+static ssize_t calibrate_ext_debugfs_write(struct file *filp,
+		const char __user *buf, size_t count, loff_t *ppos)
+{
+	struct pt_device_access_debugfs_data *data = filp->private_data;
+	struct pt_device_access_data *dad = data->dad;
+	ssize_t length;
+	u32 input_data[5];
+	int rc = 0;
+	int i = 0;
+
+	rc = simple_write_to_buffer(data->pr_buf, sizeof(data->pr_buf), ppos,
+			buf, count);
+	if (rc < 0)
+		return rc;
+
+	count = rc;
+
+	mutex_lock(&dad->sysfs_lock);
+
+	length = cmd->parse_sysfs_input(dad->dev, data->pr_buf, count,
+			input_data, ARRAY_SIZE(input_data));
+	if ((length <= 4) && (length > 0)) {
+		for (i = length; i < 4; i++)
+			input_data[i] = 0;
+
+		dad->cal_ext_data.mode = (u8)input_data[0];
+		dad->cal_ext_data.data0 = (u8)input_data[1];
+		dad->cal_ext_data.data1 = (u8)input_data[2];
+		dad->cal_ext_data.data2 = (u8)input_data[3];
+#ifdef TTDL_DIAGNOSTICS
+		pt_debug(dad->dev, DL_INFO,
+			"%s: calibration mode=%d, data[0..2]=0x%02X %02X %02X\n",
+			__func__,
+			dad->cal_ext_data.mode, dad->cal_ext_data.data0,
+			dad->cal_ext_data.data1, dad->cal_ext_data.data2);
+#endif
+	} else {
+		pt_debug(dad->dev, DL_ERROR,
+				"%s: Malformed input\n", __func__);
+		rc = -EINVAL;
+		goto exit_unlock;
+	}
+
+exit_unlock:
+	mutex_unlock(&dad->sysfs_lock);
+
+	if (rc)
+		return rc;
+
+	return count;
+}
+
+PT_DEBUGFS_FOPS(calibrate_ext,
+	calibrate_ext_debugfs_read, calibrate_ext_debugfs_write);
+
+/*******************************************************************************
+ * FUNCTION: calibrate_debugfs_read
+ *
+ * SUMMARY: Perform calibration by sending the following PIP commands:
+ *     1) Suspend Scanning
+ *     2) Execute calibrate
+ *     3) Initialize baseline conditionally
+ *     4) Resume Scanning
+ *
+ * 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 calibrate_debugfs_read(struct file *filp, char __user *buf,
+		size_t count, loff_t *ppos)
+{
+	struct pt_device_access_debugfs_data *data = filp->private_data;
+	struct pt_device_access_data *dad = data->dad;
+	struct device *dev = dad->dev;
+	int status = STATUS_FAIL;
+	int length = 0;
+	int rc;
+
+	if (*ppos)
+		goto exit;
+
+	mutex_lock(&dad->sysfs_lock);
+
+	pm_runtime_get_sync(dev);
+
+	rc = cmd->request_exclusive(dev, PT_REQUEST_EXCLUSIVE_TIMEOUT);
+	if (rc) {
+		status = rc;
+		pt_debug(dev, DL_ERROR,
+				"%s: Error on request exclusive rc = %d\n",
+				__func__, rc);
+		goto put_pm_runtime;
+	}
+
+	rc = pt_suspend_scan_cmd_(dev);
+	if (rc) {
+		status = rc;
+		pt_debug(dev, DL_ERROR, "%s: Error on suspend scan rc = %d\n",
+				__func__, rc);
+		goto release_exclusive;
+	}
+
+	rc = _pt_calibrate_idacs_cmd(dev, dad->calibrate_sensing_mode,
+			&dad->ic_buf[0]);
+	if (rc) {
+		status = rc;
+		pt_debug(dev, DL_ERROR,
+				"%s: Error on calibrate idacs rc = %d\n",
+				__func__, rc);
+		goto resume_scan;
+	}
+
+	length = 1;
+
+	/* Check if baseline initialization is requested */
+	if (dad->calibrate_initialize_baselines) {
+		/* Perform baseline initialization for all modes */
+		rc = _pt_initialize_baselines_cmd(dev, PT_IB_SM_MUTCAP |
+				PT_IB_SM_SELFCAP | PT_IB_SM_BUTTON,
+				&dad->ic_buf[length]);
+		if (rc) {
+			status = rc;
+			pt_debug(dev, DL_ERROR,
+				"%s: Error on initialize baselines rc = %d\n",
+				__func__, rc);
+			goto resume_scan;
+		}
+
+		length++;
+	}
+
+	status = STATUS_SUCCESS;
+
+resume_scan:
+	pt_resume_scan_cmd_(dev);
+
+release_exclusive:
+	cmd->release_exclusive(dev);
+
+put_pm_runtime:
+	pm_runtime_put(dev);
+
+	if (status == STATUS_FAIL)
+		length = 0;
+
+	data->pr_buf_len = prepare_print_buffer(status, dad->ic_buf, length,
+		data->pr_buf, sizeof(data->pr_buf), PT_PR_FORMAT_DEFAULT);
+
+	mutex_unlock(&dad->sysfs_lock);
+
+exit:
+	return simple_read_from_buffer(buf, count, ppos, data->pr_buf,
+			data->pr_buf_len);
+}
+
+/*******************************************************************************
+ * FUNCTION: calibrate_debugfs_write
+ *
+ * SUMMARY: Stores the calibration sense mode and a flag to control if the
+ *  baseline will be initialized for the read method of this node.
+ *
+ * RETURN: Size of debugfs data write
+ *
+ * PARAMETERS:
+ *	   *filp   - file pointer to debugfs file
+ *	   *buf    - the user space buffer to write to
+ *		count  - the maximum number of bytes to write
+ *	   *ppos   - the current position in the buffer
+ ******************************************************************************/
+static ssize_t calibrate_debugfs_write(struct file *filp,
+		const char __user *buf, size_t count, loff_t *ppos)
+{
+	struct pt_device_access_debugfs_data *data = filp->private_data;
+	struct pt_device_access_data *dad = data->dad;
+	ssize_t length;
+	u32 input_data[3];
+	int rc = 0;
+
+	rc = simple_write_to_buffer(data->pr_buf, sizeof(data->pr_buf), ppos,
+			buf, count);
+	if (rc < 0)
+		return rc;
+
+	count = rc;
+
+	mutex_lock(&dad->sysfs_lock);
+
+	length = cmd->parse_sysfs_input(dad->dev, data->pr_buf, count,
+			input_data, ARRAY_SIZE(input_data));
+	if (length != 2) {
+		pt_debug(dad->dev, DL_ERROR,
+				"%s: Malformed input\n", __func__);
+		rc = -EINVAL;
+		goto exit_unlock;
+	}
+
+	dad->calibrate_sensing_mode = input_data[0];
+	dad->calibrate_initialize_baselines = input_data[1];
+
+exit_unlock:
+	mutex_unlock(&dad->sysfs_lock);
+
+	if (rc)
+		return rc;
+
+	return count;
+}
+
+PT_DEBUGFS_FOPS(calibrate, calibrate_debugfs_read, calibrate_debugfs_write);
+
+/*******************************************************************************
+ * FUNCTION: baseline_debugfs_read
+ *
+ * SUMMARY: Perform baseline initialization by sending the following PIP
+ *  commands:
+ *	   1) Suspend Scanning
+ *	   2) Execute initialize baseline
+ *	   3) Resume Scanning
+ *
+ * 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 baseline_debugfs_read(struct file *filp, char __user *buf,
+		size_t count, loff_t *ppos)
+{
+	struct pt_device_access_debugfs_data *data = filp->private_data;
+	struct pt_device_access_data *dad = data->dad;
+	struct device *dev = dad->dev;
+	int status = STATUS_FAIL;
+	int length = 0;
+	int rc;
+
+	if (*ppos)
+		goto exit;
+
+	mutex_lock(&dad->sysfs_lock);
+
+	pm_runtime_get_sync(dev);
+
+	rc = cmd->request_exclusive(dev, PT_REQUEST_EXCLUSIVE_TIMEOUT);
+	if (rc) {
+		status = rc;
+		pt_debug(dev, DL_ERROR,
+				"%s: Error on request exclusive rc = %d\n",
+				__func__, rc);
+		goto put_pm_runtime;
+	}
+
+	rc = pt_suspend_scan_cmd_(dev);
+	if (rc) {
+		status = rc;
+		pt_debug(dev, DL_ERROR, "%s: Error on suspend scan rc = %d\n",
+				__func__, rc);
+		goto release_exclusive;
+	}
+
+	rc = _pt_initialize_baselines_cmd(dev, dad->baseline_sensing_mode,
+			&dad->ic_buf[0]);
+	if (rc) {
+		status = rc;
+		pt_debug(dev, DL_ERROR,
+				"%s: Error on initialize baselines rc = %d\n",
+				__func__, rc);
+		goto resume_scan;
+	}
+
+	length = 1;
+
+	status = STATUS_SUCCESS;
+
+resume_scan:
+	pt_resume_scan_cmd_(dev);
+
+release_exclusive:
+	cmd->release_exclusive(dev);
+
+put_pm_runtime:
+	pm_runtime_put(dev);
+
+	if (status == STATUS_FAIL)
+		length = 0;
+
+	data->pr_buf_len = prepare_print_buffer(status, dad->ic_buf, length,
+		data->pr_buf, sizeof(data->pr_buf), PT_PR_FORMAT_DEFAULT);
+
+	mutex_unlock(&dad->sysfs_lock);
+
+exit:
+	return simple_read_from_buffer(buf, count, ppos, data->pr_buf,
+			data->pr_buf_len);
+}
+
+/*******************************************************************************
+ * FUNCTION: baseline_debugfs_write
+ *
+ * SUMMARY: Store the sense mode of base initialization, the read method will
+ *  perform.
+ *
+ * RETURN: Size of debugfs data write
+ *
+ * PARAMETERS:
+ *	   *filp   - file pointer to debugfs file
+ *	   *buf    - the user space buffer to write to
+ *		count  - the maximum number of bytes to write
+ *	   *ppos   - the current position in the buffer
+ ******************************************************************************/
+static ssize_t baseline_debugfs_write(struct file *filp,
+		const char __user *buf, size_t count, loff_t *ppos)
+{
+	struct pt_device_access_debugfs_data *data = filp->private_data;
+	struct pt_device_access_data *dad = data->dad;
+	ssize_t length;
+	u32 input_data[2];
+	int rc = 0;
+
+	rc = simple_write_to_buffer(data->pr_buf, sizeof(data->pr_buf), ppos,
+			buf, count);
+	if (rc < 0)
+		return rc;
+
+	count = rc;
+
+	mutex_lock(&dad->sysfs_lock);
+
+	length = cmd->parse_sysfs_input(dad->dev, data->pr_buf, count,
+		input_data, ARRAY_SIZE(input_data));
+	if (length != 1) {
+		pt_debug(dad->dev, DL_ERROR,
+				"%s: Malformed input\n", __func__);
+		rc = -EINVAL;
+		goto exit_unlock;
+	}
+
+	dad->baseline_sensing_mode = input_data[0];
+
+exit_unlock:
+	mutex_unlock(&dad->sysfs_lock);
+
+	if (rc)
+		return rc;
+
+	return count;
+}
+
+PT_DEBUGFS_FOPS(baseline, baseline_debugfs_read, baseline_debugfs_write);
+
+/*******************************************************************************
+ * FUNCTION: auto_shorts_debugfs_read
+ *
+ * SUMMARY: Performs the "auto shorts" test and prints the result to the output
+ *  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 auto_shorts_debugfs_read(struct file *filp, char __user *buf,
+		size_t count, loff_t *ppos)
+{
+	struct pt_device_access_debugfs_data *data = filp->private_data;
+
+	if (!*ppos)
+		/* Set length to PIP_CMD_MAX_LENGTH to read all */
+		data->pr_buf_len = pt_run_and_get_selftest_result(
+			data->dad->dev, PT_CORE_CMD_PROTECTED,
+			data->pr_buf, sizeof(data->pr_buf),
+			PT_ST_ID_AUTOSHORTS, PIP_CMD_MAX_LENGTH,
+			PT_ST_GET_RESULTS_BASED_ON_DATA, PT_ST_PRINT_RESULTS,
+			PT_PR_FORMAT_DEFAULT);
+
+	return simple_read_from_buffer(buf, count, ppos, data->pr_buf,
+			data->pr_buf_len);
+}
+
+PT_DEBUGFS_FOPS(auto_shorts, auto_shorts_debugfs_read, NULL);
+
+/*******************************************************************************
+ * FUNCTION: opens_debugfs_read
+ *
+ * SUMMARY: Performs the "opens" test and prints the results to the output
+ *  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 opens_debugfs_read(struct file *filp, char __user *buf,
+		size_t count, loff_t *ppos)
+{
+	struct pt_device_access_debugfs_data *data = filp->private_data;
+
+	if (!*ppos)
+		/* Set length to PIP_CMD_MAX_LENGTH to read all */
+		data->pr_buf_len = pt_run_and_get_selftest_result(
+			data->dad->dev, PT_CORE_CMD_PROTECTED,
+			data->pr_buf, sizeof(data->pr_buf),
+			PT_ST_ID_OPENS, PIP_CMD_MAX_LENGTH,
+			PT_ST_DONT_GET_RESULTS, PT_ST_PRINT_RESULTS,
+			PT_PR_FORMAT_DEFAULT);
+
+	return simple_read_from_buffer(buf, count, ppos, data->pr_buf,
+			data->pr_buf_len);
+}
+
+PT_DEBUGFS_FOPS(opens, opens_debugfs_read, NULL);
+
+/*******************************************************************************
+ * FUNCTION: cm_panel_debugfs_read
+ *
+ * SUMMARY: Performs the "CM panel" test and prints the result to the output
+ *  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 cm_panel_debugfs_read(struct file *filp, char __user *buf,
+		size_t count, loff_t *ppos)
+{
+	struct pt_device_access_debugfs_data *data = filp->private_data;
+
+	if (!*ppos)
+		/* Set length to PIP_CMD_MAX_LENGTH to read all */
+		data->pr_buf_len = pt_run_and_get_selftest_result(
+			data->dad->dev, PT_CORE_CMD_PROTECTED,
+			data->pr_buf, sizeof(data->pr_buf),
+			PT_ST_ID_CM_PANEL, PIP_CMD_MAX_LENGTH,
+			PT_ST_GET_RESULTS, PT_ST_PRINT_RESULTS,
+			PT_PR_FORMAT_DEFAULT);
+
+	return simple_read_from_buffer(buf, count, ppos, data->pr_buf,
+			data->pr_buf_len);
+}
+
+PT_DEBUGFS_FOPS(cm_panel, cm_panel_debugfs_read, NULL);
+
+/*******************************************************************************
+ * FUNCTION: cp_panel_debugfs_read
+ *
+ * SUMMARY: Performs the "CP panel" test and prints the result to the output
+ *  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 cp_panel_debugfs_read(struct file *filp, char __user *buf,
+		size_t count, loff_t *ppos)
+{
+	struct pt_device_access_debugfs_data *data = filp->private_data;
+
+	if (!*ppos)
+		/* Set length to PIP_CMD_MAX_LENGTH to read all */
+		data->pr_buf_len = pt_run_and_get_selftest_result(
+			data->dad->dev, PT_CORE_CMD_PROTECTED,
+			data->pr_buf, sizeof(data->pr_buf),
+			PT_ST_ID_CP_PANEL, PIP_CMD_MAX_LENGTH,
+			PT_ST_GET_RESULTS, PT_ST_PRINT_RESULTS,
+			PT_PR_FORMAT_DEFAULT);
+
+	return simple_read_from_buffer(buf, count, ppos, data->pr_buf,
+			data->pr_buf_len);
+}
+
+PT_DEBUGFS_FOPS(cp_panel, cp_panel_debugfs_read, NULL);
+
+/*******************************************************************************
+ * FUNCTION: cm_button_debugfs_read
+ *
+ * SUMMARY: Performs the "CM buttons" test and prints the result to the output
+ *  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 cm_button_debugfs_read(struct file *filp, char __user *buf,
+		size_t count, loff_t *ppos)
+{
+	struct pt_device_access_debugfs_data *data = filp->private_data;
+
+	if (!*ppos)
+		/* Set length to PIP_CMD_MAX_LENGTH to read all */
+		data->pr_buf_len = pt_run_and_get_selftest_result(
+			data->dad->dev, PT_CORE_CMD_PROTECTED,
+			data->pr_buf, sizeof(data->pr_buf),
+			PT_ST_ID_CM_BUTTON, PIP_CMD_MAX_LENGTH,
+			PT_ST_GET_RESULTS, PT_ST_PRINT_RESULTS,
+			PT_PR_FORMAT_DEFAULT);
+
+	return simple_read_from_buffer(buf, count, ppos, data->pr_buf,
+			data->pr_buf_len);
+}
+
+PT_DEBUGFS_FOPS(cm_button, cm_button_debugfs_read, NULL);
+
+/*******************************************************************************
+ * FUNCTION: cp_button_debugfs_read
+ *
+ * SUMMARY: Performs the "CP buttons" test and prints the result to the output
+ *  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 cp_button_debugfs_read(struct file *filp, char __user *buf,
+		size_t count, loff_t *ppos)
+{
+	struct pt_device_access_debugfs_data *data = filp->private_data;
+
+	if (!*ppos)
+		/* Set length to PIP_CMD_MAX_LENGTH to read all */
+		data->pr_buf_len = pt_run_and_get_selftest_result(
+			data->dad->dev, PT_CORE_CMD_PROTECTED,
+			data->pr_buf, sizeof(data->pr_buf),
+			PT_ST_ID_CP_BUTTON, PIP_CMD_MAX_LENGTH,
+			PT_ST_GET_RESULTS, PT_ST_PRINT_RESULTS,
+			PT_PR_FORMAT_DEFAULT);
+
+	return simple_read_from_buffer(buf, count, ppos, data->pr_buf,
+			data->pr_buf_len);
+}
+
+PT_DEBUGFS_FOPS(cp_button, cp_button_debugfs_read, NULL);
+
+/*******************************************************************************
+ * FUNCTION: fw_self_test_debugfs_read
+ *
+ * SUMMARY: Performs the self test by firmware and prints the results to the
+ *  output 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 fw_self_test_debugfs_read(struct file *filp, char __user *buf,
+		size_t count, loff_t *ppos)
+{
+	u8 ret_status;
+	u8 ret_self_test_id;
+	u8 sys_mode = FW_SYS_MODE_UNDEFINED;
+	u16 ret_act_load_len;
+	int rc;
+	struct pt_device_access_debugfs_data *data = filp->private_data;
+	struct pt_device_access_data *dad = data->dad;
+
+	if (!*ppos) {
+		if (dad->fw_self_test_id == PT_ST_ID_INVALID ||
+			dad->fw_self_test_format >= PT_PR_FORMAT_UNDEFINE) {
+			data->pr_buf_len = scnprintf(data->pr_buf,
+				sizeof(data->pr_buf), "Status: %d\n", -EINVAL);
+			pt_debug(dad->dev, DL_ERROR,
+				"%s: Malformed input\n", __func__);
+			goto exit;
+		}
+
+		/* only send the load parameters cmd if param data exists */
+		if (dad->fw_self_test_param_len > 0) {
+			rc = cmd->request_get_fw_mode(dad->dev,
+				PT_CORE_CMD_UNPROTECTED, &sys_mode, NULL);
+			if (rc) {
+				pt_debug(dad->dev, DL_ERROR,
+					"%s: ERR on request mode rc=%d\n",
+					__func__, rc);
+				data->pr_buf_len = scnprintf(
+					data->pr_buf,
+					sizeof(data->pr_buf),
+					"Status: %d\n", rc);
+				goto exit;
+			}
+
+			if (sys_mode != FW_SYS_MODE_TEST) {
+				rc = pt_suspend_scan_cmd_(dad->dev);
+				if (rc) {
+					pt_debug(dad->dev, DL_ERROR,
+						"%s: ERR on sus scan rc=%d\n",
+						__func__, rc);
+					data->pr_buf_len = scnprintf(
+						data->pr_buf,
+						sizeof(data->pr_buf),
+						"Status: %d\n", rc);
+					goto exit;
+				}
+			}
+			cmd->nonhid_cmd->load_self_test_param(dad->dev,
+				PT_CORE_CMD_PROTECTED,
+				dad->fw_self_test_id, 0,
+				dad->fw_self_test_param_len,
+				dad->fw_self_test_param, &ret_status,
+				&ret_self_test_id, &ret_act_load_len);
+			if (ret_status) {
+				data->pr_buf_len = scnprintf(data->pr_buf,
+					sizeof(data->pr_buf),
+					"Status: %d\n", -EINVAL);
+				pt_debug(dad->dev, DL_ERROR,
+					"%s: Load Param Malformed input\n",
+					__func__);
+				goto resume_scan;
+			}
+		}
+
+		/* Set length to PIP_CMD_MAX_LENGTH to read all */
+		data->pr_buf_len = pt_run_and_get_selftest_result(
+			dad->dev, PT_CORE_CMD_PROTECTED,
+			data->pr_buf, sizeof(data->pr_buf),
+			dad->fw_self_test_id, PIP_CMD_MAX_LENGTH,
+			PT_ST_GET_RESULTS, PT_ST_PRINT_RESULTS,
+			dad->fw_self_test_format);
+
+		/* Clear the parameters so next test won't use them */
+		if (dad->fw_self_test_param_len > 0) {
+			cmd->nonhid_cmd->load_self_test_param(dad->dev,
+				PT_CORE_CMD_PROTECTED,
+				dad->fw_self_test_id, 0, 0, NULL,
+				&ret_status, &ret_self_test_id,
+				&ret_act_load_len);
+		}
+
+		dad->fw_self_test_id = PT_ST_ID_INVALID;
+		dad->fw_self_test_format = PT_PR_FORMAT_UNDEFINE;
+		dad->fw_self_test_param_len = 0;
+
+resume_scan:
+		/* Only resume scanning if we suspended it */
+		if (sys_mode == FW_SYS_MODE_SCANNING)
+			pt_resume_scan_cmd_(dad->dev);
+	}
+
+exit:
+	return simple_read_from_buffer(buf, count, ppos, data->pr_buf,
+			data->pr_buf_len);
+}
+
+/*******************************************************************************
+ * FUNCTION: fw_self_test_debugfs_write
+ *
+ * SUMMARY: Store the self test ID and output format, the read method will
+ *  perform.
+ *
+ * RETURN: Size of debugfs data write
+ *
+ * PARAMETERS:
+ *	*filp   - file pointer to debugfs file
+ *	*buf    - the user space buffer to write to
+ *	 count  - the maximum number of bytes to write
+ *	*ppos   - the current position in the buffer
+ ******************************************************************************/
+static ssize_t fw_self_test_debugfs_write(struct file *filp,
+		const char __user *buf, size_t count, loff_t *ppos)
+{
+	struct pt_device_access_debugfs_data *data = filp->private_data;
+	struct pt_device_access_data *dad = data->dad;
+	ssize_t length;
+	u32 input_data[PT_FW_SELF_TEST_MAX_PARM + 1];
+	int rc = 0;
+	int i;
+
+	rc = simple_write_to_buffer(data->pr_buf, sizeof(data->pr_buf), ppos,
+			buf, count);
+	if (rc < 0)
+		return rc;
+
+	count = rc;
+
+	mutex_lock(&dad->sysfs_lock);
+
+	length = cmd->parse_sysfs_input(dad->dev, data->pr_buf, count,
+		input_data, PT_FW_SELF_TEST_MAX_PARM + 1);
+	if (length == 1) {
+		dad->fw_self_test_id = input_data[0];
+		dad->fw_self_test_format = PT_PR_FORMAT_DEFAULT;
+		dad->fw_self_test_param_len = 0;
+	} else if (length == 2) {
+		dad->fw_self_test_id = input_data[0];
+		dad->fw_self_test_format = input_data[1];
+		dad->fw_self_test_param_len = 0;
+	} else if (length > 2 && (length <= PT_FW_SELF_TEST_MAX_PARM)) {
+		dad->fw_self_test_id = input_data[0];
+		dad->fw_self_test_format = input_data[1];
+		dad->fw_self_test_param_len = length - 2;
+		pt_debug(dad->dev, DL_INFO,
+			"%s: test_id=%d, format=%d, param_len=%d",
+			__func__, dad->fw_self_test_id,
+			dad->fw_self_test_format, dad->fw_self_test_param_len);
+		for (i = 0; i < dad->fw_self_test_param_len; i++)
+			dad->fw_self_test_param[i] = input_data[i + 2];
+	} else {
+		pt_debug(dad->dev, DL_ERROR,
+				"%s: Malformed input\n", __func__);
+		rc = -EINVAL;
+		goto exit_unlock;
+	}
+
+exit_unlock:
+	mutex_unlock(&dad->sysfs_lock);
+
+	if (rc)
+		return rc;
+
+	return count;
+}
+
+PT_DEBUGFS_FOPS(fw_self_test,
+	fw_self_test_debugfs_read, fw_self_test_debugfs_write);
+
+#ifdef TTHE_TUNER_SUPPORT
+/*******************************************************************************
+ * FUNCTION: tthe_get_panel_data_debugfs_read
+ *
+ * SUMMARY: Performs a panel scan and prints the panel data into the output
+ *  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_get_panel_data_debugfs_read(struct file *filp,
+		char __user *buf, size_t count, loff_t *ppos)
+{
+	struct pt_device_access_data *dad = filp->private_data;
+	struct device *dev;
+	struct pt_core_data *cd;
+	u8 config;
+	u16 actual_read_len;
+	u16 length = 0;
+	u8 element_size = 0;
+	u8 *buf_offset;
+	u8 *buf_out;
+	int elem;
+	int elem_offset = 0;
+	int print_idx = 0;
+	int rc;
+	int rc1;
+	int i;
+
+	mutex_lock(&dad->debugfs_lock);
+	dev = dad->dev;
+	cd = dev_get_drvdata(dev);
+	buf_out = dad->panel_scan_data_buf;
+	if (!buf_out)
+		goto release_mutex;
+
+	pm_runtime_get_sync(dev);
+
+	rc = cmd->request_exclusive(dev, PT_REQUEST_EXCLUSIVE_TIMEOUT);
+	if (rc)
+		goto put_runtime;
+
+	if (dad->heatmap.scan_start) {
+		/*
+		 * To fix CDT206291: avoid multiple scans when
+		 * return data is larger than 4096 bytes in one cycle
+		 */
+		dad->heatmap.scan_start = 0;
+
+		/* Start scan */
+		rc = pt_exec_scan_cmd_(dev, 0);
+		if (rc)
+			goto release_exclusive;
+	}
+
+	elem = dad->heatmap.num_element;
+
+#if defined(PT_ENABLE_MAX_ELEN)
+	if (elem > PT_MAX_ELEN) {
+		rc = pt_ret_scan_data_cmd_(dev, elem_offset,
+		PT_MAX_ELEN, dad->heatmap.data_type, dad->ic_buf,
+		&config, &actual_read_len, NULL);
+	} else{
+		rc = pt_ret_scan_data_cmd_(dev, elem_offset, elem,
+			dad->heatmap.data_type, dad->ic_buf, &config,
+			&actual_read_len, NULL);
+	}
+#else
+	rc = pt_ret_scan_data_cmd_(dev, elem_offset, elem,
+			dad->heatmap.data_type, dad->ic_buf, &config,
+			&actual_read_len, NULL);
+#endif
+	if (rc)
+		goto release_exclusive;
+
+	length = get_unaligned_le16(&dad->ic_buf[0]);
+	buf_offset = dad->ic_buf + length;
+
+	element_size = config & PT_CMD_RET_PANEL_ELMNT_SZ_MASK;
+
+	elem -= actual_read_len;
+	elem_offset = actual_read_len;
+	while (elem > 0) {
+#ifdef PT_ENABLE_MAX_ELEN
+		if (elem > PT_MAX_ELEN) {
+			rc = pt_ret_scan_data_cmd_(dev, elem_offset,
+			PT_MAX_ELEN, dad->heatmap.data_type, NULL, &config,
+			&actual_read_len, buf_offset);
+		} else{
+			rc = pt_ret_scan_data_cmd_(dev, elem_offset, elem,
+				dad->heatmap.data_type, NULL, &config,
+				&actual_read_len, buf_offset);
+		}
+#else
+
+		rc = pt_ret_scan_data_cmd_(dev, elem_offset, elem,
+				dad->heatmap.data_type, NULL, &config,
+				&actual_read_len, buf_offset);
+#endif
+		if (rc)
+			goto release_exclusive;
+
+		if (!actual_read_len)
+			break;
+
+		length += actual_read_len * element_size;
+		buf_offset = dad->ic_buf + length;
+		elem -= actual_read_len;
+		elem_offset += actual_read_len;
+	}
+
+	/* Reconstruct cmd header */
+	put_unaligned_le16(length, &dad->ic_buf[0]);
+	put_unaligned_le16(elem_offset, &dad->ic_buf[7]);
+
+release_exclusive:
+	rc1 = cmd->release_exclusive(dev);
+put_runtime:
+	pm_runtime_put(dev);
+
+	if (rc)
+		goto release_mutex;
+	if (cd->show_timestamp)
+		print_idx += scnprintf(buf_out, TTHE_TUNER_MAX_BUF,
+			"[%u] PT_DATA:", pt_get_time_stamp());
+	else
+		print_idx += scnprintf(buf_out, TTHE_TUNER_MAX_BUF,
+			"PT_DATA:");
+	for (i = 0; i < length; i++)
+		print_idx += scnprintf(buf_out + print_idx,
+				TTHE_TUNER_MAX_BUF - print_idx,
+				"%02X ", dad->ic_buf[i]);
+	print_idx += scnprintf(buf_out + print_idx,
+			TTHE_TUNER_MAX_BUF - print_idx,
+			":(%d bytes)\n", length);
+	rc = simple_read_from_buffer(buf, count, ppos, buf_out, print_idx);
+	print_idx = rc;
+
+release_mutex:
+	mutex_unlock(&dad->debugfs_lock);
+	return print_idx;
+}
+
+/*******************************************************************************
+ * FUNCTION: tthe_get_panel_data_debugfs_write
+ *
+ * SUMMARY: Store the panel data type to retrieve and size of panel data, the
+ *  read method will perform.
+ *
+ * RETURN: Size of debugfs data write
+ *
+ * PARAMETERS:
+ *	*filp   - file pointer to debugfs file
+ *	*buf    - the user space buffer to write to
+ *	 count  - the maximum number of bytes to write
+ *	*ppos   - the current position in the buffer
+ ******************************************************************************/
+static ssize_t tthe_get_panel_data_debugfs_write(struct file *filp,
+		const char __user *buf, size_t count, loff_t *ppos)
+{
+	struct pt_device_access_data *dad = filp->private_data;
+	struct device *dev = dad->dev;
+	ssize_t length;
+	int max_read;
+	u32 input_data[8];
+	u8 *buf_in = dad->panel_scan_data_buf;
+	int ret;
+
+	mutex_lock(&dad->debugfs_lock);
+	ret = copy_from_user(buf_in + (*ppos), buf, count);
+	if (ret)
+		goto exit;
+	buf_in[count] = 0;
+
+	length = cmd->parse_sysfs_input(dev, buf_in, count, input_data,
+			ARRAY_SIZE(input_data));
+	if (length <= 0) {
+		pt_debug(dev, DL_ERROR,
+				"%s: %s Group Data store\n",
+				__func__, "Malformed input for");
+		goto exit;
+	}
+
+	/* update parameter value */
+	dad->heatmap.num_element = input_data[3] + (input_data[4] << 8);
+	dad->heatmap.data_type = input_data[5];
+
+	if (input_data[6] > 0)
+		dad->heatmap.scan_start = true;
+	else
+		dad->heatmap.scan_start = false;
+
+	/* elem can not be bigger then buffer size */
+	max_read = PT_CMD_RET_PANEL_HDR;
+	max_read += dad->heatmap.num_element * PT_CMD_RET_PANEL_ELMNT_SZ_MAX;
+
+	if (max_read >= PT_MAX_PRBUF_SIZE) {
+		dad->heatmap.num_element =
+			(PT_MAX_PRBUF_SIZE - PT_CMD_RET_PANEL_HDR)
+			/ PT_CMD_RET_PANEL_ELMNT_SZ_MAX;
+		pt_debug(dev, DL_INFO, "%s: Will get %d element\n",
+			__func__, dad->heatmap.num_element);
+	}
+
+exit:
+	mutex_unlock(&dad->debugfs_lock);
+	pt_debug(dev, DL_INFO, "%s: return count=%zu\n",
+		__func__, count);
+	return count;
+}
+
+/*******************************************************************************
+ * FUNCTION: tthe_get_panel_data_debugfs_open
+ *
+ * SUMMARY: Open the get_panel_data debugfs node to initialize.
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *  *inode - pointer to inode structure
+ *  *filp  - pointer to file structure
+ ******************************************************************************/
+static int tthe_get_panel_data_debugfs_open(struct inode *inode,
+		struct file *filp)
+{
+	struct pt_device_access_data *dad = inode->i_private;
+
+	mutex_lock(&dad->debugfs_lock);
+
+	if (dad->tthe_get_panel_data_is_open) {
+		mutex_unlock(&dad->debugfs_lock);
+		return -EBUSY;
+	}
+
+	filp->private_data = inode->i_private;
+
+	dad->tthe_get_panel_data_is_open = 1;
+	mutex_unlock(&dad->debugfs_lock);
+	return 0;
+}
+
+/*******************************************************************************
+ * FUNCTION: tthe_get_panel_data_debugfs_close
+ *
+ * SUMMARY: Close the get_panel_data debugfs node to free pointer.
+ *
+ * RETURN:
+ *  0 = success
+ * !0 = failure
+ *
+ * PARAMETERS:
+ *	*inode - pointer to inode structure
+ *	*filp  - pointer to file structure
+ ******************************************************************************/
+static int tthe_get_panel_data_debugfs_close(struct inode *inode,
+		struct file *filp)
+{
+	struct pt_device_access_data *dad = filp->private_data;
+
+	mutex_lock(&dad->debugfs_lock);
+	filp->private_data = NULL;
+	dad->tthe_get_panel_data_is_open = 0;
+	mutex_unlock(&dad->debugfs_lock);
+
+	return 0;
+}
+
+static const struct file_operations tthe_get_panel_data_fops = {
+	.open = tthe_get_panel_data_debugfs_open,
+	.release = tthe_get_panel_data_debugfs_close,
+	.read = tthe_get_panel_data_debugfs_read,
+	.write = tthe_get_panel_data_debugfs_write,
+};
+#endif
+
+/*******************************************************************************
+ * FUNCTION: pt_setup_sysfs
+ *
+ * SUMMARY: Creates all device test dependent sysfs nodes owned by the
+ *  device access file.
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *	*dev  - pointer to device structure
+ ******************************************************************************/
+static int pt_setup_sysfs(struct device *dev)
+{
+	struct pt_device_access_data *dad
+		= pt_get_device_access_data(dev);
+	int rc = 0;
+
+	pt_debug(dev, DL_INFO, "Entering %s\n", __func__);
+
+	dad->base_dentry = debugfs_create_dir(dev_name(dev), NULL);
+	if (IS_ERR_OR_NULL(dad->base_dentry)) {
+		pt_debug(dev, DL_ERROR,
+			"%s: Error, could not create base directory\n",
+			__func__);
+		goto exit;
+	}
+
+	dad->mfg_test_dentry = debugfs_create_dir("mfg_test",
+			dad->base_dentry);
+	if (IS_ERR_OR_NULL(dad->mfg_test_dentry)) {
+		pt_debug(dev, DL_ERROR,
+			"%s: Error, could not create mfg_test directory\n",
+			__func__);
+		goto unregister_base_dir;
+	}
+
+	dad->panel_scan_debugfs = debugfs_create_file(
+		"panel_scan", 0644, dad->mfg_test_dentry, dad,
+		&panel_scan_fops);
+	if (IS_ERR_OR_NULL(dad->panel_scan_debugfs)) {
+		pt_debug(dev, DL_ERROR,
+			"%s: Error, could not create panel_scan\n",
+			__func__);
+		goto unregister_base_dir;
+	}
+
+	if (IS_ERR_OR_NULL(debugfs_create_file("get_idac", 0600,
+			dad->mfg_test_dentry, dad, &get_idac_debugfs_fops))) {
+		pt_debug(dev, DL_ERROR,
+			"%s: Error, could not create get_idac\n",
+			__func__);
+		goto unregister_base_dir;
+	}
+
+	if (IS_ERR_OR_NULL(debugfs_create_file("auto_shorts", 0400,
+			dad->mfg_test_dentry, dad,
+			&auto_shorts_debugfs_fops))) {
+		pt_debug(dev, DL_ERROR,
+			"%s: Error, could not create auto_shorts\n",
+			__func__);
+		goto unregister_base_dir;
+	}
+
+	if (IS_ERR_OR_NULL(debugfs_create_file("opens", 0400,
+			dad->mfg_test_dentry, dad, &opens_debugfs_fops))) {
+		pt_debug(dev, DL_ERROR,
+			"%s: Error, could not create opens\n",
+			__func__);
+		goto unregister_base_dir;
+	}
+
+	if (IS_ERR_OR_NULL(debugfs_create_file("calibrate_ext",
+			0600, dad->mfg_test_dentry,
+			dad, &calibrate_ext_debugfs_fops))) {
+		pt_debug(dev, DL_ERROR,
+			"%s: Error, could not create calibrate_ext\n",
+			__func__);
+		goto unregister_base_dir;
+	}
+
+	if (IS_ERR_OR_NULL(debugfs_create_file("calibrate", 0600,
+			dad->mfg_test_dentry, dad, &calibrate_debugfs_fops))) {
+		pt_debug(dev, DL_ERROR,
+			"%s: Error, could not create calibrate\n",
+			__func__);
+		goto unregister_base_dir;
+	}
+
+	if (IS_ERR_OR_NULL(debugfs_create_file("baseline", 0600,
+			dad->mfg_test_dentry, dad, &baseline_debugfs_fops))) {
+		pt_debug(dev, DL_ERROR,
+			"%s: Error, could not create baseline\n",
+			__func__);
+		goto unregister_base_dir;
+	}
+
+	if (IS_ERR_OR_NULL(debugfs_create_file("cm_panel", 0400,
+			dad->mfg_test_dentry, dad, &cm_panel_debugfs_fops))) {
+		pt_debug(dev, DL_ERROR,
+			"%s: Error, could not create cm_panel\n",
+			__func__);
+		goto unregister_base_dir;
+	}
+
+	if (IS_ERR_OR_NULL(debugfs_create_file("cp_panel", 0400,
+			dad->mfg_test_dentry, dad, &cp_panel_debugfs_fops))) {
+		pt_debug(dev, DL_ERROR,
+			"%s: Error, could not create cp_panel\n",
+			__func__);
+		goto unregister_base_dir;
+	}
+
+	if (IS_ERR_OR_NULL(debugfs_create_file("cm_button", 0400,
+			dad->mfg_test_dentry, dad, &cm_button_debugfs_fops))) {
+		pt_debug(dev, DL_ERROR,
+			"%s: Error, could not create cm_button\n",
+			__func__);
+		goto unregister_base_dir;
+	}
+
+	if (IS_ERR_OR_NULL(debugfs_create_file("cp_button", 0400,
+			dad->mfg_test_dentry, dad, &cp_button_debugfs_fops))) {
+		pt_debug(dev, DL_ERROR,
+			"%s: Error, could not create cp_button\n",
+			__func__);
+		goto unregister_base_dir;
+	}
+
+	if (IS_ERR_OR_NULL(debugfs_create_file("fw_self_test", 0600,
+		dad->mfg_test_dentry, dad, &fw_self_test_debugfs_fops))) {
+		pt_debug(dev, DL_ERROR,
+			"%s: Error, could not create fw_self_test\n",
+			__func__);
+		goto unregister_base_dir;
+	}
+
+	dad->cmcp_results_debugfs = debugfs_create_file("cmcp_results", 0644,
+		dad->mfg_test_dentry, dad, &cmcp_results_debugfs_fops);
+	if (IS_ERR_OR_NULL(dad->cmcp_results_debugfs)) {
+		pt_debug(dev, DL_ERROR,
+			"%s: Error, could not create cmcp_results\n",
+			__func__);
+		dad->cmcp_results_debugfs = NULL;
+		goto unregister_base_dir;
+	}
+
+#ifdef TTHE_TUNER_SUPPORT
+	dad->tthe_get_panel_data_debugfs = debugfs_create_file(
+			PT_TTHE_TUNER_GET_PANEL_DATA_FILE_NAME,
+			0644, NULL, dad, &tthe_get_panel_data_fops);
+	if (IS_ERR_OR_NULL(dad->tthe_get_panel_data_debugfs)) {
+		pt_debug(dev, DL_ERROR,
+			"%s: Error, could not create get_panel_data\n",
+			__func__);
+		dad->tthe_get_panel_data_debugfs = NULL;
+		goto unregister_base_dir;
+	}
+#endif
+
+	rc = device_create_file(dev, &dev_attr_cmcp_test);
+	if (rc) {
+		pt_debug(dev, DL_ERROR,
+			"%s: Error, could not create cmcp_test\n",
+			__func__);
+		goto unregister_base_dir;
+	}
+
+	rc = device_create_file(dev, &dev_attr_cmcp_threshold_loading);
+	if (rc) {
+		pt_debug(dev, DL_ERROR,
+			"%s: Error, could not create cmcp_thresold_loading\n",
+			__func__);
+		goto unregister_cmcp_test;
+	}
+
+	rc = device_create_bin_file(dev, &bin_attr_cmcp_threshold_data);
+	if (rc) {
+		pt_debug(dev, DL_ERROR,
+			"%s: Error, could not create cmcp_thresold_data\n",
+			__func__);
+		goto unregister_cmcp_thresold_loading;
+	}
+
+	dad->sysfs_nodes_created = true;
+	return rc;
+
+unregister_cmcp_thresold_loading:
+	device_remove_file(dev, &dev_attr_cmcp_threshold_loading);
+unregister_cmcp_test:
+	device_remove_file(dev, &dev_attr_cmcp_test);
+unregister_base_dir:
+	debugfs_remove_recursive(dad->base_dentry);
+exit:
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_setup_cmcp_attention
+ *
+ * SUMMARY: Function to be registered to TTDL attention list to setup sysfs and
+ *  parse threshold file for device test.
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *	*dev  - pointer to device structure
+ ******************************************************************************/
+static int pt_setup_cmcp_attention(struct device *dev)
+{
+	struct pt_device_access_data *dad
+		= pt_get_device_access_data(dev);
+	int rc = 0;
+
+	dad->si = cmd->request_sysinfo(dev);
+	if (!dad->si)
+		return -EINVAL;
+
+	rc = pt_setup_sysfs(dev);
+	schedule_work(&dad->cmcp_threshold_update);
+
+	cmd->unsubscribe_attention(dev, PT_ATTEN_STARTUP,
+		PT_DEVICE_ACCESS_NAME, pt_setup_cmcp_attention,
+		0);
+
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_cmcp_parse_threshold_file
+ *
+ * SUMMARY: Parse cmcp threshold file and store it to the structure.
+ *
+ * PARAMETERS:
+ *	*fw       - pointer to firmware structure
+ *	*context  - expected read length of the response
+ ******************************************************************************/
+static void pt_cmcp_parse_threshold_file(const struct firmware *fw,
+		void *context)
+{
+	struct device *dev = context;
+	struct pt_device_access_data *dad =
+		pt_get_device_access_data(dev);
+
+	if (!fw) {
+		pt_debug(dev, DL_WARN,
+				"%s: No builtin cmcp threshold file\n",
+				__func__);
+		goto exit;
+	}
+
+	if (!fw->data || !fw->size) {
+		pt_debug(dev, DL_ERROR,
+				"%s: Invalid builtin cmcp threshold file\n",
+		__func__);
+		goto exit;
+	}
+
+	pt_debug(dev, DL_WARN, "%s: Found cmcp threshold file.\n",
+		__func__);
+
+	pt_parse_cmcp_threshold_file_common(dev, &fw->data[0], fw->size);
+
+	dad->builtin_cmcp_threshold_status = 0;
+	release_firmware(fw);
+	return;
+
+exit:
+	if (fw)
+		release_firmware(fw);
+
+	dad->builtin_cmcp_threshold_status = -EINVAL;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_device_access_user_command
+ *
+ * SUMMARY: Wrapper function to call pt_cmcp_parse_threshold_file() by firmware
+ *  class function.
+ *
+ * PARAMETERS:
+ *	*cmcp_threshold_update - pointer to work_struct structure
+ ******************************************************************************/
+static void pt_parse_cmcp_threshold_builtin(
+	struct work_struct *cmcp_threshold_update)
+{
+	struct pt_device_access_data *dad =
+		container_of(cmcp_threshold_update,
+		struct pt_device_access_data,
+		cmcp_threshold_update);
+	struct device *dev = dad->dev;
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+	const struct firmware *fw_entry = NULL;
+	int retval;
+
+	dad->si = cmd->request_sysinfo(dev);
+	if (!dad->si) {
+		pt_debug(dev, DL_ERROR,
+			"%s: Fail get sysinfo pointer from core\n",
+			__func__);
+		return;
+	}
+
+	pt_debug(dev, DL_INFO,
+		"%s: Enabling cmcp threshold class loader built-in\n",
+		__func__);
+
+	/* Open threshold file */
+	mutex_lock(&cd->firmware_class_lock);
+#if (KERNEL_VERSION(3, 13, 0) > LINUX_VERSION_CODE)
+	retval = request_firmware(&fw_entry, CMCP_THRESHOLD_FILE_NAME, dev);
+#else
+	retval = request_firmware_direct(&fw_entry,
+				CMCP_THRESHOLD_FILE_NAME, dev);
+#endif
+	if (retval < 0) {
+		pt_debug(dev, DL_WARN,
+			"%s: Failed loading cmcp threshold file, attempting legacy file\n",
+			__func__);
+		/* Try legacy file name */
+#if (KERNEL_VERSION(3, 13, 0) > LINUX_VERSION_CODE)
+	retval = request_firmware(&fw_entry,
+				PT_CMCP_THRESHOLD_FILE_NAME, dev);
+#else
+	retval = request_firmware_direct(&fw_entry,
+				PT_CMCP_THRESHOLD_FILE_NAME, dev);
+#endif
+		if (retval < 0) {
+			mutex_unlock(&cd->firmware_class_lock);
+			dad->builtin_cmcp_threshold_status = -EINVAL;
+			pt_debug(dev, DL_WARN,
+				"%s: Fail request cmcp threshold class file load\n",
+				__func__);
+			goto exit;
+		}
+	}
+	pt_cmcp_parse_threshold_file(fw_entry, dev);
+
+	mutex_unlock(&cd->firmware_class_lock);
+
+exit:
+	return;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_device_access_probe
+ *
+ * SUMMARY: The probe function for the device access
+ *
+ * PARAMETERS:
+ *	*dev   - pointer to device structure
+ *	**data - double pointer to pt_device_access_data data to be created here
+ ******************************************************************************/
+static int pt_device_access_probe(struct device *dev, void **data)
+{
+	struct pt_device_access_data *dad;
+	struct configuration *configurations;
+	struct cmcp_data *cmcp_info;
+	struct result *result;
+
+	int tx_num = MAX_TX_SENSORS;
+	int rx_num = MAX_RX_SENSORS;
+	int btn_num = MAX_BUTTONS;
+
+	struct test_case_field *test_case_field_array;
+	struct test_case_search *test_case_search_array;
+	int rc = 0;
+
+	dad = kzalloc(sizeof(*dad), GFP_KERNEL);
+	if (!dad) {
+		rc = -ENOMEM;
+		goto pt_device_access_probe_data_failed;
+	}
+
+	configurations =
+		kzalloc(sizeof(*configurations), GFP_KERNEL);
+	if (!configurations) {
+		rc = -ENOMEM;
+		goto pt_device_access_probe_configs_failed;
+	}
+	dad->configs = configurations;
+
+	cmcp_info = kzalloc(sizeof(*cmcp_info), GFP_KERNEL);
+	if (!cmcp_info) {
+		rc = -ENOMEM;
+		goto pt_device_access_probe_cmcp_info_failed;
+	}
+	dad->cmcp_info = cmcp_info;
+
+	cmcp_info->tx_num = tx_num;
+	cmcp_info->rx_num = rx_num;
+	cmcp_info->btn_num = btn_num;
+
+	result = kzalloc(sizeof(*result), GFP_KERNEL);
+	if (!result) {
+		rc = -ENOMEM;
+		goto pt_device_access_probe_result_failed;
+	}
+	dad->result = result;
+
+	test_case_field_array =
+		kzalloc(sizeof(*test_case_field_array) * MAX_CASE_NUM,
+		GFP_KERNEL);
+	if (!test_case_field_array) {
+		rc = -ENOMEM;
+		goto pt_device_access_probe_field_array_failed;
+	}
+
+	test_case_search_array =
+		kzalloc(sizeof(*test_case_search_array) * MAX_CASE_NUM,
+		GFP_KERNEL);
+	if (!test_case_search_array) {
+		rc = -ENOMEM;
+		goto pt_device_access_probe_search_array_failed;
+	}
+
+	cmcp_info->gd_sensor_col = (struct gd_sensor *)
+		kzalloc(tx_num * sizeof(struct gd_sensor), GFP_KERNEL);
+	if (cmcp_info->gd_sensor_col == NULL)
+		goto pt_device_access_probe_gd_sensor_col_failed;
+
+	cmcp_info->gd_sensor_row = (struct gd_sensor *)
+		kzalloc(rx_num * sizeof(struct gd_sensor), GFP_KERNEL);
+	if (cmcp_info->gd_sensor_row == NULL)
+		goto pt_device_access_probe_gd_sensor_row_failed;
+
+	cmcp_info->cm_data_panel =
+		kzalloc((tx_num * rx_num + 1) * sizeof(int32_t), GFP_KERNEL);
+	if (cmcp_info->cm_data_panel == NULL)
+		goto pt_device_access_probe_cm_data_panel_failed;
+
+	cmcp_info->cp_tx_data_panel =
+		kzalloc(tx_num * sizeof(int32_t), GFP_KERNEL);
+	if (cmcp_info->cp_tx_data_panel == NULL)
+		goto pt_device_access_probe_cp_tx_data_panel_failed;
+
+	cmcp_info->cp_tx_cal_data_panel =
+		kzalloc(tx_num * sizeof(int32_t), GFP_KERNEL);
+	if (cmcp_info->cp_tx_cal_data_panel == NULL)
+		goto pt_device_access_probe_cp_tx_cal_data_panel_failed;
+
+	cmcp_info->cp_rx_data_panel =
+		kzalloc(rx_num * sizeof(int32_t), GFP_KERNEL);
+	if (cmcp_info->cp_rx_data_panel == NULL)
+		goto pt_device_access_probe_cp_rx_data_panel_failed;
+
+	cmcp_info->cp_rx_cal_data_panel =
+		kzalloc(rx_num * sizeof(int32_t), GFP_KERNEL);
+	if (cmcp_info->cp_rx_cal_data_panel == NULL)
+		goto pt_device_access_probe_cp_rx_cal_data_panel_failed;
+
+	cmcp_info->cm_btn_data = kcalloc(btn_num, sizeof(int32_t), GFP_KERNEL);
+	if (cmcp_info->cm_btn_data == NULL)
+		goto pt_device_access_probe_cm_btn_data_failed;
+
+	cmcp_info->cp_btn_data = kcalloc(btn_num, sizeof(int32_t), GFP_KERNEL);
+	if (cmcp_info->cp_btn_data == NULL)
+		goto pt_device_access_probe_cp_btn_data_failed;
+
+	cmcp_info->cm_sensor_column_delta =
+		 kzalloc(rx_num * tx_num * sizeof(int32_t), GFP_KERNEL);
+	if (cmcp_info->cm_sensor_column_delta == NULL)
+		goto pt_device_access_probe_cm_sensor_column_delta_failed;
+
+	cmcp_info->cm_sensor_row_delta =
+		 kzalloc(tx_num * rx_num  * sizeof(int32_t), GFP_KERNEL);
+	if (cmcp_info->cm_sensor_row_delta == NULL)
+		goto pt_device_access_probe_cm_sensor_row_delta_failed;
+
+	mutex_init(&dad->sysfs_lock);
+	mutex_init(&dad->cmcp_threshold_lock);
+	dad->dev = dev;
+#ifdef TTHE_TUNER_SUPPORT
+	mutex_init(&dad->debugfs_lock);
+	dad->heatmap.num_element = 200;
+#endif
+	*data = dad;
+
+	dad->test_field_array = test_case_field_array;
+	dad->test_search_array = test_case_search_array;
+	dad->test_executed = 0;
+
+	dad->cal_ext_data.mode = PT_CAL_EXT_MODE_UNDEFINED;
+	dad->panel_scan_retrieve_id = 0;
+	dad->panel_scan_type_id = 0;
+
+	INIT_WORK(&dad->cmcp_threshold_update, pt_parse_cmcp_threshold_builtin);
+
+	/* get sysinfo */
+	dad->si = cmd->request_sysinfo(dev);
+	if (dad->si) {
+		rc = pt_setup_sysfs(dev);
+		if (rc)
+			goto pt_device_access_setup_sysfs_failed;
+	} else {
+		pt_debug(dev, DL_ERROR,
+				"%s: Fail get sysinfo pointer from core p=%p\n",
+				__func__, dad->si);
+		cmd->subscribe_attention(dev, PT_ATTEN_STARTUP,
+			PT_DEVICE_ACCESS_NAME,
+			pt_setup_cmcp_attention, 0);
+	}
+
+	schedule_work(&dad->cmcp_threshold_update);
+
+	return 0;
+
+pt_device_access_setup_sysfs_failed:
+	kfree(cmcp_info->cm_sensor_row_delta);
+pt_device_access_probe_cm_sensor_row_delta_failed:
+	kfree(cmcp_info->cm_sensor_column_delta);
+pt_device_access_probe_cm_sensor_column_delta_failed:
+	kfree(cmcp_info->cp_btn_data);
+pt_device_access_probe_cp_btn_data_failed:
+	kfree(cmcp_info->cm_btn_data);
+pt_device_access_probe_cm_btn_data_failed:
+	kfree(cmcp_info->cp_rx_cal_data_panel);
+pt_device_access_probe_cp_rx_cal_data_panel_failed:
+	kfree(cmcp_info->cp_rx_data_panel);
+pt_device_access_probe_cp_rx_data_panel_failed:
+	kfree(cmcp_info->cp_tx_cal_data_panel);
+pt_device_access_probe_cp_tx_cal_data_panel_failed:
+	kfree(cmcp_info->cp_tx_data_panel);
+pt_device_access_probe_cp_tx_data_panel_failed:
+	kfree(cmcp_info->cm_data_panel);
+pt_device_access_probe_cm_data_panel_failed:
+	kfree(cmcp_info->gd_sensor_row);
+pt_device_access_probe_gd_sensor_row_failed:
+	kfree(cmcp_info->gd_sensor_col);
+pt_device_access_probe_gd_sensor_col_failed:
+	kfree(test_case_search_array);
+pt_device_access_probe_search_array_failed:
+	kfree(test_case_field_array);
+pt_device_access_probe_field_array_failed:
+	kfree(result);
+pt_device_access_probe_result_failed:
+	kfree(cmcp_info);
+pt_device_access_probe_cmcp_info_failed:
+	kfree(configurations);
+pt_device_access_probe_configs_failed:
+	kfree(dad);
+pt_device_access_probe_data_failed:
+	pt_debug(dev, DL_ERROR, "%s failed.\n", __func__);
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_device_access_release
+ *
+ * SUMMARY: Remove function for device_access module that does following
+ *  cleanup:
+ *	- Unsubscibe all registered attention tasks
+ *	- Removes all created sysfs nodes
+ *	- Frees all pointers
+ *
+ * PARAMETERS:
+ *	*dev   - pointer to device structure
+ *	*data  - pointer to the device_access data
+ ******************************************************************************/
+static void pt_device_access_release(struct device *dev, void *data)
+{
+	struct pt_device_access_data *dad = data;
+
+	if (dad->sysfs_nodes_created) {
+		debugfs_remove(dad->cmcp_results_debugfs);
+		debugfs_remove_recursive(dad->base_dentry);
+#ifdef TTHE_TUNER_SUPPORT
+		debugfs_remove(dad->tthe_get_panel_data_debugfs);
+#endif
+		device_remove_file(dev, &dev_attr_cmcp_test);
+		device_remove_file(dev, &dev_attr_cmcp_threshold_loading);
+		device_remove_bin_file(dev, &bin_attr_cmcp_threshold_data);
+		kfree(dad->cmcp_threshold_data);
+	} else {
+		cmd->unsubscribe_attention(dev, PT_ATTEN_STARTUP,
+			PT_DEVICE_ACCESS_NAME,
+			pt_setup_cmcp_attention, 0);
+	}
+
+	kfree(dad->test_search_array);
+	kfree(dad->test_field_array);
+	kfree(dad->configs);
+	pt_free_cmcp_buf(dad->cmcp_info);
+	kfree(dad->cmcp_info);
+	kfree(dad->result);
+	kfree(dad);
+}
+
+static struct pt_module device_access_module = {
+	.name = PT_DEVICE_ACCESS_NAME,
+	.probe = pt_device_access_probe,
+	.release = pt_device_access_release,
+};
+
+/*******************************************************************************
+ * FUNCTION: pt_device_access_init
+ *
+ * SUMMARY: Initialize function for device access module which to register
+ * device_access_module into TTDL module list.
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ ******************************************************************************/
+static int __init pt_device_access_init(void)
+{
+	int rc;
+
+	cmd = pt_get_commands();
+	if (!cmd)
+		return -EINVAL;
+
+	rc = pt_register_module(&device_access_module);
+	if (rc) {
+		pr_err("%s: Error, failed registering module\n",
+			__func__);
+			return rc;
+	}
+
+	pr_info("%s: Parade TTSP Device Access Driver (Built %s) rc = %d\n",
+		 __func__, PT_DRIVER_VERSION, rc);
+	return 0;
+}
+module_init(pt_device_access_init);
+
+/*******************************************************************************
+ * FUNCTION: pt_device_access_exit
+ *
+ * SUMMARY: Exit function for device access module which to unregister
+ * device_access_module from TTDL module list.
+ *
+ ******************************************************************************/
+static void __exit pt_device_access_exit(void)
+{
+	pt_unregister_module(&device_access_module);
+}
+module_exit(pt_device_access_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Parade TrueTouch(R) Standard Product Device Access Driver");
+MODULE_AUTHOR("Parade Technologies <[email protected]>");
+#endif /* !TTDL_KERNEL_SUBMISSION */

+ 1128 - 0
pt/pt_devtree.c

@@ -0,0 +1,1128 @@
+/*
+ * pt_devtree.c
+ * Parade TrueTouch(TM) Standard Product Device Tree Support 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 <[email protected]>
+ */
+
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/of_device.h>
+#include <linux/slab.h>
+#include "pt_regs.h"
+#include <linux/pt_platform.h>
+
+#define MAX_NAME_LENGTH		64
+
+static bool is_create_and_get_pdata;
+
+enum pt_device_type {
+	DEVICE_MT,
+	DEVICE_BTN,
+	DEVICE_PEN,
+	DEVICE_PROXIMITY,
+	DEVICE_TYPE_MAX,
+};
+
+struct pt_device_pdata_func {
+	void * (*create_and_get_pdata)(struct device_node *dn);
+	void (*free_pdata)(void *ptr);
+};
+
+struct pt_pdata_ptr {
+	void **pdata;
+};
+
+#ifdef ENABLE_VIRTUAL_KEYS
+static struct kobject *board_properties_kobj;
+
+struct pt_virtual_keys {
+	struct kobj_attribute kobj_attr;
+	u16 *data;
+	int size;
+};
+#endif
+
+struct pt_extended_mt_platform_data {
+	struct pt_mt_platform_data pdata;
+#ifdef ENABLE_VIRTUAL_KEYS
+	struct pt_virtual_keys vkeys;
+#endif
+};
+
+/*******************************************************************************
+ * FUNCTION: get_inp_dev_name
+ *
+ * SUMMARY: Get the name of input device from dts.
+ *
+ * RETURN:
+ *   0 = success
+ *  !0 = failure
+ *
+ * PARAMETERS:
+ *   *dev_node      - pointer to device_node structure
+ *  **inp_dev_name  - double pointer to the name of input device
+ ******************************************************************************/
+static inline int get_inp_dev_name(struct device_node *dev_node,
+		const char **inp_dev_name)
+{
+	return of_property_read_string(dev_node, "parade,inp_dev_name",
+			inp_dev_name);
+}
+
+/*******************************************************************************
+ * FUNCTION: create_and_get_u16_array
+ *
+ * SUMMARY: Create and get u16 array from dts.
+ *
+ * RETURN:
+ *   success: the pointer of the created array
+ *   fail   : error code with type of error pointer
+ *
+ * PARAMETERS:
+ *   *dev_node - pointer to device_node structure
+ *   *name     - name of device node
+ *    size     - number of u16 array elements
+ ******************************************************************************/
+static s16 *create_and_get_u16_array(struct device_node *dev_node,
+		const char *name, int *size)
+{
+	const __be32 *values;
+	s16 *val_array;
+	int len;
+	int sz;
+	int rc;
+	int i;
+
+	values = of_get_property(dev_node, name, &len);
+	if (values == NULL)
+		return NULL;
+
+	sz = len / sizeof(u32);
+	pr_debug("%s: %s size:%d\n", __func__, name, sz);
+
+	val_array = kcalloc(sz, sizeof(s16), GFP_KERNEL);
+	if (!val_array) {
+		rc = -ENOMEM;
+		goto fail;
+	}
+
+	for (i = 0; i < sz; i++)
+		val_array[i] = (s16)be32_to_cpup(values++);
+
+	*size = sz;
+
+	return val_array;
+
+fail:
+	return ERR_PTR(rc);
+}
+
+/*******************************************************************************
+ * FUNCTION: create_and_get_touch_framework
+ *
+ * SUMMARY: Create and get touch framework structure from dts.
+ *
+ * RETURN:
+ *   success: the pointer of the touch framework data
+ *   fail   : error code with type of error pointer
+ *
+ * PARAMETERS:
+ *   *dev_node - pointer to device_node structure
+ ******************************************************************************/
+static struct touch_framework *create_and_get_touch_framework(
+		struct device_node *dev_node)
+{
+	struct touch_framework *frmwrk;
+	s16 *abs;
+	int size;
+	int rc;
+
+	abs = create_and_get_u16_array(dev_node, "parade,abs", &size);
+	if (IS_ERR_OR_NULL(abs))
+		return (void *)abs;
+
+	/* Check for valid abs size */
+	if (size % PT_NUM_ABS_SET) {
+		rc = -EINVAL;
+		goto fail_free_abs;
+	}
+
+	frmwrk = kzalloc(sizeof(*frmwrk), GFP_KERNEL);
+	if (!frmwrk) {
+		rc = -ENOMEM;
+		goto fail_free_abs;
+	}
+
+	frmwrk->abs = abs;
+	frmwrk->size = size;
+
+	return frmwrk;
+
+fail_free_abs:
+	kfree(abs);
+
+	return ERR_PTR(rc);
+}
+
+/*******************************************************************************
+ * FUNCTION: free_touch_framework
+ *
+ * SUMMARY: Free all the pointer of touch framework structure.
+ *
+ * PARAMETERS:
+ *   *frmwrk - pointer to touch framework structure
+ ******************************************************************************/
+static void free_touch_framework(struct touch_framework *frmwrk)
+{
+	kfree(frmwrk->abs);
+	kfree(frmwrk);
+}
+
+#ifdef ENABLE_VIRTUAL_KEYS
+#define VIRTUAL_KEY_ELEMENT_SIZE	5
+/*******************************************************************************
+ * FUNCTION: virtual_keys_show
+ *
+ * SUMMARY: Show method for the board_properties sysfs node that will show the
+ *  information for all virtual keys
+ *
+ * RETURN: size of data written to sysfs node
+ *
+ * PARAMETERS:
+ *	*kobj  - pointer to kobject structure
+ *	*attr  - pointer to kobject attributes
+ *	*buf   - pointer to print output buffer
+ ******************************************************************************/
+static ssize_t virtual_keys_show(struct kobject *kobj,
+		struct kobj_attribute *attr, char *buf)
+{
+	struct pt_virtual_keys *vkeys = container_of(attr,
+		struct pt_virtual_keys, kobj_attr);
+	u16 *data = vkeys->data;
+	int size = vkeys->size;
+	int index;
+	int i;
+
+	index = 0;
+	for (i = 0; i < size; i += VIRTUAL_KEY_ELEMENT_SIZE)
+		index += scnprintf(buf + index, PT_MAX_PRBUF_SIZE - index,
+			"0x01:%d:%d:%d:%d:%d\n",
+			data[i], data[i+1], data[i+2], data[i+3], data[i+4]);
+
+	return index;
+}
+
+/*******************************************************************************
+ * FUNCTION: setup_virtual_keys
+ *
+ * SUMMARY: Create virtual key data array from dts and set up dynamic sysfs for
+ *  board_properties node.
+ *
+ * RETURN:
+ *   0 = success
+ *  !0 = failure
+ *
+ * PARAMETERS:
+ *   *dev_node      - pointer to device_node structure
+ *  **inp_dev_name  - double pointer to the name of input device
+ *   *vkeys         - pointer to virtual key structure
+ ******************************************************************************/
+static int setup_virtual_keys(struct device_node *dev_node,
+		const char *inp_dev_name, struct pt_virtual_keys *vkeys)
+{
+	char *name;
+	u16 *data;
+	int size;
+	int rc;
+
+	data = create_and_get_u16_array(dev_node, "parade,virtual_keys", &size);
+	if (data == NULL)
+		return 0;
+	else if (IS_ERR(data)) {
+		rc = PTR_ERR(data);
+		goto fail;
+	}
+
+	/* Check for valid virtual keys size */
+	if (size % VIRTUAL_KEY_ELEMENT_SIZE) {
+		rc = -EINVAL;
+		goto fail_free_data;
+	}
+
+	name = kzalloc(MAX_NAME_LENGTH, GFP_KERNEL);
+	if (!name) {
+		rc = -ENOMEM;
+		goto fail_free_data;
+	}
+
+	snprintf(name, MAX_NAME_LENGTH, "virtualkeys.%s", inp_dev_name);
+
+	vkeys->data = data;
+	vkeys->size = size;
+
+	/* TODO: Instantiate in board file and export it */
+	if (board_properties_kobj == NULL)
+		board_properties_kobj =
+			kobject_create_and_add("board_properties", NULL);
+	if (board_properties_kobj == NULL) {
+		pr_err("%s: Cannot get board_properties kobject!\n", __func__);
+		rc = -EINVAL;
+		goto fail_free_name;
+	}
+
+	/* Initialize dynamic SysFs attribute */
+	sysfs_attr_init(&vkeys->kobj_attr.attr);
+	vkeys->kobj_attr.attr.name = name;
+	vkeys->kobj_attr.attr.mode = 0444;
+	vkeys->kobj_attr.show = virtual_keys_show;
+
+	rc = sysfs_create_file(board_properties_kobj, &vkeys->kobj_attr.attr);
+	if (rc)
+		goto fail_del_kobj;
+
+	return 0;
+
+fail_del_kobj:
+	kobject_del(board_properties_kobj);
+fail_free_name:
+	kfree(name);
+	vkeys->kobj_attr.attr.name = NULL;
+fail_free_data:
+	kfree(data);
+	vkeys->data = NULL;
+fail:
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: free_virtual_keys
+ *
+ * SUMMARY: Remove board_properties node and free all pointers.
+ *
+ * PARAMETERS:
+ *   *vkeys  - pointer to virtual key structure
+ ******************************************************************************/
+static void free_virtual_keys(struct pt_virtual_keys *vkeys)
+{
+	if (board_properties_kobj)
+		sysfs_remove_file(board_properties_kobj,
+			&vkeys->kobj_attr.attr);
+
+
+	kobject_del(board_properties_kobj);
+	board_properties_kobj = NULL;
+
+	kfree(vkeys->data);
+	kfree(vkeys->kobj_attr.attr.name);
+}
+#endif
+
+/*******************************************************************************
+ * FUNCTION: create_and_get_mt_pdata
+ *
+ * SUMMARY: Create and get touch platform data from dts.Touch framework and
+ *  virtual keys are set up in this function.
+ *
+ * RETURN:
+ *   success: the pointer of the platform data
+ *   fail   : error code with type of error pointer
+ *
+ * PARAMETERS:
+ *   *dev_node - pointer to device_node structure
+ ******************************************************************************/
+static void *create_and_get_mt_pdata(struct device_node *dev_node)
+{
+	struct pt_extended_mt_platform_data *ext_pdata;
+	struct pt_mt_platform_data *pdata;
+	u32 value;
+	int rc;
+
+	ext_pdata = kzalloc(sizeof(*ext_pdata), GFP_KERNEL);
+	if (!ext_pdata) {
+		rc = -ENOMEM;
+		goto fail;
+	}
+
+	pdata = &ext_pdata->pdata;
+
+	rc = get_inp_dev_name(dev_node, &pdata->inp_dev_name);
+	if (rc)
+		goto fail_free_pdata;
+
+	/* Optional fields */
+	rc = of_property_read_u32(dev_node, "parade,flags", &value);
+	if (!rc)
+		pdata->flags = value;
+
+	rc = of_property_read_u32(dev_node, "parade,vkeys_x", &value);
+	if (!rc)
+		pdata->vkeys_x = value;
+
+	rc = of_property_read_u32(dev_node, "parade,vkeys_y", &value);
+	if (!rc)
+		pdata->vkeys_y = value;
+
+	/* Required fields */
+	pdata->frmwrk = create_and_get_touch_framework(dev_node);
+	if (pdata->frmwrk == NULL) {
+		rc = -EINVAL;
+		goto fail_free_pdata;
+	} else if (IS_ERR(pdata->frmwrk)) {
+		rc = PTR_ERR(pdata->frmwrk);
+		goto fail_free_pdata;
+	}
+#ifdef ENABLE_VIRTUAL_KEYS
+	rc = setup_virtual_keys(dev_node, pdata->inp_dev_name,
+			&ext_pdata->vkeys);
+	if (rc) {
+		pr_err("%s: Cannot setup virtual keys!\n", __func__);
+		goto fail_free_pdata;
+	}
+#endif
+	return pdata;
+
+fail_free_pdata:
+	kfree(ext_pdata);
+fail:
+	return ERR_PTR(rc);
+}
+
+/*******************************************************************************
+ * FUNCTION: free_mt_pdata
+ *
+ * SUMMARY: Free all pointers that include touch framework, virtual keys and
+ *  touch data.
+ *
+ * PARAMETERS:
+ *   *vkeys  - pointer to virtual key structure
+ ******************************************************************************/
+static void free_mt_pdata(void *pdata)
+{
+	struct pt_mt_platform_data *mt_pdata =
+		(struct pt_mt_platform_data *)pdata;
+	struct pt_extended_mt_platform_data *ext_mt_pdata =
+		container_of(mt_pdata,
+			struct pt_extended_mt_platform_data, pdata);
+
+	free_touch_framework(mt_pdata->frmwrk);
+#ifdef ENABLE_VIRTUAL_KEYS
+	free_virtual_keys(&ext_mt_pdata->vkeys);
+#endif
+	kfree(ext_mt_pdata);
+}
+
+/*******************************************************************************
+ * FUNCTION: create_and_get_btn_pdata
+ *
+ * SUMMARY: Create and get button platform data from dts.
+ *
+ * RETURN:
+ *   success: the pointer of the platform data
+ *   fail   : error code with type of error pointer
+ *
+ * PARAMETERS:
+ *   *dev_node - pointer to device_node structure
+ ******************************************************************************/
+static void *create_and_get_btn_pdata(struct device_node *dev_node)
+{
+	struct pt_btn_platform_data *pdata;
+	int rc;
+
+	pdata = kzalloc(sizeof(*pdata), GFP_KERNEL);
+	if (!pdata) {
+		rc = -ENOMEM;
+		goto fail;
+	}
+
+	rc = get_inp_dev_name(dev_node, &pdata->inp_dev_name);
+	if (rc)
+		goto fail_free_pdata;
+
+	return pdata;
+
+fail_free_pdata:
+	kfree(pdata);
+fail:
+	return ERR_PTR(rc);
+}
+
+/*******************************************************************************
+ * FUNCTION: free_btn_pdata
+ *
+ * SUMMARY: Free all pointers for button platform data.
+ *
+ * PARAMETERS:
+ *   *vkeys  - pointer to virtual key structure
+ ******************************************************************************/
+static void free_btn_pdata(void *pdata)
+{
+	struct pt_btn_platform_data *btn_pdata =
+		(struct pt_btn_platform_data *)pdata;
+
+	kfree(btn_pdata);
+}
+
+/*******************************************************************************
+ * FUNCTION: create_and_get_pen_pdata
+ *
+ * SUMMARY: Create and get pen platform data from dts.
+ *
+ * RETURN:
+ *   success: the pointer of the platform data
+ *   fail   : error code with type of error pointer
+ *
+ * PARAMETERS:
+ *   *dev_node - pointer to device_node structure
+ ******************************************************************************/
+static void *create_and_get_pen_pdata(struct device_node *dev_node)
+{
+	struct pt_pen_platform_data *pdata;
+	int rc;
+
+	pdata = kzalloc(sizeof(*pdata), GFP_KERNEL);
+	if (!pdata) {
+		rc = -ENOMEM;
+		goto fail;
+	}
+
+	rc = get_inp_dev_name(dev_node, &pdata->inp_dev_name);
+	if (rc)
+		goto fail_free_pdata;
+
+	return pdata;
+
+fail_free_pdata:
+	kfree(pdata);
+fail:
+	return ERR_PTR(rc);
+}
+
+/*******************************************************************************
+ * FUNCTION: free_pen_pdata
+ *
+ * SUMMARY: Free all pointers for pen platform data.
+ *
+ * PARAMETERS:
+ *   *pdata  - pointer to virtual key structure
+ ******************************************************************************/
+static void free_pen_pdata(void *pdata)
+{
+	struct pt_pen_platform_data *pen_pdata =
+		(struct pt_pen_platform_data *)pdata;
+
+	kfree(pen_pdata);
+}
+
+
+/*******************************************************************************
+ * FUNCTION: create_and_get_proximity_pdata
+ *
+ * SUMMARY: Create and get proximity platform data from dts.
+ *
+ * RETURN:
+ *   success: the pointer of the proximity platform data
+ *   fail   : error code with type of error pointer
+ *
+ * PARAMETERS:
+ *   *dev_node - pointer to device_node structure
+ ******************************************************************************/
+static void *create_and_get_proximity_pdata(struct device_node *dev_node)
+{
+	struct pt_proximity_platform_data *pdata;
+	int rc;
+
+	pdata = kzalloc(sizeof(*pdata), GFP_KERNEL);
+	if (!pdata) {
+		rc = -ENOMEM;
+		goto fail;
+	}
+
+	rc = get_inp_dev_name(dev_node, &pdata->inp_dev_name);
+	if (rc)
+		goto fail_free_pdata;
+
+	pdata->frmwrk = create_and_get_touch_framework(dev_node);
+	if (pdata->frmwrk == NULL) {
+		rc = -EINVAL;
+		goto fail_free_pdata;
+	} else if (IS_ERR(pdata->frmwrk)) {
+		rc = PTR_ERR(pdata->frmwrk);
+		goto fail_free_pdata;
+	}
+
+	return pdata;
+
+fail_free_pdata:
+	kfree(pdata);
+fail:
+	return ERR_PTR(rc);
+}
+
+/*******************************************************************************
+ * FUNCTION: free_proximity_pdata
+ *
+ * SUMMARY: Free all pointers for proximity platform data.
+ *
+ * PARAMETERS:
+ *   *vkeys  - pointer to virtual key structure
+ ******************************************************************************/
+static void free_proximity_pdata(void *pdata)
+{
+	struct pt_proximity_platform_data *proximity_pdata =
+		(struct pt_proximity_platform_data *)pdata;
+
+	free_touch_framework(proximity_pdata->frmwrk);
+
+	kfree(proximity_pdata);
+}
+
+static struct pt_device_pdata_func device_pdata_funcs[DEVICE_TYPE_MAX] = {
+	[DEVICE_MT] = {
+		.create_and_get_pdata = create_and_get_mt_pdata,
+		.free_pdata = free_mt_pdata,
+	},
+	[DEVICE_BTN] = {
+		.create_and_get_pdata = create_and_get_btn_pdata,
+		.free_pdata = free_btn_pdata,
+	},
+	[DEVICE_PEN] = {
+		.create_and_get_pdata = create_and_get_pen_pdata,
+		.free_pdata = free_pen_pdata,
+	},
+	[DEVICE_PROXIMITY] = {
+		.create_and_get_pdata = create_and_get_proximity_pdata,
+		.free_pdata = free_proximity_pdata,
+	},
+};
+
+static struct pt_pdata_ptr pdata_ptr[DEVICE_TYPE_MAX];
+
+static const char *device_names[DEVICE_TYPE_MAX] = {
+	[DEVICE_MT] = "parade,mt",
+	[DEVICE_BTN] = "parade,btn",
+	[DEVICE_PEN] = "parade,pen",
+	[DEVICE_PROXIMITY] = "parade,proximity",
+};
+
+/*******************************************************************************
+ * FUNCTION: set_pdata_ptr
+ *
+ * SUMMARY: Set platform data pointer for touch, button, proximity module.
+ *
+ * PARAMETERS:
+ *   *pdata  - pointer to platform data structure
+ ******************************************************************************/
+static void set_pdata_ptr(struct pt_platform_data *pdata)
+{
+	pdata_ptr[DEVICE_MT].pdata = (void **)&pdata->mt_pdata;
+	pdata_ptr[DEVICE_BTN].pdata = (void **)&pdata->btn_pdata;
+	pdata_ptr[DEVICE_PEN].pdata = (void **)&pdata->pen_pdata;
+	pdata_ptr[DEVICE_PROXIMITY].pdata = (void **)&pdata->prox_pdata;
+}
+
+/*******************************************************************************
+ * FUNCTION: get_device_type
+ *
+ * SUMMARY: Determine the device type[mt,btn,proximity] from dts.
+ *
+ * RETURN:
+ *   0 = success
+ *  !0 = failure
+ *
+ * PARAMETERS:
+ *   *dev_node  - pointer to device_node structure
+ *   *type      - pointer to store the device type
+ ******************************************************************************/
+static int get_device_type(struct device_node *dev_node,
+		enum pt_device_type *type)
+{
+	const char *name;
+	enum pt_device_type t;
+	int rc;
+
+	rc = of_property_read_string(dev_node, "name", &name);
+	if (rc)
+		return rc;
+
+	for (t = 0; t < DEVICE_TYPE_MAX; t++)
+		if (!strncmp(name, device_names[t], MAX_NAME_LENGTH)) {
+			*type = t;
+			return 0;
+		}
+
+	return -EINVAL;
+}
+
+/*******************************************************************************
+ * FUNCTION: create_and_get_device_pdata
+ *
+ * SUMMARY: Create platform data for mt, btn, proximity module.
+ *
+ * RETURN:
+ *   success: the pointer of the platform data
+ *   fail   : error code with type of error pointer
+ *
+ * PARAMETERS:
+ *   *dev_node - pointer to device_node structure
+ *    type     - determine the device type
+ ******************************************************************************/
+static inline void *create_and_get_device_pdata(struct device_node *dev_node,
+		enum pt_device_type type)
+{
+	return device_pdata_funcs[type].create_and_get_pdata(dev_node);
+}
+
+/*******************************************************************************
+ * FUNCTION: free_device_pdata
+ *
+ * SUMMARY: Free platform data for mt, btn, proximity module.
+ *
+ * PARAMETERS:
+ *    type - determine the device type
+ ******************************************************************************/
+static inline void free_device_pdata(enum pt_device_type type)
+{
+	device_pdata_funcs[type].free_pdata(*pdata_ptr[type].pdata);
+}
+
+/*******************************************************************************
+ * FUNCTION: create_and_get_touch_setting
+ *
+ * SUMMARY: Create and get touch settings from dts.
+ *
+ * RETURN:
+ *   success: the pointer of touch settings
+ *   fail   : error code with type of error pointer
+ *
+ * PARAMETERS:
+ *   *core_node - pointer to device_node structure
+ *    name      - name of touch setting
+ ******************************************************************************/
+static struct touch_settings *create_and_get_touch_setting(
+		struct device_node *core_node, const char *name)
+{
+	struct touch_settings *setting;
+	char *tag_name;
+	u32 tag_value;
+	u16 *data;
+	int size;
+	int rc;
+
+	data = create_and_get_u16_array(core_node, name, &size);
+	if (IS_ERR_OR_NULL(data))
+		return (void *)data;
+
+	pr_debug("%s: Touch setting:'%s' size:%d\n", __func__, name, size);
+
+	setting = kzalloc(sizeof(*setting), GFP_KERNEL);
+	if (!setting) {
+		rc = -ENOMEM;
+		goto fail_free_data;
+	}
+
+	setting->data = (u8 *)data;
+	setting->size = size;
+
+	tag_name = kzalloc(MAX_NAME_LENGTH, GFP_KERNEL);
+	if (!tag_name) {
+		rc = -ENOMEM;
+		goto fail_free_setting;
+	}
+
+	snprintf(tag_name, MAX_NAME_LENGTH, "%s-tag", name);
+
+	rc = of_property_read_u32(core_node, tag_name, &tag_value);
+	if (!rc)
+		setting->tag = tag_value;
+
+	kfree(tag_name);
+
+	return setting;
+
+fail_free_setting:
+	kfree(setting);
+fail_free_data:
+	kfree(data);
+
+	return ERR_PTR(rc);
+}
+
+/*******************************************************************************
+ * FUNCTION: free_touch_setting
+ *
+ * SUMMARY: Free touch setting data.
+ *
+ * PARAMETERS:
+ *    setting - pointer to touch setting
+ ******************************************************************************/
+static void free_touch_setting(struct touch_settings *setting)
+{
+	if (setting) {
+		kfree(setting->data);
+		kfree(setting);
+	}
+}
+
+static char *touch_setting_names[PT_IC_GRPNUM_NUM] = {
+	NULL,			/* PT_IC_GRPNUM_RESERVED */
+	"parade,cmd_regs",		/* PT_IC_GRPNUM_CMD_REGS */
+	"parade,tch_rep",		/* PT_IC_GRPNUM_TCH_REP */
+	"parade,data_rec",		/* PT_IC_GRPNUM_DATA_REC */
+	"parade,test_rec",		/* PT_IC_GRPNUM_TEST_REC */
+	"parade,pcfg_rec",		/* PT_IC_GRPNUM_PCFG_REC */
+	"parade,tch_parm_val",	/* PT_IC_GRPNUM_TCH_PARM_VAL */
+	"parade,tch_parm_size",	/* PT_IC_GRPNUM_TCH_PARM_SIZE */
+	NULL,			/* PT_IC_GRPNUM_RESERVED1 */
+	NULL,			/* PT_IC_GRPNUM_RESERVED2 */
+	"parade,opcfg_rec",		/* PT_IC_GRPNUM_OPCFG_REC */
+	"parade,ddata_rec",		/* PT_IC_GRPNUM_DDATA_REC */
+	"parade,mdata_rec",		/* PT_IC_GRPNUM_MDATA_REC */
+	"parade,test_regs",		/* PT_IC_GRPNUM_TEST_REGS */
+	"parade,btn_keys",		/* PT_IC_GRPNUM_BTN_KEYS */
+	NULL,			/* PT_IC_GRPNUM_TTHE_REGS */
+};
+
+/*******************************************************************************
+ * FUNCTION: create_and_get_core_pdata
+ *
+ * SUMMARY: Create and get core module platform data from dts.
+ *
+ * RETURN:
+ *   success: the pointer of core platform data
+ *   fail   : error code with type of error pointer
+ *
+ * PARAMETERS:
+ *   *core_node - pointer to device_node structure
+ ******************************************************************************/
+static struct pt_core_platform_data *create_and_get_core_pdata(
+		struct device_node *core_node)
+{
+	struct pt_core_platform_data *pdata;
+	u32 value;
+	int rc;
+	int i;
+
+	pdata = kzalloc(sizeof(*pdata), GFP_KERNEL);
+	if (!pdata) {
+		rc = -ENOMEM;
+		goto fail;
+	}
+
+	/* Required fields */
+	rc = of_property_read_u32(core_node, "parade,irq_gpio", &value);
+	if (rc)
+		goto fail_free;
+	pdata->irq_gpio = value;
+
+	rc = of_property_read_u32(core_node, "parade,hid_desc_register",
+		&value);
+	if (rc)
+		goto fail_free;
+	pdata->hid_desc_register = value;
+
+	/* Optional fields */
+	/* rst_gpio is optional since a platform may use
+	 * power cycling instead of using the XRES pin
+	 */
+	rc = of_property_read_u32(core_node, "parade,rst_gpio", &value);
+	if (!rc)
+		pdata->rst_gpio = value;
+
+	rc = of_property_read_u32(core_node, "parade,ddi_rst_gpio", &value);
+	if (!rc)
+		pdata->ddi_rst_gpio = value;
+
+	rc = of_property_read_u32(core_node, "parade,vddi_gpio", &value);
+	if (!rc)
+		pdata->vddi_gpio = value;
+
+	rc = of_property_read_u32(core_node, "parade,vcc_gpio", &value);
+	if (!rc)
+		pdata->vcc_gpio = value;
+
+	rc = of_property_read_u32(core_node, "parade,avdd_gpio", &value);
+	if (!rc)
+		pdata->avdd_gpio = value;
+
+	rc = of_property_read_u32(core_node, "parade,avee_gpio", &value);
+	if (!rc)
+		pdata->avee_gpio = value;
+
+	rc = of_property_read_u32(core_node, "parade,level_irq_udelay", &value);
+	if (!rc)
+		pdata->level_irq_udelay = value;
+
+	rc = of_property_read_u32(core_node, "parade,vendor_id", &value);
+	if (!rc)
+		pdata->vendor_id = value;
+
+	rc = of_property_read_u32(core_node, "parade,product_id", &value);
+	if (!rc)
+		pdata->product_id = value;
+
+	rc = of_property_read_u32(core_node, "parade,flags", &value);
+	if (!rc)
+		pdata->flags = value;
+
+	rc = of_property_read_u32(core_node, "parade,easy_wakeup_gesture",
+		&value);
+	if (!rc)
+		pdata->easy_wakeup_gesture = (u8)value;
+
+	rc = of_property_read_u32(core_node, "parade,config_dut_generation",
+		&value);
+	if (!rc)
+		pdata->config_dut_generation = (u8)value;
+	else {
+		pr_err("%s: dut_generation is not configured, set default: DUT_PIP2_CAPABLE!\n",
+			__func__);
+		pdata->config_dut_generation = CONFIG_DUT_PIP2_CAPABLE;
+	}
+
+	rc = of_property_read_u32(core_node, "parade,watchdog_force_stop",
+		&value);
+	if (!rc) {
+		if (value)
+			pdata->watchdog_force_stop = true;
+		else
+			pdata->watchdog_force_stop = false;
+	} else {
+		pr_err("%s: watchdog_force_stop is not configured, set default: false!\n",
+			__func__);
+		pdata->watchdog_force_stop = false;
+	}
+
+	rc = of_property_read_u32(core_node, "parade,panel_id_support",
+		&value);
+	if (!rc) {
+		pdata->panel_id_support = (u8)value;
+	} else {
+		pr_err("%s: panel_id_support is not configured, set default: PT_PANEL_ID_DISABLE\n",
+			__func__);
+		pdata->panel_id_support = PT_PANEL_ID_DISABLE;
+	}
+
+	for (i = 0; (unsigned int)i < ARRAY_SIZE(touch_setting_names); i++) {
+		if (touch_setting_names[i] == NULL)
+			continue;
+
+		pdata->sett[i] = create_and_get_touch_setting(core_node,
+				touch_setting_names[i]);
+		if (IS_ERR(pdata->sett[i])) {
+			rc = PTR_ERR(pdata->sett[i]);
+			goto fail_free_sett;
+		} else if (pdata->sett[i] == NULL)
+			pr_debug("%s: No data for setting '%s'\n", __func__,
+				touch_setting_names[i]);
+	}
+
+	pr_debug("%s: irq_gpio:%d rst_gpio:%d\n"
+		"hid_desc_reg:%d level_irq_udelay:%d vendor_id:%d prod_id:%d\n"
+		"flags:%d easy_wakeup_gesture:%d\n", __func__,
+		pdata->irq_gpio, pdata->rst_gpio,
+		pdata->hid_desc_register,
+		pdata->level_irq_udelay, pdata->vendor_id, pdata->product_id,
+		pdata->flags, pdata->easy_wakeup_gesture);
+
+	pdata->xres = pt_xres;
+	pdata->init = pt_init;
+	pdata->power = pt_power;
+	pdata->detect = pt_detect;
+	pdata->irq_stat = pt_irq_stat;
+	pdata->setup_power = pt_setup_power;
+	pdata->setup_irq = pt_setup_irq;
+
+	return pdata;
+
+fail_free_sett:
+	for (i--; i >= 0; i--)
+		free_touch_setting(pdata->sett[i]);
+fail_free:
+	kfree(pdata);
+fail:
+	return ERR_PTR(rc);
+}
+
+/*******************************************************************************
+ * FUNCTION: free_core_pdata
+ *
+ * SUMMARY: Free the core module platform data and touch settings data.
+ *
+ * RETURN:
+ *   success: the pointer of core platform data
+ *   fail   : error code with type of error pointer
+ *
+ * PARAMETERS:
+ *   *core_node - pointer to device_node structure
+ ******************************************************************************/
+static void free_core_pdata(void *pdata)
+{
+	struct pt_core_platform_data *core_pdata = pdata;
+	unsigned int i;
+
+	for (i = 0; i < ARRAY_SIZE(touch_setting_names); i++)
+		free_touch_setting(core_pdata->sett[i]);
+	kfree(core_pdata);
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_devtree_create_and_get_pdata
+ *
+ * SUMMARY: Parse dts and set up platform data for core module, multi-touch(mt)
+ *  module, button(btn) module, proximity module.And Assign platform data
+ *  pointer for loader module.
+ *
+ * RETURN:
+ *   0 = success
+ *  !0 = failure
+ *
+ * PARAMETERS:
+ *  *adap_dev  - pointer to device structure
+ ******************************************************************************/
+int pt_devtree_create_and_get_pdata(struct device *adap_dev)
+{
+	struct pt_platform_data *pdata;
+	struct device_node *core_node, *dev_node, *dev_node_fail;
+	enum pt_device_type type;
+	int count = 0;
+	int rc = 0;
+
+	if (is_create_and_get_pdata == true)
+		return 0;
+
+	if (!adap_dev->of_node)
+		return 0;
+
+	pdata = kzalloc(sizeof(*pdata), GFP_KERNEL);
+	if (!pdata)
+		return -ENOMEM;
+
+	adap_dev->platform_data = pdata;
+	set_pdata_ptr(pdata);
+
+	/* There should be only one core node */
+	for_each_child_of_node(adap_dev->of_node, core_node) {
+		const char *name;
+
+		rc = of_property_read_string(core_node, "name", &name);
+		if (!rc)
+			pr_debug("%s: name:%s\n", __func__, name);
+
+		pdata->core_pdata = create_and_get_core_pdata(core_node);
+		if (IS_ERR(pdata->core_pdata)) {
+			rc = PTR_ERR(pdata->core_pdata);
+			break;
+		}
+
+		/* Increment reference count */
+		of_node_get(core_node);
+
+		for_each_child_of_node(core_node, dev_node) {
+			count++;
+			rc = get_device_type(dev_node, &type);
+			if (rc)
+				break;
+			*pdata_ptr[type].pdata
+				= create_and_get_device_pdata(dev_node, type);
+			if (IS_ERR(*pdata_ptr[type].pdata))
+				rc = PTR_ERR(*pdata_ptr[type].pdata);
+			if (rc)
+				break;
+			/* Increment reference count */
+			of_node_get(dev_node);
+		}
+
+		if (rc) {
+			free_core_pdata(pdata->core_pdata);
+			of_node_put(core_node);
+			for_each_child_of_node(core_node, dev_node_fail) {
+				if (dev_node == dev_node_fail)
+					break;
+				rc = get_device_type(dev_node, &type);
+				if (rc)
+					break;
+				free_device_pdata(type);
+				of_node_put(dev_node);
+			}
+			break;
+		}
+		pdata->loader_pdata = &_pt_loader_platform_data;
+	}
+	is_create_and_get_pdata = true;
+	pr_debug("%s: %d child node(s) found\n", __func__, count);
+
+	return rc;
+}
+EXPORT_SYMBOL_GPL(pt_devtree_create_and_get_pdata);
+
+/*******************************************************************************
+ * FUNCTION: pt_devtree_clean_pdata
+ *
+ * SUMMARY: Free all platform data for core module, multi-touch(mt) module,
+ *  button(btn) module, proximity module.
+ *
+ * RETURN:
+ *   0 = success
+ *  !0 = failure
+ *
+ * PARAMETERS:
+ *  *adap_dev  - pointer to device structure
+ ******************************************************************************/
+int pt_devtree_clean_pdata(struct device *adap_dev)
+{
+	struct pt_platform_data *pdata;
+	struct device_node *core_node, *dev_node;
+	enum pt_device_type type;
+	int rc = 0;
+
+	if (is_create_and_get_pdata == false)
+		return 0;
+
+	if (!adap_dev->of_node)
+		return 0;
+
+	pdata = dev_get_platdata(adap_dev);
+	set_pdata_ptr(pdata);
+	for_each_child_of_node(adap_dev->of_node, core_node) {
+		free_core_pdata(pdata->core_pdata);
+		of_node_put(core_node);
+		for_each_child_of_node(core_node, dev_node) {
+			rc = get_device_type(dev_node, &type);
+			if (rc)
+				break;
+			free_device_pdata(type);
+			of_node_put(dev_node);
+		}
+	}
+	is_create_and_get_pdata = false;
+
+	return rc;
+}
+EXPORT_SYMBOL_GPL(pt_devtree_clean_pdata);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Parade TrueTouch(R) Standard Product DeviceTree Driver");
+MODULE_AUTHOR("Parade Technologies <[email protected]>");

+ 570 - 0
pt/pt_i2c.c

@@ -0,0 +1,570 @@
+/*
+ * pt_i2c.c
+ * Parade TrueTouch(TM) Standard Product I2C 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 <[email protected]>
+ */
+
+#include "pt_regs.h"
+
+#include <linux/i2c.h>
+#include <linux/version.h>
+
+#define PT_I2C_DATA_SIZE  (2 * 256)
+
+#ifdef TTDL_PTVIRTDUT_SUPPORT
+#define VIRT_DUT_BUF_SIZE  2048
+static unsigned char pt_dut_cmd_buf[VIRT_DUT_BUF_SIZE];
+static unsigned char pt_dut_out_buf[VIRT_DUT_BUF_SIZE];
+static int pt_dut_cmd_len;
+static int pt_dut_out_len;
+DEFINE_MUTEX(virt_i2c_lock);
+
+/*******************************************************************************
+ * FUNCTION: virt_i2c_transfer
+ *
+ * SUMMARY: Copies the current i2c output message to the temporary buffer
+ *	used by the dut_cmd sysfs node
+ *
+ * RETURN VALUE:
+ *	Number of messages transferred which in this function will be 1
+ *
+ * PARAMETERS:
+ *      *buf - pointer to i2c command
+ *	 len - length of command in the buffer
+ ******************************************************************************/
+static int virt_i2c_transfer(u8 *buf, int len)
+{
+	int rc = 0;
+
+	mutex_lock(&virt_i2c_lock);
+	if (len <= sizeof(pt_dut_cmd_buf)) {
+		memcpy(pt_dut_cmd_buf, buf, len);
+		pt_dut_cmd_len = len;
+		rc = 1;
+	} else
+		rc = 0;
+	mutex_unlock(&virt_i2c_lock);
+
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: virt_i2c_master_recv
+ *
+ * SUMMARY: Copies the i2c input message from the dut_out sysfs node into a
+ *	temporary buffer.
+ *
+ * RETURN VALUE:
+ *	Length of data transferred
+ *
+ * PARAMETERS:
+ *      *dev  - pointer to device struct
+ *      *buf  - pointer to i2c incoming report
+ *       size - size to be read
+ ******************************************************************************/
+static int virt_i2c_master_recv(struct device *dev, u8 *buf, int size)
+{
+#ifndef PT_POLL_RESP_BY_BUS
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+	int i = 0;
+#endif
+
+	mutex_lock(&virt_i2c_lock);
+	memcpy(buf, pt_dut_out_buf, size);
+
+	/* Set "empty buffer" */
+	pt_dut_out_buf[1] = 0xFF;
+	pt_dut_out_len = 0;
+	mutex_unlock(&virt_i2c_lock);
+
+#ifndef PT_POLL_RESP_BY_BUS
+	if (cd->bl_with_no_int) {
+		/*
+		 * Wait for IRQ gpio to be released, make read operation
+		 * synchronize with PtVirtDut tool.
+		 * Safety net: Exit after 500ms (50us * 10000 loops = 500ms)
+		 */
+		while (i < VIRT_MAX_IRQ_RELEASE_TIME_US &&
+		       !gpio_get_value(cd->cpdata->irq_gpio)) {
+			pt_debug(dev, DL_INFO, "%s: %d IRQ still Enabled\n",
+				__func__, i);
+			usleep_range(50, 60);
+			i += 50;
+		}
+	}
+#endif
+
+	pt_debug(dev, DL_INFO,
+		"%s: Copy msg from dut_out to i2c buffer, size=%d\n",
+		__func__, size);
+
+	return size;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_dut_cmd_show
+ *
+ * SUMMARY: The show function for the dut_cmd sysfs node. Provides read access
+ *	to the pt_dut_cmd_buf and clears it after it has been read.
+ *
+ * RETURN VALUE:
+ *	Number of bytes transferred
+ *
+ * PARAMETERS:
+ *      *dev  - pointer to device structure
+ *      *attr - pointer to device attributes
+ *	*buf  - pointer to output buffer
+ ******************************************************************************/
+static ssize_t pt_dut_cmd_show(struct device *dev,
+	struct device_attribute *attr, char *buf)
+{
+	int i;
+	int index = 0;
+
+	/* Only print to sysfs if the buffer has data */
+	mutex_lock(&virt_i2c_lock);
+	if (pt_dut_cmd_len > 0) {
+		for (i = 0; i < pt_dut_cmd_len; i++)
+			index += scnprintf(buf + index, strlen(buf), "%02X",
+				pt_dut_cmd_buf[i]);
+		index += scnprintf(buf + index, strlen(buf), "\n");
+	}
+	pt_dut_cmd_len = 0;
+	mutex_unlock(&virt_i2c_lock);
+	return index;
+}
+static DEVICE_ATTR(dut_cmd, 0444, pt_dut_cmd_show, NULL);
+
+/*******************************************************************************
+ * FUNCTION: pt_dut_out_store
+ *
+ * SUMMARY: The store function for the dut_out sysfs node. Provides write
+ *	access to the pt_dut_out_buf. The smallest valid PIP response is 2
+ *	bytes so don't update buffer if only 1 byte passed in.
+ *
+ * RETURN VALUE:
+ *	Number of bytes read from virtual DUT
+ *
+ * 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_dut_out_store(struct device *dev,
+	struct device_attribute *attr, const char *buf, size_t size)
+{
+	int loop_max = ARRAY_SIZE(pt_dut_out_buf);
+	int hex_str_len = strlen(buf)/2;
+	int i;
+	const char *pos = buf;
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+
+	/* Clear out the last message */
+	mutex_lock(&virt_i2c_lock);
+	memset(pt_dut_out_buf, 0, VIRT_DUT_BUF_SIZE);
+	pt_dut_out_len = 0;
+
+	/* Only update the dut_out buffer if at least 2 byte payload */
+	if (size >= 2 && hex_str_len <= loop_max) {
+		/* Convert string of hex values to byte array */
+		for (i = 0; i < hex_str_len; i++) {
+			pt_dut_out_buf[i] = ((HEXOF(*pos)) << 4) +
+					     HEXOF(*(pos + 1));
+			pos += 2;
+		}
+		/*
+		 * In HID we send the full string, non HID we send based
+		 * on the 2 byte length.
+		 */
+		if (cd->dut_status.protocol_mode == PT_PROTOCOL_MODE_HID)
+			pt_dut_out_len = hex_str_len;
+		else
+			pt_dut_out_len = get_unaligned_le16(&pt_dut_out_buf[0]);
+	} else if (size >= PT_PIP_1P7_EMPTY_BUF) {
+		/* Message too large, set to 'empty buffer' message */
+		pt_dut_out_buf[1] = 0xFF;
+		pt_dut_out_len = 0;
+	}
+	mutex_unlock(&virt_i2c_lock);
+
+	return size;
+}
+static DEVICE_ATTR(dut_out, 0200, NULL, pt_dut_out_store);
+#endif /* TTDL_PTVIRTDUT_SUPPORT */
+
+/*******************************************************************************
+ * FUNCTION: pt_i2c_read_default
+ *
+ * SUMMARY: Read a certain number of bytes from the I2C bus
+ *
+ * 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_i2c_read_default(struct device *dev, void *buf, int size)
+{
+#ifdef TTDL_PTVIRTDUT_SUPPORT
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+#endif /* TTDL_PTVIRTDUT_SUPPORT */
+	struct i2c_client *client = to_i2c_client(dev);
+	int rc;
+	int read_size = size;
+
+	if (!buf || !size || size > VIRT_DUT_BUF_SIZE)
+		return -EINVAL;
+
+#ifdef TTDL_PTVIRTDUT_SUPPORT
+	if (cd->route_bus_virt_dut)
+		rc = virt_i2c_master_recv(dev, buf, read_size);
+	else
+		rc = i2c_master_recv(client, buf, read_size);
+#else
+	rc = i2c_master_recv(client, buf, read_size);
+#endif /* TTDL_PTVIRTDUT_SUPPORT */
+
+	return (rc < 0) ? rc : rc != read_size ? -EIO : 0;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_i2c_read_default_nosize
+ *
+ * SUMMARY: Read from the I2C bus in two transactions first reading the HID
+ *	packet size (2 bytes) followed by reading the rest of the packet based
+ *	on the size initially read.
+ *	NOTE: The empty buffer 'size' was redefined in PIP version 1.7.
+ *
+ * PARAMETERS:
+ *      *dev  - pointer to Device structure
+ *      *buf  - pointer to buffer where the data read will be stored
+ *       max  - max size that can be read
+ ******************************************************************************/
+static int pt_i2c_read_default_nosize(struct device *dev, u8 *buf, u32 max)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct i2c_msg msgs[2];
+	u8 msg_count = 1;
+	int rc;
+	u32 size;
+#ifdef TTDL_PTVIRTDUT_SUPPORT
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+
+	if (cd->route_bus_virt_dut) {
+		mutex_lock(&virt_i2c_lock);
+		size = pt_dut_out_len;
+		mutex_unlock(&virt_i2c_lock);
+		pt_debug(dev, DL_INFO, "%s: pt_dut_out_len=%d\n",
+			__func__, pt_dut_out_len);
+		/* Only copy 2 bytes for "empty buffer" or "FW sentinel" */
+		if (!size || size == 2 || size >= PT_PIP_1P7_EMPTY_BUF)
+			size = 2;
+		goto skip_read_len;
+	}
+
+#endif /* TTDL_PTVIRTDUT_SUPPORT */
+
+	if (!buf)
+		return -EINVAL;
+
+	msgs[0].addr = client->addr;
+	msgs[0].flags = (client->flags & I2C_M_TEN) | I2C_M_RD;
+	msgs[0].len = 2;
+	msgs[0].buf = buf;
+	rc = i2c_transfer(client->adapter, msgs, msg_count);
+	if (rc < 0 || rc != msg_count)
+		return (rc < 0) ? rc : -EIO;
+
+	size = get_unaligned_le16(&buf[0]);
+	if (!size || size == 2 || size >= PT_PIP_1P7_EMPTY_BUF)
+		/*
+		 * Before PIP 1.7, empty buffer is 0x0002;
+		 * From PIP 1.7, empty buffer is 0xFFXX
+		 */
+		return 0;
+
+	if (size > max)
+		return -EINVAL;
+
+#ifdef TTDL_PTVIRTDUT_SUPPORT
+skip_read_len:
+	if (cd->route_bus_virt_dut)
+		rc = virt_i2c_master_recv(dev, buf, size);
+	else
+		rc = i2c_master_recv(client, buf, size);
+
+	pt_debug(dev, DL_INFO, "%s: rc = %d\n", __func__, rc);
+#else
+	rc = i2c_master_recv(client, buf, size);
+#endif /* TTDL_PTVIRTDUT_SUPPORT */
+	return (rc < 0) ? rc : rc != (int)size ? -EIO : 0;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_i2c_write_read_specific
+ *
+ * SUMMARY: Write the contents of write_buf to the I2C device and then read
+ *  the response using pt_i2c_read_default_nosize()
+ *
+ * PARAMETERS:
+ *  *dev       - pointer to Device structure
+ *   write_len - length of data buffer write_buf
+ *  *write_buf - pointer to buffer to write
+ *  *read_buf  - pointer to buffer to read response into
+ *   read_len  - length to read, 0 to use pt_i2c_read_default_nosize
+ ******************************************************************************/
+static int pt_i2c_write_read_specific(struct device *dev, u16 write_len,
+		u8 *write_buf, u8 *read_buf, u16 read_len)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+#ifdef TTDL_PTVIRTDUT_SUPPORT
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+#endif /* TTDL_PTVIRTDUT_SUPPORT */
+	struct i2c_msg msgs[2];
+	u8 msg_count = 1;
+	int rc;
+
+	/* Ensure no packet larger than what the PIP spec allows */
+	if (write_len > PT_MAX_PIP2_MSG_SIZE)
+		return -EINVAL;
+
+	if (!write_buf || !write_len) {
+		if (!write_buf)
+			pt_debug(dev, DL_ERROR,
+				"%s write_buf is NULL", __func__);
+		if (!write_len)
+			pt_debug(dev, DL_ERROR,
+				"%s write_len is NULL", __func__);
+		return -EINVAL;
+	}
+
+	msgs[0].addr = client->addr;
+	msgs[0].flags = client->flags & I2C_M_TEN;
+	msgs[0].len = write_len;
+	msgs[0].buf = write_buf;
+#ifdef TTDL_PTVIRTDUT_SUPPORT
+	if (cd->route_bus_virt_dut) {
+		rc = virt_i2c_transfer(msgs[0].buf, msgs[0].len);
+		pt_debug(dev, DL_DEBUG, "%s: Virt transfer size = %d",
+			__func__, msgs[0].len);
+	} else
+		rc = i2c_transfer(client->adapter, msgs, msg_count);
+#else
+	rc = i2c_transfer(client->adapter, msgs, msg_count);
+#endif /* TTDL_PTVIRTDUT_SUPPORT */
+
+	if (rc < 0 || rc != msg_count)
+		return (rc < 0) ? rc : -EIO;
+
+	rc = 0;
+
+	if (read_buf) {
+#ifdef TTDL_PTVIRTDUT_SUPPORT
+		if (cd->route_bus_virt_dut &&
+		    cd->dut_status.protocol_mode == PT_PROTOCOL_MODE_HID) {
+			/* Simulate clock stretch */
+			usleep_range(3000, 4000);
+		}
+#endif /* TTDL_PTVIRTDUT_SUPPORT */
+		if (read_len > 0)
+			rc = pt_i2c_read_default(dev, read_buf, read_len);
+		else
+			rc = pt_i2c_read_default_nosize(dev, read_buf,
+				PT_I2C_DATA_SIZE);
+	}
+
+	return rc;
+}
+
+static struct pt_bus_ops pt_i2c_bus_ops = {
+	.bustype = BUS_I2C,
+	.read_default = pt_i2c_read_default,
+	.read_default_nosize = pt_i2c_read_default_nosize,
+	.write_read_specific = pt_i2c_write_read_specific,
+};
+
+#ifdef CONFIG_TOUCHSCREEN_PARADE_DEVICETREE_SUPPORT
+static const struct of_device_id pt_i2c_of_match[] = {
+	{ .compatible = "parade,pt_i2c_adapter", },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, pt_i2c_of_match);
+#endif
+
+
+/*******************************************************************************
+ * FUNCTION: pt_i2c_probe
+ *
+ * SUMMARY: Probe functon for the I2C module
+ *
+ * PARAMETERS:
+ *      *client - pointer to i2c client structure
+ *      *i2c_id - pointer to i2c device structure
+ ******************************************************************************/
+static int pt_i2c_probe(struct i2c_client *client,
+	const struct i2c_device_id *i2c_id)
+{
+	struct device *dev = &client->dev;
+#ifdef CONFIG_TOUCHSCREEN_PARADE_DEVICETREE_SUPPORT
+	const struct of_device_id *match;
+#endif
+	int rc;
+
+	if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+		pt_debug(dev, DL_ERROR, "I2C functionality not Supported\n");
+		return -EIO;
+	}
+
+#ifdef CONFIG_TOUCHSCREEN_PARADE_DEVICETREE_SUPPORT
+	match = of_match_device(of_match_ptr(pt_i2c_of_match), dev);
+	if (match) {
+		rc = pt_devtree_create_and_get_pdata(dev);
+		if (rc < 0)
+			return rc;
+	}
+#endif
+
+#ifdef TTDL_PTVIRTDUT_SUPPORT
+	/*
+	 * When using the virtual DUT these files must be created before
+	 * pt_probe is called.
+	 */
+	mutex_lock(&virt_i2c_lock);
+	device_create_file(dev, &dev_attr_dut_cmd);
+	device_create_file(dev, &dev_attr_dut_out);
+	mutex_unlock(&virt_i2c_lock);
+#endif /* TTDL_PTVIRTDUT_SUPPORT */
+
+	rc = pt_probe(&pt_i2c_bus_ops, &client->dev, client->irq,
+			  PT_I2C_DATA_SIZE);
+
+#ifdef CONFIG_TOUCHSCREEN_PARADE_DEVICETREE_SUPPORT
+	if (rc && match)
+		pt_devtree_clean_pdata(dev);
+#endif
+#ifdef TTDL_PTVIRTDUT_SUPPORT
+	if (rc) {
+		mutex_lock(&virt_i2c_lock);
+		device_remove_file(dev, &dev_attr_dut_cmd);
+		device_remove_file(dev, &dev_attr_dut_out);
+		mutex_unlock(&virt_i2c_lock);
+	}
+#endif /* TTDL_PTVIRTDUT_SUPPORT */
+
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_i2c_remove
+ *
+ * SUMMARY: Remove functon for the I2C module
+ *
+ * PARAMETERS:
+ *      *client - pointer to i2c client structure
+ ******************************************************************************/
+static int pt_i2c_remove(struct i2c_client *client)
+{
+#ifdef CONFIG_TOUCHSCREEN_PARADE_DEVICETREE_SUPPORT
+	const struct of_device_id *match;
+#endif
+	struct device *dev = &client->dev;
+	struct pt_core_data *cd = i2c_get_clientdata(client);
+
+	pt_release(cd);
+#ifdef TTDL_PTVIRTDUT_SUPPORT
+	mutex_lock(&virt_i2c_lock);
+	device_remove_file(dev, &dev_attr_dut_cmd);
+	device_remove_file(dev, &dev_attr_dut_out);
+	mutex_unlock(&virt_i2c_lock);
+#endif /* TTDL_PTVIRTDUT_SUPPORT */
+
+#ifdef CONFIG_TOUCHSCREEN_PARADE_DEVICETREE_SUPPORT
+	match = of_match_device(of_match_ptr(pt_i2c_of_match), dev);
+	if (match)
+		pt_devtree_clean_pdata(dev);
+#endif
+
+	return 0;
+}
+
+static const struct i2c_device_id pt_i2c_id[] = {
+	{ PT_I2C_NAME, 0, },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, pt_i2c_id);
+
+static struct i2c_driver pt_i2c_driver = {
+	.driver = {
+		.name = PT_I2C_NAME,
+		.owner = THIS_MODULE,
+		.pm = &pt_pm_ops,
+#ifdef CONFIG_TOUCHSCREEN_PARADE_DEVICETREE_SUPPORT
+		.of_match_table = pt_i2c_of_match,
+#endif
+	},
+	.probe = pt_i2c_probe,
+	.remove = pt_i2c_remove,
+	.id_table = pt_i2c_id,
+};
+
+#if (KERNEL_VERSION(3, 3, 0) <= LINUX_VERSION_CODE)
+module_i2c_driver(pt_i2c_driver);
+#else
+/*******************************************************************************
+ * FUNCTION: pt_i2c_init
+ *
+ * SUMMARY: Initialize function to register i2c module to kernel.
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ ******************************************************************************/
+static int __init pt_i2c_init(void)
+{
+	int rc = i2c_add_driver(&pt_i2c_driver);
+
+	pr_info("%s: Parade TTDL I2C Driver (Build %s) rc=%d\n",
+			__func__, PT_DRIVER_VERSION, rc);
+	return rc;
+}
+module_init(pt_i2c_init);
+
+/*******************************************************************************
+ * FUNCTION: pt_i2c_exit
+ *
+ * SUMMARY: Exit function to unregister i2c module from kernel.
+ *
+ ******************************************************************************/
+static void __exit pt_i2c_exit(void)
+{
+	i2c_del_driver(&pt_i2c_driver);
+}
+module_exit(pt_i2c_exit);
+#endif
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Parade TrueTouch(R) Standard Product I2C driver");
+MODULE_AUTHOR("Parade Technologies <[email protected]>");

+ 5956 - 0
pt/pt_loader.c

@@ -0,0 +1,5956 @@
+/*
+ * pt_loader.c
+ * Parade TrueTouch(TM) Standard Product FW Loader 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 <[email protected]>
+ */
+
+#include "pt_regs.h"
+#include <linux/firmware.h>
+
+#define PT_LOADER_NAME "pt_loader"
+
+#ifdef UPGRADE_FW_AND_CONFIG_IN_PROBE
+/* Enable UPGRADE_FW_AND_CONFIG_IN_PROBE definition
+ * to perform FW and config upgrade during probe
+ * instead of scheduling a work for it
+ */
+/* #define UPGRADE_FW_AND_CONFIG_IN_PROBE */
+#endif
+
+#define PT_AUTO_LOAD_FOR_CORRUPTED_FW 1
+#define PT_LOADER_FW_UPGRADE_RETRY_COUNT 3
+
+#define PT_FW_UPGRADE \
+	(defined(CONFIG_TOUCHSCREEN_PARADE_PLATFORM_FW_UPGRADE) \
+	|| defined(CONFIG_TOUCHSCREEN_PARADE_BINARY_FW_UPGRADE))
+
+#define PT_TTCONFIG_UPGRADE \
+	(defined(CONFIG_TOUCHSCREEN_PARADE_PLATFORM_TTCONFIG_UPGRADE) \
+	|| defined(CONFIG_TOUCHSCREEN_PARADE_MANUAL_TTCONFIG_UPGRADE))
+
+/* Timeout values in ms. */
+#define PT_LDR_REQUEST_EXCLUSIVE_TIMEOUT		3000
+#define PT_LDR_SWITCH_TO_APP_MODE_TIMEOUT		300
+
+#define PT_MAX_STATUS_SIZE				32
+
+#define PT_DATA_MAX_ROW_SIZE				256
+#define PT_DATA_ROW_SIZE				128
+
+#define PT_ARRAY_ID_OFFSET				0
+#define PT_ROW_NUM_OFFSET				1
+#define PT_ROW_SIZE_OFFSET				3
+#define PT_ROW_DATA_OFFSET				5
+
+#define PT_POST_TT_CFG_CRC_MASK				0x2
+static inline struct pt_loader_data *pt_get_loader_data(
+		struct device *dev);
+
+static struct pt_core_commands *cmd;
+
+#define PIP2_LAUNCH_APP_DELAY	400
+
+struct pip2_file_read {
+	s8 para_num;
+	u8 file_handle;
+	int file_offset;
+	int file_max_size;
+	int file_read_size;
+	int file_print_size;
+	u8 *file_print_buf;
+	int file_print_left;
+};
+
+#ifdef TTDL_DIAGNOSTICS
+struct pip2_file_crc {
+	s8 para_num;
+	u8 file_handle;
+	int file_offset;
+	int file_max_size;
+	int file_read_size;
+};
+#endif
+
+struct pip2_loader_data {
+	struct device *dev;
+	struct completion pip2_fw_upgrade_complete; /* mutex for loader */
+	u8 pip2_file_handle;
+};
+
+struct pt_loader_data {
+	struct device *dev;
+	struct pt_sysinfo *si;
+	u8 status_buf[PT_MAX_STATUS_SIZE];
+	struct completion int_running;
+	struct completion calibration_complete;
+#ifdef CONFIG_TOUCHSCREEN_PARADE_BINARY_FW_UPGRADE
+	int builtin_bin_fw_status;
+	bool is_manual_upgrade_enabled;
+#endif
+	struct work_struct fw_and_config_upgrade;
+	struct work_struct calibration_work;
+	struct pt_loader_platform_data *loader_pdata;
+#ifdef CONFIG_TOUCHSCREEN_PARADE_MANUAL_TTCONFIG_UPGRADE
+	struct mutex config_lock;
+	u8 *config_data;
+	int config_size;
+	bool config_loading;
+#endif
+	struct pip2_loader_data *pip2_data;
+	u8 pip2_load_file_no;
+	bool pip2_load_builtin;
+	u8 pip2_file_erase_file_no;
+	struct pip2_file_read pip2_file_data;
+#ifdef TTDL_DIAGNOSTICS
+	struct pip2_file_crc pip2_fcrc;
+#endif
+#ifdef CONFIG_TOUCHSCREEN_PARADE_BINARY_FW_UPGRADE
+	struct work_struct bl_from_file;
+	struct work_struct pip2_bl_from_file;
+#endif
+};
+
+static u8 write_file_status;
+#ifndef TTDL_KERNEL_SUBMISSION
+static int pip2_erase_status;
+static int pip2_erase_rc;
+#endif /* !TTDL_KERNEL_SUBMISSION */
+
+/* PIP2 update fw status codes. 1-99% are "active" */
+enum UPDATE_FW_STATUS {
+	UPDATE_FW_IDLE                   = 0,
+	UPDATE_FW_REQUEST_EXCLUSIVE      = 1,
+	UPDATE_FW_ACTIVE_10              = 10,
+	UPDATE_FW_ACTIVE_90              = 90,
+	UPDATE_FW_ACTIVE_99              = 99,
+	UPDATE_FW_COMPLETE               = 100,
+	UPDATE_FW_GENERAL_ERROR          = 200,
+	UPDATE_FW_PIP_VERSION_ERROR      = 201,
+	UPDATE_FW_VERSION_ERROR          = 202,
+	UPDATE_FW_ERASE_ERROR            = 203,
+	UPDATE_FW_FILE_CLOSE_ERROR       = 204,
+	UPDATE_FW_WRITE_ERROR            = 205,
+	UPDATE_FW_EXECUTE_ERROR          = 206,
+	UPDATE_FW_RESET_ERROR            = 207,
+	UPDATE_FW_MODE_ERROR             = 208,
+	UPDATE_FW_ENTER_BL_ERROR         = 209,
+	UPDATE_FW_FILE_OPEN_ERROR        = 210,
+	UPDATE_FW_SENTINEL_NOT_SEEN      = 211,
+	UPDATE_FW_EXCLUSIVE_ACCESS_ERROR = 212,
+	UPDATE_FW_NO_FW_PROVIDED         = 213,
+	UPDATE_FW_INVALID_FW_IMAGE       = 214,
+	UPDATE_FW_FILE_SEEK_ERROR        = 215,
+	UPDATE_FW_MISALIGN_FW_IMAGE      = 230,
+	UPDATE_FW_SYSTEM_NOMEM           = 231,
+	UPDATE_FW_INIT_BL_ERROR          = 232,
+	UPDATE_FW_PARSE_ROW_ERROR        = 233,
+	UPDATE_FW_PROGRAM_ROW_ERROR      = 234,
+	UPDATE_FW_EXIT_BL_ERROR          = 235,
+	UPDATE_FW_CHECK_SUM_ERROR        = 236,
+	UPDATE_FW_NO_PLATFORM_DATA       = 237,
+	UPDATE_FW_NOT_SUPPORTED_FILE_NO  = 238,
+	UPDATE_FW_UNDEFINED_ERROR        = 255,
+	UPDATE_FW_INCREMENT              = 300,
+	UPDATE_FW_INCREMENT_BY_1         = 301,
+	UPDATE_FW_INCREMENT_BY_5         = 305,
+	UPDATE_FW_UNDEFINED_INC          = 400,
+};
+
+enum PIP2_FILE_ERASE_STATUS {
+	PIP2_FILE_ERASE_STATUS_DUT_BUSY		= 101,
+	PIP2_FILE_ERASE_STATUS_ENTER_BL_ERROR	= 102,
+};
+
+struct pt_dev_id {
+	u32 silicon_id;
+	u8 rev_id;
+	u32 bl_ver;
+};
+
+struct pt_hex_image {
+	u8 array_id;
+	u16 row_num;
+	u16 row_size;
+	u8 row_data[PT_DATA_ROW_SIZE];
+} __packed;
+
+#ifdef CONFIG_TOUCHSCREEN_PARADE_BINARY_FW_UPGRADE
+static int pt_pip2_upgrade_firmware_from_builtin(struct device *dev);
+#endif
+
+typedef int (*PIP2_SEND_CMD)(struct device *, int,
+		u8, u8 *, u16, u8 *, u16 *);
+static struct pt_module loader_module;
+
+/*******************************************************************************
+ * FUNCTION: pt_get_loader_data
+ *
+ * SUMMARY: Inline function to get pt_loader_data pointer from loader module.
+ *
+ * RETURN:
+ *  pointer to pt_loader_data structure
+ *
+ * PARAMETERS:
+ *  *dev  - pointer to device structure
+ ******************************************************************************/
+static inline struct pt_loader_data *pt_get_loader_data(
+		struct device *dev)
+{
+	return pt_get_module_data(dev, &loader_module);
+}
+
+#if PT_FW_UPGRADE || PT_TTCONFIG_UPGRADE
+/*******************************************************************************
+ * FUNCTION: pt_calibrate_idacs
+ *
+ * SUMMARY: Calibrate idac for mutual-cap,buttons,self-cap by called functions.
+ * It needs stop panel scan during calibration.
+ *
+ * PARAMETERS:
+ *	*calibration_work - pointer to work_struct structure
+ ******************************************************************************/
+static void pt_calibrate_idacs(struct work_struct *calibration_work)
+{
+	struct pt_loader_data *ld = container_of(calibration_work,
+			struct pt_loader_data, calibration_work);
+	struct device *dev = ld->dev;
+	struct pt_cal_ext_data cal_data = {0};
+	u8 dut_gen = cmd->request_dut_generation(dev);
+	u8 mode;
+	u8 status;
+	int rc;
+
+	pt_debug(dev, DL_INFO, "Entering %s\n", __func__);
+	rc = cmd->request_exclusive(dev, PT_LDR_REQUEST_EXCLUSIVE_TIMEOUT);
+	if (rc < 0)
+		goto exit;
+
+	rc = cmd->nonhid_cmd->suspend_scanning(dev, 0);
+	if (rc < 0)
+		goto release;
+
+	if (dut_gen == DUT_PIP1_ONLY) {
+		for (mode = 0; mode < 3; mode++) {
+			rc = cmd->nonhid_cmd->calibrate_idacs(dev, 0, mode,
+							      &status);
+			if (rc < 0) {
+				pt_debug(dev, DL_ERROR,
+					 "%s: calibrate idac error, rc = %d\n",
+					 __func__, rc);
+				break;
+			}
+		}
+	} else {
+		/*
+		 * Manual calibration is not need after fw 3.4.932110.
+		 * Therefore, PT_LOADER_FLAG_NONE should be used to avoid
+		 * manual calibration if the FW is newer than 3.4.932110.
+		 */
+		memset(&cal_data, 0, sizeof(struct pt_cal_ext_data));
+		rc = cmd->nonhid_cmd->calibrate_ext(dev, 0, &cal_data,
+						    &status);
+		if (rc < 0) {
+			pt_debug(dev, DL_ERROR,
+				 "%s: extended calibrate error, rc = %d\n",
+				 __func__, rc);
+		}
+	}
+
+	rc = cmd->nonhid_cmd->resume_scanning(dev, 0);
+	if (rc < 0)
+		goto release;
+
+	pt_debug(dev, DL_WARN, "%s: Calibration Done\n", __func__);
+
+release:
+	cmd->release_exclusive(dev);
+exit:
+	complete(&ld->calibration_complete);
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_calibration_attention
+ *
+ * SUMMARY: Wrapper function to schedule calibration work used to subscribe into
+ * TTDL attention list.Once called will unsubscribe from attention list.
+ *
+ * RETURN:
+ *   0 = success
+ *  !0 = failure
+ *
+ * PARAMETERS:
+ *  *dev - pointer to device structure
+ ******************************************************************************/
+static int pt_calibration_attention(struct device *dev)
+{
+	struct pt_loader_data *ld = pt_get_loader_data(dev);
+	int rc = 0;
+
+	schedule_work(&ld->calibration_work);
+
+	cmd->unsubscribe_attention(dev, PT_ATTEN_STARTUP, PT_LOADER_NAME,
+		pt_calibration_attention, 0);
+
+	return rc;
+}
+#endif /* PT_FW_UPGRADE || PT_TTCONFIG_UPGRADE */
+
+#if PT_FW_UPGRADE \
+	|| defined(CONFIG_TOUCHSCREEN_PARADE_PLATFORM_TTCONFIG_UPGRADE)
+
+/*******************************************************************************
+ * FUNCTION: pt_get_panel_id
+ *
+ * SUMMARY: Get panel id from core data.
+ *
+ * RETURN:
+ *	 panel id
+ *
+ * PARAMETERS:
+ *	*dev  - pointer to device structure
+ ******************************************************************************/
+static u8 pt_get_panel_id(struct device *dev)
+{
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+
+	return cd->pid_for_loader;
+}
+#endif
+
+
+#if (PT_FW_UPGRADE || PT_TTCONFIG_UPGRADE)
+/*******************************************************************************
+ * FUNCTION: pt_check_firmware_version
+ *
+ * SUMMARY: Compare fw's version and revision control number with fw image's.
+ *
+ * RETURN:
+ *  -1: Do not upgrade firmware
+ *   0: Version info same, let caller decide
+ *   1: Do a firmware upgrade
+ *
+ * PARAMETERS:
+ *  *dev             - pointer to device structure
+ *   fw_ver_new      - firmware version
+ *   fw_revctrl_new  - firmware revision control number
+ ******************************************************************************/
+static int pt_check_firmware_version(struct device *dev,
+		u32 fw_ver_new, u32 fw_revctrl_new)
+{
+	struct pt_loader_data *ld = pt_get_loader_data(dev);
+	u32 fw_ver_img;
+	u32 fw_revctrl_img;
+
+	fw_ver_img = ld->si->ttdata.fw_ver_major << 8;
+	fw_ver_img += ld->si->ttdata.fw_ver_minor;
+
+	pt_debug(dev, DL_WARN,
+		"%s: Current FW ver:0x%04X New FW ver:0x%04X\n", __func__,
+			fw_ver_img, fw_ver_new);
+
+	if (fw_ver_new > fw_ver_img) {
+		pt_debug(dev, DL_WARN,
+			"%s: Image is newer, will upgrade\n", __func__);
+		return 1;
+	}
+
+	if (fw_ver_new < fw_ver_img) {
+		pt_debug(dev, DL_WARN,
+			"%s: Image is older, will NOT upgrade\n", __func__);
+		return -1;
+	}
+
+	fw_revctrl_img = ld->si->ttdata.revctrl;
+
+	pt_debug(dev, DL_WARN,
+		"%s: Current FW rev:%d New FW rev:%d\n",
+		__func__, fw_revctrl_img, fw_revctrl_new);
+
+	if (fw_revctrl_new > fw_revctrl_img) {
+		pt_debug(dev, DL_WARN,
+			"%s: Image is newer, will upgrade\n", __func__);
+		return 1;
+	}
+
+	if (fw_revctrl_new < fw_revctrl_img) {
+		pt_debug(dev, DL_WARN,
+			"%s: Image is older, will NOT upgrade\n", __func__);
+		return -1;
+	}
+
+	return 0;
+}
+
+#endif /* PT_FW_UPGRADE || PT_TTCONFIG_UPGRADE */
+
+#if PT_FW_UPGRADE
+/*******************************************************************************
+ * FUNCTION: pt_get_row_
+ *
+ * SUMMARY: Copy the image data to the "row_buf".
+ *
+ * RETURN:
+ *	 pointer to image buffer plus copy size
+ *
+ * PARAMETERS:
+ *  *dev        - pointer to device structure
+ *  *row_buf    - pointer to written buffer to program chip
+ *  *image_buf  - pointer to image buffer
+ *   size       - size of written data
+ ******************************************************************************/
+static u8 *pt_get_row_(struct device *dev, u8 *row_buf,
+		u8 *image_buf, int size)
+{
+	memcpy(row_buf, image_buf, size);
+	return image_buf + size;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_ldr_enter_
+ *
+ * SUMMARY: Enter bootloader state and update device id(silicon id, rev id,
+ *  bootloader version).
+ *
+ * RETURN:
+ *   0 = success
+ *  !0 = failure
+ *
+ * PARAMETERS:
+ *  *dev     - pointer to device structure
+ *  *dev_id  - pointer to device id
+ ******************************************************************************/
+static int pt_ldr_enter_(struct device *dev, struct pt_dev_id *dev_id)
+{
+	int rc;
+	u8 return_data[8];
+	u8 mode = PT_MODE_OPERATIONAL;
+
+	dev_id->silicon_id = 0;
+	dev_id->rev_id = 0;
+	dev_id->bl_ver = 0;
+
+	/*
+	 * Reset to get to a known state, sleep to allow sentinel processing.
+	 *
+	 * NOTE: This msleep MUST be greater than the auto launch timeout
+	 * that is built into the FW.
+	 */
+	cmd->request_reset(dev, PT_CORE_CMD_UNPROTECTED);
+	msleep(20);
+
+	rc = cmd->request_get_mode(dev, 0, &mode);
+	pt_debug(dev, DL_INFO, "%s:request_get_mode rc=%d mode=%d\n",
+		__func__, rc, mode);
+	if (rc)
+		return rc;
+
+	if (mode == PT_MODE_UNKNOWN)
+		return -EINVAL;
+
+	if (mode == PT_MODE_OPERATIONAL) {
+		rc = cmd->nonhid_cmd->start_bl(dev, PT_CORE_CMD_UNPROTECTED);
+		pt_debug(dev, DL_INFO, "%s:start_bl rc=%d\n", __func__, rc);
+		if (rc)
+			return rc;
+		rc = cmd->request_get_mode(dev, 0, &mode);
+		pt_debug(dev, DL_INFO, "%s:request_get_mode rc=%d mode=%d\n",
+			__func__, rc, mode);
+		if (rc)
+			return rc;
+		if (mode != PT_MODE_BOOTLOADER)
+			return -EINVAL;
+	}
+
+	rc = cmd->nonhid_cmd->get_bl_info(dev,
+		PT_CORE_CMD_UNPROTECTED, return_data);
+	pt_debug(dev, DL_INFO, "%s:get_bl_info rc=%d\n", __func__, rc);
+	if (rc)
+		return rc;
+
+	dev_id->silicon_id = get_unaligned_le32(&return_data[0]);
+	dev_id->rev_id = return_data[4];
+	dev_id->bl_ver = return_data[5] + (return_data[6] << 8)
+		+ (return_data[7] << 16);
+
+	return 0;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_ldr_init_
+ *
+ * SUMMARY: Erase 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:
+ *  *dev           - pointer to device structure
+ *  *pt_hex_image  - pointer to hex image structure
+ ******************************************************************************/
+static int pt_ldr_init_(struct device *dev,
+		struct pt_hex_image *row_image)
+{
+	return cmd->nonhid_cmd->initiate_bl(dev, 0, 8,
+			(u8 *)pt_data_block_security_key, row_image->row_size,
+			row_image->row_data);
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_ldr_parse_row_
+ *
+ * SUMMARY: Parse and copy the row buffer data to hex image structure.
+ *
+ * RETURN:
+ *   0 = success
+ *  !0 = failure
+ *
+ * PARAMETERS:
+ *  *dev        - pointer to device structure
+ *  *row_buf    - pointer to row buffer
+ *  *row_image  - pointer to hex image structure
+ ******************************************************************************/
+static int pt_ldr_parse_row_(struct device *dev, u8 *row_buf,
+	struct pt_hex_image *row_image)
+{
+	int rc = 0;
+
+	row_image->array_id = row_buf[PT_ARRAY_ID_OFFSET];
+	row_image->row_num = get_unaligned_be16(&row_buf[PT_ROW_NUM_OFFSET]);
+	row_image->row_size = get_unaligned_be16(&row_buf[PT_ROW_SIZE_OFFSET]);
+
+	if (row_image->row_size > ARRAY_SIZE(row_image->row_data)) {
+		pt_debug(dev, DL_ERROR,
+			"%s: row data buffer overflow\n", __func__);
+		rc = -EOVERFLOW;
+		goto pt_ldr_parse_row_exit;
+	}
+
+	memcpy(row_image->row_data, &row_buf[PT_ROW_DATA_OFFSET],
+	       row_image->row_size);
+pt_ldr_parse_row_exit:
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_ldr_prog_row_
+ *
+ * SUMMARY: Program one row that the hex image structure data to the chip.
+ *
+ * RETURN:
+ *   0 = success
+ *  !0 = failure
+ *
+ * PARAMETERS:
+ *  *dev        - pointer to device structure
+ *  *row_image  - pointer to hex image structure
+ ******************************************************************************/
+static int pt_ldr_prog_row_(struct device *dev,
+				 struct pt_hex_image *row_image)
+{
+	int rc = 0;
+	u16 length = row_image->row_size + 3;
+	u8 *data = NULL;
+	u8 offset = 0;
+
+	if (length > PT_DATA_MAX_ROW_SIZE)
+		return -EINVAL;
+
+	data = kzalloc(length, GFP_KERNEL);
+	if (!data)
+		return -ENOMEM;
+
+	data[offset++] = row_image->array_id;
+	data[offset++] = LOW_BYTE(row_image->row_num);
+	data[offset++] = HI_BYTE(row_image->row_num);
+	memcpy(data + 3, row_image->row_data, row_image->row_size);
+	rc = cmd->nonhid_cmd->prog_and_verify(dev, 0, length, data);
+	kfree(data);
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_ldr_verify_chksum_
+ *
+ * SUMMARY: Perform a full verification of the application integrity by
+ *  calculating the CRC of the TrueTouch application image in flash and
+ *  comparing it to the expected CRC stored in the TrueTouch application CRC
+ *  value stored in the Metadata row.
+ *
+ * RETURN:
+ *   0 = success
+ *  !0 = failure
+ *
+ * PARAMETERS:
+ *  *dev  - pointer to device structure
+ ******************************************************************************/
+static int pt_ldr_verify_chksum_(struct device *dev)
+{
+	u8 result;
+	int rc;
+
+	rc = cmd->nonhid_cmd->verify_app_integrity(dev, 0, &result);
+	if (rc)
+		return rc;
+
+	/* fail */
+	if (result == 0)
+		return -EINVAL;
+
+	return 0;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_ldr_exit_
+ *
+ * SUMMARY: Launch the application from bootloader.
+ *
+ * RETURN:
+ *   0 = success
+ *  !0 = failure
+ *
+ * PARAMETERS:
+ *  *dev  - pointer to device structure
+ ******************************************************************************/
+static int pt_ldr_exit_(struct device *dev)
+{
+	return cmd->nonhid_cmd->launch_app(dev, 0);
+}
+
+/*******************************************************************************
+ * FUNCTION: _pt_pip2_update_bl_status
+ *
+ * SUMMARY: Update the value in the bl_status global by either setting to a
+ *	specific value or incrementing it ensuring we don't go out of bounds.
+ *
+ * PARAMETERS:
+ *	*dev  - pointer to device structure
+ *	value - Value to force.
+ ******************************************************************************/
+static u8 _pt_update_write_file_status(struct device *dev, u16 value)
+{
+	u8 inc = 0;
+
+	pt_debug(dev, DL_DEBUG,
+		"%s: fw_status = %d, request val=%d\n",
+		__func__, write_file_status, value);
+
+	/* Reset status if value is 0 */
+	if (value == UPDATE_FW_IDLE) {
+		write_file_status = value;
+		pt_debug(dev, DL_WARN, "%s: ATM - Reset BL Status to %d\n",
+			__func__, value);
+		return write_file_status;
+	}
+
+	/* Set to value if valid */
+	if (value > UPDATE_FW_IDLE && value < UPDATE_FW_UNDEFINED_ERROR) {
+		if (value <= UPDATE_FW_COMPLETE && value > write_file_status) {
+			write_file_status = value;
+			pt_debug(dev, DL_DEBUG, "%s: Set BL Status to %d\n",
+				__func__, value);
+		} else if (value >= UPDATE_FW_GENERAL_ERROR) {
+			write_file_status = value;
+			pt_debug(dev, DL_WARN,
+				"%s: BL Status set to error code %d\n",
+				__func__, value);
+		}
+		return write_file_status;
+	}
+
+	if (value > UPDATE_FW_INCREMENT && value < UPDATE_FW_UNDEFINED_INC) {
+		inc = value - UPDATE_FW_INCREMENT;
+		if (write_file_status + inc <= UPDATE_FW_COMPLETE)
+			write_file_status += inc;
+		else
+			pt_debug(dev, DL_ERROR,
+				"%s: Inc Request out of bounds: status=%d inc=%d\n",
+				__func__, write_file_status, inc);
+	} else
+		pt_debug(dev, DL_ERROR,
+			"%s: Value Request out of bounds: status=%d value=%d\n",
+			__func__, write_file_status, value);
+
+	return write_file_status;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_load_app_
+ *
+ * SUMMARY: Program the firmware image to the chip.
+ *
+ * RETURN:
+ *   0 = success
+ *  !0 = failure
+ *
+ * PARAMETERS:
+ *  *dev           - pointer to device structure
+ *  *fw            - pointer to the firmware
+ *   fw_size       - size of firmware
+ *   update_status - store the update status of firmware
+ ******************************************************************************/
+static int pt_load_app_(struct device *dev, const u8 *fw, int fw_size,
+	u8 *update_status)
+{
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+	struct pt_dev_id *dev_id;
+	struct pt_hex_image *row_image;
+	u8 *row_buf;
+	size_t image_rec_size;
+	size_t row_buf_size = PT_DATA_MAX_ROW_SIZE;
+	int row_count = 0;
+	u8 *p;
+	u8 *last_row;
+	int rc;
+	int rc_tmp;
+	int percent_cmplt;
+	int total_row_count;
+
+	if (update_status == NULL) {
+		rc = -EINVAL;
+		pt_debug(dev, DL_ERROR,
+			"%s: update_status is NULL pointer %d\n",
+			__func__, rc);
+		return rc;
+	} else if (*update_status > UPDATE_FW_ACTIVE_10) {
+		pt_debug(dev, DL_WARN,
+			"%s: update_status is illegal = %d, fixed to %d\n",
+			__func__, *update_status, UPDATE_FW_ACTIVE_10);
+		*update_status = UPDATE_FW_ACTIVE_10;
+	}
+
+	image_rec_size = sizeof(struct pt_hex_image);
+	if (fw_size % image_rec_size != 0) {
+		pt_debug(dev, DL_ERROR,
+			"%s: Firmware image is misaligned\n", __func__);
+		*update_status = UPDATE_FW_MISALIGN_FW_IMAGE;
+		rc = -EINVAL;
+		goto _pt_load_app_error;
+	}
+	*update_status += 1;
+	total_row_count = fw_size / image_rec_size - 1;
+
+	pt_debug(dev, DL_INFO, "%s: start load app\n", __func__);
+#ifdef TTHE_TUNER_SUPPORT
+	cmd->request_tthe_print(dev, NULL, 0, "start load app");
+#endif
+
+	row_buf = kzalloc(row_buf_size, GFP_KERNEL);
+	row_image = kzalloc(sizeof(struct pt_hex_image), GFP_KERNEL);
+	dev_id = kzalloc(sizeof(struct pt_dev_id), GFP_KERNEL);
+	if (!row_buf || !row_image || !dev_id) {
+		*update_status = UPDATE_FW_SYSTEM_NOMEM;
+		rc = -ENOMEM;
+		goto _pt_load_app_exit;
+	}
+	*update_status += 1;
+
+	cmd->request_stop_wd(dev);
+
+	pt_debug(dev, DL_INFO, "%s: Send BL Loader Enter\n", __func__);
+#ifdef TTHE_TUNER_SUPPORT
+	cmd->request_tthe_print(dev, NULL, 0, "Send BL Loader Enter");
+#endif
+	rc = pt_ldr_enter_(dev, dev_id);
+	if (rc) {
+		*update_status = UPDATE_FW_ENTER_BL_ERROR;
+		pt_debug(dev, DL_ERROR, "%s: Error cannot start Loader (ret=%d)\n",
+			__func__, rc);
+		goto _pt_load_app_exit;
+	}
+	pt_debug(dev, DL_INFO,
+		"%s: dev: silicon id=%08X rev=%02X bl=%08X\n", __func__,
+		dev_id->silicon_id, dev_id->rev_id, dev_id->bl_ver);
+	*update_status += 1;
+
+	/*
+	 * since start loader is successful, firmware mode can be assumed
+	 * at bl mode directly. Updating this variable is helpful to detect
+	 * firmware entered application mode.
+	 */
+	cd->mode = PT_MODE_BOOTLOADER;
+
+	/* get last row */
+	last_row = (u8 *)fw + fw_size - image_rec_size;
+	pt_get_row_(dev, row_buf, last_row, image_rec_size);
+	pt_ldr_parse_row_(dev, row_buf, row_image);
+
+	/* initialise bootloader */
+	rc = pt_ldr_init_(dev, row_image);
+	if (rc) {
+		*update_status = UPDATE_FW_ERASE_ERROR;
+		pt_debug(dev, DL_ERROR, "%s: Error cannot init Loader (ret=%d)\n",
+			__func__, rc);
+		goto _pt_load_app_exit;
+	}
+	*update_status += 5;
+
+	pt_debug(dev, DL_INFO,
+		"%s: Send BL Loader Blocks\n", __func__);
+#ifdef TTHE_TUNER_SUPPORT
+	cmd->request_tthe_print(dev, NULL, 0, "Send BL Loader Blocks");
+#endif
+	p = (u8 *)fw;
+	while (p < last_row) {
+		/* Get row */
+		row_count += 1;
+		pt_debug(dev, DL_INFO, "%s: read row=%d\n",
+			__func__, row_count);
+		memset(row_buf, 0, row_buf_size);
+		p = pt_get_row_(dev, row_buf, p, image_rec_size);
+
+		/* Don't update BL status on every pass */
+		if (row_count / 8 * 8 == row_count) {
+			/* Calculate % complete for update_status sysfs */
+			percent_cmplt = row_count * 100 / total_row_count;
+			if (percent_cmplt > UPDATE_FW_ACTIVE_99)
+				percent_cmplt = UPDATE_FW_ACTIVE_99;
+			*update_status = (percent_cmplt > *update_status) ?
+				percent_cmplt : *update_status;
+#ifdef TTDL_DIAGNOSTICS
+			pt_debug(dev, DL_INFO,
+				"Wrote row num %d of total %d\n",
+				row_count, total_row_count);
+#endif
+		}
+
+		/* Parse row */
+		pt_debug(dev, DL_INFO, "%s: p=%p buf=%p buf[0]=%02X\n",
+			__func__, p, row_buf, row_buf[0]);
+		rc = pt_ldr_parse_row_(dev, row_buf, row_image);
+		pt_debug(dev, DL_INFO,
+			"%s: array_id=%02X row_num=%04X(%d) row_size=%04X(%d)\n",
+			__func__, row_image->array_id,
+			row_image->row_num, row_image->row_num,
+			row_image->row_size, row_image->row_size);
+		if (rc) {
+			*update_status = UPDATE_FW_PARSE_ROW_ERROR;
+			pt_debug(dev, DL_ERROR,
+				"%s: Parse Row Error (a=%d r=%d ret=%d\n",
+				__func__, row_image->array_id,
+				row_image->row_num, rc);
+			goto _pt_load_app_exit;
+		} else {
+			pt_debug(dev, DL_INFO,
+				"%s: Parse Row (a=%d r=%d ret=%d\n",
+				__func__, row_image->array_id,
+				row_image->row_num, rc);
+		}
+
+		/* program row */
+		rc = pt_ldr_prog_row_(dev, row_image);
+		if (rc) {
+			*update_status = UPDATE_FW_PROGRAM_ROW_ERROR;
+			pt_debug(dev, DL_ERROR,
+				"%s: Prog Row Error (array=%d row=%d ret=%d)\n",
+				__func__, row_image->array_id,
+				row_image->row_num, rc);
+			goto _pt_load_app_exit;
+		}
+
+		pt_debug(dev, DL_INFO,
+			"%s: array=%d row_cnt=%d row_num=%04X\n",
+			__func__, row_image->array_id, row_count,
+			row_image->row_num);
+	}
+
+	/* exit loader */
+	pt_debug(dev, DL_INFO, "%s: Send BL Loader Terminate\n", __func__);
+#ifdef TTHE_TUNER_SUPPORT
+	cmd->request_tthe_print(dev, NULL, 0, "Send BL Loader Terminate");
+#endif
+	rc = pt_ldr_exit_(dev);
+	if (rc) {
+		*update_status = UPDATE_FW_EXIT_BL_ERROR;
+		pt_debug(dev, DL_ERROR, "%s: Error on exit Loader (ret=%d)\n",
+			__func__, rc);
+
+		/* verify app checksum */
+		rc_tmp = pt_ldr_verify_chksum_(dev);
+		if (rc_tmp) {
+			*update_status = UPDATE_FW_CHECK_SUM_ERROR;
+			pt_debug(dev, DL_ERROR,
+				"%s: ldr_verify_chksum fail r=%d\n",
+				__func__, rc_tmp);
+		} else
+			pt_debug(dev, DL_INFO,
+				"%s: APP Checksum Verified\n", __func__);
+	} else
+		*update_status = UPDATE_FW_ACTIVE_99;
+
+_pt_load_app_exit:
+	kfree(row_buf);
+	kfree(row_image);
+	kfree(dev_id);
+_pt_load_app_error:
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_upgrade_firmware
+ *
+ * SUMMARY: Program the firmware image and set call back for start up.
+ *
+ * RETURN:
+ *   0 = success
+ *  !0 = failure
+ *
+ * PARAMETERS:
+ *  *dev           - pointer to device structure
+ *  *fw_img        - pointer to the firmware
+ *   fw_size       - size of firmware
+ *  *update_status - store the update status of firmware
+ ******************************************************************************/
+static int pt_upgrade_firmware(struct device *dev, const u8 *fw_img,
+		int fw_size, u8 *update_status)
+{
+	struct pt_loader_data *ld = pt_get_loader_data(dev);
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+	int retry = PT_LOADER_FW_UPGRADE_RETRY_COUNT;
+	bool wait_for_calibration_complete = false;
+	int rc;
+	int t;
+
+	_pt_update_write_file_status(dev, UPDATE_FW_IDLE);
+
+	if (update_status == NULL) {
+		rc = -EINVAL;
+		pt_debug(dev, DL_ERROR,
+			"%s: update_status is NULL pointer %d\n",
+			__func__, rc);
+		return rc;
+	} else if (*update_status > UPDATE_FW_ACTIVE_10) {
+		pt_debug(dev, DL_WARN,
+			"%s: update_status is illegal = %d, fixed to %d\n",
+			__func__, *update_status, UPDATE_FW_ACTIVE_10);
+		*update_status = UPDATE_FW_ACTIVE_10;
+	}
+
+	/* Ensure no enum task is pending */
+	pt_debug(dev, DL_WARN, "%s: Cancel enum work thread\n", __func__);
+	cancel_work_sync(&cd->enum_work);
+
+	pm_runtime_get_sync(dev);
+	*update_status += 1;
+
+	rc = cmd->request_exclusive(dev, PT_LDR_REQUEST_EXCLUSIVE_TIMEOUT);
+	if (rc < 0) {
+		*update_status = UPDATE_FW_EXCLUSIVE_ACCESS_ERROR;
+		goto exit;
+	}
+	*update_status += 1;
+
+	t = *update_status;
+	while (retry--) {
+		/* Restore the update_status */
+		*update_status = t;
+		rc = pt_load_app_(dev, fw_img, fw_size, update_status);
+		if (rc < 0)
+			pt_debug(dev, DL_ERROR,
+				"%s: Firmware update failed rc=%d, retry:%d\n",
+				__func__, rc, retry);
+		else
+			break;
+		msleep(20);
+	}
+	if (rc < 0) {
+		pt_debug(dev, DL_ERROR,
+			"%s: Firmware update failed with error code %d\n",
+			__func__, rc);
+	} else if (ld->loader_pdata &&
+			(ld->loader_pdata->flags
+			 & PT_LOADER_FLAG_CALIBRATE_AFTER_FW_UPGRADE)) {
+#if (KERNEL_VERSION(3, 13, 0) <= LINUX_VERSION_CODE)
+		reinit_completion(&ld->calibration_complete);
+#else
+		INIT_COMPLETION(ld->calibration_complete);
+#endif
+		/* set up call back for startup */
+		pt_debug(dev, DL_INFO,
+			"%s: Adding callback for calibration\n", __func__);
+		rc = cmd->subscribe_attention(dev, PT_ATTEN_STARTUP,
+			PT_LOADER_NAME, pt_calibration_attention, 0);
+		if (rc) {
+			pt_debug(dev, DL_ERROR,
+				"%s: Failed adding callback for calibration\n",
+				__func__);
+			pt_debug(dev, DL_ERROR,
+				"%s: No calibration will be performed\n",
+				__func__);
+			rc = 0;
+		} else
+			wait_for_calibration_complete = true;
+	}
+
+	cmd->release_exclusive(dev);
+
+exit:
+	if (!rc) {
+		cmd->request_enum(dev, true);
+
+		/*
+		 * Wait for FW reset sentinel from reset or execute for up
+		 * to 500ms
+		 */
+		t = wait_event_timeout(cd->wait_q,
+			(cd->startup_status >=
+				STARTUP_STATUS_FW_RESET_SENTINEL) &&
+			(cd->mode == PT_MODE_OPERATIONAL),
+			msecs_to_jiffies(PT_BL_WAIT_FOR_SENTINEL));
+		if (IS_TMO(t)) {
+			pt_debug(dev, DL_WARN,
+				"%s: 0x%04X Timeout waiting for FW sentinel",
+				__func__, cd->startup_status);
+		}
+
+		/* Double verify DUT is alive and well in Application mode */
+		if (!(cd->startup_status & STARTUP_STATUS_FW_RESET_SENTINEL)) {
+			pt_debug(dev, DL_ERROR,
+				"%s FW sentinel not seen\n", __func__);
+			*update_status = UPDATE_FW_SENTINEL_NOT_SEEN;
+		} else if (cd->mode != PT_MODE_OPERATIONAL) {
+			*update_status = UPDATE_FW_MODE_ERROR;
+			pt_debug(dev, DL_ERROR,
+				"%s ERROR: Not in App mode as expected\n",
+				__func__);
+		} else {
+			*update_status = UPDATE_FW_COMPLETE;
+			pt_debug(dev, DL_INFO,
+				"%s == PIP1 FW upgrade finished ==\n",
+				__func__);
+		}
+	} else if (*update_status < UPDATE_FW_COMPLETE) {
+		*update_status = UPDATE_FW_UNDEFINED_ERROR;
+		pt_debug(dev, DL_ERROR, "%s undefined error!\n", __func__);
+	}
+
+	pm_runtime_put_sync(dev);
+
+	if (wait_for_calibration_complete)
+		wait_for_completion(&ld->calibration_complete);
+
+	return rc;
+}
+
+#endif /* PT_FW_UPGRADE */
+
+#ifdef CONFIG_TOUCHSCREEN_PARADE_PLATFORM_FW_UPGRADE
+/*******************************************************************************
+ * FUNCTION: pt_check_firmware_version_platform
+ *
+ * SUMMARY: The caller of function pt_check_firmware_version() to determine
+ *  whether to load firmware from touch_firmware structure.
+ *
+ * RETURN:
+ *   0: Don't upgrade
+ *   1: Upgrade
+ *
+ * PARAMETERS:
+ *  *dev  - pointer to device structure
+ *  *fw   - pointer to the touch_firmware structure
+ ******************************************************************************/
+static int pt_check_firmware_version_platform(struct device *dev,
+		struct pt_touch_firmware *fw)
+{
+	struct pt_loader_data *ld = pt_get_loader_data(dev);
+	u32 fw_ver_new;
+	u32 fw_revctrl_new;
+	int upgrade;
+
+	if (!ld->si) {
+		pt_debug(dev, DL_WARN,
+			"%s: No firmware info found, DUT FW may be corrupted\n",
+			__func__);
+		return PT_AUTO_LOAD_FOR_CORRUPTED_FW;
+	}
+
+	fw_ver_new = get_unaligned_be16(fw->ver + 2);
+	/* 4 middle bytes are not used */
+	fw_revctrl_new = get_unaligned_be32(fw->ver + 8);
+	pt_debug(dev, DL_WARN, "%s: Built-in FW version 0x%04x rev %d\n",
+		__func__, fw_ver_new, fw_revctrl_new);
+
+	upgrade = pt_check_firmware_version(dev, fw_ver_new,
+		fw_revctrl_new);
+
+	if (upgrade > 0)
+		return 1;
+
+	return 0;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_get_platform_firmware
+ *
+ * SUMMARY: To get the pointer of right touch_firmware structure by panel id.
+ *
+ * RETURN:
+ *   pointer to touch_firmware structure or null pointer if fail
+ *
+ * PARAMETERS:
+ *  *dev  - pointer to device structure
+ ******************************************************************************/
+static struct pt_touch_firmware *pt_get_platform_firmware(
+		struct device *dev)
+{
+	struct pt_loader_data *ld = pt_get_loader_data(dev);
+	struct pt_touch_firmware **fws;
+	struct pt_touch_firmware *fw;
+	u8 panel_id;
+
+	panel_id = pt_get_panel_id(dev);
+	if (panel_id == PANEL_ID_NOT_ENABLED) {
+		pt_debug(dev, DL_WARN,
+			"%s: Panel ID not enabled, using legacy firmware\n",
+			__func__);
+		return ld->loader_pdata->fw;
+	}
+
+	fws = ld->loader_pdata->fws;
+	if (!fws) {
+		pt_debug(dev, DL_ERROR,
+			"%s: No firmwares provided\n", __func__);
+		return NULL;
+	}
+
+	/* Find FW according to the Panel ID */
+	while ((fw = *fws++)) {
+		if (fw->panel_id == panel_id) {
+			pt_debug(dev, DL_WARN,
+				"%s: Found matching fw:%p with Panel ID: 0x%02X\n",
+				__func__, fw, fw->panel_id);
+			return fw;
+		}
+		pt_debug(dev, DL_WARN,
+			"%s: Found mismatching fw:%p with Panel ID: 0x%02X\n",
+			__func__, fw, fw->panel_id);
+	}
+
+	return NULL;
+}
+
+/*******************************************************************************
+ * FUNCTION: upgrade_firmware_from_platform
+ *
+ * SUMMARY: Get touch_firmware structure and perform upgrade if pass the
+ *  firmware version check.
+ *
+ * RETURN:
+ *   0 = success
+ *  !0 = failure
+ *
+ * PARAMETERS:
+ *  *dev     - pointer to device structure
+ *   forced  - flag to force upgrade(1:force to upgrade)
+ ******************************************************************************/
+static int upgrade_firmware_from_platform(struct device *dev,
+		bool forced)
+{
+	struct pt_loader_data *ld = pt_get_loader_data(dev);
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+	struct pt_touch_firmware *fw;
+	int rc = -ENODEV;
+	int upgrade;
+	int retry = 3;
+
+retry_bl:
+	if (!ld->loader_pdata) {
+		_pt_update_write_file_status(dev, UPDATE_FW_NO_PLATFORM_DATA);
+		pt_debug(dev, DL_ERROR,
+			"%s: No loader platform data\n", __func__);
+		return rc;
+	}
+
+	fw = pt_get_platform_firmware(dev);
+	if (!fw || !fw->img || !fw->size) {
+		_pt_update_write_file_status(dev, UPDATE_FW_NO_FW_PROVIDED);
+		pt_debug(dev, DL_ERROR,
+			"%s: No platform firmware\n", __func__);
+		return rc;
+	}
+
+	if (!fw->ver || !fw->vsize) {
+		_pt_update_write_file_status(dev, UPDATE_FW_INVALID_FW_IMAGE);
+		pt_debug(dev, DL_ERROR, "%s: No platform firmware version\n",
+			__func__);
+		return rc;
+	}
+
+	if (forced)
+		upgrade = forced;
+	else
+		upgrade = pt_check_firmware_version_platform(dev, fw);
+
+	if (upgrade) {
+		rc = pt_upgrade_firmware(dev,
+			fw->img, fw->size, &write_file_status);
+
+		/* An extra BL may be needed if default PID was wrong choice */
+		if ((cd->panel_id_support & PT_PANEL_ID_BY_SYS_INFO) &&
+				!rc && !ld->si && retry--) {
+			pt_debug(dev, DL_WARN, "%s: ATM - An extra BL may be needed\n",
+				__func__);
+			/* Panel_ID coming from sysinfo, ensure we have it */
+			ld->si = cmd->request_sysinfo(dev);
+			goto retry_bl;
+		}
+	}
+
+	return rc;
+}
+#endif /* CONFIG_TOUCHSCREEN_PARADE_PLATFORM_FW_UPGRADE */
+
+#ifdef CONFIG_TOUCHSCREEN_PARADE_BINARY_FW_UPGRADE
+/*******************************************************************************
+ * FUNCTION: _pt_pip1_bl_from_file
+ *
+ * SUMMARY: Wrapper function for _pt_firmware_cont() to perform bl with an
+ *  image file from userspace.
+ *
+ * PARAMETERS:
+ *  *fw      - pointer to firmware structure
+ *   forced  - flag to force upgrade(1:force to upgrade)
+ ******************************************************************************/
+static int _pt_pip1_bl_from_file(struct device *dev)
+{
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+	int fw_size = 0;
+	int rc = 0;
+	u8 *fw_img = NULL;
+	u8 header_size = 0;
+
+	_pt_update_write_file_status(dev, UPDATE_FW_IDLE);
+
+	fw_img = kzalloc(PT_PIP2_MAX_FILE_SIZE, GFP_KERNEL);
+	if (!fw_img) {
+		_pt_update_write_file_status(dev, UPDATE_FW_SYSTEM_NOMEM);
+		rc = -ENOMEM;
+		goto exit;
+	}
+
+	rc = cmd->nonhid_cmd->read_us_file(dev, cd->pip2_us_file_path,
+					    fw_img, &fw_size);
+	if (rc) {
+		pt_debug(dev, DL_ERROR, "%s: No firmware provided to load\n",
+			 __func__);
+		_pt_update_write_file_status(dev, UPDATE_FW_NO_FW_PROVIDED);
+		goto exit;
+	}
+
+	if (!fw_img || !fw_size) {
+		pt_debug(dev, DL_ERROR,
+			"%s: No firmware received\n", __func__);
+		_pt_update_write_file_status(dev, UPDATE_FW_INVALID_FW_IMAGE);
+		goto exit;
+	}
+
+	header_size = fw_img[0];
+	if (header_size >= (fw_size + 1)) {
+		pt_debug(dev, DL_ERROR,
+			"%s: Firmware format is invalid\n", __func__);
+		_pt_update_write_file_status(dev, UPDATE_FW_INVALID_FW_IMAGE);
+		goto exit;
+	}
+
+	pt_upgrade_firmware(dev, &(fw_img[header_size + 1]),
+		fw_size - (header_size + 1), &write_file_status);
+exit:
+	kfree(fw_img);
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: _pt_firmware_cont
+ *
+ * SUMMARY: Firmware upgrade continue function that verifies the firmware size
+ *  in the firmware class and then upgrades the firmware.
+ *
+ * PARAMETERS:
+ *  *fw      - pointer to firmware structure
+ *   forced  - flag to force upgrade(1:force to upgrade)
+ ******************************************************************************/
+static void _pt_firmware_cont(const struct firmware *fw, void *context)
+{
+	struct device *dev = context;
+	struct pt_loader_data *ld = pt_get_loader_data(dev);
+	u8 header_size = 0;
+
+	_pt_update_write_file_status(dev, UPDATE_FW_IDLE);
+	if (!fw) {
+		pt_debug(dev, DL_ERROR, "%s: No firmware\n", __func__);
+		_pt_update_write_file_status(dev, UPDATE_FW_NO_FW_PROVIDED);
+		goto pt_firmware_cont_exit;
+	}
+
+	if (!fw->data || !fw->size) {
+		pt_debug(dev, DL_ERROR,
+			"%s: No firmware received\n", __func__);
+		_pt_update_write_file_status(dev, UPDATE_FW_INVALID_FW_IMAGE);
+		goto pt_firmware_cont_release_exit;
+	}
+
+	header_size = fw->data[0];
+	if (header_size >= (fw->size + 1)) {
+		pt_debug(dev, DL_ERROR,
+			"%s: Firmware format is invalid\n", __func__);
+		_pt_update_write_file_status(dev, UPDATE_FW_INVALID_FW_IMAGE);
+		goto pt_firmware_cont_release_exit;
+	}
+
+	pt_upgrade_firmware(dev, &(fw->data[header_size + 1]),
+		fw->size - (header_size + 1), &write_file_status);
+
+pt_firmware_cont_release_exit:
+	if (fw)
+		release_firmware(fw);
+
+pt_firmware_cont_exit:
+	ld->is_manual_upgrade_enabled = 0;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_check_firmware_config_version
+ *
+ * SUMMARY: Compare fw's config version with fw image's. If they are differnt
+ *	report a FW upgrade is needed.
+ *
+ * RETURN:
+ *  -1: Do not upgrade firmware
+ *   0: Version info same, let caller decide
+ *   1: Do a firmware upgrade
+ *
+ * PARAMETERS:
+ *  *dev               - pointer to device structure
+ *   image_config_ver  - Image's firmware config version
+ ******************************************************************************/
+static int pt_check_firmware_config_version(struct device *dev,
+		u16 image_config_ver)
+{
+	struct pt_loader_data *ld = pt_get_loader_data(dev);
+	u16 fw_config_ver;
+
+	fw_config_ver = ld->si->ttdata.fw_ver_conf;
+
+	pt_debug(dev, DL_WARN,
+		 "%s: Current config ver:0x%04X New config ver:0x%04X\n",
+		 __func__, fw_config_ver, image_config_ver);
+
+	if (image_config_ver != fw_config_ver) {
+		pt_debug(dev, DL_WARN,
+			 "%s: Image config ver is different, will upgrade\n",
+			 __func__);
+		return 1;
+	}
+
+	if (image_config_ver == fw_config_ver) {
+		pt_debug(dev, DL_WARN,
+			 "%s: Image config ver is the same, will NOT upgrade\n",
+			 __func__);
+		return 0;
+	}
+
+	return -1;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_check_firmware_version_builtin
+ *
+ * SUMMARY: The caller of function pt_check_firmware_version() to determine
+ *  whether to load built-in firmware.
+ *
+ * RETURN:
+ *   0: Don't upgrade
+ *   1: Upgrade
+ *
+ * PARAMETERS:
+ *  *dev  - pointer to device structure
+ *  *fw   - pointer to the firmware structure
+ ******************************************************************************/
+static int pt_check_firmware_version_builtin(struct device *dev,
+		const struct firmware *fw)
+{
+	struct pt_loader_data *ld = pt_get_loader_data(dev);
+	u16 fw_config_ver_new;
+	u32 fw_ver_new;
+	u32 fw_revctrl_new;
+	int upgrade;
+
+	if (!ld->si) {
+		pt_debug(dev, DL_WARN,
+			"%s: No firmware info found, DUT FW may be corrupted\n",
+			__func__);
+		return PT_AUTO_LOAD_FOR_CORRUPTED_FW;
+	}
+
+	fw_ver_new = get_unaligned_be16(fw->data + 3);
+	/* 4 middle bytes are not used */
+	fw_revctrl_new = get_unaligned_be32(fw->data + 9);
+	/* Offset 17,18 is the TT Config version*/
+	fw_config_ver_new = get_unaligned_be16(fw->data + 17);
+
+	pt_debug(dev, DL_WARN,
+		"%s: Built-in FW version=0x%04x rev=%d config=0x%04X\n",
+		__func__, fw_ver_new, fw_revctrl_new, fw_config_ver_new);
+
+	upgrade = pt_check_firmware_version(dev, fw_ver_new,
+			fw_revctrl_new);
+
+	/* Only check config version if FW version was an exact match */
+	if (upgrade == 0)
+		upgrade = pt_check_firmware_config_version(dev,
+					fw_config_ver_new);
+
+	if (upgrade > 0)
+		return 1;
+
+	return 0;
+}
+
+/*******************************************************************************
+ * FUNCTION: _pt_firmware_cont_builtin
+ *
+ * SUMMARY: Perform upgrade if pass the firmware version check.
+ *
+ * PARAMETERS:
+ *  *dev     - pointer to device structure
+ *   forced  - flag to force upgrade(1:force to upgrade)
+ ******************************************************************************/
+static void _pt_firmware_cont_builtin(const struct firmware *fw,
+		void *context)
+{
+	struct device *dev = context;
+	struct pt_loader_data *ld = pt_get_loader_data(dev);
+	int upgrade;
+
+	if (!fw) {
+		pt_debug(dev, DL_INFO,
+			"%s: No builtin firmware\n", __func__);
+		goto _pt_firmware_cont_builtin_exit;
+	}
+
+	if (!fw->data || !fw->size) {
+		pt_debug(dev, DL_ERROR,
+			"%s: Invalid builtin firmware\n", __func__);
+		goto _pt_firmware_cont_builtin_exit;
+	}
+
+	pt_debug(dev, DL_WARN, "%s: Found firmware\n", __func__);
+
+	upgrade = pt_check_firmware_version_builtin(dev, fw);
+	if (upgrade) {
+		_pt_firmware_cont(fw, dev);
+		ld->builtin_bin_fw_status = 0;
+		return;
+	}
+
+_pt_firmware_cont_builtin_exit:
+	if (fw)
+		release_firmware(fw);
+
+	ld->builtin_bin_fw_status = -EINVAL;
+}
+
+/*******************************************************************************
+ * FUNCTION: upgrade_firmware_from_class
+ *
+ * SUMMARY: Create the firmware class but don't actually load any FW to the
+ *	DUT. This creates all the sysfs nodes needed for a user to bootload
+ *	the DUT with their own bin file.
+ *
+ * RETURN:
+ *   0 = success
+ *
+ * PARAMETERS:
+ *  *dev     - pointer to device structure
+ ******************************************************************************/
+static int upgrade_firmware_from_class(struct device *dev)
+{
+	int retval;
+
+	pt_debug(dev, DL_INFO,
+		"%s: Enabling firmware class loader\n", __func__);
+
+	retval = request_firmware_nowait(THIS_MODULE, FW_ACTION_NOHOTPLUG,
+			dev_name(dev), dev, GFP_KERNEL, dev,
+			_pt_firmware_cont);
+	if (retval < 0) {
+		pt_debug(dev, DL_ERROR,
+			"%s: Fail request firmware class file load\n",
+			__func__);
+		return retval;
+	}
+
+	return 0;
+}
+
+#define FILENAME_LEN_MAX 64
+/*******************************************************************************
+ * FUNCTION: generate_firmware_filename
+ *
+ * SUMMARY: Generate firmware file name by panel id. Generates binary FW
+ *  filename as following:
+ *  - Panel ID not enabled: tt_fw.bin
+ *  - Panel ID enabled: tt_fw_pidXX.bin
+ *
+ * RETURN:
+ *   pointer to file name or null pointer if fail
+ *
+ * PARAMETERS:
+ *  *dev  - pointer to device structure
+ ******************************************************************************/
+static char *generate_firmware_filename(struct device *dev)
+{
+	char *filename;
+	u8 panel_id;
+
+	filename = kzalloc(FILENAME_LEN_MAX, GFP_KERNEL);
+	if (!filename)
+		return NULL;
+
+	panel_id = pt_get_panel_id(dev);
+	if (panel_id == PANEL_ID_NOT_ENABLED)
+		snprintf(filename, FILENAME_LEN_MAX, "%s", PT_FW_FILE_NAME);
+	else
+		snprintf(filename, FILENAME_LEN_MAX, "%s_pid%02X%s",
+			PT_FW_FILE_PREFIX, panel_id, PT_FW_FILE_SUFFIX);
+
+	pt_debug(dev, DL_INFO, "%s: Filename: %s\n",
+		__func__, filename);
+
+	return filename;
+}
+
+/*******************************************************************************
+ * FUNCTION: generate_silicon_id_firmware_filename
+ *
+ * SUMMARY: Generate firmware file name with the HW version prefix followed by
+ * panel id. Generates binary FW filename as following (where XXXX is the
+ * silicon ID):
+ *  - Panel ID not enabled: XXXX_tt_fw.bin
+ *  - Panel ID enabled: XXXX_tt_fw_pidXX.bin
+ *
+ * RETURN:
+ *   pointer to file name or null pointer if fail
+ *
+ * PARAMETERS:
+ *  *dev  - pointer to device structure
+ ******************************************************************************/
+static char *generate_silicon_id_firmware_filename(struct device *dev)
+{
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+	char *filename;
+	char si_id[5] = "DEAD";
+	u8 panel_id;
+
+	filename = kzalloc(FILENAME_LEN_MAX, GFP_KERNEL);
+	if (!filename)
+		return NULL;
+
+	panel_id = pt_get_panel_id(dev);
+	memcpy(si_id, cd->hw_version, 4);
+
+	if (panel_id == PANEL_ID_NOT_ENABLED)
+		snprintf(filename, FILENAME_LEN_MAX, "%s_%s", si_id,
+			PT_FW_FILE_NAME);
+	else
+		snprintf(filename, FILENAME_LEN_MAX, "%s_%s_pid%02X%s",
+			si_id, PT_FW_FILE_PREFIX, panel_id, PT_FW_FILE_SUFFIX);
+
+	pt_debug(dev, DL_INFO, "%s: Filename: %s\n", __func__, filename);
+
+	return filename;
+}
+
+#define MAX_FILE_NAMES 2
+/*******************************************************************************
+ * FUNCTION: upgrade_firmware_from_builtin
+ *
+ * SUMMARY: Create the firmware class load FW by searching the name of built-in
+ *	file. Then perform upgrade after getting the file.
+ *
+ * RETURN:
+ *   0 = success
+ *  !0 = failure
+ *
+ * PARAMETERS:
+ *  *dev     - pointer to device structure
+ ******************************************************************************/
+static int upgrade_firmware_from_builtin(struct device *dev)
+{
+	struct pt_loader_data *ld = pt_get_loader_data(dev);
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+	char *filename[MAX_FILE_NAMES];
+	int index = 0;
+	int retval;
+	const struct firmware *fw_entry = NULL;
+	int retry = 3;
+
+retry_bl:
+	pt_debug(dev, DL_WARN,
+		"%s: Enabling firmware class loader built-in\n",
+		__func__);
+
+	/* Load the supported file names in the search order */
+
+	/* 0 = Flash file name with optional PID "pt_fw<_PIDX>.bin" */
+	filename[0] = generate_firmware_filename(dev);
+	if (!filename[0]) {
+		pt_debug(dev, DL_ERROR,
+			"%s: ERROR - Could not generate filename\n", __func__);
+		return -ENOMEM;
+	}
+
+	/*
+	 * 1 = Flash file name with Silicon ID prefix and optional PID
+	 * "XXXX_pt_fw<_PIDX>.bin"
+	 */
+	filename[1] = generate_silicon_id_firmware_filename(dev);
+	if (!filename[1]) {
+		pt_debug(dev, DL_ERROR,
+			"%s: ERROR - Could not generate filename\n", __func__);
+		return -ENOMEM;
+	}
+
+	mutex_lock(&cd->firmware_class_lock);
+	while (index < MAX_FILE_NAMES) {
+		pt_debug(dev, DL_WARN, "%s: Request FW file %s\n", __func__,
+			filename[index]);
+#if (KERNEL_VERSION(3, 13, 0) > LINUX_VERSION_CODE)
+		retval = request_firmware(&fw_entry, filename[index], dev);
+#else
+		retval = request_firmware_direct(&fw_entry,
+			filename[index], dev);
+#endif
+		if (retval < 0) {
+			pt_debug(dev, DL_WARN, "%s: Fail request FW %s load\n",
+				__func__, filename[index]);
+		} else {
+			pt_debug(dev, DL_WARN, "%s: FW %s class file loading\n",
+				__func__, filename[index]);
+			break;
+		}
+		index++;
+	}
+
+	/* Proceed with the BL if a matching file was found */
+	if (index != MAX_FILE_NAMES) {
+		_pt_firmware_cont_builtin(fw_entry, dev);
+		/* An extra BL may be needed if default PID was wrong choice */
+		if ((cd->panel_id_support & PT_PANEL_ID_BY_SYS_INFO) &&
+				!ld->si && retry--) {
+			pt_debug(dev, DL_WARN, "%s: ATM - An extra BL may be needed\n",
+				__func__);
+			/* Free allocated memory */
+			index = 0;
+			while (index < MAX_FILE_NAMES)
+				kfree(filename[index++]);
+			/* Reset index to 0 */
+			index = 0;
+			mutex_unlock(&cd->firmware_class_lock);
+
+			/* Panel_ID coming from sysinfo, ensure we have it */
+			ld->si = cmd->request_sysinfo(dev);
+			goto retry_bl;
+		}
+		retval = ld->builtin_bin_fw_status;
+	}
+
+	index = 0;
+	while (index < MAX_FILE_NAMES)
+		kfree(filename[index++]);
+
+	mutex_unlock(&cd->firmware_class_lock);
+	return retval;
+}
+#endif /* CONFIG_TOUCHSCREEN_PARADE_BINARY_FW_UPGRADE */
+
+#ifndef TTDL_KERNEL_SUBMISSION
+#if PT_TTCONFIG_UPGRADE
+/*******************************************************************************
+ * FUNCTION: pt_write_config_row_
+ *
+ * SUMMARY: Allow to program the data block area that includes configuration
+ *  data, manufacturing data, design data.
+ *
+ * RETURN:
+ *   0 = success
+ *  !0 = failure
+ *
+ * PARAMETERS:
+ *  *dev         - pointer to device structure
+ *   ebid        - block id to determine the block name(configuration,etc)
+ *   row_number  - row number if written block
+ *   row_size    - row size of written data
+ *  *data        - pointer to the data to write
+ ******************************************************************************/
+static int pt_write_config_row_(struct device *dev, u8 ebid,
+		u16 row_number, u16 row_size, u8 *data)
+{
+	int rc;
+	u16 actual_write_len;
+
+	rc = cmd->nonhid_cmd->write_data_block(dev, row_number,
+			row_size, ebid, data, (u8 *)pt_data_block_security_key,
+			&actual_write_len);
+	if (rc) {
+		pt_debug(dev, DL_ERROR,
+			"%s: Fail Put EBID=%d row=%d cmd fail r=%d\n",
+			__func__, ebid, row_number, rc);
+		return rc;
+	}
+
+	if (actual_write_len != row_size) {
+		pt_debug(dev, DL_ERROR,
+			"%s: Fail Put EBID=%d row=%d wrong write size=%d\n",
+			__func__, ebid, row_number, actual_write_len);
+		rc = -EINVAL;
+	}
+
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_upgrade_ttconfig
+ *
+ * SUMMARY: Program ttconfig_data with following steps:
+ *  1) Suspend scanning
+ *  2) Write data to the data block
+ *  3) Verify the crc for data block
+ *  4) Resume scanning
+ *  5) Set up call back for calibration if required
+ *
+ * RETURN:
+ *   0 = success
+ *  !0 = failure
+ *
+ * PARAMETERS:
+ *  *dev            - pointer to device structure
+ *  *ttconfig_data  - pointer to the config data to write to data block
+ *   ttconfig_size  - size of config data to write
+ ******************************************************************************/
+static int pt_upgrade_ttconfig(struct device *dev,
+		const u8 *ttconfig_data, int ttconfig_size)
+{
+	struct pt_loader_data *ld = pt_get_loader_data(dev);
+	bool wait_for_calibration_complete = false;
+	u8 ebid = PT_TCH_PARM_EBID;
+	u16 row_size = PT_DATA_ROW_SIZE;
+	u16 table_size;
+	u16 row_count;
+	u16 residue;
+	u8 *row_buf;
+	u8 verify_crc_status;
+	u16 calculated_crc;
+	u16 stored_crc;
+	int rc = 0;
+	int i;
+
+	table_size = ttconfig_size;
+	row_count = table_size / row_size;
+	row_buf = (u8 *)ttconfig_data;
+	pt_debug(dev, DL_INFO, "%s: size:%d row_size=%d row_count=%d\n",
+		__func__, table_size, row_size, row_count);
+
+	pm_runtime_get_sync(dev);
+
+	rc = cmd->request_exclusive(dev, PT_LDR_REQUEST_EXCLUSIVE_TIMEOUT);
+	if (rc < 0)
+		goto exit;
+
+	rc = cmd->nonhid_cmd->suspend_scanning(dev, 0);
+	if (rc < 0)
+		goto release;
+
+	for (i = 0; i < row_count; i++) {
+		pt_debug(dev, DL_INFO, "%s: row=%d size=%d\n",
+			__func__, i, row_size);
+		rc = pt_write_config_row_(dev, ebid, i, row_size,
+				row_buf);
+		if (rc) {
+			pt_debug(dev, DL_ERROR, "%s: Fail put row=%d r=%d\n",
+				__func__, i, rc);
+			break;
+		}
+		row_buf += row_size;
+	}
+	if (!rc) {
+		residue = table_size % row_size;
+		pt_debug(dev, DL_WARN, "%s: row=%d size=%d\n",
+			__func__, i, residue);
+		rc = pt_write_config_row_(dev, ebid, i, residue,
+				row_buf);
+		row_count++;
+		if (rc)
+			pt_debug(dev, DL_ERROR, "%s: Fail put row=%d r=%d\n",
+				__func__, i, rc);
+	}
+
+	if (!rc)
+		pt_debug(dev, DL_WARN,
+			"%s: TT_CFG updated: rows:%d bytes:%d\n",
+			__func__, row_count, table_size);
+
+	rc = cmd->nonhid_cmd->verify_cfg_block_crc(dev, 0, ebid,
+			&verify_crc_status, &calculated_crc, &stored_crc);
+	if (rc || verify_crc_status)
+		pt_debug(dev, DL_ERROR,
+			"%s: CRC Failed, ebid=%d, status=%d, scrc=%X ccrc=%X\n",
+			__func__, ebid, verify_crc_status,
+			calculated_crc, stored_crc);
+	else
+		pt_debug(dev, DL_INFO,
+			"%s: CRC PASS, ebid=%d, status=%d, scrc=%X ccrc=%X\n",
+			__func__, ebid, verify_crc_status,
+			calculated_crc, stored_crc);
+
+	rc = cmd->nonhid_cmd->resume_scanning(dev, 0);
+	if (rc < 0)
+		goto release;
+
+	if (ld->loader_pdata &&
+			(ld->loader_pdata->flags
+			 & PT_LOADER_FLAG_CALIBRATE_AFTER_TTCONFIG_UPGRADE)) {
+#if (KERNEL_VERSION(3, 13, 0) <= LINUX_VERSION_CODE)
+		reinit_completion(&ld->calibration_complete);
+#else
+		INIT_COMPLETION(ld->calibration_complete);
+#endif
+		/* set up call back for startup */
+		pt_debug(dev, DL_INFO, "%s: Adding callback for calibration\n",
+			__func__);
+		rc = cmd->subscribe_attention(dev, PT_ATTEN_STARTUP,
+			PT_LOADER_NAME, pt_calibration_attention, 0);
+		if (rc) {
+			pt_debug(dev, DL_ERROR,
+				"%s: Failed adding callback for calibration\n",
+				__func__);
+			pt_debug(dev, DL_ERROR,
+				"%s: No calibration will be performed\n",
+				__func__);
+			rc = 0;
+		} else
+			wait_for_calibration_complete = true;
+	}
+
+release:
+	cmd->release_exclusive(dev);
+
+exit:
+	if (!rc)
+		cmd->request_enum(dev, true);
+
+	pm_runtime_put_sync(dev);
+
+	if (wait_for_calibration_complete)
+		wait_for_completion(&ld->calibration_complete);
+
+	return rc;
+}
+#endif /* PT_TTCONFIG_UPGRADE */
+
+#ifdef CONFIG_TOUCHSCREEN_PARADE_PLATFORM_TTCONFIG_UPGRADE
+/*******************************************************************************
+ * FUNCTION: pt_get_ttconfig_crc
+ *
+ * SUMMARY: Get crc from ttconfig data.
+ *
+ * RETURN:
+ *   0 = success
+ *  !0 = failure
+ *
+ * PARAMETERS:
+ *  *dev            - pointer to device structure
+ *  *ttconfig_data  - pointer to the config data
+ *   ttconfig_size  - size of config data
+ *  *crc            - pointer to the crc of configure to be stored
+ ******************************************************************************/
+static int pt_get_ttconfig_crc(struct device *dev,
+		const u8 *ttconfig_data, int ttconfig_size, u16 *crc)
+{
+	u16 crc_loc;
+
+	crc_loc = get_unaligned_le16(&ttconfig_data[2]);
+	if (ttconfig_size < crc_loc + 2)
+		return -EINVAL;
+
+	*crc = get_unaligned_le16(&ttconfig_data[crc_loc]);
+
+	return 0;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_get_ttconfig_version
+ *
+ * SUMMARY: Get version number from ttconfig data.
+ *
+ * RETURN:
+ *   0 = success
+ *  !0 = failure
+ *
+ * PARAMETERS:
+ *  *dev            - pointer to device structure
+ *  *ttconfig_data  - pointer to the config data
+ *   ttconfig_size  - size of config data
+ *  *version        - pointer to the version of configure to be stored
+ ******************************************************************************/
+static int pt_get_ttconfig_version(struct device *dev,
+		const u8 *ttconfig_data, int ttconfig_size, u16 *version)
+{
+	if (ttconfig_size < PT_TTCONFIG_VERSION_OFFSET
+			+ PT_TTCONFIG_VERSION_SIZE)
+		return -EINVAL;
+
+	*version = get_unaligned_le16(
+		&ttconfig_data[PT_TTCONFIG_VERSION_OFFSET]);
+
+	return 0;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_check_ttconfig_version
+ *
+ * SUMMARY: Check the configure version and crc value to determine whether to
+ *  upgrade,the upgrade conditions as following:
+ *      1) To upgrade if the config version is newer than current config, but
+ *         this check is based on the flag in loader plarform data.
+ *      2) To upgrade if config CRC is different.
+ *      3) Don't upgrade when can't match any of above conditions.
+ *
+ * RETURN:
+ *   0: Don't upgrade
+ *   1: Upgrade
+ *
+ * PARAMETERS:
+ *  *dev            - pointer to device structure
+ *  *ttconfig_data  - pointer to the config data
+ *   ttconfig_size  - size of config data
+ ******************************************************************************/
+static int pt_check_ttconfig_version(struct device *dev,
+		const u8 *ttconfig_data, int ttconfig_size)
+{
+	struct pt_loader_data *ld = pt_get_loader_data(dev);
+	u16 cfg_crc_new;
+	int rc;
+
+	if (!ld->si)
+		return 0;
+
+	/* Check for config version */
+	if (ld->loader_pdata->flags &
+			PT_LOADER_FLAG_CHECK_TTCONFIG_VERSION) {
+		u16 cfg_ver_new;
+
+		rc = pt_get_ttconfig_version(dev, ttconfig_data,
+				ttconfig_size, &cfg_ver_new);
+		if (rc)
+			return 0;
+
+		pt_debug(dev, DL_INFO, "%s: img_ver:0x%04X new_ver:0x%04X\n",
+			__func__, ld->si->ttdata.fw_ver_conf, cfg_ver_new);
+
+		/* Check if config version is newer */
+		if (cfg_ver_new > ld->si->ttdata.fw_ver_conf) {
+			pt_debug(dev, DL_WARN,
+			"%s: Config version newer, will upgrade\n", __func__);
+			return 1;
+		}
+
+		pt_debug(dev, DL_WARN,
+			"%s: Config version is identical or older, will NOT upgrade\n",
+			__func__);
+	/* Check for config CRC */
+	} else {
+		rc = pt_get_ttconfig_crc(dev, ttconfig_data,
+				ttconfig_size, &cfg_crc_new);
+		if (rc)
+			return 0;
+
+		pt_debug(dev, DL_INFO, "%s: img_crc:0x%04X new_crc:0x%04X\n",
+			__func__, ld->si->ttconfig.crc, cfg_crc_new);
+
+		if (cfg_crc_new != ld->si->ttconfig.crc) {
+			pt_debug(dev, DL_WARN,
+				"%s: Config CRC different, will upgrade\n",
+				__func__);
+			return 1;
+		}
+
+		pt_debug(dev, DL_WARN,
+			"%s: Config CRC equal, will NOT upgrade\n", __func__);
+	}
+
+	return 0;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_check_ttconfig_version_platform
+ *
+ * SUMMARY: To call the function pt_check_ttconfig_version() to determine
+ *  whether to load config if the firmware version match with current firmware.
+ *
+ * RETURN:
+ *   0: Don't upgrade
+ *   1: Upgrade
+ *
+ * PARAMETERS:
+ *  *dev       - pointer to device structure
+ *  *ttconfig  - pointer to touch_config structure
+ ******************************************************************************/
+static int pt_check_ttconfig_version_platform(struct device *dev,
+		struct pt_touch_config *ttconfig)
+{
+	struct pt_loader_data *ld = pt_get_loader_data(dev);
+	u32 fw_ver_config;
+	u32 fw_revctrl_config;
+
+	if (!ld->si) {
+		pt_debug(dev, DL_INFO,
+			"%s: No firmware info found, DUT FW may be corrupted\n",
+			__func__);
+		return 0;
+	}
+
+	fw_ver_config = get_unaligned_be16(ttconfig->fw_ver + 2);
+	/* 4 middle bytes are not used */
+	fw_revctrl_config = get_unaligned_be32(ttconfig->fw_ver + 8);
+
+	/* FW versions should match */
+	if (pt_check_firmware_version(dev, fw_ver_config,
+			fw_revctrl_config)) {
+		pt_debug(dev, DL_ERROR,
+			"%s: FW versions mismatch\n", __func__);
+		return 0;
+	}
+
+	/* Check PowerOn Self Test, TT_CFG CRC bit */
+	if ((ld->si->ttdata.post_code & PT_POST_TT_CFG_CRC_MASK) == 0) {
+		pt_debug(dev, DL_ERROR,
+			"%s: POST, TT_CFG failed (%X), will upgrade\n",
+			__func__, ld->si->ttdata.post_code);
+		return 1;
+	}
+
+	return pt_check_ttconfig_version(dev, ttconfig->param_regs->data,
+			ttconfig->param_regs->size);
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_get_platform_ttconfig
+ *
+ * SUMMARY: To get the pointer of right touch_config structure by panel id.
+ *
+ * RETURN:
+ *   pointer to touch_config structure or null pointer if fail
+ *
+ * PARAMETERS:
+ *  *dev  - pointer to device structure
+ ******************************************************************************/
+static struct pt_touch_config *pt_get_platform_ttconfig(
+		struct device *dev)
+{
+	struct pt_loader_data *ld = pt_get_loader_data(dev);
+	struct pt_touch_config **ttconfigs;
+	struct pt_touch_config *ttconfig;
+	u8 panel_id;
+
+	panel_id = pt_get_panel_id(dev);
+	if (panel_id == PANEL_ID_NOT_ENABLED) {
+		/* TODO: Make debug message */
+		pt_debug(dev, DL_INFO,
+			"%s: Panel ID not enabled, using legacy ttconfig\n",
+			__func__);
+		return ld->loader_pdata->ttconfig;
+	}
+
+	ttconfigs = ld->loader_pdata->ttconfigs;
+	if (!ttconfigs)
+		return NULL;
+
+	/* Find TT config according to the Panel ID */
+	while ((ttconfig = *ttconfigs++)) {
+		if (ttconfig->panel_id == panel_id) {
+			/* TODO: Make debug message */
+			pt_debug(dev, DL_INFO,
+				"%s: Found matching ttconfig:%p with Panel ID: 0x%02X\n",
+				__func__, ttconfig, ttconfig->panel_id);
+			return ttconfig;
+		}
+		pt_debug(dev, DL_ERROR,
+			"%s: Found mismatching ttconfig:%p with Panel ID: 0x%02X\n",
+			__func__, ttconfig, ttconfig->panel_id);
+	}
+
+	return NULL;
+}
+
+/*******************************************************************************
+ * FUNCTION: upgrade_ttconfig_from_platform
+ *
+ * SUMMARY: Get touch_firmware structure and perform upgrade if pass the
+ *  firmware version check.
+ *
+ * RETURN:
+ *   0 = success
+ *  !0 = failure
+ *
+ * PARAMETERS:
+ *  *dev     - pointer to device structure
+ *   forced  - flag to force upgrade(1:force to upgrade)
+ ******************************************************************************/
+static int upgrade_ttconfig_from_platform(struct device *dev)
+{
+	struct pt_loader_data *ld = pt_get_loader_data(dev);
+	struct pt_touch_config *ttconfig;
+	struct touch_settings *param_regs;
+
+	int rc = -ENODEV;
+	int upgrade;
+
+	if (!ld->loader_pdata) {
+		pt_debug(dev, DL_ERROR,
+			"%s: No loader platform data\n", __func__);
+		return rc;
+	}
+
+	ttconfig = pt_get_platform_ttconfig(dev);
+	if (!ttconfig) {
+		pt_debug(dev, DL_ERROR, "%s: No ttconfig data\n", __func__);
+		return rc;
+	}
+
+	param_regs = ttconfig->param_regs;
+	if (!param_regs) {
+		pt_debug(dev, DL_ERROR, "%s: No touch parameters\n",
+			__func__);
+		return rc;
+	}
+
+	if (!param_regs->data || !param_regs->size) {
+		pt_debug(dev, DL_ERROR,
+			"%s: Invalid touch parameters\n", __func__);
+		return rc;
+	}
+
+	if (!ttconfig->fw_ver || !ttconfig->fw_vsize) {
+		pt_debug(dev, DL_ERROR,
+			"%s: Invalid FW version for touch parameters\n",
+			__func__);
+		return rc;
+	}
+
+	upgrade = pt_check_ttconfig_version_platform(dev, ttconfig);
+	if (upgrade)
+		return pt_upgrade_ttconfig(dev, param_regs->data,
+				param_regs->size);
+
+	return rc;
+}
+#endif /* CONFIG_TOUCHSCREEN_PARADE_PLATFORM_TTCONFIG_UPGRADE */
+
+#ifdef CONFIG_TOUCHSCREEN_PARADE_MANUAL_TTCONFIG_UPGRADE
+/*******************************************************************************
+ * FUNCTION: pt_config_data_write
+ *
+ * SUMMARY: The write method for the config_data sysfs node. The passed
+ *  in data (config file) is written to the config_data buffer.
+ *
+ * RETURN: Size of passed in buffer is success
+ *
+ * PARAMETERS:
+ *  *filp     - pointer to file structure
+ *  *kobj     - pointer to kobject structure
+ *  *bin_attr - pointer to bin_attribute structure
+ *   buf      - pointer to cmd input buffer
+ *   offset   - offset index to store input buffer
+ *   count    - size of data in buffer
+ ******************************************************************************/
+static ssize_t pt_config_data_write(struct file *filp,
+		struct kobject *kobj, struct bin_attribute *bin_attr,
+		char *buf, loff_t offset, size_t count)
+{
+	struct device *dev = container_of(kobj, struct device, kobj);
+	struct pt_loader_data *data = pt_get_loader_data(dev);
+	u8 *p;
+
+	pt_debug(dev, DL_INFO, "%s: offset:%lld count:%zu\n",
+		__func__, offset, count);
+
+	mutex_lock(&data->config_lock);
+
+	if (!data->config_loading) {
+		mutex_unlock(&data->config_lock);
+		return -ENODEV;
+	}
+
+	p = krealloc(data->config_data, offset + count, GFP_KERNEL);
+	if (!p) {
+		kfree(data->config_data);
+		data->config_data = NULL;
+		mutex_unlock(&data->config_lock);
+		return -ENOMEM;
+	}
+	data->config_data = p;
+
+	memcpy(&data->config_data[offset], buf, count);
+	data->config_size += count;
+
+	mutex_unlock(&data->config_lock);
+
+	return count;
+}
+
+static struct bin_attribute bin_attr_config_data = {
+	.attr = {
+		.name = "config_data",
+		.mode = 0200,
+	},
+	.size = 0,
+	.write = pt_config_data_write,
+};
+
+/*******************************************************************************
+ * FUNCTION: pt_verify_ttconfig_binary
+ *
+ * SUMMARY: Perform a simple size check if the firmware version match.And
+ *  calculate the start pointer of config data to write and the size to write.
+ *
+ * RETURN:
+ *   0 = success
+ *  !0 = failure
+ *
+ * PARAMETERS:
+ *   *dev              - pointer to device structure
+ *   *bin_config_data  - pointer to binary config data
+ *    bin_config_size  - size of binary config data
+ *  **start            - double pointer to config data where to be written
+ *   *len              - pointer to the size of config data to store
+ ******************************************************************************/
+static int pt_verify_ttconfig_binary(struct device *dev,
+		u8 *bin_config_data, int bin_config_size, u8 **start, int *len)
+{
+	struct pt_loader_data *ld = pt_get_loader_data(dev);
+	int header_size;
+	u16 config_size;
+	u32 fw_ver_config;
+	u32 fw_revctrl_config;
+
+	if (!ld->si) {
+		pt_debug(dev, DL_ERROR,
+			"%s: No firmware info found, DUT FW may be corrupted\n",
+			__func__);
+		return -ENODEV;
+	}
+
+	/*
+	 * We need 11 bytes for FW version control info and at
+	 * least 6 bytes in config (Length + Max Length + CRC)
+	 */
+	header_size = bin_config_data[0] + 1;
+	if (header_size < 11 || header_size >= bin_config_size - 6) {
+		pt_debug(dev, DL_ERROR,
+			"%s: Invalid header size %d\n", __func__,
+			header_size);
+		return -EINVAL;
+	}
+
+	fw_ver_config = get_unaligned_be16(&bin_config_data[1]);
+	/* 4 middle bytes are not used */
+	fw_revctrl_config = get_unaligned_be32(&bin_config_data[7]);
+
+	/* FW versions should match */
+	if (pt_check_firmware_version(dev, fw_ver_config,
+			fw_revctrl_config)) {
+		pt_debug(dev, DL_ERROR,
+			"%s: FW versions mismatch\n", __func__);
+		return -EINVAL;
+	}
+
+	config_size = get_unaligned_le16(&bin_config_data[header_size]);
+	/* Perform a simple size check (2 bytes for CRC) */
+	if (config_size != bin_config_size - header_size - 2) {
+		pt_debug(dev, DL_ERROR,
+			"%s: Config size invalid\n", __func__);
+		return -EINVAL;
+	}
+
+	*start = &bin_config_data[header_size];
+	*len = bin_config_size - header_size;
+
+	return 0;
+}
+
+/*
+ * 1: Start loading TT Config
+ * 0: End loading TT Config and perform upgrade
+ *-1: Exit loading
+ */
+
+/*******************************************************************************
+ * FUNCTION: pt_config_loading_store
+ *
+ * SUMMARY: The store method for the config_loading sysfs node. The
+ *  passed in value controls if config loading is performed.
+ *
+ * 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_config_loading_store(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t size)
+{
+	struct pt_loader_data *ld = pt_get_loader_data(dev);
+	long value;
+	u8 *start;
+	int length;
+	int rc;
+
+	rc = kstrtol(buf, 10, &value);
+	if (rc < 0 || value < -1 || value > 1) {
+		pt_debug(dev, DL_ERROR, "%s: Invalid value\n", __func__);
+		return size;
+	}
+
+	mutex_lock(&ld->config_lock);
+
+	if (value == 1)
+		ld->config_loading = true;
+	else if (value == -1)
+		ld->config_loading = false;
+	else if (value == 0 && ld->config_loading) {
+		ld->config_loading = false;
+		if (ld->config_size == 0) {
+			pt_debug(dev, DL_ERROR,
+				"%s: No config data\n", __func__);
+			goto exit_free;
+		}
+
+		rc = pt_verify_ttconfig_binary(dev,
+				ld->config_data, ld->config_size,
+				&start, &length);
+		if (rc)
+			goto exit_free;
+
+		rc = pt_upgrade_ttconfig(dev, start, length);
+	}
+
+exit_free:
+	kfree(ld->config_data);
+	ld->config_data = NULL;
+	ld->config_size = 0;
+
+	mutex_unlock(&ld->config_lock);
+
+	if (rc)
+		return rc;
+
+	return size;
+}
+
+static DEVICE_ATTR(config_loading, 0200,
+	NULL, pt_config_loading_store);
+#endif /* CONFIG_TOUCHSCREEN_PARADE_MANUAL_TTCONFIG_UPGRADE */
+#endif /* !TTDL_KERNEL_SUBMISSION */
+
+#ifdef CONFIG_TOUCHSCREEN_PARADE_PLATFORM_FW_UPGRADE
+/*******************************************************************************
+ * FUNCTION: pt_forced_upgrade_store
+ *
+ * SUMMARY: The store method for the forced_upgrade sysfs node. The firmware
+ *  loading is forced to performed with platform upgrade strategy.
+ *
+ * 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_forced_upgrade_store(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t size)
+{
+	int rc = upgrade_firmware_from_platform(dev, true);
+
+	if (rc)
+		return rc;
+	return size;
+}
+
+static DEVICE_ATTR(forced_upgrade, 0200,
+	NULL, pt_forced_upgrade_store);
+#endif
+
+#ifdef CONFIG_TOUCHSCREEN_PARADE_BINARY_FW_UPGRADE
+/*******************************************************************************
+ * FUNCTION: pt_manual_upgrade_store
+ *
+ * SUMMARY: The store method for the forced_upgrade sysfs node that it is
+ *  caller for function upgrade_firmware_from_class() to allow upgrade firmware
+ *  manually.
+ *
+ * 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_manual_upgrade_store(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t size)
+{
+	struct pt_loader_data *ld = pt_get_loader_data(dev);
+	u32 input_data[2] = {0};
+	int length;
+	int rc = 0;
+
+	length = cmd->parse_sysfs_input(dev, buf, size, input_data,
+			ARRAY_SIZE(input_data));
+
+	if (length != 1) {
+		pt_debug(dev, DL_WARN, "%s: Invalid number of arguments\n",
+			__func__);
+		rc = -EINVAL;
+		goto exit;
+	}
+
+	if (input_data[0] < 0 || input_data[0] > 1) {
+		pt_debug(dev, DL_WARN, "%s: Invalid arguments\n", __func__);
+		rc = -EINVAL;
+		goto exit;
+	}
+
+	_pt_update_write_file_status(dev, UPDATE_FW_IDLE);
+	if (ld->is_manual_upgrade_enabled) {
+		rc = -EBUSY;
+		goto exit;
+	}
+
+	ld->is_manual_upgrade_enabled = 1;
+
+	rc = upgrade_firmware_from_class(ld->dev);
+	if (rc < 0)
+		ld->is_manual_upgrade_enabled = 0;
+
+exit:
+	if (rc)
+		return rc;
+	return size;
+}
+
+#ifndef TTDL_KERNEL_SUBMISSION
+static DEVICE_ATTR(manual_upgrade, 0200, NULL, pt_manual_upgrade_store);
+#endif /* !TTDL_KERNEL_SUBMISSION */
+#endif /* CONFIG_TOUCHSCREEN_PARADE_BINARY_FW_UPGRADE */
+
+/*******************************************************************************
+ * FUNCTION: pt_fw_and_config_upgrade
+ *
+ * SUMMARY: Perform all methods for firmware upgrade and config upgrade
+ *  according to the definition of macro.
+ *
+ * PARAMETERS:
+ *  *work_struct  - pointer to work_struct structure
+ ******************************************************************************/
+static void pt_fw_and_config_upgrade(
+		struct work_struct *fw_and_config_upgrade)
+{
+	struct pt_loader_data *ld = container_of(fw_and_config_upgrade,
+			struct pt_loader_data, fw_and_config_upgrade);
+	struct device *dev = ld->dev;
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+	int retry = 200;
+#if PT_FW_UPGRADE \
+	|| defined(CONFIG_TOUCHSCREEN_PARADE_PLATFORM_TTCONFIG_UPGRADE)
+	u8 dut_gen = cmd->request_dut_generation(dev);
+#endif
+
+	/*
+	 * Since the resume_scan command in pt_probe_complete() has
+	 * no protection,it may cause problem for the commands in fw
+	 * upgrade process during probe. Waiting for the probe to
+	 * complete before performing fw upgrade can avoid this failure.
+	 */
+	while (!cd->core_probe_complete && retry--)
+		msleep(20);
+
+	ld->si = cmd->request_sysinfo(dev);
+	if (!ld->si)
+		pt_debug(dev, DL_ERROR,
+			"%s: Fail get sysinfo pointer from core\n",
+			__func__);
+#if !PT_FW_UPGRADE
+	pt_debug(dev, DL_INFO,
+		"%s: No FW upgrade method selected!\n", __func__);
+#endif
+
+#ifdef CONFIG_TOUCHSCREEN_PARADE_PLATFORM_FW_UPGRADE
+	if (dut_gen == DUT_PIP1_ONLY) {
+		if (!upgrade_firmware_from_platform(dev, false))
+			return;
+	}
+#endif
+
+#ifdef CONFIG_TOUCHSCREEN_PARADE_BINARY_FW_UPGRADE
+	if (dut_gen == DUT_PIP2_CAPABLE) {
+		if (!pt_pip2_upgrade_firmware_from_builtin(dev))
+			return;
+		pt_debug(dev, DL_WARN, "%s: Builtin FW upgrade failed\n",
+			__func__);
+	} else {
+		if (!upgrade_firmware_from_builtin(dev))
+			return;
+	}
+#endif
+
+#ifdef CONFIG_TOUCHSCREEN_PARADE_PLATFORM_TTCONFIG_UPGRADE
+	if (dut_gen == DUT_PIP1_ONLY) {
+		if (!upgrade_ttconfig_from_platform(dev))
+			return;
+	}
+#endif
+}
+
+#ifdef CONFIG_TOUCHSCREEN_PARADE_BINARY_FW_UPGRADE
+#ifdef TTDL_DIAGNOSTICS
+/*******************************************************************************
+ * FUNCTION: _pt_pip2_get_flash_info
+ *
+ * SUMMARY: Sends a FLASH_INFO command to the DUT logging the results to kmsg
+ *
+ * PARAMETERS:
+ *	*dev - pointer to device structure
+ *	*read_buf - pointer to the read buffer array to store the response
+ ******************************************************************************/
+static void _pt_pip2_get_flash_info(struct device *dev, u8 *read_buf)
+{
+	u16 actual_read_len;
+	int ret;
+
+	/* Get flash info for debugging information */
+	ret = cmd->nonhid_cmd->pip2_send_cmd(dev,
+		PT_CORE_CMD_UNPROTECTED, PIP2_CMD_ID_FLASH_INFO,
+		NULL, 0, read_buf, &actual_read_len);
+	if (!ret) {
+		pt_debug(dev, DL_DEBUG,
+			"%s --- FLASH Information ---\n", __func__);
+		pt_debug(dev, DL_DEBUG,
+			"%s Manufacturer ID: 0x%02x\n",
+			__func__, read_buf[PIP2_RESP_BODY_OFFSET]);
+		pt_debug(dev, DL_DEBUG,
+			"%s Memory Type    : 0x%02x\n",
+			__func__, read_buf[PIP2_RESP_BODY_OFFSET + 1]);
+		pt_debug(dev, DL_DEBUG,
+			"%s Num Sectors    : 0x%02x%02x%02x%02x\n",
+			__func__, read_buf[PIP2_RESP_BODY_OFFSET + 2],
+			read_buf[PIP2_RESP_BODY_OFFSET + 3],
+			read_buf[PIP2_RESP_BODY_OFFSET + 4],
+			read_buf[PIP2_RESP_BODY_OFFSET + 5]);
+		pt_debug(dev, DL_DEBUG,
+			"%s Sectors Size   : 0x%02x%02x%02x%02x\n",
+			__func__, read_buf[PIP2_RESP_BODY_OFFSET + 6],
+			read_buf[PIP2_RESP_BODY_OFFSET + 7],
+			read_buf[PIP2_RESP_BODY_OFFSET + 8],
+			read_buf[PIP2_RESP_BODY_OFFSET + 9]);
+		pt_debug(dev, DL_DEBUG,
+			"%s Page Size      : 0x%02x%02x%02x%02x\n",
+			__func__, read_buf[PIP2_RESP_BODY_OFFSET + 10],
+			read_buf[PIP2_RESP_BODY_OFFSET + 11],
+			read_buf[PIP2_RESP_BODY_OFFSET + 12],
+			read_buf[PIP2_RESP_BODY_OFFSET + 13]);
+		if (actual_read_len > 21) {
+			pt_debug(dev, DL_DEBUG,
+				"%s Status Reg1    : 0x%02x\n",
+				__func__,
+				read_buf[PIP2_RESP_BODY_OFFSET + 14]);
+			pt_debug(dev, DL_DEBUG,
+				"%s Status Reg2    : 0x%02x\n",
+				__func__,
+				read_buf[PIP2_RESP_BODY_OFFSET + 15]);
+		}
+	}
+}
+#endif /* TTDL_DIAGNOSTICS */
+
+/*******************************************************************************
+ * FUNCTION: _pt_pip2_file_write_packet
+ *
+ * SUMMARY: Using the BL PIP2 commands to write a file.
+ *
+ * NOTE#1: This function support No Interrupt Solution and switch
+ *  "pip2_send_cmd" function according to the global variable "bl_with_no_int".
+ * NOTE#2: The maximum write len is limited to 256 as well as the total buffer
+ *  size is limited as PT_MAX_PIP2_MSG_SIZE.
+ *
+ * RETURNS:
+ *   0 = success
+ *  !0 = failure
+ *
+ * PARAMETERS:
+ *  *dev       - pointer to device structure
+ *   file_hd   - file handle for file operation
+ *  *write_buf - pointer of buffer to write
+ *   write_len - length to write
+ *  *status    - pointer to store the STATUS in response
+ ******************************************************************************/
+static int _pt_pip2_file_write_packet(struct device *dev, u8 file_hd,
+			       u8 *write_buf, u16 write_len, u8 *status)
+{
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+	PIP2_SEND_CMD pip2_send_cmd;
+	int ret = 0;
+	u16 actual_read_len = 0;
+	u8 read_buf[PT_MAX_PIP2_MSG_SIZE];
+	u8 buf[PT_MAX_PIP2_MSG_SIZE];
+
+	if (write_len > PIP2_FILE_WRITE_MAX_LEN_PER_PACKET) {
+		pt_debug(dev, DL_ERROR, "%s write_len (%d) is over size\n",
+			 __func__, write_len);
+		return -EINVAL;
+	}
+	if (cd->bl_with_no_int)
+		pip2_send_cmd = cmd->nonhid_cmd->pip2_send_cmd_no_int;
+	else
+		pip2_send_cmd = cmd->nonhid_cmd->pip2_send_cmd;
+
+	buf[0] = file_hd;
+	memcpy(&buf[1], write_buf, write_len);
+	ret = pip2_send_cmd(dev, PT_CORE_CMD_UNPROTECTED,
+			  PIP2_CMD_ID_FILE_WRITE, buf, write_len + 1,
+			  read_buf, &actual_read_len);
+	if (status)
+		*status = read_buf[PIP2_RESP_STATUS_OFFSET];
+	return ret;
+}
+
+/*******************************************************************************
+ * FUNCTION: _pt_pip2_execute_app
+ *
+ * SUMMARY: Using the BL PIP2 commands to executes an image downloaded into the
+ *  SRAM.
+ *
+ * RETURNS:
+ *   0 = success
+ *  !0 = failure
+ *
+ * PARAMETERS:
+ *  *dev       - pointer to device structure
+ *  *status    - pointer to store the STATUS in response
+ ******************************************************************************/
+static int _pt_pip2_execute_app(struct device *dev, u8 *status)
+{
+	int ret = 0;
+	u16 actual_read_len = 0;
+	u8 read_buf[PT_MAX_PIP2_MSG_SIZE];
+
+	ret = cmd->nonhid_cmd->pip2_send_cmd(dev, PT_CORE_CMD_UNPROTECTED,
+					     PIP2_CMD_ID_EXECUTE, NULL, 0,
+					     read_buf, &actual_read_len);
+	if (status)
+		*status = read_buf[PIP2_RESP_STATUS_OFFSET];
+	return ret;
+}
+
+/*******************************************************************************
+ * FUNCTION: _pt_pip2_log_last_error
+ *
+ * SUMMARY: Sends a STATUS command to the DUT logging the results until all
+ *	errors are cleared. Also sends a GET_LAST_ERRNO to get any Boot errors.
+ *	This must be sent after the STATUS flush in order not to have this
+ *	command cause another error.
+ *
+ * NOTE: This function support No Interrupt Solution and switch "pip2_send_cmd"
+ *  function according to the global variable "bl_with_no_int".
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *	*dev - pointer to device structure
+ *	*read_buf - pointer to the read buffer array to store the response
+ ******************************************************************************/
+static int _pt_pip2_log_last_error(struct device *dev, u8 *read_buf)
+{
+	u16 actual_read_len;
+	u8 loop = 5;
+	u8 info = 0xFF;
+	u8 error = 0xFF;
+	int ret;
+	PIP2_SEND_CMD pip2_send_cmd;
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+
+	if (cd->bl_with_no_int)
+		pip2_send_cmd = cmd->nonhid_cmd->pip2_send_cmd_no_int;
+	else
+		pip2_send_cmd = cmd->nonhid_cmd->pip2_send_cmd;
+	/*
+	 * Send the STATUS command until no errors are found.
+	 * The BL will store an error code for each layer of the stack,
+	 * and each read will return one error.
+	 */
+	while (loop > 0 && error) {
+
+		ret = pip2_send_cmd(dev,
+			PT_CORE_CMD_UNPROTECTED, PIP2_CMD_ID_STATUS,
+			NULL, 0, read_buf, &actual_read_len);
+
+		if (!ret) {
+			info  = (u8)read_buf[PIP2_RESP_BODY_OFFSET];
+			error = (u8)read_buf[PIP2_RESP_BODY_OFFSET + 1];
+
+			pt_debug(dev, DL_ERROR,
+				"%s: STATUS: Status=0x%02X BOOT=%d BUSY=%d INT=%d ERR_PHY=%d ERR_REG=%d ERROR=0x%02X",
+				__func__,
+				(u8)read_buf[PIP2_RESP_STATUS_OFFSET],
+				info & 0x01,
+				(info & 0x02) >> 1,
+				(info & 0x04) >> 2,
+				(info & 0x18) >> 3,
+				(info & 0xE0) >> 5,
+				error);
+		}
+		loop--;
+	}
+
+	/* Send the GET_LAST_ERROR command to get the last BL startup error */
+	ret = pip2_send_cmd(dev,
+		PT_CORE_CMD_UNPROTECTED, PIP2_CMD_ID_GET_LAST_ERRNO,
+		NULL, 0, read_buf, &actual_read_len);
+	if (!ret) {
+		pt_debug(dev, DL_ERROR,
+			"%s: GET_LAST_ERR: Status=0x%02X ERRNO=0x%02X BOOTMODE=%d\n",
+			__func__,
+			(u8)read_buf[PIP2_RESP_STATUS_OFFSET],
+			(u8)read_buf[PIP2_RESP_BODY_OFFSET],
+			(u8)read_buf[PIP2_RESP_BODY_OFFSET + 1]);
+	}
+
+	return ret;
+}
+
+/*******************************************************************************
+ * FUNCTION: _pt_pip2_file_write_packet_and_log_err
+ *
+ * SUMMARY: Wrapper function for File Write. If meet failure, it will call
+ *  function _pt_pip2_log_last_error() and do a retry within 3x.
+ *
+ * NOTE: This function support No Interrupt Solution and switch "pip2_send_cmd"
+ *  function according to the global variable "bl_with_no_int".
+ *
+ * RETURN:
+ *   0 = success
+ *  !0 = failure
+ *
+ * PARAMETERS:
+ *  *dev        - pointer to device structure
+ *   file_hd    - file handle for file operation
+ *  *write_buf  - pointer of buffer to write
+ *   write_len  - length to write
+ *   last_write - flag for last_write (1: is last write)
+ ******************************************************************************/
+#define PIP2_FILE_WRITE_RETRY_MAX (3)
+static int _pt_pip2_file_write_packet_and_log_err(struct device *dev,
+			u8 file_hd, u8 *write_buf, u16 write_len, u8 last_write)
+{
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+	int ret = 0;
+	int retry_packet = 0;
+	u8 read_buf[PT_MAX_PIP2_MSG_SIZE];
+	u8 status = 0;
+
+	do {
+		ret = _pt_pip2_file_write_packet(dev, file_hd, write_buf,
+					  write_len, &status);
+		/* Write cmd successful with a fail status */
+		if (ret) {
+			pt_debug(dev, DL_ERROR, "%s %d - Packet cmd error\n",
+				 __func__, ret);
+		} else if (status) {
+			/*
+			 * The last time through the loop when remain_bytes =
+			 * write_len, no partial payload will remain, the last
+			 * successful write (when writing to RAM) will respond
+			 * with EOF status, writing to FLASH will respond with a
+			 * standard success status of 0x00
+			 */
+			if (last_write && (file_hd == PIP2_RAM_FILE) &&
+			    (status == PIP2_RSP_ERR_END_OF_FILE)) {
+				pt_debug(dev, DL_WARN,
+					 "%s Last write, ret = 0x%02x\n",
+					 __func__, status);
+				status = 0;
+				break;
+			}
+
+			pt_debug(dev, DL_ERROR, "%s %d - Packet status error\n",
+				 __func__, status);
+			/* Manually assign ret to negative value */
+			ret = -EINVAL;
+		} else {
+			/* No issue is seen, and break the loop */
+			break;
+		}
+
+#ifdef TTDL_DIAGNOSTICS
+		cd->bl_retry_packet_count++;
+		cmd->request_toggle_err_gpio(dev, PT_ERR_GPIO_BL_RETRY_PACKET);
+		pt_debug(dev, DL_WARN, "%s: === Retry Packet #%d ===\n",
+			 __func__, retry_packet);
+#endif
+		/* Get and log the last error(s) */
+		_pt_pip2_log_last_error(dev, read_buf);
+	} while (retry_packet++ < PIP2_FILE_WRITE_RETRY_MAX);
+
+	return ret;
+}
+
+/*******************************************************************************
+ * FUNCTION: _pt_pip2_check_fw_ver
+ *
+ * SUMMARY: Compare the FW version in the bin file to the current FW. If the
+ *	FW version in the bin file is greater an upgrade should be done.
+ *
+ * RETURN:
+ *  -1: Do not upgrade firmware
+ *   0: Version info same, let caller decide
+ *   1: Do a firmware upgrade
+ *
+ * PARAMETERS:
+ *	*dev - pointer to device structure
+ *	*fw  - pointer to the new FW image to load
+ *	*hdr - pointer to the PIP2 bin file hdr structure
+ ******************************************************************************/
+static int _pt_pip2_check_fw_ver(struct device *dev,
+	const struct firmware *fw, struct pt_bin_file_hdr *hdr)
+{
+	u8 img_app_major_ver = fw->data[3];
+	u8 img_app_minor_ver = fw->data[4];
+	u32 img_app_rev_ctrl  = fw->data[9]<<24 | fw->data[10]<<16 |
+			    fw->data[11]<<8 | fw->data[12];
+
+	pt_debug(dev, DL_WARN,
+		"%s ATM - BL Image Version:   %02x.%02x.%d\n",
+		__func__, img_app_major_ver, img_app_minor_ver,
+		img_app_rev_ctrl);
+
+	pt_debug(dev, DL_WARN,
+		"%s ATM - Current FW Version: %02X.%02X.%d\n",
+		__func__, hdr->fw_major, hdr->fw_minor, hdr->fw_rev_ctrl);
+
+	if ((256 * img_app_major_ver + img_app_minor_ver) >
+	    (256 * hdr->fw_major + hdr->fw_minor)) {
+		pt_debug(dev, DL_WARN,
+			"ATM - bin file version > FW, will upgrade FW");
+		return 1;
+	}
+
+	if ((256 * img_app_major_ver + img_app_minor_ver) <
+	    (256 * hdr->fw_major + hdr->fw_minor)) {
+		pt_debug(dev, DL_WARN,
+			"ATM - bin file version < FW, will NOT upgrade FW");
+		return -1;
+	}
+
+	if (img_app_rev_ctrl > hdr->fw_rev_ctrl) {
+		pt_debug(dev, DL_WARN,
+			"bin file rev ctrl > FW, will upgrade FW");
+		return 1;
+	}
+
+	if (img_app_rev_ctrl < hdr->fw_rev_ctrl) {
+		pt_debug(dev, DL_WARN,
+			"bin file rev ctrl > FW, will NOT upgrade FW");
+		return -1;
+	}
+
+	return 0;
+}
+
+/*******************************************************************************
+ * FUNCTION: _pt_pip2_check_config_ver
+ *
+ * SUMMARY: Compare the Config version in the bin file to the current FW. If the
+ *	config version in the bin file is greater an upgrade should be done.
+ *
+ * RETURN:
+ *  -1: Do not upgrade firmware
+ *   0: Version info same, let caller decide
+ *   1: Do a firmware upgrade
+ *
+ * PARAMETERS:
+ *	*dev - pointer to device structure
+ *	*fw  - pointer to the new FW image to load
+ *	*hdr - pointer to the PIP2 bin file hdr structure
+ ******************************************************************************/
+static int _pt_pip2_check_config_ver(struct device *dev,
+	const struct firmware *fw, struct pt_bin_file_hdr *hdr)
+{
+	u16 fw_config_ver_new;
+
+	/* Offset 17,18 is the TT Config version*/
+	fw_config_ver_new = get_unaligned_be16(fw->data + 17);
+	pt_debug(dev, DL_WARN,
+		"%s ATM - BL Image Config Version:   %d\n",
+		__func__, fw_config_ver_new);
+
+	pt_debug(dev, DL_WARN,
+		"%s ATM - Current Config Version:    %d\n",
+		__func__, hdr->config_ver);
+
+	if (fw_config_ver_new > hdr->config_ver) {
+		pt_debug(dev, DL_WARN,
+			"ATM - bin file Config version > FW, will upgrade FW");
+		return 1;
+	} else
+		pt_debug(dev, DL_WARN,
+		"ATM - bin file Config version <= FW, will NOT upgrade FW");
+
+	return 0;
+}
+
+/*******************************************************************************
+ * FUNCTION: _pt_pip2_need_upgrade_due_to_fw_ver
+ *
+ * SUMMARY: Compare the FW version in the bin file to the current FW. If the
+ *	FW version in the bin file is greater an upgrade should be done.
+ *
+ * PARAMETERS:
+ *	*dev - pointer to device structure
+ *	*fw  - pointer to the new FW image to load
+ ******************************************************************************/
+static u8 _pt_pip2_need_upgrade_due_to_fw_ver(struct device *dev,
+	const struct firmware *fw)
+{
+	int ret;
+	u8 should_upgrade = 0;
+	struct pt_bin_file_hdr hdr = {0};
+
+	ret = cmd->request_pip2_bin_hdr(dev, &hdr);
+	if (ret != 0 || hdr.fw_major == 0xFF) {
+		pt_debug(dev, DL_WARN,
+			"App ver info not available, will upgrade FW");
+		should_upgrade = 1;
+		goto exit;
+	}
+
+	ret = _pt_pip2_check_fw_ver(dev, fw, &hdr);
+	if (ret == 0)
+		ret = _pt_pip2_check_config_ver(dev, fw, &hdr);
+
+	if (ret > 0)
+		should_upgrade = 1;
+
+exit:
+	return should_upgrade;
+}
+
+/*******************************************************************************
+ * FUNCTION: _pt_calibrate_flashless_dut
+ *
+ * SUMMARY: On a flashless DUT the FW would need to re-calibrate on every power
+ *	cycle, so to speed up the BL process the FW does the calibraiton on the
+ *	first power up and TTDL will read and store it in RAM to be able to
+ *	restore it on subsequent resets of the DUT.
+ *
+ * RETURN:
+ *       0 = success
+ *      !0 = failure
+ *
+ * PARAMETERS:
+ *	*dev              - pointer to device structure
+ ******************************************************************************/
+static int _pt_calibrate_flashless_dut(struct device *dev)
+{
+	u8 rc = 0;
+	u8 cal_status = 1;
+	u16 cal_size = 0;
+	struct pt_cal_ext_data cal_data = {0};
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+	struct pt_ttdata *ttdata = &cd->sysinfo.ttdata;
+	unsigned short cache_chip_id = 0;
+	unsigned short active_chip_id = 0;
+
+	cmd->nonhid_cmd->manage_cal_data(dev, PT_CAL_DATA_INFO, &cal_size,
+		&cache_chip_id);
+	pt_debug(dev, DL_WARN, "%s: Stored CAL   ID=0x%04X size=%d\n",
+		__func__, cache_chip_id, cal_size);
+
+	active_chip_id = cmd->nonhid_cmd->calc_crc((u8 *)&ttdata->chip_rev,
+			4 + PT_UID_SIZE);
+	pt_debug(dev, DL_WARN, "%s: Current Chip ID=0x%04X\n",
+		__func__, active_chip_id);
+
+	if (cal_size == 0 || active_chip_id != cache_chip_id) {
+		memset(&cal_data, 0, sizeof(struct pt_cal_ext_data));
+
+		/* Calibrate_ext will also save CAL Data in TTDL cache */
+		rc = cmd->nonhid_cmd->calibrate_ext(dev, 0, &cal_data,
+			&cal_status);
+		pt_debug(dev, DL_INFO,
+			"%s: Calibration Finished rc=%d\n", __func__, rc);
+
+		/* Afer successful calibration read the stored size */
+		if (!rc && cal_status == 0) {
+			rc = cmd->nonhid_cmd->manage_cal_data(dev,
+				PT_CAL_DATA_INFO, &cal_size, &cache_chip_id);
+			if (!rc) {
+				pt_debug(dev, DL_WARN,
+					"%s: First BL, Read %d Bytes of CAL\n",
+					__func__, cal_size);
+			} else {
+				pt_debug(dev, DL_ERROR,
+					"%s: First BL, Failed to read CAL\n",
+					__func__);
+			}
+		} else {
+			pt_debug(dev, DL_ERROR,
+				"%s: First BL, Calibration failed rc=%d\n",
+				__func__, rc);
+		}
+		rc = cmd->nonhid_cmd->resume_scanning(dev, 0);
+		if (rc)
+			pt_debug(dev, DL_ERROR,
+				"%s: First BL, Resume Scan failed rc=%d\n",
+				__func__, rc);
+	} else {
+		rc = cmd->nonhid_cmd->manage_cal_data(dev, PT_CAL_DATA_RESTORE,
+			&cal_size, &cache_chip_id);
+		if (!rc)
+			pt_debug(dev, DL_WARN, "%s: Restored CAL %d Bytes\n",
+				__func__, cal_size);
+		else
+			pt_debug(dev, DL_ERROR, "%s: Failed to restore CAL\n",
+				__func__);
+
+		rc = cmd->nonhid_cmd->resume_scanning(dev, 0);
+		if (rc)
+			pt_debug(dev, DL_ERROR,
+				"%s: Resume Scan failed rc=%d\n",
+				__func__, rc);
+	}
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: _search_fw_from_builtin
+ *
+ * SUMMARY: Check the existence of builtin FW by filename and set the pointer
+ *   to FW image
+ *
+ * RETURN:
+ *   0 = success
+ *  !0 = failure
+ *
+ * PARAMETERS:
+ *  *dev      - pointer to device structure
+ *  *filename - pointer of file name
+ * **fw       - pointer to store the pointer of FW image to load
+ ******************************************************************************/
+static int _search_fw_from_builtin(struct device *dev, char *filename,
+				   const struct firmware **fw)
+{
+	int ret = 0;
+#if (KERNEL_VERSION(3, 13, 0) > LINUX_VERSION_CODE)
+	ret = request_firmware(fw, filename, dev);
+#else
+	ret = request_firmware_direct(fw, filename, dev);
+#endif
+	if (ret) {
+		pt_debug(dev, DL_WARN, "%s: ATM - Fail request FW %s load\n",
+			 __func__, filename);
+	} else {
+		pt_debug(dev, DL_INFO, "%s: FW %s class file loading\n",
+			 __func__, filename);
+	}
+
+	return ret;
+}
+
+/*******************************************************************************
+ * FUNCTION: _search_fw_from_us
+ *
+ * SUMMARY: Check the existence of FW from user space by file name defined in
+ *  "pip2_us_file_path".
+ *
+ * RETURN:
+ *   0 = success
+ *  !0 = failure
+ *
+ * PARAMETERS:
+ *  *dev      - pointer to device structure
+ ******************************************************************************/
+static int _search_fw_from_us(struct device *dev)
+{
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+	int read_size = 16;
+	int ret = 0;
+	u8 image[16];
+
+	if (cd->pip2_us_file_path[0] == '\0') {
+		pt_debug(dev, DL_WARN,
+			 "%s: US Path not defined, BL from built-in\n",
+			 __func__);
+		ret = -EINVAL;
+	} else {
+		/* Read a few bytes to see if file exists */
+		ret = cmd->nonhid_cmd->read_us_file(dev, cd->pip2_us_file_path,
+						    image, &read_size);
+		if (!ret) {
+			pt_debug(dev, DL_WARN, "%s: %s Found, BL from US\n",
+				 __func__, cd->pip2_us_file_path);
+		} else {
+			pt_debug(dev, DL_WARN,
+				 "%s: ATM - %s NOT Found, BL from built-in\n",
+				 __func__, cd->pip2_us_file_path);
+		}
+	}
+	return ret;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_pip2_write_file
+ *
+ * SUMMARY: Compelete steps to wite a FILE in external SPI flash over PIP2 BL
+ *  commands. It includes: Open->Erase->Write->Close.
+ *
+ * NOTE: "write_file_status" is updated from x to 99 in this scope, and higher
+ *  level function is allowed to reset to 0 or report an error status.
+ *
+ * RETURN:
+ *   0 = success
+ *  !0 = failure
+ *
+ * PARAMETERS:
+ *  *dev     - pointer to device structure
+ *  *fw      - pointer to FW image to load
+ *   file_no - Identifies the files to write
+ ******************************************************************************/
+static int pt_pip2_write_file(struct device *dev, const struct firmware *fw,
+			      u8 file_no)
+{
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+	struct pt_loader_data *ld = pt_get_loader_data(dev);
+	int ret = 0;
+	int erase_status = 0;
+	u32 percent_cmplt;
+	u32 remain_bytes = 0;
+	u32 fw_size = 0;
+	u32 offset = 0;
+	u32 max_file_size = 0;
+	u16 packet_size = 0;
+	u16 sector_num = 0;
+	u8 *fw_img = NULL;
+	u8 file_handle = 0;
+	u8 read_buf[PT_MAX_PIP2_MSG_SIZE];
+
+	if (!fw || !fw->data || !fw->size) {
+		pt_debug(dev, DL_ERROR, "%s The FW is invalid\n", __func__);
+		return -EINVAL;
+	}
+	fw_img = (u8 *)&(fw->data[0]);
+	fw_size = fw->size;
+
+	if (cd->bus_ops->bustype == BUS_I2C)
+		packet_size = PIP2_BL_I2C_FILE_WRITE_LEN_PER_PACKET;
+	else
+		packet_size = PIP2_BL_SPI_FILE_WRITE_LEN_PER_PACKET;
+
+	/* 1. Open file before any file operation */
+	pt_debug(dev, DL_INFO, "%s OPEN File %d for write\n",
+		__func__, file_no);
+	ret = cmd->nonhid_cmd->pip2_file_open(dev, file_no);
+	if (ret < 0) {
+		pt_debug(dev, DL_ERROR, "%s Open file %d failed\n",
+			__func__, file_no);
+		_pt_update_write_file_status(dev, UPDATE_FW_FILE_OPEN_ERROR);
+		goto exit;
+	}
+	file_handle = ret;
+
+	/* Write File Status: inc to 11 */
+	_pt_update_write_file_status(dev, UPDATE_FW_INCREMENT_BY_1);
+
+	/*
+	 * 2. Erase file
+	 * 2.1 Obtain the size of FILE
+	 * 2.2 Confirm content to be written is not too large
+	 * 2.3 Check whether Sector Erase is valid
+	 * 2.4 Log flash information
+	 * 2.5 Do Sector Erase or File Erase
+	 * 2.6 Reset FILE pointer
+	 * NOTE: Regarding to TC3315, the size of RAM_FILE is less than fw
+	 * image, so will not do step 2.2 for PIP2_RAM_FILE.
+	 */
+	if (file_no != PIP2_RAM_FILE) {
+		ret = cmd->nonhid_cmd->pip2_file_get_stats(
+		    dev, file_handle, NULL, &max_file_size);
+		if (ret) {
+			pt_debug(dev, DL_ERROR,
+				"%s: Failed to get_file_state ret=%d\n",
+				__func__, ret);
+			goto exit_close_file;
+		}
+		if (fw_size > max_file_size) {
+			pt_debug(dev, DL_ERROR,
+				"%s: Firmware image(%d) is over size(%d)\n",
+					__func__, fw_size, max_file_size);
+			_pt_update_write_file_status(dev,
+				UPDATE_FW_INVALID_FW_IMAGE);
+			goto exit_close_file;
+		}
+
+		/* calculate number of sector */
+		sector_num = max_file_size / PT_PIP2_FILE_SECTOR_SIZE;
+		if (max_file_size % PT_PIP2_FILE_SECTOR_SIZE) {
+			pt_debug(dev, DL_WARN,
+				"%s: file size %d misalign, don't use sector erase\n",
+				 __func__, max_file_size);
+			/*
+			 * TODO: Not sure whether can have this case, and this
+			 * is a workaround to ensure the safety in logic.
+			 * Force sector number to 0 will use the default method
+			 * to do file erase by FILE instead of SECTOR.
+			 */
+			sector_num = 0;
+		}
+
+#ifdef TTDL_DIAGNOSTICS
+		/* Log the Flash part info */
+		if (cd->debug_level >= DL_DEBUG)
+			_pt_pip2_get_flash_info(dev, read_buf);
+#endif
+		/* Erase file before loading */
+		ret = cmd->nonhid_cmd->pip2_file_erase(dev,
+			ld->pip2_load_file_no, sector_num, &erase_status);
+		if (ret < 0) {
+			pt_debug(dev, DL_ERROR,
+				"%s: File erase failed rc=%d status=%d\n",
+				__func__, ret, erase_status);
+			_pt_update_write_file_status(dev,
+				UPDATE_FW_ERASE_ERROR);
+			goto exit_close_file;
+		}
+
+		/* Write File Status: inc to 12 */
+		_pt_update_write_file_status(dev, UPDATE_FW_INCREMENT_BY_1);
+
+		/* Reset file pointer as sector erase moved it */
+		ret = cmd->nonhid_cmd->pip2_file_seek_offset(dev,
+			ld->pip2_load_file_no, 0, 0);
+		if (ret) {
+			pt_debug(dev, DL_ERROR,
+				"%s: Failed to seek file offset rc=%d\n",
+				__func__, ret);
+			_pt_update_write_file_status(dev,
+				UPDATE_FW_FILE_SEEK_ERROR);
+			goto exit_close_file;
+		}
+		/* Write File Status: inc to 17 */
+		_pt_update_write_file_status(dev, UPDATE_FW_INCREMENT_BY_5);
+	}
+
+	/*
+	 * 3. Write file
+	 * 3.1* Disable IRQ when using polling method to save BL time
+	 * 3.2 Looping to write file, and the last packet need extra check.
+	 * 3.3* Enable IRQ
+	 */
+	pt_debug(dev, DL_WARN,
+		"%s: ATM - Writing %d bytes of firmware data now\n",
+		__func__, fw_size);
+
+	/*
+	 * No IRQ function is used to BL to reduce BL time due to any IRQ
+	 * latency.
+	 */
+	if (cd->bl_with_no_int)
+		disable_irq_nosync(cd->irq);
+
+	while (offset < fw_size) {
+		remain_bytes = fw_size - offset;
+		/* Don't update BL status on every pass */
+		if (remain_bytes % 2000 < packet_size) {
+			/* Calculate % complete for write_file_status sysfs */
+			percent_cmplt =
+			    (fw_size - remain_bytes) * 100 / fw_size + 1;
+			if (percent_cmplt > UPDATE_FW_ACTIVE_90)
+				percent_cmplt = UPDATE_FW_ACTIVE_90;
+			/*
+			 * Write File Status: set from 1 to 90 when
+			 * fw_size is big enough (such as 82033). But the
+			 * function doesn't update "write_file_status" if the
+			 * input value is less than current stored value.
+			 */
+			_pt_update_write_file_status(dev, percent_cmplt);
+#ifdef TTDL_DIAGNOSTICS
+			pt_debug(dev, DL_INFO,
+				 "Wrote %d bytes with %d bytes remaining\n",
+				 offset, remain_bytes);
+#endif
+		}
+		if (remain_bytes > packet_size) {
+			/* The last para passes in with last_packet=0 (false) */
+			ret = _pt_pip2_file_write_packet_and_log_err(
+			    dev, file_handle, &fw_img[offset], packet_size, 0);
+			offset += packet_size;
+			if (ret)
+				break;
+		} else {
+			pt_debug(dev, DL_INFO,
+				 "Write last %d bytes to File = 0x%02x\n",
+				 remain_bytes, ld->pip2_load_file_no);
+			/* The last para passes in with last_packet=1 (true) */
+			ret = _pt_pip2_file_write_packet_and_log_err(
+			    dev, file_handle, &fw_img[offset], remain_bytes, 1);
+			offset += remain_bytes;
+			break;
+		}
+	}
+
+	if (cd->bl_with_no_int)
+		enable_irq(cd->irq);
+
+	if (!ret) {
+		/* Write File Status: set to 99 */
+		_pt_update_write_file_status(dev, UPDATE_FW_ACTIVE_99);
+		pt_debug(dev, DL_INFO,
+			"%s: BIN file write finished successfully\n", __func__);
+	} else
+		_pt_update_write_file_status(dev, UPDATE_FW_WRITE_ERROR);
+
+exit_close_file:
+	/* 4. Close file */
+	ret = cmd->nonhid_cmd->pip2_file_close(dev, file_handle);
+	if (ret != ld->pip2_load_file_no) {
+		pt_debug(dev, DL_ERROR,
+			"%s file close failure\n", __func__);
+		_pt_update_write_file_status(dev, UPDATE_FW_FILE_CLOSE_ERROR);
+		ret = -EBADF;
+	} else
+		ret = 0;
+
+exit:
+	return ret;
+}
+
+/*******************************************************************************
+ * FUNCTION: _pt_pip2_update_fw
+ *
+ * SUMMARY: Compelete steps to update FW. It includes following steps:
+ *   1) Enter bootloader
+ *   2) Compare IMG version with FW version
+ *   3) Write IMG to target FILE
+ *   4) Exit bootloader
+ *   5) Trigger Enum and check the sentinel
+ *  Also includes exclusive protection, PM function, Watchdog function, and
+ *  complete progress for "update_bl_status".
+ *
+ * NOTE: "write_file_status" is updated from 0 to 100 in this scope, and higher
+ *  level function is allowed to reset status to 0 or report an error status.
+ *
+ * RETURN:
+ *   0 = success
+ *  !0 = failure
+ *
+ * PARAMETERS:
+ *  *dev     - pointer to device structure
+ *  *fw      - pointer to FW image to load
+ *   file_no - Identifies the files to write
+ ******************************************************************************/
+static int _pt_pip2_update_fw(struct device *dev, const struct firmware *fw,
+			      u8 file_no)
+{
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+	struct pt_loader_data *ld = pt_get_loader_data(dev);
+	int ret = 0;
+	int t = 0;
+	bool wait_for_calibration_complete = false;
+	u8 mode = PT_MODE_UNKNOWN;
+	u8 status = 0;
+	u8 read_buf[PT_MAX_PIP2_MSG_SIZE];
+
+	pt_debug(dev, DL_WARN, "%s: ATM - Begin BL\n", __func__);
+	/* Write File Status: reset to 0 */
+	_pt_update_write_file_status(dev, UPDATE_FW_IDLE);
+
+	if (file_no > PIP2_FW_FILE) {
+		pt_debug(dev, DL_ERROR,
+			"%s: Invalid File_no = %d\n", __func__, file_no);
+		_pt_update_write_file_status(dev,
+			UPDATE_FW_NOT_SUPPORTED_FILE_NO);
+		goto exit;
+	}
+
+	/* Write File Status: set to 0 */
+	ret = cmd->request_exclusive(dev, PT_LDR_REQUEST_EXCLUSIVE_TIMEOUT);
+	if (ret) {
+		pt_debug(dev, DL_ERROR,
+			"%s: Failed to aquire exclusive access\n", __func__);
+		_pt_update_write_file_status(dev,
+			UPDATE_FW_EXCLUSIVE_ACCESS_ERROR);
+		goto exit;
+	}
+	/* Write File Status: set to 1 */
+	_pt_update_write_file_status(dev, UPDATE_FW_REQUEST_EXCLUSIVE);
+
+	cd->fw_updating = true;
+	wake_up(&cd->wait_q);
+
+	/* Write File Status: inc to 2 */
+	_pt_update_write_file_status(dev, UPDATE_FW_INCREMENT_BY_1);
+
+	pt_debug(dev, DL_INFO,
+		"%s: Found file of size: %d bytes\n", __func__, (int)fw->size);
+
+	pm_runtime_get_sync(dev);
+	/* Write File Status: inc to 3 */
+	_pt_update_write_file_status(dev, UPDATE_FW_INCREMENT_BY_1);
+
+	cmd->request_stop_wd(dev);
+	/* Write File Status: inc to 4 */
+	_pt_update_write_file_status(dev, UPDATE_FW_INCREMENT_BY_1);
+
+	cd->bl_pip_ver_ready = false;
+	cd->app_pip_ver_ready = false;
+
+	/*
+	 * 'mode' is used below, if DUT was already in BL before attempting to
+	 * enter the BL, there was either no FW to run or the FW was corrupt
+	 * so either way force a BL
+	 */
+	ret = cmd->request_pip2_enter_bl(dev, &mode, NULL);
+	if (ret) {
+		pt_debug(dev, DL_ERROR, "%s: Failed to enter BL\n",
+			__func__);
+		_pt_update_write_file_status(dev, UPDATE_FW_ENTER_BL_ERROR);
+		goto exit;
+	}
+	/* Write File Status: inc to 5 */
+	_pt_update_write_file_status(dev, UPDATE_FW_INCREMENT_BY_1);
+	/* Only compare FW ver or previous mode when doing a built-in upgrade */
+	if (ld->pip2_load_builtin) {
+		if (_pt_pip2_need_upgrade_due_to_fw_ver(dev, fw) ||
+		    mode == PT_MODE_BOOTLOADER) {
+			/* Write File Status: inc to 6 */
+			_pt_update_write_file_status(dev,
+				UPDATE_FW_INCREMENT_BY_1);
+		} else {
+			_pt_update_write_file_status(dev,
+				UPDATE_FW_VERSION_ERROR);
+			goto exit;
+		}
+	}
+
+	/*
+	 * Write File Status: set to 10 before pt_pip2_write_file() is called to
+	 * align with _pt_pip2_write_file_cont().
+	 */
+	_pt_update_write_file_status(dev, UPDATE_FW_ACTIVE_10);
+	/* Write File Status: inc from 10, the max is not bigger than 99 */
+	ret = pt_pip2_write_file(dev, fw, file_no);
+	if (ret) {
+		pt_debug(dev, DL_ERROR, "%s: Failed to write FILE_%d\n",
+		__func__, file_no);
+		goto exit;
+	}
+
+	if ((file_no == PIP2_RAM_FILE) &&
+	    (write_file_status < UPDATE_FW_COMPLETE)) {
+		/* When writing to RAM don't reset, just launch application */
+		pt_debug(dev, DL_INFO,
+			"%s Sending execute command now...\n", __func__);
+		cd->startup_status = STARTUP_STATUS_START;
+		ret = _pt_pip2_execute_app(dev, &status);
+		if (ret || status) {
+			pt_debug(dev, DL_ERROR,
+				"%s Execute command failure\n", __func__);
+			_pt_update_write_file_status(dev,
+				UPDATE_FW_EXECUTE_ERROR);
+			goto exit;
+		}
+	} else if (file_no == PIP2_FW_FILE &&
+		   write_file_status < UPDATE_FW_COMPLETE) {
+		pt_debug(dev, DL_INFO,
+			"%s Toggle TP_XRES now...\n", __func__);
+		cmd->request_reset(dev, PT_CORE_CMD_UNPROTECTED);
+	}
+	pt_debug(dev, DL_INFO, "%s: APP launched\n", __func__);
+
+	/* If any error occurred simply close the file and exit */
+	if (write_file_status > UPDATE_FW_COMPLETE)
+		goto exit;
+
+	/* Wait for FW reset sentinel from reset or execute for up to 500ms */
+	t = wait_event_timeout(cd->wait_q,
+		(cd->startup_status >= STARTUP_STATUS_FW_RESET_SENTINEL),
+		msecs_to_jiffies(PT_BL_WAIT_FOR_SENTINEL));
+	if (IS_TMO(t)) {
+		pt_debug(dev, DL_WARN,
+			"%s: 0x%04X Timeout waiting for FW sentinel",
+			__func__, cd->startup_status);
+	}
+
+	/* Double verify DUT is alive and well in Application mode */
+	if (cd->startup_status & STARTUP_STATUS_FW_RESET_SENTINEL) {
+		ret = cmd->request_pip2_get_mode_sysmode(dev,
+			PT_CORE_CMD_UNPROTECTED, &mode, NULL);
+		pt_debug(dev, DL_WARN, "%s: mode = %d (Expected 2)",
+			__func__, mode);
+		if (mode != PT_MODE_OPERATIONAL) {
+			pt_debug(dev, DL_ERROR,
+				"%s ERROR: Not in App mode as expected\n",
+				__func__);
+			_pt_update_write_file_status(dev, UPDATE_FW_MODE_ERROR);
+			goto exit;
+		}
+	} else {
+		pt_debug(dev, DL_ERROR, "%s: FW sentinel not seen 0x%04X\n",
+			__func__,  cd->startup_status);
+		_pt_pip2_log_last_error(dev, read_buf);
+		_pt_update_write_file_status(dev, UPDATE_FW_SENTINEL_NOT_SEEN);
+		goto exit;
+	}
+
+	/* On a Flashless DUT save or restore the CAL data */
+	if (cd->cal_cache_in_host == PT_FEATURE_ENABLE)
+		_pt_calibrate_flashless_dut(dev);
+
+	/* Subscribe calibration task if calibration flag is set */
+	if (ld->loader_pdata
+		&& (ld->loader_pdata->flags
+			 & PT_LOADER_FLAG_CALIBRATE_AFTER_FW_UPGRADE)
+		&& (cd->cal_cache_in_host == PT_FEATURE_DISABLE)) {
+#if (KERNEL_VERSION(3, 13, 0) <= LINUX_VERSION_CODE)
+		reinit_completion(&ld->calibration_complete);
+#else
+		INIT_COMPLETION(ld->calibration_complete);
+#endif
+		/* set up call back for startup */
+		pt_debug(dev, DL_INFO, "%s: Adding callback for calibration\n",
+			__func__);
+		ret = cmd->subscribe_attention(dev, PT_ATTEN_STARTUP,
+			PT_LOADER_NAME, pt_calibration_attention, 0);
+		if (ret) {
+			pt_debug(dev, DL_ERROR,
+				"%s: Failed adding callback for calibration\n",
+				__func__);
+			ret = 0;
+		} else
+			wait_for_calibration_complete = true;
+	}
+
+	pt_debug(dev, DL_INFO, "%s: == PIP2 FW upgrade finished ==\n",
+		__func__);
+
+exit:
+	cd->fw_updating = false;
+	/*
+	 * For built-in FW update, it should not warn builtin_bin_fw_status
+	 * for each bootup since write_file_status would be
+	 * UPDATE_FW_VERSION_ERROR in most situations because firmware
+	 * should have been up to date.
+	 */
+	if (ld->pip2_load_builtin) {
+		if ((write_file_status == UPDATE_FW_ACTIVE_99) ||
+		    (write_file_status == UPDATE_FW_VERSION_ERROR))
+			ld->builtin_bin_fw_status = 0;
+		else
+			ld->builtin_bin_fw_status = -EINVAL;
+	}
+
+	cmd->release_exclusive(dev);
+	pm_runtime_put_sync(dev);
+
+	if ((write_file_status == UPDATE_FW_ACTIVE_99) ||
+	    (write_file_status == UPDATE_FW_VERSION_ERROR)) {
+		pt_debug(dev, DL_WARN, "%s: Queue ENUM\n", __func__);
+		cmd->request_enum(dev, true);
+	}
+
+	/* Write File Status: set to 100 */
+	if (write_file_status < UPDATE_FW_COMPLETE)
+		_pt_update_write_file_status(dev, UPDATE_FW_COMPLETE);
+
+	if (wait_for_calibration_complete)
+		wait_for_completion(&ld->calibration_complete);
+
+	pt_debug(dev, DL_INFO, "%s: Starting watchdog\n", __func__);
+	cmd->request_start_wd(dev);
+	/*
+	 * When in No-Flash mode allow auto BL after any BL.
+	 * There is an issue where setting flashless mode via drv_debug
+	 * can happen in the middle of pt_pip2_enter_bl() which will revert
+	 * the flashless_auto_bl value back to what it was when the function
+	 * started.
+	 */
+	if (cd->flashless_dut)
+		cd->flashless_auto_bl = PT_ALLOW_AUTO_BL;
+
+	/*
+	 * ret is not always updated, while builtin_bin_fw_status can reflect
+	 * the result.
+	 */
+	return ld->builtin_bin_fw_status;
+}
+
+/*******************************************************************************
+ * FUNCTION: _pt_pip2_write_file_cont
+ *
+ * SUMMARY: Write the file to either SRAM or FLASH
+ *
+ * NOTE: The DUT must stay in bootloader. This function doesn't try to
+ *   enter/exit bootloader.
+ *
+ * PARAMETERS:
+ *	*fw      - pointer to the new FW image to load
+ *	*context - pointer to the device
+ ******************************************************************************/
+static void _pt_pip2_write_file_cont(const struct firmware *fw, void *context)
+{
+	struct device *dev = context;
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+	struct pt_loader_data *ld = pt_get_loader_data(dev);
+	int ret = 0;
+
+	if (!fw) {
+		pt_debug(dev, DL_ERROR, "%s: No FW is provided\n", __func__);
+		_pt_update_write_file_status(dev, UPDATE_FW_NO_FW_PROVIDED);
+		goto exit;
+	}
+
+	if (!fw->size) {
+		pt_debug(dev, DL_ERROR, "%s: Invalid fw or file size=%d\n",
+			 __func__, (int)fw->size);
+		_pt_update_write_file_status(dev, UPDATE_FW_INVALID_FW_IMAGE);
+		goto exit_release;
+	}
+
+	if (ld->pip2_load_file_no == PIP2_FW_FILE) {
+		if (fw->data[0] >= (fw->size + 1)) {
+			pt_debug(dev, DL_ERROR,
+				"%s: Firmware format is invalid\n", __func__);
+			_pt_update_write_file_status(dev,
+				UPDATE_FW_INVALID_FW_IMAGE);
+			goto exit_release;
+		}
+	}
+
+	ret = cmd->request_exclusive(dev, PT_LDR_REQUEST_EXCLUSIVE_TIMEOUT);
+	if (ret < 0) {
+		pt_debug(dev, DL_ERROR,
+			"%s: Failed to aquire exclusive access\n", __func__);
+		goto exit_release;
+	}
+
+	/* Write File Status: set to 1 */
+	_pt_update_write_file_status(dev, UPDATE_FW_REQUEST_EXCLUSIVE);
+
+	cd->fw_updating = true;
+	/* Write File Status: inc to 2 */
+	_pt_update_write_file_status(dev, UPDATE_FW_INCREMENT_BY_1);
+
+	/*
+	 * Write File Status: set to 10 before pt_pip2_write_file() is called to
+	 * align with _pt_pip2_firmware_cont().
+	 */
+	_pt_update_write_file_status(dev, UPDATE_FW_ACTIVE_10);
+	/* Write File Status: inc from 10, the max is not bigger than 99 */
+	ret = pt_pip2_write_file(dev, fw, ld->pip2_load_file_no);
+	if (ret) {
+		pt_debug(dev, DL_ERROR, "%s: Failed to write FILE_%d\n",
+		__func__, ld->pip2_load_file_no);
+	}
+
+	cd->fw_updating = false;
+
+	/* Write File Status: set to 100 */
+	if (write_file_status < UPDATE_FW_COMPLETE)
+		_pt_update_write_file_status(dev, UPDATE_FW_COMPLETE);
+
+	cmd->release_exclusive(dev);
+
+exit_release:
+	if (fw)
+		release_firmware(fw);
+exit:
+	ld->is_manual_upgrade_enabled = 0;
+}
+
+
+/*******************************************************************************
+ * FUNCTION: _pt_pip2_firmware_cont
+ *
+ * SUMMARY: Bootload the DUT with a FW image using the PIP2 protocol. This
+ *	includes getting the DUT into BL mode, writing the file to either SRAM
+ *	or FLASH, and launching the application directly in SRAM or by resetting
+ *	the DUT without the hostmode pin asserted.
+ *
+ *	NOTE: Special care must be taken to support a DUT communicating in
+ *		PIP2.0 where the length field is defined differently.
+ *	NOTE: The write packet len is set so that the overall packet size is
+ *		less than 255. The overhead is 9 bytes: 2 byte address (0101),
+ *		4 byte header, 1 byte file no. 2 byte CRC
+ *
+ * PARAMETERS:
+ *	*fw      - pointer to the new FW image to load
+ *	*context - pointer to the device
+ ******************************************************************************/
+static void _pt_pip2_firmware_cont(const struct firmware *fw,
+		void *context)
+{
+	struct device *dev = context;
+	struct pt_loader_data *ld = pt_get_loader_data(dev);
+
+	if (!fw) {
+		pt_debug(dev, DL_ERROR, "%s: No FW is provided\n", __func__);
+		_pt_update_write_file_status(dev, UPDATE_FW_NO_FW_PROVIDED);
+		goto pt_firmware_cont_exit;
+	}
+
+	if (!fw->data || !fw->size) {
+		pt_debug(dev, DL_ERROR, "%s: Invalid fw or file size=%d\n",
+			 __func__, (int)fw->size);
+		_pt_update_write_file_status(dev, UPDATE_FW_INVALID_FW_IMAGE);
+		goto pt_firmware_cont_release_exit;
+	}
+
+	if (ld->pip2_load_file_no == PIP2_FW_FILE) {
+		if (fw->data[0] >= (fw->size + 1)) {
+			pt_debug(dev, DL_ERROR,
+				 "%s: Firmware format is invalid\n", __func__);
+			_pt_update_write_file_status(
+			    dev, UPDATE_FW_INVALID_FW_IMAGE);
+			goto pt_firmware_cont_release_exit;
+		}
+	}
+
+	_pt_pip2_update_fw(dev, fw, ld->pip2_load_file_no);
+
+pt_firmware_cont_release_exit:
+	if (fw)
+		release_firmware(fw);
+
+pt_firmware_cont_exit:
+	ld->is_manual_upgrade_enabled = 0;
+}
+
+/*******************************************************************************
+ * FUNCTION: _pt_pip2_update_fw_from_builtin
+ *
+ * SUMMARY: Bootload the DUT with a built in firmware binary image.
+ *
+ * RETURN:
+ *   0 = success
+ *  !0 = failure
+ *
+ * PARAMETERS:
+ *	*dev - pointer to the device structure
+ ******************************************************************************/
+#define PIP2_MAX_FILE_NAMES 3
+static int _pt_pip2_update_fw_from_builtin(struct device *dev)
+{
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+	struct pt_loader_data *ld = pt_get_loader_data(dev);
+	const struct firmware *fw = NULL;
+	int ret = 0;
+	int index = 0;
+	int file_count = 0;
+	char *filename[PIP2_MAX_FILE_NAMES];
+
+	/*
+	 * 1. Generate FW Name for builtin kernel
+	 * Load the supported filenames in the correct search order
+	 * 0 - "tt_fw<_PIDX>.bin"
+	 * 1 - "XXXX_tt_fw<_PIDX>.bin" where XXXX = Silicon ID
+	 * 2 - "tt_fw.bin", default FW name
+	 */
+	filename[file_count++] = generate_firmware_filename(dev);
+	filename[file_count++] = generate_silicon_id_firmware_filename(dev);
+	if (pt_get_panel_id(dev) != PANEL_ID_NOT_ENABLED) {
+		filename[file_count] =
+		    kzalloc(sizeof(PT_FW_FILE_NAME), GFP_KERNEL);
+		memcpy(filename[file_count++], PT_FW_FILE_NAME,
+		       sizeof(PT_FW_FILE_NAME));
+	}
+
+	for (index = 0; index < file_count; index++) {
+		if (!filename[index])
+			return -ENOMEM;
+	}
+	/* 2. Look for any FW file name match */
+	mutex_lock(&cd->firmware_class_lock);
+	index = 0;
+	while (index < file_count) {
+		pt_debug(dev, DL_INFO, "%s: Request FW class file: %s\n",
+			 __func__, filename[index]);
+		if (_search_fw_from_builtin(dev, filename[index], &fw))
+			index++;
+		else
+			break;
+	}
+
+	/* No matching file names found */
+	if (index == file_count) {
+		pt_debug(dev, DL_WARN, "%s: No FW is found\n", __func__);
+		_pt_update_write_file_status(dev, UPDATE_FW_NO_FW_PROVIDED);
+		ret = -EINVAL;
+		goto exit;
+	}
+
+	/* 3. Validate the FW */
+	if (!fw) {
+		pt_debug(dev, DL_ERROR, "%s: No FW is provided\n", __func__);
+		_pt_update_write_file_status(dev, UPDATE_FW_NO_FW_PROVIDED);
+		ret = -EINVAL;
+		goto exit_release_fw;
+	}
+
+	if (!fw->size) {
+		pt_debug(dev, DL_ERROR, "%s: Invalid fw or file size=%d\n",
+			 __func__, (int)fw->size);
+		_pt_update_write_file_status(dev, UPDATE_FW_INVALID_FW_IMAGE);
+		ret = -EINVAL;
+		goto exit_release_fw;
+	}
+
+	if (ld->pip2_load_file_no == PIP2_FW_FILE) {
+		if (fw->data[0] >= (fw->size + 1)) {
+			pt_debug(dev, DL_ERROR,
+				"%s: Firmware format is invalid\n", __func__);
+			_pt_update_write_file_status(dev,
+				UPDATE_FW_INVALID_FW_IMAGE);
+			ret = -EINVAL;
+			goto exit_release_fw;
+		}
+	}
+
+	/* 4. update the FW */
+	ret = _pt_pip2_update_fw(dev, fw, ld->pip2_load_file_no);
+
+exit_release_fw:
+	/* 5. Recycle */
+	if (fw)
+		release_firmware(fw);
+exit:
+	index = 0;
+	while (index < file_count)
+		kfree(filename[index++]);
+
+	mutex_unlock(&cd->firmware_class_lock);
+	return ret;
+}
+
+/*******************************************************************************
+ * FUNCTION: _pt_pip2_update_fw_from_us
+ *
+ * SUMMARY: Bootload the DUT with firmware binary image in user space.
+ *
+ * RETURN:
+ *   0 = success
+ *  !0 = failure
+ *
+ * PARAMETERS:
+ *	*dev - pointer to the device structure
+ ******************************************************************************/
+static int _pt_pip2_update_fw_from_us(struct device *dev)
+{
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+	struct pt_loader_data *ld = pt_get_loader_data(dev);
+	struct firmware fw_us;
+	int ret = 0;
+	u32 fw_size = 0;
+	u8 *fw_img = NULL;
+
+	fw_img = kzalloc(PT_PIP2_MAX_FILE_SIZE, GFP_KERNEL);
+	if (!fw_img) {
+		_pt_update_write_file_status(dev, UPDATE_FW_NO_FW_PROVIDED);
+		return -ENOMEM;
+	}
+
+	ret = cmd->nonhid_cmd->read_us_file(dev, cd->pip2_us_file_path,
+					    fw_img, &fw_size);
+	if (ret) {
+		pt_debug(dev, DL_ERROR, "%s: No firmware provided to load\n",
+			 __func__);
+		pt_debug(dev, DL_ERROR, "%s: Exit BL\n", __func__);
+		_pt_update_write_file_status(dev, UPDATE_FW_NO_FW_PROVIDED);
+		goto exit;
+	}
+
+	if (!fw_size) {
+		pt_debug(dev, DL_ERROR, "%s: Invalid fw file size=%d\n",
+			 __func__, (int)fw_size);
+		_pt_update_write_file_status(dev, UPDATE_FW_INVALID_FW_IMAGE);
+		goto exit;
+	}
+
+	if (ld->pip2_load_file_no == PIP2_FW_FILE) {
+		if (fw_img[0] >= (fw_size + 1)) {
+			pt_debug(dev, DL_ERROR,
+				"%s: Firmware format is invalid\n", __func__);
+			_pt_update_write_file_status(dev,
+				UPDATE_FW_INVALID_FW_IMAGE);
+			goto exit;
+		}
+	}
+
+	memset(&fw_us, 0, sizeof(fw_us));
+	fw_us.data = fw_img;
+	fw_us.size = fw_size;
+	ret = _pt_pip2_update_fw(dev, &fw_us, ld->pip2_load_file_no);
+
+exit:
+	kfree(fw_img);
+	return ret;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_pip2_upgrade_firmware_from_builtin
+ *
+ * SUMMARY: Bootload the DUT with a built in firmware binary image.
+ *	Load either a SRAM image "ttdl_fw_RAM.bin" or a FLASH image
+ *	"ttdl_fw.bin" with the priority being the SRAM image.
+ *
+ * PARAMETERS:
+ *	*dev - pointer to the device structure
+ ******************************************************************************/
+static int pt_pip2_upgrade_firmware_from_builtin(struct device *dev)
+{
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+	struct pt_loader_data *ld = pt_get_loader_data(dev);
+	int ret = 0;
+
+	/* 1. Look for FW from us at first */
+	if (cd->flashless_dut) {
+		pt_debug(dev, DL_INFO, "%s: Proceed to BL flashless DUT\n",
+			 __func__);
+		ld->pip2_load_file_no = PIP2_RAM_FILE;
+		ret = _search_fw_from_us(dev);
+		if (!ret) {
+			ld->pip2_load_builtin = false;
+			/*  2. Do update with fw from user space */
+			ret = _pt_pip2_update_fw_from_us(dev);
+			return ret;
+		} else {
+			ld->pip2_load_builtin = true;
+		}
+	} else {
+		ld->pip2_load_file_no = PIP2_FW_FILE;
+		ld->pip2_load_builtin = true;
+	}
+
+	/* 2. Do update with fw from builtin */
+	ret = _pt_pip2_update_fw_from_builtin(dev);
+	return ret;
+}
+
+/*******************************************************************************
+ * FUNCTION: _pt_pip2_update_fw_from_class
+ *
+ * SUMMARY: Create the firmware class but don't actually laod any FW to the
+ *	DUT. This creates all the sysfs nodes needed for a user to bootload
+ *	the DUT with their own bin file.
+ *
+ * PARAMETERS:
+ *	*pip2_data     - pointer to the PIP2 loader data structure
+ ******************************************************************************/
+static int _pt_pip2_update_fw_from_class(struct device *dev)
+{
+	struct pt_loader_data *ld = pt_get_loader_data(dev);
+	int ret = 0;
+
+	/*
+	 * The file name dev_name(dev) is tied with bus name and usually
+	 * it is "x-0024". This name is wanted to keep consistency
+	 * (e.g. /sys/class/firmware/x-0024/) for the path of fw class
+	 * nodes with different kernel release. Also it is an invalid bin
+	 * file name used intentionally because request_firmware_nowait
+	 * will not find the file which is what we want and then simply
+	 * create the fw class nodes.
+	 */
+	ld->pip2_load_builtin = false;
+	pt_debug(dev, DL_INFO, "%s: Request FW Class", __func__);
+	ret = request_firmware_nowait(THIS_MODULE, FW_ACTION_NOHOTPLUG,
+			dev_name(dev), dev, GFP_KERNEL, dev,
+			_pt_pip2_firmware_cont);
+	if (ret) {
+		pt_debug(dev, DL_ERROR,
+			"%s: ERROR requesting firmware class\n", __func__);
+	}
+
+	return ret;
+}
+
+/*******************************************************************************
+ * FUNCTION: _pt_pip2_write_file_from_class
+ *
+ * SUMMARY: Similar function of _pt_pip2_update_fw_from_class() but to call
+ *  function _pt_pip2_write_file_cont() which doesn't perform enter/exit
+ *  bootloader action.
+ *
+ * PARAMETERS:
+ *	*pip2_data     - pointer to the PIP2 loader data structure
+ ******************************************************************************/
+static int _pt_pip2_write_file_from_class(struct device *dev)
+{
+
+	int ret = 0;
+	struct pt_loader_data *ld = pt_get_loader_data(dev);
+
+	/*
+	 * The file name dev_name(dev) is tied with bus name and usually
+	 * it is "x-0024". This name is wanted to keep consistency
+	 * (e.g. /sys/class/firmware/x-0024/) for the path of fw class
+	 * nodes with different kernel release. Also it is an invalid bin
+	 * file name used intentionally because request_firmware_nowait
+	 * will not find the file which is what we want and then simply
+	 * create the fw class nodes.
+	 */
+	ld->pip2_load_builtin = false;
+	pt_debug(dev, DL_INFO, "%s: Request FW Class", __func__);
+	ret = request_firmware_nowait(THIS_MODULE, FW_ACTION_NOHOTPLUG,
+			dev_name(dev), dev, GFP_KERNEL, dev,
+			_pt_pip2_write_file_cont);
+	if (ret) {
+		pt_debug(dev, DL_ERROR,
+			"%s: ERROR requesting firmware class\n", __func__);
+	}
+
+	return ret;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_pip2_bl_from_file_work
+ *
+ * SUMMARY: The work function to schedule the BL work for PIP2 only.
+ *
+ * PARAMETERS:
+ *	*bl_from_file - pointer to work_struct structure
+ ******************************************************************************/
+static void pt_pip2_bl_from_file_work(struct work_struct *pip2_bl_from_file)
+{
+	struct pt_loader_data *ld = container_of(pip2_bl_from_file,
+			struct pt_loader_data, pip2_bl_from_file);
+	struct device *dev = ld->dev;
+
+	_pt_pip2_update_fw_from_us(dev);
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_bl_from_file_work
+ *
+ * SUMMARY: The work function to schedule the BL work for PIP2 or PIP1
+ *  according to the active_dut_generation in core data.
+ *
+ * PARAMETERS:
+ *	*bl_from_file - pointer to work_struct structure
+ ******************************************************************************/
+static void pt_bl_from_file_work(struct work_struct *bl_from_file)
+{
+	struct pt_loader_data *ld = container_of(bl_from_file,
+			struct pt_loader_data, bl_from_file);
+	struct device *dev = ld->dev;
+	u8 dut_gen = cmd->request_dut_generation(dev);
+
+	if (dut_gen == DUT_PIP2_CAPABLE)
+		_pt_pip2_update_fw_from_us(dev);
+	else if (dut_gen == DUT_PIP1_ONLY)
+		_pt_pip1_bl_from_file(dev);
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_pip2_bl_from_file_show
+ *
+ * SUMMARY: The show method for the "pip2_bl_from_file" sysfs node. The
+ *  scheduled work will perform PIP2 BL.
+ *
+ * NOTE: Since this function doesn't set pip2_load_file_no, it will use the
+ * value what has been stored there.
+ *
+ * PARAMETERS:
+ *	*dev  - pointer to device structure
+ *	*attr - pointer to device attributes structure
+ *	*buf  - pointer to print output buffer
+ ******************************************************************************/
+static ssize_t pt_pip2_bl_from_file_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct pt_loader_data *ld = pt_get_loader_data(dev);
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+	int rc = 0;
+	int read_size = 2;
+	u8 image[2];
+
+	/* Write File Status: reset to 0 */
+	_pt_update_write_file_status(dev, UPDATE_FW_IDLE);
+
+	mutex_lock(&cd->firmware_class_lock);
+	ld->pip2_load_builtin = false;
+	mutex_unlock(&cd->firmware_class_lock);
+
+	/* Read a few bytes to see if file exists */
+	rc = cmd->nonhid_cmd->read_us_file(dev,
+		cd->pip2_us_file_path, image, &read_size);
+
+	if (!rc) {
+		schedule_work(&ld->pip2_bl_from_file);
+
+		return snprintf(buf, PT_MAX_PRBUF_SIZE,
+			"Status: %d\n"
+			"BL File: %s\n",
+			rc, cd->pip2_us_file_path);
+	} else {
+		_pt_update_write_file_status(dev, UPDATE_FW_NO_FW_PROVIDED);
+		return snprintf(buf, PT_MAX_PRBUF_SIZE,
+			"Status: %d\n"
+			"BL File: '%s' - Does not exist\n",
+			rc, cd->pip2_us_file_path);
+	}
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_bl_from_file_show
+ *
+ * SUMMARY: The show method for the "pt_bl_from_file" sysfs node. The scheduled
+ *  work can perform either PIP1 BL and PIP2 BL according to the
+ *  active_dut_generation of core data.
+ *
+ * NOTE: Since this function doesn't set pip2_load_file_no, it will use the
+ * value what has been stored there.
+ *
+ * PARAMETERS:
+ *	*dev  - pointer to device structure
+ *	*attr - pointer to device attributes structure
+ *	*buf  - pointer to print output buffer
+ ******************************************************************************/
+static ssize_t pt_bl_from_file_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct pt_loader_data *ld = pt_get_loader_data(dev);
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+	int rc = 0;
+	int read_size = 2;
+	u8 dut_gen = cmd->request_dut_generation(dev);
+	u8 image[2];
+
+	/* Write File Status: reset to 0 */
+	_pt_update_write_file_status(dev, UPDATE_FW_IDLE);
+
+	mutex_lock(&cd->firmware_class_lock);
+	ld->pip2_load_builtin = false;
+	mutex_unlock(&cd->firmware_class_lock);
+
+	/* Read a few bytes to see if file exists */
+	rc = cmd->nonhid_cmd->read_us_file(dev,
+		cd->pip2_us_file_path, image, &read_size);
+
+	if (dut_gen == DUT_UNKNOWN) {
+		_pt_update_write_file_status(dev, UPDATE_FW_MODE_ERROR);
+		rc = -EINVAL;
+		return snprintf(buf, PT_MAX_PRBUF_SIZE,
+			"Status: %d\n"
+			"BL File: '%s' - Failed, DUT Generation could not be determined\n",
+			rc, cd->pip2_us_file_path);
+	} else if (!rc) {
+		schedule_work(&ld->bl_from_file);
+
+		return snprintf(buf, PT_MAX_PRBUF_SIZE,
+			"Status: %d\n"
+			"BL File: %s\n",
+			rc, cd->pip2_us_file_path);
+	} else {
+		_pt_update_write_file_status(dev, UPDATE_FW_NO_FW_PROVIDED);
+		return snprintf(buf, PT_MAX_PRBUF_SIZE,
+			"Status: %d\n"
+			"BL File: '%s' - Does not exist\n",
+			rc, cd->pip2_us_file_path);
+	}
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_pip2_bl_from_file_store
+ *
+ * SUMMARY: The store method for the "pip2_bl_from_file" and "pt_bl_from_file"
+ *  sysfs node. Used to allow any file path[necessary] and file_no[optional] to
+ *  be used to BL, for example: "echo /data/pt_fw 1 > pip2_bl_from_file", and
+ *  do the "cat" will perform FW loader process.
+ *
+ * NOTE: the last char of file path in buf which is a '\n' is not copied.
+ * NOTE: the default file_no is PIP2_RAM_FILE.
+ *
+ * PARAMETERS:
+ *      *dev   - pointer to device structure
+ *      *attr  - pointer to device attributes
+ *      *buf   - pointer to output buffer
+ *       size   - size of data in buffer
+ ******************************************************************************/
+static ssize_t pt_pip2_bl_from_file_store(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t size)
+{
+	struct pt_loader_data *ld = pt_get_loader_data(dev);
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+	const char *deli_space = " ",  *deli_comma = ",";
+	char name_space[PT_MAX_PATH_SIZE];
+	char *ptr_left = NULL, *ptr_right = name_space;
+	u8 file_no = PIP2_RAM_FILE;
+	u32 input_data[2];
+	int length;
+	bool file_no_set = false;
+
+	memset(name_space, 0, PT_MAX_PATH_SIZE);
+	memset(cd->pip2_us_file_path, 0, PT_MAX_PATH_SIZE);
+	if (size <= PT_MAX_PATH_SIZE) {
+		memcpy(name_space, buf, size);
+		ptr_left = strsep(&ptr_right, deli_space);
+		if (ptr_right == NULL) {
+			ptr_right = name_space;
+			ptr_left = strsep(&ptr_right, deli_comma);
+		}
+
+		if (ptr_right != NULL) {
+			length = cmd->parse_sysfs_input(
+			    dev, ptr_right, strlen(ptr_right), input_data,
+			    ARRAY_SIZE(input_data));
+			if (length <= 0) {
+				pt_debug(dev, DL_ERROR,
+					 "%s: Input format error!\n", __func__);
+				return -EINVAL;
+			}
+			file_no_set = true;
+			file_no = input_data[0];
+		}
+
+		pt_debug(dev, DL_WARN, "%s:Path=%s, File_no=%s(%d)\n", __func__,
+			 ptr_left, ptr_right, file_no);
+
+		if ((file_no_set) && (file_no > PIP2_FW_FILE)) {
+			pt_debug(dev, DL_WARN, "%s:Invalid File_no = %d\n",
+				__func__, file_no);
+			return -EINVAL;
+		}
+
+		mutex_lock(&cd->firmware_class_lock);
+		ld->pip2_load_file_no = file_no;
+		mutex_unlock(&cd->firmware_class_lock);
+
+		if (ptr_left[strlen(ptr_left) - 1] == '\n')
+			memcpy(cd->pip2_us_file_path, ptr_left,
+			       strlen(ptr_left) - 1);
+		else
+			memcpy(cd->pip2_us_file_path, ptr_left,
+			       strlen(ptr_left));
+	}
+
+	return size;
+}
+static DEVICE_ATTR(pip2_bl_from_file, 0644,
+		pt_pip2_bl_from_file_show, pt_pip2_bl_from_file_store);
+
+static DEVICE_ATTR(pt_bl_from_file, 0644,
+		pt_bl_from_file_show, pt_pip2_bl_from_file_store);
+
+/*******************************************************************************
+ * FUNCTION: pt_pip2_manual_upgrade_store
+ *
+ * SUMMARY: Store method for the pip2_manual_upgrade sysfs node. Allows
+ *	sysfs control of bootloading a new FW image to FLASH.
+ *
+ * PARAMETERS:
+ *      *dev   - pointer to device structure
+ *      *attr  - pointer to device attributes
+ *      *buf   - pointer to output buffer
+ *       size   - size of data in buffer
+ ******************************************************************************/
+static ssize_t pt_pip2_manual_upgrade_store(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t size)
+{
+	struct pt_loader_data *ld = pt_get_loader_data(dev);
+	u32 input_data[2] = {0};
+	int length;
+	int rc = 0;
+
+	length = cmd->parse_sysfs_input(dev, buf, size, input_data,
+			ARRAY_SIZE(input_data));
+
+	if (length != 1) {
+		pt_debug(dev, DL_WARN, "%s: Invalid number of arguments\n",
+			__func__);
+		rc = -EINVAL;
+		goto exit;
+	}
+
+	if (input_data[0] < 0 || input_data[0] > 1) {
+		pt_debug(dev, DL_WARN, "%s: Invalid arguments\n", __func__);
+		rc = -EINVAL;
+		goto exit;
+	}
+
+	if (ld->is_manual_upgrade_enabled) {
+		pt_debug(dev, DL_ERROR,
+			"%s: ERROR - Manual upgrade busy\n", __func__);
+		rc = -EBUSY;
+		goto exit;
+	}
+	/* Write File Status: reset to 0 */
+	_pt_update_write_file_status(dev, UPDATE_FW_IDLE);
+	ld->pip2_load_file_no = PIP2_FW_FILE;
+	pt_debug(dev, DL_DEBUG, "%s: ATM - File number is %d\n",
+		__func__, ld->pip2_load_file_no);
+
+	ld->is_manual_upgrade_enabled = 1;
+	rc = _pt_pip2_update_fw_from_class(dev);
+	ld->is_manual_upgrade_enabled = 0;
+	if (rc < 0)
+		pt_debug(dev, DL_ERROR,
+			"%s: ERROR - FLASH Upgrade failed\n", __func__);
+
+exit:
+	if (rc)
+		return rc;
+	return size;
+}
+#ifndef TTDL_KERNEL_SUBMISSION
+static DEVICE_ATTR(pip2_manual_upgrade, 0200,
+	NULL, pt_pip2_manual_upgrade_store);
+
+/*******************************************************************************
+ * FUNCTION: pt_pip2_manual_ram_upgrade_store
+ *
+ * SUMMARY: Store method for the pip2_manual_ram_upgrade sysfs node. Allows
+ *	sysfs control of bootloading a new FW image to SRAM.
+ *
+ * PARAMETERS:
+ *      *dev   - pointer to device structure
+ *      *attr  - pointer to device attributes
+ *      *buf   - pointer to output buffer
+ *       size   - size of data in buffer
+ ******************************************************************************/
+static ssize_t pt_pip2_manual_ram_upgrade_store(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t size)
+{
+	struct pt_loader_data *ld = pt_get_loader_data(dev);
+	u32 input_data[2] = {0};
+	int length;
+	int rc = 0;
+
+	length = cmd->parse_sysfs_input(dev, buf, size, input_data,
+			ARRAY_SIZE(input_data));
+
+	if (length != 1) {
+		pt_debug(dev, DL_WARN, "%s: Invalid number of arguments\n",
+			__func__);
+		rc = -EINVAL;
+		goto exit;
+	}
+
+	if (input_data[0] < 0 || input_data[0] > 1) {
+		pt_debug(dev, DL_WARN, "%s: Invalid arguments\n", __func__);
+		rc = -EINVAL;
+		goto exit;
+	}
+
+	if (ld->is_manual_upgrade_enabled) {
+		pt_debug(dev, DL_ERROR,
+			"%s: ERROR - Manual upgrade busy\n", __func__);
+		rc = -EBUSY;
+		goto exit;
+	}
+
+	/* Write File Status: reset to 0 */
+	_pt_update_write_file_status(dev, UPDATE_FW_IDLE);
+	ld->pip2_load_file_no = PIP2_RAM_FILE;
+	pt_debug(dev, DL_DEBUG, "%s: ATM - File number is %d\n",
+		__func__, ld->pip2_load_file_no);
+
+	ld->is_manual_upgrade_enabled = 1;
+	rc = _pt_pip2_update_fw_from_class(dev);
+	ld->is_manual_upgrade_enabled = 0;
+	if (rc < 0)
+		pt_debug(dev, DL_ERROR,
+			"%s: ERROR - RAM Upgrade failed\n", __func__);
+
+exit:
+	if (rc)
+		return rc;
+	return size;
+}
+static DEVICE_ATTR(pip2_manual_ram_upgrade, 0200,
+	NULL, pt_pip2_manual_ram_upgrade_store);
+
+/*******************************************************************************
+ * FUNCTION: pt_pip2_file_write_store
+ *
+ * SUMMARY: Store method for the pip2_file_write sysfs node. Allows
+ *	sysfs control to "load" data into any file.
+ *
+ * PARAMETERS:
+ *      *dev   - pointer to device structure
+ *      *attr  - pointer to device attributes
+ *      *buf   - pointer to output buffer
+ *       size   - size of data in buffer
+ ******************************************************************************/
+static ssize_t pt_pip2_file_write_store(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t size)
+{
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+	struct pt_loader_data *ld = pt_get_loader_data(dev);
+	int rc;
+	u32 input_data[3];
+	int length;
+
+	if (ld->is_manual_upgrade_enabled) {
+		pt_debug(dev, DL_ERROR,
+			"%s: ERROR - Manual upgrade busy\n", __func__);
+		rc = -EBUSY;
+		goto exit;
+	}
+
+	length = cmd->parse_sysfs_input(dev, buf, size, input_data,
+			ARRAY_SIZE(input_data));
+	if (length <= 0 || length > 2) {
+		pt_debug(dev, DL_ERROR, "%s: Invalid number of arguments\n",
+			__func__);
+		ld->pip2_file_data.para_num = 0;
+		rc = -EINVAL;
+		goto exit;
+	}
+
+	if (input_data[0] < PIP2_FW_FILE || input_data[0] > PIP2_FILE_MAX) {
+		pt_debug(dev, DL_ERROR, "%s: Invalid file handle\n", __func__);
+		ld->pip2_file_data.para_num = 0;
+		rc = -EINVAL;
+		goto exit;
+	}
+
+	/* This functionality is only available in the BL */
+	if (cd->mode != PT_MODE_BOOTLOADER) {
+		rc = -EPERM;
+		pt_debug(dev, DL_ERROR, "%s: Invalid DUT mode = %d\n",
+			 __func__, cd->mode);
+		goto exit;
+	}
+
+	/* Write File Status: reset to 0 */
+	_pt_update_write_file_status(dev, UPDATE_FW_IDLE);
+	ld->pip2_load_file_no = input_data[0];
+	if (length == 2)
+		ld->pip2_file_data.file_offset = input_data[1];
+
+	ld->is_manual_upgrade_enabled = 1;
+	rc = _pt_pip2_write_file_from_class(dev);
+	ld->is_manual_upgrade_enabled = 0;
+	if (rc < 0)
+		pt_debug(dev, DL_ERROR,
+			"%s: ERROR - RAM Upgrade failed\n", __func__);
+exit:
+	if (rc)
+		return rc;
+	return size;
+}
+static DEVICE_ATTR(pip2_file_write, 0200, NULL, pt_pip2_file_write_store);
+#endif /* !TTDL_KERNEL_SUBMISSION */
+
+/*******************************************************************************
+ * FUNCTION: pt_update_fw_store
+ *
+ * SUMMARY: Store method for the update_fw sysfs node. This node is required
+ *	by ChromeOS to first determine if loading is available and then perform
+ *	the loading if required. This function is simply a wrapper to call:
+ *		pt_pip2_manual_upgrade_store - for the TC3XXX or TT7XXX parts
+ *		pt_manual_upgrade_store - for the legacy Gen5/6 devices.
+ *
+ * PARAMETERS:
+ *      *dev   - pointer to device structure
+ *      *attr  - pointer to device attributes
+ *      *buf   - pointer to output buffer
+ *       size   - size of data in buffer
+ ******************************************************************************/
+static ssize_t pt_update_fw_store(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t size)
+{
+	u8 dut_gen = cmd->request_dut_generation(dev);
+
+	if (dut_gen == DUT_PIP2_CAPABLE)
+		size = pt_pip2_manual_upgrade_store(dev, attr, buf, size);
+	else if (dut_gen == DUT_PIP1_ONLY)
+		size = pt_manual_upgrade_store(dev, attr, buf, size);
+
+	return size;
+}
+static DEVICE_ATTR(update_fw, 0200, NULL, pt_update_fw_store);
+#endif /* CONFIG_TOUCHSCREEN_PARADE_BINARY_FW_UPGRADE */
+
+#ifndef TTDL_KERNEL_SUBMISSION
+#ifdef TTDL_DIAGNOSTICS
+/*******************************************************************************
+ * FUNCTION: pt_pip2_file_read_show
+ *
+ * SUMMARY: The read method for the pip2_file_read sysfs node. Allows to
+ *  perform flash read action according to stored value. This function will
+ *  re-enter always until it returns:
+ *        0: No data to be written to sysfs node
+ *       <0: Error happens
+ * (For kernel version large than 3.11(not tested), if the function returns
+ * non-zero value in previous chunk, to return 0 once can not stop re-enter.
+ * It needs one more time to stop read action by returned value <= 0. But for
+ * older version, read action will stop when returned value <= 0 once).
+ *
+ * NOTE: Up to PIP2_FILE_WRITE_LEN_PER_PACKET(245) bytes of data are read for
+ * each enter. When last package is read, pip2_file_data.para_num is assigned
+ * as negatie value(-1), then next enter can return 0 to indicate the read
+ * method can be finished.
+ *
+ * RETURN: Size of data written to sysfs node
+ *
+ * PARAMETERS:
+ *  *filp     - pointer to file structure
+ *  *kobj     - pointer to kobject structure
+ *  *bin_attr - pointer to bin_attribute structure
+ *   buf      - pointer to cmd input buffer
+ *   offset   - offset index to store input buffer
+ *   count    - size of data in buffer
+ ******************************************************************************/
+static ssize_t pt_pip2_file_read_show(struct file *filp,
+		struct kobject *kobj, struct bin_attribute *bin_attr,
+		char *buf, loff_t offset, size_t count)
+{
+	struct device *dev = container_of(kobj, struct device, kobj);
+	struct pt_loader_data *ld = pt_get_loader_data(dev);
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+	int rc = 0;
+	u8 file_handle = ld->pip2_file_data.file_handle;
+	u8 *pr_buf = ld->pip2_file_data.file_print_buf;
+	int print_idx = 0, read_size = 0, i;
+	u8  read_buf[PT_MAX_PIP2_MSG_SIZE];
+	u8  read_len;
+	u32 address, file_size;
+
+	if (ld->pip2_file_data.file_print_left) {
+		pt_debug(dev, DL_INFO, "%s: print left=%d, count=%zu\n",
+			__func__, ld->pip2_file_data.file_print_left, count);
+
+		print_idx = ld->pip2_file_data.file_print_left;
+		if (count < print_idx) {
+			memcpy(buf, pr_buf, count);
+			ld->pip2_file_data.file_print_left = print_idx - count;
+			for (i = 0; i < ld->pip2_file_data.file_print_left; i++)
+				pr_buf[i] = pr_buf[i+count];
+			print_idx = count;
+		} else {
+			memcpy(buf, pr_buf, print_idx);
+			ld->pip2_file_data.file_print_left = 0;
+		}
+		return print_idx;
+	}
+
+	if (ld->pip2_file_data.para_num == 0) {
+		/*
+		 * When offset != 0, it means the extra call for splice out,
+		 * don't need a warning.
+		 */
+		if (offset != 0)
+			return 0;
+
+		print_idx += scnprintf(buf, count, "Status: %d\n"
+			"No input!\n", -EINVAL);
+		pt_debug(dev, DL_ERROR, "%s: Invalid para_num = %d!\n",
+			__func__, ld->pip2_file_data.para_num);
+		return print_idx;
+	} else if (ld->pip2_file_data.para_num == -1) {
+		ld->pip2_file_data.para_num = 0;
+		pt_debug(dev, DL_INFO, "%s: flash read finish!\n",
+				__func__);
+		rc = 0;
+		goto exit_release;
+	} else if (ld->pip2_file_data.para_num < -1) {
+		ld->pip2_file_data.para_num = 0;
+		pt_debug(dev, DL_ERROR, "%s: Exit directly due to errors!\n",
+				__func__);
+		return 0;
+	} else if (ld->pip2_file_data.para_num > 3) {
+		ld->pip2_file_data.para_num = 0;
+		pt_debug(dev, DL_ERROR,
+			"%s: Exit directly due to invalid parameter!\n",
+				__func__);
+		return 0;
+	}
+
+	if (offset == 0) {
+		ld->pip2_file_data.file_print_buf = kzalloc(PIPE_BUF,
+			GFP_KERNEL);
+		if (!ld->pip2_file_data.file_print_buf) {
+			rc = -ENOMEM;
+			goto exit;
+		}
+		/* This functionality is only available in the BL */
+		if (cd->mode != PT_MODE_BOOTLOADER) {
+			rc = -EPERM;
+			goto exit_free;
+		}
+
+		pr_buf = ld->pip2_file_data.file_print_buf;
+
+		rc = cmd->request_exclusive(dev,
+				PT_LDR_REQUEST_EXCLUSIVE_TIMEOUT);
+		if (rc) {
+			pt_debug(dev, DL_ERROR,
+				"%s: Failed to request exclusive rc=%d\n",
+				__func__, rc);
+			goto exit_free;
+		}
+
+		rc = cmd->nonhid_cmd->pip2_file_open(dev, file_handle);
+		if (rc < 0) {
+			pt_debug(dev, DL_ERROR,
+				"%s: Failed to file_open rc=%d\n",
+				__func__, rc);
+			goto exit_release;
+		}
+
+		rc = cmd->nonhid_cmd->pip2_file_get_stats(dev, file_handle,
+				&address, &file_size);
+		if (rc) {
+			pt_debug(dev, DL_ERROR,
+				"%s: Failed to get_file_state rc=%d\n",
+				__func__, rc);
+			goto exit_file_close;
+		}
+
+		ld->pip2_file_data.file_print_size = 0;
+		ld->pip2_file_data.file_max_size = file_size;
+		ld->pip2_file_data.file_print_left = 0;
+		print_idx += scnprintf(pr_buf, PIPE_BUF, "ROM_DATA:");
+
+		if (ld->pip2_file_data.para_num == 1)
+			ld->pip2_file_data.file_read_size = file_size;
+		else if (ld->pip2_file_data.para_num == 2) {
+			if (ld->pip2_file_data.file_offset < file_size)
+				ld->pip2_file_data.file_read_size = file_size -
+					ld->pip2_file_data.file_offset;
+			else {
+				rc = -EINVAL;
+				pt_debug(dev, DL_ERROR,
+					"%s: File read out of bounds rc=%d\n",
+					__func__, rc);
+				goto exit_file_close;
+			}
+		} else if (ld->pip2_file_data.para_num == 3) {
+			if ((ld->pip2_file_data.file_read_size +
+				ld->pip2_file_data.file_offset) > file_size) {
+				rc = -EINVAL;
+				pt_debug(dev, DL_ERROR,
+					"%s: File read out of bounds rc=%d\n",
+					__func__, rc);
+				goto exit_file_close;
+			}
+		} else {
+			pt_debug(dev, DL_ERROR,
+				"%s: Invalid number of parameters!\n",
+				__func__);
+			goto exit_file_close;
+		}
+	}
+
+	offset = ld->pip2_file_data.file_print_size +
+			ld->pip2_file_data.file_offset;
+	if ((offset >= ld->pip2_file_data.file_max_size) ||
+		(ld->pip2_file_data.file_print_size >=
+		ld->pip2_file_data.file_read_size))
+		goto exit_file_close;
+	else if ((ld->pip2_file_data.file_print_size +
+		PIP2_FILE_WRITE_LEN_PER_PACKET) >=
+		ld->pip2_file_data.file_read_size)
+		read_len = ld->pip2_file_data.file_read_size -
+		ld->pip2_file_data.file_print_size;
+	else
+		read_len = PIP2_FILE_WRITE_LEN_PER_PACKET;
+
+	rc = cmd->nonhid_cmd->pip2_file_seek_offset(dev,
+			file_handle, offset, 0);
+	if (rc) {
+		pt_debug(dev, DL_ERROR,
+			"%s: Failed to seek file offset rc=%d\n",
+			__func__, rc);
+		goto exit_file_close;
+	}
+
+	read_size = cmd->nonhid_cmd->pip2_file_read(dev,
+			file_handle, read_len, read_buf);
+	if (read_size < 0) {
+		pt_debug(dev, DL_ERROR, "%s: Failed to read file rc=%d\n",
+				__func__, read_size);
+		goto exit_file_close;
+	}
+
+	for (i = 0; i < read_size; i++)
+		print_idx += scnprintf(pr_buf + print_idx,
+			PIPE_BUF - print_idx,
+			"%02X ", read_buf[i + PIP2_RESP_BODY_OFFSET]);
+	ld->pip2_file_data.file_print_size += read_size;
+	if (count < print_idx) {
+		memcpy(buf, pr_buf, count);
+		ld->pip2_file_data.file_print_left = print_idx - count;
+		for (i = 0; i < ld->pip2_file_data.file_print_left; i++)
+			pr_buf[i] = pr_buf[i+count];
+		print_idx = count;
+	} else {
+		memcpy(buf, pr_buf, print_idx);
+	}
+	goto exit_for_next_read;
+
+exit_file_close:
+	if (rc)
+		print_idx += scnprintf(pr_buf + print_idx,
+			PIPE_BUF - print_idx, "(READ ERROR)\n");
+	else if (ld->pip2_file_data.file_print_size)
+		print_idx += scnprintf(pr_buf + print_idx,
+				PIPE_BUF - print_idx,
+				":(%d bytes)\n",
+				ld->pip2_file_data.file_print_size);
+	else
+		print_idx += scnprintf(pr_buf + print_idx,
+				PIPE_BUF - print_idx, "No Data\n");
+	if (count < print_idx) {
+		memcpy(buf, pr_buf, count);
+		ld->pip2_file_data.file_print_left = print_idx - count;
+		for (i = 0; i < ld->pip2_file_data.file_print_left; i++)
+			pr_buf[i] = pr_buf[i+count];
+		print_idx = count;
+	} else {
+		memcpy(buf, pr_buf, print_idx);
+	}
+	rc = cmd->nonhid_cmd->pip2_file_close(dev, file_handle);
+	if (file_handle != rc)
+		pt_debug(dev, DL_ERROR,
+			"%s Failed to close file %d, rc = %d\n", __func__,
+			file_handle, rc);
+	/*
+	 * Mark para_num as negative value to finish read method during
+	 * next enter.
+	 */
+	ld->pip2_file_data.para_num = -1;
+
+exit_for_next_read:
+	pt_debug(dev, DL_INFO,
+		"%s: %s=%d, %s=%d, %s=%d, %s=%d, %s=%d\n",
+		__func__,
+		"para_num", ld->pip2_file_data.para_num,
+		"handle", ld->pip2_file_data.file_handle,
+		"offset", ld->pip2_file_data.file_offset,
+		"read", ld->pip2_file_data.file_read_size,
+		"print", ld->pip2_file_data.file_print_size);
+	return print_idx;
+exit_release:
+	cmd->release_exclusive(dev);
+exit_free:
+	kfree(ld->pip2_file_data.file_print_buf);
+exit:
+	if (rc) {
+		ld->pip2_file_data.para_num = -2;
+		print_idx = scnprintf(buf, count, "Status: %d\n", rc);
+		return print_idx;
+	} else
+		return 0;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_pip2_file_read_store
+ *
+ * SUMMARY: The write method for the pip2_file_read node. The passed
+ *  in data is needed by read method.
+ *
+ * RETURN: Size of passed in buffer is success
+ *
+ * PARAMETERS:
+ *  *filp     - pointer to file structure
+ *  *kobj     - pointer to kobject structure
+ *  *bin_attr - pointer to bin_attribute structure
+ *   buf      - pointer to cmd input buffer
+ *   offset   - offset index to store input buffer
+ *   count    - size of data in buffer
+ ******************************************************************************/
+static ssize_t pt_pip2_file_read_store(struct file *filp,
+		struct kobject *kobj, struct bin_attribute *bin_attr,
+		char *buf, loff_t offset, size_t count)
+{
+	int rc = 0;
+	int length;
+	struct device *dev = container_of(kobj, struct device, kobj);
+	struct pt_loader_data *ld = pt_get_loader_data(dev);
+	int ic_buffer[4] = {0};
+
+	length = cmd->parse_sysfs_input(dev, buf, count, ic_buffer,
+			ARRAY_SIZE(ic_buffer));
+	if (length <= 0 || length > 3) {
+		pt_debug(dev, DL_ERROR, "%s: Input format error!\n",
+				__func__);
+		ld->pip2_file_data.para_num = 0;
+		rc = -EINVAL;
+		goto error;
+	}
+
+	if (ic_buffer[0] < PIP2_FW_FILE || ic_buffer[0] > PIP2_FILE_MAX) {
+		pt_debug(dev, DL_ERROR, "%s: Invalid file handle!\n",
+				__func__);
+		ld->pip2_file_data.para_num = 0;
+		rc = -EINVAL;
+		goto error;
+	}
+
+	switch (length) {
+	case 1:
+		ld->pip2_file_data.file_handle = ic_buffer[0];
+		ld->pip2_file_data.file_offset = 0;
+		ld->pip2_file_data.file_read_size = 0;
+		break;
+	case 2:
+		ld->pip2_file_data.file_handle = ic_buffer[0];
+		ld->pip2_file_data.file_offset = ic_buffer[1];
+		ld->pip2_file_data.file_read_size = 0;
+		break;
+	case 3:
+		ld->pip2_file_data.file_handle = ic_buffer[0];
+		ld->pip2_file_data.file_offset = ic_buffer[1];
+		ld->pip2_file_data.file_read_size = ic_buffer[2];
+		break;
+	default:
+		break;
+	}
+
+	ld->pip2_file_data.para_num = length;
+error:
+	pt_debug(dev, DL_INFO,
+		"%s: %s=%d, %s=%d, %s=%d, %s=%d, %s=%d\n",
+		__func__,
+		"para_num", ld->pip2_file_data.para_num,
+		"handle", ld->pip2_file_data.file_handle,
+		"offset", ld->pip2_file_data.file_offset,
+		"read", ld->pip2_file_data.file_read_size,
+		"print", ld->pip2_file_data.file_print_size);
+
+	if (rc)
+		return rc;
+	return count;
+}
+
+static struct bin_attribute bin_attr_pip2_file_read = {
+	.attr = {
+		.name = "pip2_file_read",
+		.mode = (0644),
+	},
+	.read = pt_pip2_file_read_show,
+	.write = pt_pip2_file_read_store,
+};
+
+/******************************************************************************
+ * FUNCTION: pt_pip2_file_crc_show
+ *
+ * SUMMARY: The show method for the "pip2_file_crc" sysfs node.
+ *	Shows the CRC of a file or portion of the file.
+ *
+ *	NOTE: If function is called when DUT is already in BL mode, the DUT
+ *	will remain in BL mode when function exits, however if DUT is in
+ *	normal mode when function is called, the DUT will be forced into BL
+ *	mode and then returned to normal mode when function exits.
+ *
+ *	NOTE: This sysfs node only can be used for BL version 1.8 or greater.
+ *
+ * PARAMETERS:
+ *	*dev  - pointer to device structure
+ *	*attr - pointer to device attributes structure
+ *	*buf  - pointer to print output buffer
+ ******************************************************************************/
+static ssize_t pt_pip2_file_crc_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct pt_loader_data *ld = pt_get_loader_data(dev);
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+	int rc = 0;
+	u8 file_handle;
+	int print_idx = 0;
+	u8  read_buf[PT_MAX_PIP2_MSG_SIZE];
+	u32 address, file_size;
+	u32 length;
+	u32 offset;
+	u16 file_crc;
+	u16 status;
+
+	if (ld->pip2_fcrc.para_num == 0 ||
+	    ld->pip2_fcrc.file_handle == 0 ||
+	    ld->pip2_fcrc.file_read_size < 0 ||
+	    ld->pip2_fcrc.file_offset < 0) {
+		pt_debug(dev, DL_ERROR,
+			"%s: Invalid parameters!\n",
+			__func__);
+		print_idx = snprintf(buf, PT_MAX_PRBUF_SIZE,
+			"Status: %d\n"
+			"Invalid parameters!\n", -EINVAL);
+		return print_idx;
+	}
+
+	file_handle = ld->pip2_fcrc.file_handle;
+	offset = ld->pip2_fcrc.file_offset;
+	length = ld->pip2_fcrc.file_read_size;
+
+	/* This functionality is only available in the BL */
+	if (cd->mode != PT_MODE_BOOTLOADER) {
+		rc = -EPERM;
+		print_idx = snprintf(buf, PT_MAX_PRBUF_SIZE,
+			"Status: %d\n", rc);
+		goto exit;
+	}
+
+	rc = cmd->request_exclusive(dev, PT_LDR_REQUEST_EXCLUSIVE_TIMEOUT);
+	if (rc) {
+		pt_debug(dev, DL_ERROR,
+			"%s: Failed to request exclusive rc=%d\n",
+			__func__, rc);
+		print_idx = snprintf(buf, PT_MAX_PRBUF_SIZE,
+			"Status: %d\n", rc);
+		goto exit;
+	}
+
+	rc = cmd->nonhid_cmd->pip2_file_open(dev, file_handle);
+	if (rc < 0) {
+		pt_debug(dev, DL_ERROR,
+			"%s: Failed to file_open rc=%d\n",
+			__func__, rc);
+		print_idx = snprintf(buf, PT_MAX_PRBUF_SIZE,
+			"Status: %d\n", rc);
+		goto exit_release;
+	}
+
+	rc = cmd->nonhid_cmd->pip2_file_get_stats(dev, file_handle,
+			&address, &file_size);
+	if (rc) {
+		pt_debug(dev, DL_ERROR,
+			"%s: Failed to get_file_state rc=%d\n",
+			__func__, rc);
+		print_idx = snprintf(buf, PT_MAX_PRBUF_SIZE,
+			"Status: %d\n", rc);
+		goto exit_file_close;
+	}
+
+	ld->pip2_fcrc.file_max_size = file_size;
+
+	if (ld->pip2_fcrc.file_read_size > file_size
+		|| ld->pip2_fcrc.file_offset > file_size
+		|| ((ld->pip2_fcrc.file_offset +
+		    ld->pip2_fcrc.file_read_size) > file_size)) {
+		pt_debug(dev, DL_ERROR,
+				"%s: Invalid parameters!\n",
+				__func__);
+		print_idx = snprintf(buf, PT_MAX_PRBUF_SIZE,
+			"Status: %d\n", rc);
+		goto exit_file_close;
+	}
+
+	rc = cmd->nonhid_cmd->pip2_file_crc(dev,
+			file_handle, offset, length, read_buf);
+	if (rc) {
+		pt_debug(dev, DL_ERROR,
+			"%s: Failed to get file crc, rc=%d\n",
+			__func__, rc);
+		print_idx = snprintf(buf, PT_MAX_PRBUF_SIZE,
+			"Status: %d\n", rc);
+	} else {
+		status = read_buf[PIP2_RESP_STATUS_OFFSET];
+		if (status == PIP2_RSP_ERR_NONE) {
+			file_crc = get_unaligned_le16(&read_buf[5]);
+			print_idx = snprintf(buf, PT_MAX_PRBUF_SIZE,
+				"Status: %d\n"
+				"FILE CRC: %04X\n",
+				status, file_crc);
+		} else
+			print_idx = snprintf(buf, PT_MAX_PRBUF_SIZE,
+				"Status: %d\n"
+				"FILE CRC: n/a\n",
+				status);
+	}
+
+exit_file_close:
+	rc = cmd->nonhid_cmd->pip2_file_close(dev, file_handle);
+	if (file_handle != rc)
+		pt_debug(dev, DL_ERROR,
+			"%s Failed to close file %d, rc = %d\n", __func__,
+			file_handle, rc);
+
+exit_release:
+	cmd->release_exclusive(dev);
+exit:
+	ld->pip2_fcrc.para_num = 0;
+	return print_idx;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_pip2_file_crc_store
+ *
+ * SUMMARY: The store method for the "pip2_file_crc" sysfs node. The passed in
+ *  data including file_handle, offset and read size are needed by show method.
+ *
+ *	NOTE: This sysfs node only can be used for BL version 1.8 or greater..
+ *
+ * PARAMETERS:
+ *	*dev  - pointer to device structure
+ *	*attr - pointer to device attributes structure
+ *	*buf  - pointer to print output buffer
+ *	 size - size of buffer
+ ******************************************************************************/
+static ssize_t pt_pip2_file_crc_store(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t size)
+{
+	struct pt_loader_data *ld = pt_get_loader_data(dev);
+	u32 input_data[4] = {0};
+	int length;
+	int rc = 0;
+
+	ld->pip2_fcrc.file_handle = PIP2_RAM_FILE;
+	ld->pip2_fcrc.file_offset = -1;
+	ld->pip2_fcrc.file_read_size = -1;
+
+	length = cmd->parse_sysfs_input(dev, buf, size, input_data,
+			ARRAY_SIZE(input_data));
+
+	if (length != 3) {
+		pt_debug(dev, DL_WARN, "%s: Invalid number of arguments\n",
+			__func__);
+		ld->pip2_fcrc.para_num = 0;
+		rc = -EINVAL;
+		goto exit;
+	}
+
+	if ((input_data[0] < PIP2_FW_FILE) || (input_data[0] > PIP2_FILE_MAX)) {
+		pt_debug(dev, DL_WARN, "%s: Invalid file_no %d\n",
+			__func__, input_data[0]);
+		rc = -EINVAL;
+		goto exit;
+	}
+
+	ld->pip2_fcrc.file_handle = input_data[0];
+	ld->pip2_fcrc.file_offset = input_data[1];
+	ld->pip2_fcrc.file_read_size = input_data[2];
+	ld->pip2_fcrc.para_num = 3;
+
+	pt_debug(dev, DL_INFO,
+		"%s: %s=%d, %s=%d, %s=%d, %s=%d\n",
+		__func__,
+		"para_num", ld->pip2_fcrc.para_num,
+		"handle", ld->pip2_fcrc.file_handle,
+		"offset", ld->pip2_fcrc.file_offset,
+		"length", ld->pip2_fcrc.file_read_size);
+exit:
+	if (rc)
+		return rc;
+	return size;
+}
+
+static DEVICE_ATTR(pip2_file_crc, 0644,
+	pt_pip2_file_crc_show, pt_pip2_file_crc_store);
+#endif
+/*******************************************************************************
+ * FUNCTION: pt_pip2_file_erase_show
+ *
+ * SUMMARY: The show method for the "pip2_file_erase" sysfs node.
+ *	Prints current erase status to output buffer.
+ *
+ * PARAMETERS:
+ *	*dev  - pointer to device structure
+ *	*attr - pointer to device attributes structure
+ *	*buf  - pointer to print output buffer
+ ******************************************************************************/
+static ssize_t pt_pip2_file_erase_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct pt_loader_data *ld = pt_get_loader_data(dev);
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+	u8 file_handle;
+	u8 file = ld->pip2_file_erase_file_no;
+	u16 sector_num = 0;
+	u32 max_file_size = 0;
+	int rc;
+
+	pip2_erase_status = -1;
+	pip2_erase_rc = 0;
+
+	if (file == PIP2_RAM_FILE) {
+		rc = -EINVAL;
+		goto exit;
+	}
+
+	/* This functionality is only available in the BL */
+	if (cd->mode != PT_MODE_BOOTLOADER) {
+		rc = -EPERM;
+		goto exit;
+	}
+
+	rc = cmd->request_exclusive(dev, PT_LDR_REQUEST_EXCLUSIVE_TIMEOUT);
+	if (rc < 0) {
+		pt_debug(dev, DL_ERROR,
+			"%s: Failed to get exclusive access rc=%d\n",
+			__func__, rc);
+		goto exit;
+	}
+
+	file_handle = cmd->nonhid_cmd->pip2_file_open(dev, file);
+	if (file_handle != file) {
+		rc = -EBADF;
+		goto exit_release;
+	}
+
+	rc = cmd->nonhid_cmd->pip2_file_get_stats(dev,
+			file, NULL, &max_file_size);
+	if (rc) {
+		pt_debug(dev, DL_ERROR,
+			"%s: Failed to get_file_state ret=%d\n",
+			__func__, rc);
+		goto exit_release;
+	}
+
+	sector_num = max_file_size / PT_PIP2_FILE_SECTOR_SIZE;
+	if (max_file_size % PT_PIP2_FILE_SECTOR_SIZE) {
+		pt_debug(dev, DL_WARN,
+			 "%s: file size %d misalign, don't use sector erase\n",
+			 __func__, max_file_size);
+		/*
+		 * TODO: Not sure whether can have this case, and this
+		 * is a workaround to ensure the safety in logic.
+		 * Force sector number to 0 will use the default method
+		 * to do file erase by FILE instead of SECTOR.
+		 */
+		sector_num = 0;
+	}
+
+	file_handle = cmd->nonhid_cmd->pip2_file_erase(dev, file,
+		sector_num, &pip2_erase_status);
+	if (file_handle < 0) {
+		rc = file_handle;
+		pt_debug(dev, DL_INFO, "%s: File erase error rc = %d\n",
+			__func__, rc);
+	} else if (file_handle == file) {
+		pt_debug(dev, DL_INFO, "%s: File %d erased\n",
+			__func__, file_handle);
+	} else {
+		rc = -EBADF;
+	}
+
+	file_handle = cmd->nonhid_cmd->pip2_file_close(dev, file);
+	if (file_handle != file && !rc)
+		rc = -EBADF;
+
+exit_release:
+	cmd->release_exclusive(dev);
+exit:
+	pip2_erase_rc = rc;
+	if (pip2_erase_status == -1) {
+		return snprintf(buf, PT_MAX_PRBUF_SIZE,
+			"Status: %d\n"
+			"Erase Status: n/a\n",
+			pip2_erase_rc);
+	}
+	return snprintf(buf, PT_MAX_PRBUF_SIZE,
+		"Status: %d\n"
+		"Erase Status: 0x%02X\n",
+		pip2_erase_rc, pip2_erase_status);
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_pip2_file_erase_store
+ *
+ * SUMMARY: The store method for the "pip2_file_erase" sysfs node. Allows the
+ *	caller to provide the file number to erase in FLASH.
+ *
+ *	NOTE: The DUT must be in BL mode before calling this function.
+ *
+ * PARAMETERS:
+ *	*dev  - pointer to device structure
+ *	*attr - pointer to device attributes structure
+ *	*buf  - pointer to print output buffer
+ *	 size - size of buffer
+ ******************************************************************************/
+static ssize_t pt_pip2_file_erase_store(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t size)
+{
+	struct pt_loader_data *ld = pt_get_loader_data(dev);
+	u32 input_data[2] = {0};
+	int length;
+	int rc = 0;
+
+	length = cmd->parse_sysfs_input(dev, buf, size, input_data,
+			ARRAY_SIZE(input_data));
+
+	if (length != 1) {
+		pt_debug(dev, DL_WARN, "%s: Invalid number of arguments\n",
+			__func__);
+		rc = -EINVAL;
+		goto exit;
+	}
+
+	/* Only allow valid files to be erased */
+	if (input_data[0] < PIP2_FW_FILE || input_data[0] > PIP2_FILE_MAX) {
+		pip2_erase_status = PIP2_RSP_ERR_BAD_FILE;
+		pt_debug(dev, DL_ERROR, "%s: ERROR - Invalid File\n",
+			__func__);
+		rc = -EINVAL;
+		goto exit;
+	}
+
+	ld->pip2_file_erase_file_no = input_data[0];
+
+exit:
+	if (rc)
+		return rc;
+	return size;
+}
+
+static DEVICE_ATTR(pip2_file_erase, 0644,
+	pt_pip2_file_erase_show, pt_pip2_file_erase_store);
+
+/*******************************************************************************
+ * FUNCTION: pt_write_file_status_show
+ *
+ * SUMMARY: The show method for the pip2_bl_status sysfs node.
+ *	Shows the percent completion of the current BL or an error message.
+ *
+ * PARAMETERS:
+ *	*dev  - pointer to device structure
+ *	*attr - pointer to device attributes structure
+ *	*buf  - pointer to print output buffer
+ ******************************************************************************/
+static ssize_t pt_write_file_status_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	ssize_t ret;
+	u8 status = write_file_status;
+
+	if (write_file_status <= UPDATE_FW_COMPLETE) {
+		pt_debug(dev, DL_DEBUG,
+			"%s BL_STATUS = %d\n", __func__, write_file_status);
+		return scnprintf(buf, strlen(buf), "%d\n", write_file_status);
+	}
+
+	switch (status) {
+	case UPDATE_FW_GENERAL_ERROR:
+		ret = scnprintf(buf, strlen(buf),
+			"ERROR: %d - General programming failure\n", status);
+		break;
+	case UPDATE_FW_PIP_VERSION_ERROR:
+		ret = scnprintf(buf, strlen(buf),
+			"ERROR: %d - Wrong PIP version detected\n", status);
+		break;
+	case UPDATE_FW_VERSION_ERROR:
+		ret = scnprintf(buf, strlen(buf),
+			"ERROR: %d - FW vervion newer than bin file\n", status);
+		break;
+	case UPDATE_FW_ERASE_ERROR:
+		ret = scnprintf(buf, strlen(buf),
+			"ERROR: %d - ROM BL failed to erase FW file in FLASH\n",
+			status);
+		break;
+	case UPDATE_FW_FILE_CLOSE_ERROR:
+		ret = scnprintf(buf, strlen(buf),
+			"ERROR: %d - ROM BL failed to close FW file in FLASH\n",
+			status);
+		break;
+	case UPDATE_FW_WRITE_ERROR:
+		ret = scnprintf(buf, strlen(buf),
+			"ERROR: %d - ROM BL file write failure\n", status);
+		break;
+	case UPDATE_FW_EXECUTE_ERROR:
+		ret = scnprintf(buf, strlen(buf),
+			"ERROR: %d - ROM BL failed to execute RAM image\n",
+			status);
+		break;
+	case UPDATE_FW_RESET_ERROR:
+		ret = scnprintf(buf, strlen(buf),
+			"ERROR: %d - Reset DUT failure\n",
+			status);
+		break;
+	case UPDATE_FW_MODE_ERROR:
+		ret = scnprintf(buf, strlen(buf),
+			"ERROR: %d - Program complete, Incorrect BL/APP mode detected after reset sentinel\n",
+			status);
+		break;
+	case UPDATE_FW_ENTER_BL_ERROR:
+		ret = scnprintf(buf, strlen(buf),
+			"ERROR: %d - Could not enter the BL\n", status);
+		break;
+	case UPDATE_FW_FILE_OPEN_ERROR:
+		ret = scnprintf(buf, strlen(buf),
+			"ERROR: %d - ROM BL failed to open FW file in FLASH\n",
+			status);
+		break;
+	case UPDATE_FW_SENTINEL_NOT_SEEN:
+		ret = scnprintf(buf, strlen(buf),
+			"ERROR: %d - FW Reset Sentinel not seen after XRES\n",
+			status);
+		break;
+	case UPDATE_FW_EXCLUSIVE_ACCESS_ERROR:
+		ret = scnprintf(buf, strlen(buf),
+			"ERROR: %d - Failed to get DUT exclusive access\n",
+			status);
+		break;
+	case UPDATE_FW_NO_FW_PROVIDED:
+		ret = scnprintf(buf, strlen(buf),
+			"ERROR: %d - No FW provided to load\n", status);
+		break;
+	case UPDATE_FW_INVALID_FW_IMAGE:
+		ret = scnprintf(buf, strlen(buf), "ERROR: %d - Invalid FW image\n", status);
+		break;
+	case UPDATE_FW_FILE_SEEK_ERROR:
+		ret = scnprintf(buf, strlen(buf), "ERROR: %d - File seek failure\n", status);
+		break;
+	case UPDATE_FW_MISALIGN_FW_IMAGE:
+		ret = scnprintf(buf, strlen(buf),
+			"ERROR: %d - FW image is misaligned\n", status);
+		break;
+	case UPDATE_FW_SYSTEM_NOMEM:
+		ret = scnprintf(buf, strlen(buf),
+			"ERROR: %d - Failed to alloc memory\n", status);
+		break;
+	case UPDATE_FW_INIT_BL_ERROR:
+		ret = scnprintf(buf, strlen(buf),
+			"ERROR: %d - Failed to init bootloader\n", status);
+		break;
+	case UPDATE_FW_PARSE_ROW_ERROR:
+		ret = scnprintf(buf, strlen(buf),
+			"ERROR: %d - Failed to parse row of FW image\n",
+			status);
+		break;
+	case UPDATE_FW_PROGRAM_ROW_ERROR:
+		ret = scnprintf(buf, strlen(buf),
+			"ERROR: %d - Failed to progrem FW image\n", status);
+		break;
+	case UPDATE_FW_EXIT_BL_ERROR:
+		ret = scnprintf(buf, strlen(buf),
+			"ERROR: %d - Failed to exit bootloader\n", status);
+		break;
+	case UPDATE_FW_CHECK_SUM_ERROR:
+		ret = scnprintf(buf, strlen(buf),
+			"ERROR: %d - Failed to verify app checksum\n", status);
+		break;
+	case UPDATE_FW_NO_PLATFORM_DATA:
+		ret = scnprintf(buf, strlen(buf),
+			"ERROR: %d - No platform data\n", status);
+		break;
+	case UPDATE_FW_NOT_SUPPORTED_FILE_NO:
+		ret = scnprintf(buf, strlen(buf),
+			"ERROR: %d - Not supported file number\n", status);
+		break;
+	case UPDATE_FW_UNDEFINED_ERROR:
+	default:
+		ret = scnprintf(buf, strlen(buf), "ERROR: %d - Unknown error\n", status);
+		break;
+	}
+	return ret;
+}
+static DEVICE_ATTR(pip2_bl_status, 0444, pt_write_file_status_show, NULL);
+#if PT_FW_UPGRADE
+static DEVICE_ATTR(update_fw_status, 0444, pt_write_file_status_show, NULL);
+#endif
+/*******************************************************************************
+ * FUNCTION: pt_pip2_get_last_error_show
+ *
+ * SUMMARY: The show method for the pip2_get_last_error_show sysfs node.
+ *	Shows the last BL error code.
+ *
+ *	NOTE: If function is called when DUT is already in BL mode, the DUT
+ *	will remain in BL mode when function exits, however if DUT is in
+ *	normal mode when function is called, the DUT will be forced into BL
+ *	mode and then returned to normal mode when function exits.
+ *
+ * PARAMETERS:
+ *	*dev  - pointer to device structure
+ *	*attr - pointer to device attributes structure
+ *	*buf  - pointer to print output buffer
+ ******************************************************************************/
+static ssize_t pt_pip2_get_last_error_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+	int rc;
+	u8 read_buf[256];
+
+	rc = cmd->request_exclusive(dev, PT_LDR_REQUEST_EXCLUSIVE_TIMEOUT);
+	if (rc)
+		goto exit;
+
+	cmd->request_stop_wd(dev);
+
+	/* This functionality is only available in the BL */
+	if (cd->mode != PT_MODE_BOOTLOADER) {
+		rc = -EPERM;
+		goto exit_release;
+	}
+
+	/* Get and log the last error(s) */
+	rc = _pt_pip2_log_last_error(dev, read_buf);
+
+exit_release:
+	cmd->release_exclusive(dev);
+exit:
+	if (rc)
+		return snprintf(buf, PT_MAX_PRBUF_SIZE, "Status: %d\n", rc);
+
+	if (read_buf[PIP2_RESP_STATUS_OFFSET] == PIP2_RSP_ERR_NONE) {
+		return snprintf(buf, PT_MAX_PRBUF_SIZE,
+			"Status: %d\n"
+			"Last Error No: 0x%02X\n",
+			PIP2_RSP_ERR_NONE,
+			read_buf[PIP2_RESP_BODY_OFFSET]);
+	} else {
+		return snprintf(buf, PT_MAX_PRBUF_SIZE,
+			"Status: %d\n"
+			"Last Error No: n/a\n",
+			read_buf[PIP2_RESP_STATUS_OFFSET]);
+	}
+}
+static DEVICE_ATTR(pip2_get_last_error, 0444,
+	pt_pip2_get_last_error_show, NULL);
+#endif /* !TTDL_KERNEL_SUBMISSION */
+
+#if PT_FW_UPGRADE
+/*******************************************************************************
+ * FUNCTION: pt_loader_attention
+ *
+ * SUMMARY: Function to be registered to TTDL attention list to set up
+ *  int_running semaphore.
+ *
+ * RETURN:
+ *   0 = success
+ *
+ * PARAMETERS:
+ *  *dev  - pointer to device structure
+ ******************************************************************************/
+static int pt_loader_attention(struct device *dev)
+{
+	struct pt_loader_data *ld = pt_get_loader_data(dev);
+
+	complete(&ld->int_running);
+	return 0;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_fw_upgrade_cb
+ *
+ * SUMMARY: Function to be registered to TTDL attention list to allow upgrade
+ *  if host cannot get response from firmware with ping command.
+ * RETURN:
+ *   0 = success
+ *
+ * PARAMETERS:
+ *  *dev  - pointer to device structure
+ ******************************************************************************/
+static int pt_fw_upgrade_cb(struct device *dev)
+{
+	u8 dut_gen = cmd->request_dut_generation(dev);
+
+#ifdef CONFIG_TOUCHSCREEN_PARADE_PLATFORM_FW_UPGRADE
+	if (dut_gen == DUT_PIP1_ONLY) {
+		pt_debug(dev, DL_WARN, "%s: Upgrade Platform FW", __func__);
+		if (!upgrade_firmware_from_platform(dev, false))
+			return 0;
+	}
+#endif
+
+#ifdef CONFIG_TOUCHSCREEN_PARADE_BINARY_FW_UPGRADE
+	pt_debug(dev, DL_WARN, "%s: Upgrade Builtin FW", __func__);
+	if (dut_gen == DUT_PIP2_CAPABLE) {
+		if (!pt_pip2_upgrade_firmware_from_builtin(dev))
+			return 0;
+		pt_debug(dev, DL_WARN, "%s: Builtin FW upgrade failed",
+			__func__);
+	} else {
+		if (!upgrade_firmware_from_builtin(dev))
+			return 0;
+	}
+#endif
+	return 0;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_cancel_fw_upgrade_cb
+ *
+ * SUMMARY: Function to be registered to TTDL attention list to allow an upgrade
+ *	to be canceled.
+ * RETURN:
+ *   0 = success
+ *
+ * PARAMETERS:
+ *  *dev  - pointer to device structure
+ ******************************************************************************/
+static int pt_cancel_fw_upgrade_cb(struct device *dev)
+{
+	struct pt_loader_data *ld = pt_get_loader_data(dev);
+
+	pt_debug(dev, DL_WARN,
+		"%s: CANCELLING All Loader work\n", __func__);
+	cancel_work_sync(&ld->bl_from_file);
+	cancel_work_sync(&ld->pip2_bl_from_file);
+	cancel_work_sync(&ld->calibration_work);
+	cancel_work_sync(&ld->fw_and_config_upgrade);
+
+	return 0;
+}
+#endif /* PT_FW_UPGRADE */
+
+/*******************************************************************************
+ * FUNCTION: pt_loader_probe
+ *
+ * SUMMARY: The probe function for the FW loader.
+ *
+ * PARAMETERS:
+ *   *dev   - pointer to device structure
+ *  **data  - double pointer to the loader data to be created here
+ ******************************************************************************/
+static int pt_loader_probe(struct device *dev, void **data)
+{
+	struct pt_loader_data *ld;
+	struct pip2_loader_data *pip2_data;
+	struct pt_platform_data *pdata = dev_get_platdata(dev);
+	int rc;
+	u8 dut_gen = cmd->request_dut_generation(dev);
+
+#ifdef TTDL_DIAGNOSTICS
+	pt_debug(dev, DL_INFO,
+		"%s: entering %s\n", __func__, __func__);
+#endif /* TTDL_DIAGNOSTICS */
+
+	ld = kzalloc(sizeof(*ld), GFP_KERNEL);
+	if (!ld) {
+		rc = -ENOMEM;
+		goto error_alloc_data_failed;
+	}
+
+#if PT_FW_UPGRADE
+	/* Initialize boot loader status */
+	if (write_file_status != UPDATE_FW_COMPLETE)
+		_pt_update_write_file_status(dev, UPDATE_FW_IDLE);
+#endif
+
+	if (dut_gen == DUT_PIP2_CAPABLE) {
+		pip2_data = kzalloc(sizeof(*pip2_data), GFP_KERNEL);
+		if (!pip2_data) {
+			rc = -ENOMEM;
+			goto error_alloc_data_failed;
+		}
+		pip2_data->dev = dev;
+		ld->pip2_data = pip2_data;
+
+#if PT_FW_UPGRADE
+		rc = device_create_file(dev, &dev_attr_update_fw_status);
+		if (rc) {
+			pt_debug(dev, DL_ERROR,
+				"%s: Error creating update_fw_status\n",
+				__func__);
+			goto remove_files;
+		}
+#endif
+
+#ifdef CONFIG_TOUCHSCREEN_PARADE_BINARY_FW_UPGRADE
+		rc = device_create_file(dev, &dev_attr_pt_bl_from_file);
+		if (rc) {
+			pt_debug(dev, DL_ERROR,
+				"%s: Error creating pt_bl_from_file\n",
+				__func__);
+			goto remove_files;
+		}
+		rc = device_create_file(dev, &dev_attr_update_fw);
+		if (rc) {
+			pt_debug(dev, DL_ERROR,
+				"%s: Error creating update_fw\n",
+				__func__);
+			goto remove_files;
+		}
+#endif /* CONFIG_TOUCHSCREEN_PARADE_BINARY_FW_UPGRADE */
+
+#ifndef TTDL_KERNEL_SUBMISSION
+#ifdef CONFIG_TOUCHSCREEN_PARADE_BINARY_FW_UPGRADE
+		rc = device_create_file(dev, &dev_attr_pip2_manual_upgrade);
+		if (rc) {
+			pt_debug(dev, DL_ERROR,
+				"%s: Error creating pip2_manual_upgrade\n",
+				__func__);
+			goto remove_files;
+		}
+		rc = device_create_file(dev, &dev_attr_pip2_manual_ram_upgrade);
+		if (rc) {
+			pt_debug(dev, DL_ERROR,
+				"%s: Error creating pip2_manual_ram_upgrade\n",
+				__func__);
+			goto remove_files;
+		}
+		rc = device_create_file(dev, &dev_attr_pip2_file_write);
+		if (rc) {
+			pt_debug(dev, DL_ERROR,
+				"%s: Error creating pip2_file_write\n",
+				__func__);
+			goto remove_files;
+		}
+		rc = device_create_file(dev, &dev_attr_pip2_bl_from_file);
+		if (rc) {
+			pt_debug(dev, DL_ERROR,
+				"%s: Error creating pip2_bl_from_file\n",
+				__func__);
+			goto remove_files;
+		}
+
+#endif /* CONFIG_TOUCHSCREEN_PARADE_BINARY_FW_UPGRADE */
+		rc = device_create_file(dev, &dev_attr_pip2_file_erase);
+		if (rc) {
+			pt_debug(dev, DL_ERROR,
+				"%s: Error creating pip2_file_erase\n",
+				__func__);
+			goto remove_files;
+		}
+		rc = device_create_file(dev, &dev_attr_pip2_bl_status);
+		if (rc) {
+			pt_debug(dev, DL_ERROR,
+				"%s: Error creating pip2_bl_status\n",
+				__func__);
+			goto remove_files;
+		}
+
+#ifdef TTDL_DIAGNOSTICS
+		rc = device_create_file(dev, &dev_attr_pip2_get_last_error);
+		if (rc) {
+			pt_debug(dev, DL_ERROR,
+				"%s: Error creating pip2_get_last_error\n",
+				__func__);
+			goto remove_files;
+		}
+		rc = device_create_bin_file(dev, &bin_attr_pip2_file_read);
+		if (rc) {
+			pt_debug(dev, DL_ERROR,
+				"%s: Error creating bin_attr_pip2_file_read\n",
+				__func__);
+			goto remove_files;
+		}
+		rc = device_create_file(dev, &dev_attr_pip2_file_crc);
+		if (rc) {
+			pt_debug(dev, DL_ERROR,
+				"%s: Error creating pip2_file_crc\n",
+				__func__);
+			goto remove_files;
+		}
+#endif
+#endif /* !TTDL_KERNEL_SUBMISSION */
+	} else {
+#if PT_FW_UPGRADE
+		rc = device_create_file(dev, &dev_attr_update_fw_status);
+		if (rc) {
+			pt_debug(dev, DL_ERROR,
+				"%s: Error creating update_fw_status\n",
+				__func__);
+			goto remove_files;
+		}
+#endif
+
+#ifdef CONFIG_TOUCHSCREEN_PARADE_BINARY_FW_UPGRADE
+		rc = device_create_file(dev, &dev_attr_pt_bl_from_file);
+		if (rc) {
+			pt_debug(dev, DL_ERROR,
+				"%s: Error creating pt_bl_from_file\n",
+				__func__);
+			goto remove_files;
+		}
+		rc = device_create_file(dev, &dev_attr_update_fw);
+		if (rc) {
+			pt_debug(dev, DL_ERROR,
+				"%s: Error creating update_fw\n",
+				__func__);
+			goto remove_files;
+		}
+#endif /* CONFIG_TOUCHSCREEN_PARADE_BINARY_FW_UPGRADE */
+#ifndef TTDL_KERNEL_SUBMISSION
+#ifdef CONFIG_TOUCHSCREEN_PARADE_PLATFORM_FW_UPGRADE
+		rc = device_create_file(dev, &dev_attr_forced_upgrade);
+		if (rc) {
+			pt_debug(dev, DL_ERROR,
+				"%s: Error creating forced_upgrade\n",
+				__func__);
+			goto remove_files;
+		}
+#endif /* CONFIG_TOUCHSCREEN_PARADE_PLATFORM_FW_UPGRADE */
+#ifdef CONFIG_TOUCHSCREEN_PARADE_BINARY_FW_UPGRADE
+		rc = device_create_file(dev, &dev_attr_manual_upgrade);
+		if (rc) {
+			pt_debug(dev, DL_ERROR,
+				"%s: Error creating manual_upgrade\n",
+				__func__);
+			goto remove_files;
+		}
+#endif /* CONFIG_TOUCHSCREEN_PARADE_BINARY_FW_UPGRADE */
+#ifdef CONFIG_TOUCHSCREEN_PARADE_MANUAL_TTCONFIG_UPGRADE
+		rc = device_create_file(dev, &dev_attr_config_loading);
+		if (rc) {
+			pt_debug(dev, DL_ERROR,
+				"%s: Error creating config_loading\n",
+				__func__);
+			goto remove_files;
+		}
+
+		rc = device_create_bin_file(dev, &bin_attr_config_data);
+		if (rc) {
+			pt_debug(dev, DL_ERROR,
+				"%s: Error creating config_data\n",
+				__func__);
+			goto remove_files;
+		}
+#endif /* CONFIG_TOUCHSCREEN_PARADE_MANUAL_TTCONFIG_UPGRADE */
+#endif /* !TTDL_KERNEL_SUBMISSION */
+	}
+
+	if (!pdata || !pdata->loader_pdata) {
+		pt_debug(dev, DL_ERROR,
+			"%s: Missing platform data\n", __func__);
+		rc = -ENODEV;
+		goto error_no_pdata;
+	}
+
+	/* Default erase file to an invalid file */
+	ld->pip2_file_erase_file_no = PIP2_RAM_FILE;
+
+	ld->loader_pdata = pdata->loader_pdata;
+	ld->dev = dev;
+	*data = ld;
+
+#ifdef CONFIG_TOUCHSCREEN_PARADE_BINARY_FW_UPGRADE
+	INIT_WORK(&ld->bl_from_file, pt_bl_from_file_work);
+	INIT_WORK(&ld->pip2_bl_from_file, pt_pip2_bl_from_file_work);
+#endif /* CONFIG_TOUCHSCREEN_PARADE_MANUAL_TTCONFIG_UPGRADE */
+
+#if PT_FW_UPGRADE
+	init_completion(&ld->int_running);
+
+	cmd->subscribe_attention(dev, PT_ATTEN_IRQ, PT_LOADER_NAME,
+		pt_loader_attention, PT_MODE_BOOTLOADER);
+
+	cmd->subscribe_attention(dev, PT_ATTEN_LOADER, PT_LOADER_NAME,
+		pt_fw_upgrade_cb, PT_MODE_UNKNOWN);
+
+	cmd->subscribe_attention(dev, PT_ATTEN_CANCEL_LOADER, PT_LOADER_NAME,
+		pt_cancel_fw_upgrade_cb, PT_MODE_UNKNOWN);
+#endif
+#if PT_FW_UPGRADE || PT_TTCONFIG_UPGRADE
+	pt_debug(dev, DL_INFO, "%s: INIT_WORK pt_calibrate_idacs\n",
+		__func__);
+	init_completion(&ld->calibration_complete);
+	INIT_WORK(&ld->calibration_work, pt_calibrate_idacs);
+#endif
+
+#ifdef CONFIG_TOUCHSCREEN_PARADE_MANUAL_TTCONFIG_UPGRADE
+	if (dut_gen == DUT_PIP1_ONLY)
+		mutex_init(&ld->config_lock);
+#endif
+
+#ifdef UPGRADE_FW_AND_CONFIG_IN_PROBE
+	/* Call FW and config upgrade directly in probe */
+	pt_fw_and_config_upgrade(&ld->fw_and_config_upgrade);
+#else
+	pt_debug(dev, DL_INFO, "%s: Schedule FW upgrade work\n", __func__);
+	INIT_WORK(&ld->fw_and_config_upgrade, pt_fw_and_config_upgrade);
+	schedule_work(&ld->fw_and_config_upgrade);
+#endif
+
+	pt_debug(dev, DL_INFO, "%s: Successful probe %s\n",
+		__func__, dev_name(dev));
+	return 0;
+
+
+remove_files:
+#ifndef TTDL_KERNEL_SUBMISSION
+#ifdef CONFIG_TOUCHSCREEN_PARADE_MANUAL_TTCONFIG_UPGRADE
+	device_remove_file(dev, &dev_attr_config_loading);
+#endif
+#ifdef CONFIG_TOUCHSCREEN_PARADE_BINARY_FW_UPGRADE
+	device_remove_file(dev, &dev_attr_manual_upgrade);
+#endif
+#ifdef CONFIG_TOUCHSCREEN_PARADE_PLATFORM_FW_UPGRADE
+	device_remove_file(dev, &dev_attr_forced_upgrade);
+#endif
+#ifdef TTDL_DIAGNOSTICS
+	device_remove_file(dev, &dev_attr_pip2_get_last_error);
+	device_remove_bin_file(dev, &bin_attr_pip2_file_read);
+	device_remove_file(dev, &dev_attr_pip2_file_crc);
+#endif
+	device_remove_file(dev, &dev_attr_pip2_bl_status);
+	device_remove_file(dev, &dev_attr_pip2_file_erase);
+#ifdef CONFIG_TOUCHSCREEN_PARADE_BINARY_FW_UPGRADE
+	device_remove_file(dev, &dev_attr_pip2_bl_from_file);
+	device_remove_file(dev, &dev_attr_pip2_file_write);
+	device_remove_file(dev, &dev_attr_pt_bl_from_file);
+	device_remove_file(dev, &dev_attr_pip2_manual_ram_upgrade);
+	device_remove_file(dev, &dev_attr_pip2_manual_upgrade);
+	device_remove_file(dev, &dev_attr_update_fw);
+	device_remove_file(dev, &dev_attr_update_fw_status);
+#endif /* CONFIG_TOUCHSCREEN_PARADE_BINARY_FW_UPGRADE */
+#endif /* !TTDL_KERNEL_SUBMISSION */
+
+	kfree(ld->pip2_data);
+	kfree(ld);
+error_alloc_data_failed:
+error_no_pdata:
+	pt_debug(dev, DL_ERROR, "%s failed.\n", __func__);
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_loader_release
+ *
+ * SUMMARY: Remove function for loader module that does following cleanup:
+ *  - Unsubscibe all registered attention tasks
+ *  - Removes all created sysfs nodes
+ *  - Frees all pointers
+ *
+ * PARAMETERS:
+ *  *dev   - pointer to device structure
+ *  *data  - pointer to the loader data
+ ******************************************************************************/
+static void pt_loader_release(struct device *dev, void *data)
+{
+	struct pt_loader_data *ld = (struct pt_loader_data *)data;
+	u8 dut_gen =  cmd->request_dut_generation(dev);
+
+#if PT_FW_UPGRADE
+	cmd->unsubscribe_attention(dev, PT_ATTEN_IRQ, PT_LOADER_NAME,
+		pt_loader_attention, PT_MODE_BOOTLOADER);
+
+	cmd->unsubscribe_attention(dev, PT_ATTEN_LOADER, PT_LOADER_NAME,
+		pt_fw_upgrade_cb, PT_MODE_UNKNOWN);
+
+	cmd->unsubscribe_attention(dev, PT_ATTEN_CANCEL_LOADER, PT_LOADER_NAME,
+		pt_cancel_fw_upgrade_cb, PT_MODE_UNKNOWN);
+#endif
+#if PT_FW_UPGRADE
+	device_remove_file(dev, &dev_attr_update_fw_status);
+#endif
+	if (dut_gen == DUT_PIP2_CAPABLE) {
+#ifdef CONFIG_TOUCHSCREEN_PARADE_BINARY_FW_UPGRADE
+		device_remove_file(dev, &dev_attr_update_fw);
+#endif /* CONFIG_TOUCHSCREEN_PARADE_BINARY_FW_UPGRADE */
+#ifndef TTDL_KERNEL_SUBMISSION
+#ifdef TTDL_DIAGNOSTICS
+		device_remove_bin_file(dev, &bin_attr_pip2_file_read);
+		device_remove_file(dev, &dev_attr_pip2_file_crc);
+#endif
+		device_remove_file(dev, &dev_attr_pip2_get_last_error);
+		device_remove_file(dev, &dev_attr_pip2_bl_status);
+		device_remove_file(dev, &dev_attr_pip2_file_erase);
+#ifdef CONFIG_TOUCHSCREEN_PARADE_BINARY_FW_UPGRADE
+		device_remove_file(dev, &dev_attr_pip2_file_write);
+		device_remove_file(dev, &dev_attr_pip2_bl_from_file);
+		device_remove_file(dev, &dev_attr_pt_bl_from_file);
+		device_remove_file(dev, &dev_attr_pip2_manual_ram_upgrade);
+		device_remove_file(dev, &dev_attr_pip2_manual_upgrade);
+#endif /* CONFIG_TOUCHSCREEN_PARADE_BINARY_FW_UPGRADE */
+#endif /* !TTDL_KERNEL_SUBMISSION */
+		kfree(ld->pip2_data);
+	} else {
+#ifdef CONFIG_TOUCHSCREEN_PARADE_BINARY_FW_UPGRADE
+		device_remove_file(dev, &dev_attr_update_fw);
+#endif /* CONFIG_TOUCHSCREEN_PARADE_BINARY_FW_UPGRADE */
+#ifndef TTDL_KERNEL_SUBMISSION
+#ifdef CONFIG_TOUCHSCREEN_PARADE_MANUAL_TTCONFIG_UPGRADE
+		device_remove_bin_file(dev, &bin_attr_config_data);
+		device_remove_file(dev, &dev_attr_config_loading);
+		if (!ld->config_data)
+			kfree(ld->config_data);
+#endif /* CONFIG_TOUCHSCREEN_PARADE_MANUAL_TTCONFIG_UPGRADE */
+#ifdef CONFIG_TOUCHSCREEN_PARADE_BINARY_FW_UPGRADE
+		device_remove_file(dev, &dev_attr_pt_bl_from_file);
+		device_remove_file(dev, &dev_attr_manual_upgrade);
+#endif /* CONFIG_TOUCHSCREEN_PARADE_BINARY_FW_UPGRADE */
+#ifdef CONFIG_TOUCHSCREEN_PARADE_PLATFORM_FW_UPGRADE
+		device_remove_file(dev, &dev_attr_forced_upgrade);
+#endif /* CONFIG_TOUCHSCREEN_PARADE_PLATFORM_FW_UPGRADE */
+#endif /* !TTDL_KERNEL_SUBMISSION */
+	}
+	kfree(ld);
+}
+
+static struct pt_module loader_module = {
+	.name = PT_LOADER_NAME,
+	.probe = pt_loader_probe,
+	.release = pt_loader_release,
+};
+
+/*******************************************************************************
+ * FUNCTION: pt_loader_init
+ *
+ * SUMMARY: Initialize function for loader module which to register
+ * loader_module into TTDL module list.
+ *
+ * RETURN:
+ *   0 = success
+ *  !0 = failure
+ *
+ * PARAMETERS:
+ *  *dev   - pointer to device structure
+ *  *data  - pointer to the loader data
+ ******************************************************************************/
+static int __init pt_loader_init(void)
+{
+	int rc;
+
+	cmd = pt_get_commands();
+	if (!cmd)
+		return -EINVAL;
+
+	rc = pt_register_module(&loader_module);
+	if (rc < 0) {
+		pr_err("%s: Error, failed registering module\n",
+			__func__);
+			return rc;
+	}
+
+	pr_info("%s: Parade FW Loader Driver (Version %s) rc=%d\n",
+		 __func__, PT_DRIVER_VERSION, rc);
+	return 0;
+}
+module_init(pt_loader_init);
+
+/*******************************************************************************
+ * FUNCTION: pt_loader_exit
+ *
+ * SUMMARY: Exit function for loader module which to unregister loader_module
+ * from TTDL module list.
+ *
+ ******************************************************************************/
+static void __exit pt_loader_exit(void)
+{
+	pt_unregister_module(&loader_module);
+}
+module_exit(pt_loader_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Parade TrueTouch(R) Standard Product FW Loader Driver");
+MODULE_AUTHOR("Parade Technologies <[email protected]>");

+ 970 - 0
pt/pt_mt_common.c

@@ -0,0 +1,970 @@
+/*
+ * pt_mt_common.c
+ * Parade TrueTouch(TM) Standard Product Multi-Touch Reports 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 <[email protected]>
+ */
+
+#include "pt_regs.h"
+
+#define MT_PARAM_SIGNAL(md, sig_ost) PARAM_SIGNAL(md->pdata->frmwrk, sig_ost)
+#define MT_PARAM_MIN(md, sig_ost) PARAM_MIN(md->pdata->frmwrk, sig_ost)
+#define MT_PARAM_MAX(md, sig_ost) PARAM_MAX(md->pdata->frmwrk, sig_ost)
+#define MT_PARAM_FUZZ(md, sig_ost) PARAM_FUZZ(md->pdata->frmwrk, sig_ost)
+#define MT_PARAM_FLAT(md, sig_ost) PARAM_FLAT(md->pdata->frmwrk, sig_ost)
+
+/*******************************************************************************
+ * FUNCTION: pt_mt_lift_all
+ *
+ * SUMMARY: Reports touch liftoff action
+ *
+ * PARAMETERS:
+ *     *md - pointer to touch data structure
+ ******************************************************************************/
+static void pt_mt_lift_all(struct pt_mt_data *md)
+{
+	int max = md->si->tch_abs[PT_TCH_T].max;
+
+	if (md->num_prv_rec != 0) {
+		if (md->mt_function.report_slot_liftoff)
+			md->mt_function.report_slot_liftoff(md, max);
+		input_sync(md->input);
+		md->num_prv_rec = 0;
+	}
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_get_touch_hdr
+ *
+ * SUMMARY: Get the header of touch report
+ *
+ * PARAMETERS:
+ *     *md      - pointer to touch data structure
+ *     *touch   - pointer to pt_touch structure
+ *     *xy_mode - pointer to touch mode data
+ ******************************************************************************/
+static void pt_get_touch_hdr(struct pt_mt_data *md,
+	struct pt_touch *touch, u8 *xy_mode)
+{
+	struct device *dev = md->dev;
+	struct pt_sysinfo *si = md->si;
+	enum pt_tch_hdr hdr;
+
+	for (hdr = PT_TCH_TIME; hdr < PT_TCH_NUM_HDR; hdr++) {
+		if (!si->tch_hdr[hdr].report)
+			continue;
+		pt_get_touch_field(dev, &touch->hdr[hdr],
+			si->tch_hdr[hdr].size,
+			si->tch_hdr[hdr].max,
+			xy_mode + si->tch_hdr[hdr].ofs,
+			si->tch_hdr[hdr].bofs);
+		pt_debug(dev, DL_DEBUG, "%s: get %s=%04X(%d)\n",
+			__func__, pt_tch_hdr_string[hdr],
+			touch->hdr[hdr], touch->hdr[hdr]);
+	}
+
+	pt_debug(dev, DL_INFO,
+		"%s: time=%X tch_num=%d lo=%d noise=%d counter=%d\n",
+		__func__,
+		touch->hdr[PT_TCH_TIME],
+		touch->hdr[PT_TCH_NUM],
+		touch->hdr[PT_TCH_LO],
+		touch->hdr[PT_TCH_NOISE],
+		touch->hdr[PT_TCH_COUNTER]);
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_get_touch_record
+ *
+ * SUMMARY: Gets axis of touch report
+ *
+ * PARAMETERS:
+ *     *md      - pointer to touch data structure
+ *     *touch   - pointer to pt_touch structure
+ *     *xy_data - pointer to touch data
+ ******************************************************************************/
+static void pt_get_touch_record(struct pt_mt_data *md,
+	struct pt_touch *touch, u8 *xy_data)
+{
+	struct device *dev = md->dev;
+	struct pt_sysinfo *si = md->si;
+	enum pt_tch_abs abs;
+
+	for (abs = PT_TCH_X; abs < PT_TCH_NUM_ABS; abs++) {
+		if (!si->tch_abs[abs].report)
+			continue;
+		pt_get_touch_field(dev, &touch->abs[abs],
+			si->tch_abs[abs].size,
+			si->tch_abs[abs].max,
+			xy_data + si->tch_abs[abs].ofs,
+			si->tch_abs[abs].bofs);
+		pt_debug(dev, DL_DEBUG, "%s: get %s=%04X(%d)\n",
+			__func__, pt_tch_abs_string[abs],
+			touch->abs[abs], touch->abs[abs]);
+	}
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_mt_process_touch
+ *
+ * SUMMARY: Process touch includes oritation,axis invert and
+ *  convert MAJOR/MINOR from mm to resolution
+ *
+ * PARAMETERS:
+ *     *md      - pointer to touch data structure
+ *     *touch   - pointer to pt_touch structure
+ ******************************************************************************/
+static void pt_mt_process_touch(struct pt_mt_data *md,
+	struct pt_touch *touch)
+{
+	struct device *dev = md->dev;
+	struct pt_sysinfo *si = md->si;
+	int tmp;
+	bool flipped;
+
+
+	/* Orientation is signed */
+	touch->abs[PT_TCH_OR] = (int8_t)touch->abs[PT_TCH_OR];
+
+	if (md->pdata->flags & PT_MT_FLAG_FLIP) {
+		tmp = touch->abs[PT_TCH_X];
+		touch->abs[PT_TCH_X] = touch->abs[PT_TCH_Y];
+		touch->abs[PT_TCH_Y] = tmp;
+		if (touch->abs[PT_TCH_OR] > 0)
+			touch->abs[PT_TCH_OR] =
+				md->or_max - touch->abs[PT_TCH_OR];
+		else
+			touch->abs[PT_TCH_OR] =
+				md->or_min - touch->abs[PT_TCH_OR];
+		flipped = true;
+	} else
+		flipped = false;
+
+	/*
+	 * 1 is subtracted from each touch location to make the location
+	 * 0 based. e.g. If the resolution of touch panel is 1200x1600,
+	 * the FW touch report must be (0~1199,0~1599). The driver
+	 * should register the (min,max) value to Linux input system as
+	 * (0~1199,0~1599). When the host needs to invert the
+	 * coordinates, the driver would incorrectly use the resolution
+	 * to subtract the reported point directly, such as
+	 * 1200-(0~1199). The input system will lose the 0 point report
+	 * and the 1200 point will be ignored.
+	 */
+	if (md->pdata->flags & PT_MT_FLAG_INV_X) {
+		if (flipped)
+			touch->abs[PT_TCH_X] = si->sensing_conf_data.res_y -
+				touch->abs[PT_TCH_X] - 1;
+		else
+			touch->abs[PT_TCH_X] = si->sensing_conf_data.res_x -
+				touch->abs[PT_TCH_X] - 1;
+		touch->abs[PT_TCH_OR] *= -1;
+	}
+	if (md->pdata->flags & PT_MT_FLAG_INV_Y) {
+		if (flipped)
+			touch->abs[PT_TCH_Y] = si->sensing_conf_data.res_x -
+				touch->abs[PT_TCH_Y] - 1;
+		else
+			touch->abs[PT_TCH_Y] = si->sensing_conf_data.res_y -
+				touch->abs[PT_TCH_Y] - 1;
+		touch->abs[PT_TCH_OR] *= -1;
+	}
+
+	/* Convert MAJOR/MINOR from mm to resolution */
+	tmp = touch->abs[PT_TCH_MAJ] * 100 * si->sensing_conf_data.res_x;
+	touch->abs[PT_TCH_MAJ] = tmp / si->sensing_conf_data.len_x;
+	tmp = touch->abs[PT_TCH_MIN] * 100 * si->sensing_conf_data.res_x;
+	touch->abs[PT_TCH_MIN] = tmp / si->sensing_conf_data.len_x;
+
+	pt_debug(dev, DL_INFO,
+		"%s: flip=%s inv-x=%s inv-y=%s x=%04X(%d) y=%04X(%d)\n",
+		__func__, flipped ? "true" : "false",
+		md->pdata->flags & PT_MT_FLAG_INV_X ? "true" : "false",
+		md->pdata->flags & PT_MT_FLAG_INV_Y ? "true" : "false",
+		touch->abs[PT_TCH_X], touch->abs[PT_TCH_X],
+		touch->abs[PT_TCH_Y], touch->abs[PT_TCH_Y]);
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_report_event
+ *
+ * SUMMARY: Reports touch event
+ *
+ * PARAMETERS:
+ *     *md      - pointer to touch data structure
+ *      event   - type of touch event
+ *      value   - value of report event
+ ******************************************************************************/
+static void pt_report_event(struct pt_mt_data *md, int event,
+		int value)
+{
+	int sig = MT_PARAM_SIGNAL(md, event);
+
+	if (sig != PT_IGNORE_VALUE)
+		input_report_abs(md->input, sig, value);
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_get_mt_touches
+ *
+ * SUMMARY: Parse and report touch event
+ *
+ * PARAMETERS:
+ *     *md          - pointer to touch data structure
+ *     *tch         - pointer to touch structure
+ *      num_cur_tch - number of current touch
+ ******************************************************************************/
+static void pt_get_mt_touches(struct pt_mt_data *md,
+		struct pt_touch *tch, int num_cur_tch)
+{
+	struct device *dev = md->dev;
+	struct pt_sysinfo *si = md->si;
+	int sig;
+	int i, j, t = 0;
+	DECLARE_BITMAP(ids, PT_TOUCH_ID_MAX);
+	int mt_sync_count = 0;
+	u8 *tch_addr;
+
+	if (PT_TOUCH_ID_MAX < si->tch_abs[PT_TCH_T].max) {
+		pt_debug(dev, DL_ERROR,
+			 "%s: Touch ID %d is allocated less than needed %d\n",
+			 __func__, PT_TOUCH_ID_MAX,
+			(int)si->tch_abs[PT_TCH_T].max);
+		return;
+	}
+
+	bitmap_zero(ids, PT_TOUCH_ID_MAX);
+	memset(tch->abs, 0, sizeof(tch->abs));
+
+	for (i = 0; i < num_cur_tch; i++) {
+		tch_addr = si->xy_data + (i * si->desc.tch_record_size);
+		pt_get_touch_record(md, tch, tch_addr);
+
+		/*  Discard proximity event */
+		if (tch->abs[PT_TCH_O] == PT_OBJ_PROXIMITY) {
+			pt_debug(dev, DL_INFO,
+				"%s: Discarding proximity event\n",
+				__func__);
+			continue;
+		}
+
+		/* Validate track_id */
+		t = tch->abs[PT_TCH_T];
+		if (t < md->t_min || t > md->t_max) {
+			pt_debug(dev, DL_INFO,
+				"%s: tch=%d -> bad trk_id=%d max_id=%d\n",
+				__func__, i, t, md->t_max);
+			if (md->mt_function.input_sync)
+				md->mt_function.input_sync(md->input);
+			mt_sync_count++;
+			continue;
+		}
+
+		/* Lift-off */
+		if (tch->abs[PT_TCH_E] == PT_EV_LIFTOFF) {
+			pt_debug(dev, DL_INFO, "%s: t=%d e=%d lift-off\n",
+				__func__, t, tch->abs[PT_TCH_E]);
+			goto pt_get_mt_touches_pr_tch;
+		}
+
+		/* Process touch */
+		pt_mt_process_touch(md, tch);
+
+		/* use 0 based track id's */
+		t -= md->t_min;
+
+		sig = MT_PARAM_SIGNAL(md, PT_ABS_ID_OST);
+		if (sig != PT_IGNORE_VALUE) {
+			if (md->mt_function.input_report)
+				md->mt_function.input_report(md->input, sig,
+						t, tch->abs[PT_TCH_O]);
+			__set_bit(t, ids);
+		}
+
+		pt_report_event(md, PT_ABS_D_OST, 0);
+
+		/* all devices: position and pressure fields */
+		for (j = 0; j <= PT_ABS_W_OST; j++) {
+			if (!si->tch_abs[j].report)
+				continue;
+			pt_report_event(md, PT_ABS_X_OST + j,
+					tch->abs[PT_TCH_X + j]);
+		}
+
+		/* Get the extended touch fields */
+		for (j = 0; j < PT_NUM_EXT_TCH_FIELDS; j++) {
+			if (!si->tch_abs[PT_ABS_MAJ_OST + j].report)
+				continue;
+			pt_report_event(md, PT_ABS_MAJ_OST + j,
+					tch->abs[PT_TCH_MAJ + j]);
+		}
+		if (md->mt_function.input_sync)
+			md->mt_function.input_sync(md->input);
+		mt_sync_count++;
+
+pt_get_mt_touches_pr_tch:
+		pt_debug(dev, DL_INFO,
+			"%s: t=%d x=%d y=%d z=%d M=%d m=%d o=%d e=%d obj=%d tip=%d\n",
+			__func__, t,
+			tch->abs[PT_TCH_X],
+			tch->abs[PT_TCH_Y],
+			tch->abs[PT_TCH_P],
+			tch->abs[PT_TCH_MAJ],
+			tch->abs[PT_TCH_MIN],
+			tch->abs[PT_TCH_OR],
+			tch->abs[PT_TCH_E],
+			tch->abs[PT_TCH_O],
+			tch->abs[PT_TCH_TIP]);
+	}
+
+	if (md->mt_function.final_sync)
+		md->mt_function.final_sync(md->input,
+				si->tch_abs[PT_TCH_T].max, mt_sync_count, ids);
+
+	md->num_prv_rec = num_cur_tch;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_xy_worker
+ *
+ * SUMMARY: Read xy_data for all current touches
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *     *md - pointer to touch data structure
+ ******************************************************************************/
+static int pt_xy_worker(struct pt_mt_data *md)
+{
+	struct device *dev = md->dev;
+	struct pt_sysinfo *si = md->si;
+	int max_tch = si->sensing_conf_data.max_tch;
+	struct pt_touch tch;
+	u8 num_cur_tch;
+	int rc = 0;
+
+	pt_get_touch_hdr(md, &tch, si->xy_mode + 3);
+
+	num_cur_tch = tch.hdr[PT_TCH_NUM];
+	if (num_cur_tch > max_tch) {
+		pt_debug(dev, DL_ERROR, "%s: Num touch err detected (n=%d)\n",
+			__func__, num_cur_tch);
+		num_cur_tch = max_tch;
+	}
+
+	if (tch.hdr[PT_TCH_LO]) {
+		pt_debug(dev, DL_INFO, "%s: Large area detected\n",
+			__func__);
+		if (md->pdata->flags & PT_MT_FLAG_NO_TOUCH_ON_LO)
+			num_cur_tch = 0;
+	}
+
+	if (num_cur_tch == 0 && md->num_prv_rec == 0)
+		goto pt_xy_worker_exit;
+
+	/* extract xy_data for all currently reported touches */
+	pt_debug(dev, DL_DEBUG, "%s: extract data num_cur_tch=%d\n",
+		__func__, num_cur_tch);
+	if (num_cur_tch)
+		pt_get_mt_touches(md, &tch, num_cur_tch);
+	else
+		pt_mt_lift_all(md);
+
+	rc = 0;
+
+pt_xy_worker_exit:
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_mt_send_dummy_event
+ *
+ * SUMMARY: Send dummy/key event to wakeup upper layer of system
+ *
+ * PARAMETERS:
+ *     *cd  - pointer to core data structure
+ *     *md  - pointer to touch data structure
+ ******************************************************************************/
+static void pt_mt_send_dummy_event(struct pt_core_data *cd,
+		struct pt_mt_data *md)
+{
+#ifndef EASYWAKE_TSG6
+	/* TSG5 EasyWake */
+	unsigned long ids = 0;
+
+	/* for easy wakeup */
+	if (md->mt_function.input_report)
+		md->mt_function.input_report(md->input, ABS_MT_TRACKING_ID,
+			0, PT_OBJ_STANDARD_FINGER);
+	if (md->mt_function.input_sync)
+		md->mt_function.input_sync(md->input);
+	if (md->mt_function.final_sync)
+		md->mt_function.final_sync(md->input, 0, 1, &ids);
+	if (md->mt_function.report_slot_liftoff)
+		md->mt_function.report_slot_liftoff(md, 1);
+	if (md->mt_function.final_sync)
+		md->mt_function.final_sync(md->input, 1, 1, &ids);
+#else
+	/* TSG6 FW1.3 and above only. TSG6 FW1.0 - 1.2 does not */
+	/*  support EasyWake, and this function will not be called */
+	u8 key_value = 0;
+
+	switch (cd->gesture_id) {
+	case GESTURE_DOUBLE_TAP:
+		key_value = KEY_F1;
+		break;
+	case GESTURE_TWO_FINGERS_SLIDE:
+		key_value = KEY_F2;
+		break;
+	case GESTURE_TOUCH_DETECTED:
+		key_value = KEY_F3;
+		break;
+	case GESTURE_PUSH_BUTTON:
+		key_value = KEY_F4;
+		break;
+	case GESTURE_SINGLE_SLIDE_DE_TX:
+		key_value = KEY_F5;
+		break;
+	case GESTURE_SINGLE_SLIDE_IN_TX:
+		key_value = KEY_F6;
+		break;
+	case GESTURE_SINGLE_SLIDE_DE_RX:
+		key_value = KEY_F7;
+		break;
+	case GESTURE_SINGLE_SLIDE_IN_RX:
+		key_value = KEY_F8;
+		break;
+	default:
+		break;
+	}
+
+	if (key_value > 0) {
+		input_report_key(md->input, key_value, 1);
+		mdelay(10);
+		input_report_key(md->input, key_value, 0);
+		input_sync(md->input);
+	}
+
+	/*
+	 * Caution - this debug print is needed by the TTDL automated
+	 *           regression test suite
+	 */
+	pt_debug(md->dev, DL_INFO, "%s: report key: %d\n",
+		__func__, key_value);
+#endif
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_mt_attention
+ *
+ * SUMMARY: Wrapper function for pt_xy_worker() that subscribe into the TTDL
+ *  attention list.
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *     *dev - pointer to device structure
+ ******************************************************************************/
+static int pt_mt_attention(struct device *dev)
+{
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+	struct pt_mt_data *md = &cd->md;
+	int rc;
+
+	if (md->si->xy_mode[2] !=  md->si->desc.tch_report_id)
+		return 0;
+
+	/* core handles handshake */
+	mutex_lock(&md->mt_lock);
+	rc = pt_xy_worker(md);
+	mutex_unlock(&md->mt_lock);
+	if (rc < 0)
+		pt_debug(dev, DL_ERROR,
+			"%s: xy_worker error r=%d\n", __func__, rc);
+
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_mt_wake_attention
+ *
+ * SUMMARY: Wrapper function for pt_mt_send_dummy_event() that register to
+ *  attention list.
+ *
+ * RETURN:
+ *	 0 = success
+ *
+ * PARAMETERS:
+ *     *dev - pointer to device structure
+ ******************************************************************************/
+static int pt_mt_wake_attention(struct device *dev)
+{
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+	struct pt_mt_data *md = &cd->md;
+
+	mutex_lock(&md->mt_lock);
+	pt_mt_send_dummy_event(cd, md);
+	mutex_unlock(&md->mt_lock);
+	return 0;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_startup_attention
+ *
+ * SUMMARY: Wrapper function for pt_mt_lift_all() that subcribe into the TTDL
+ *  attention list.
+ *
+ * RETURN:
+ *	 0 = success
+ *
+ * PARAMETERS:
+ *     *dev - pointer to device structure
+ ******************************************************************************/
+static int pt_startup_attention(struct device *dev)
+{
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+	struct pt_mt_data *md = &cd->md;
+
+	mutex_lock(&md->mt_lock);
+	pt_mt_lift_all(md);
+	mutex_unlock(&md->mt_lock);
+
+	return 0;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_mt_suspend_attention
+ *
+ * SUMMARY: Function for touch to enter suspend state that as following steps:
+ *          1) Lift all touch
+ *          2) Set flag with suspend state
+ *          3) Decrese pm system count
+ *
+ * RETURN:
+ *	 0 = success
+ *
+ * PARAMETERS:
+ *     *dev - pointer to device structure
+ ******************************************************************************/
+static int pt_mt_suspend_attention(struct device *dev)
+{
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+	struct pt_mt_data *md = &cd->md;
+
+	mutex_lock(&md->mt_lock);
+	pt_mt_lift_all(md);
+	md->is_suspended = true;
+	mutex_unlock(&md->mt_lock);
+
+	pm_runtime_put(dev);
+
+	return 0;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_mt_resume_attention
+ *
+ * SUMMARY: Function for touch to leave suspend state that as following steps:
+ *          1) Increse pm system count
+ *          2) Clear suspend state flag
+ *
+ * RETURN:
+ *	 0 = success
+ *
+ * PARAMETERS:
+ *     *dev - pointer to device structure
+ ******************************************************************************/
+static int pt_mt_resume_attention(struct device *dev)
+{
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+	struct pt_mt_data *md = &cd->md;
+
+	pm_runtime_get(dev);
+
+	mutex_lock(&md->mt_lock);
+	md->is_suspended = false;
+	mutex_unlock(&md->mt_lock);
+
+	return 0;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_mt_open
+ *
+ * SUMMARY: Open method for input device(touch) that sets up call back
+ *  functions to TTDL attention list
+ *
+ * RETURN:
+ *	 0 = success
+ *
+ * PARAMETERS:
+ *     *input - pointer to input_dev structure
+ ******************************************************************************/
+static int pt_mt_open(struct input_dev *input)
+{
+	struct device *dev = input->dev.parent;
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+	struct pt_mt_data *md = &cd->md;
+
+	pm_runtime_get_sync(dev);
+
+	mutex_lock(&md->mt_lock);
+	md->is_suspended = false;
+	mutex_unlock(&md->mt_lock);
+
+	pt_debug(dev, DL_INFO, "%s: setup subscriptions\n", __func__);
+
+	/* set up touch call back */
+	_pt_subscribe_attention(dev, PT_ATTEN_IRQ, PT_MT_NAME,
+		pt_mt_attention, PT_MODE_OPERATIONAL);
+
+	/* set up startup call back */
+	_pt_subscribe_attention(dev, PT_ATTEN_STARTUP, PT_MT_NAME,
+		pt_startup_attention, 0);
+
+	/* set up wakeup call back */
+	_pt_subscribe_attention(dev, PT_ATTEN_WAKE, PT_MT_NAME,
+		pt_mt_wake_attention, 0);
+
+	/* set up suspend call back */
+	_pt_subscribe_attention(dev, PT_ATTEN_SUSPEND, PT_MT_NAME,
+		pt_mt_suspend_attention, 0);
+
+	/* set up resume call back */
+	_pt_subscribe_attention(dev, PT_ATTEN_RESUME, PT_MT_NAME,
+		pt_mt_resume_attention, 0);
+
+	return 0;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_mt_close
+ *
+ * SUMMARY: Close method for input device(touch) that clears call back
+ *  functions from TTDL attention list.
+ *
+ * PARAMETERS:
+ *     *input - pointer to input_dev structure
+ ******************************************************************************/
+static void pt_mt_close(struct input_dev *input)
+{
+	struct device *dev = input->dev.parent;
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+	struct pt_mt_data *md = &cd->md;
+
+	_pt_unsubscribe_attention(dev, PT_ATTEN_IRQ, PT_MT_NAME,
+		pt_mt_attention, PT_MODE_OPERATIONAL);
+
+	_pt_unsubscribe_attention(dev, PT_ATTEN_STARTUP, PT_MT_NAME,
+		pt_startup_attention, 0);
+
+	_pt_unsubscribe_attention(dev, PT_ATTEN_WAKE, PT_MT_NAME,
+		pt_mt_wake_attention, 0);
+
+	_pt_unsubscribe_attention(dev, PT_ATTEN_SUSPEND, PT_MT_NAME,
+		pt_mt_suspend_attention, 0);
+
+	_pt_unsubscribe_attention(dev, PT_ATTEN_RESUME, PT_MT_NAME,
+		pt_mt_resume_attention, 0);
+
+	mutex_lock(&md->mt_lock);
+	if (!md->is_suspended) {
+		pm_runtime_put(dev);
+		md->is_suspended = true;
+	}
+	mutex_unlock(&md->mt_lock);
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_setup_input_device
+ *
+ * SUMMARY: Set up resolution, event signal capabilities and
+ *  register input device for touch.
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *     *dev - pointer to device structure
+ ******************************************************************************/
+static int pt_setup_input_device(struct device *dev)
+{
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+	struct pt_mt_data *md = &cd->md;
+	int signal = PT_IGNORE_VALUE;
+	int max_x, max_y, max_p, min, max;
+	int max_x_tmp, max_y_tmp;
+	int i;
+	int rc;
+
+	pt_debug(dev, DL_INFO, "%s: Initialize event signals\n",
+		__func__);
+	__set_bit(EV_ABS, md->input->evbit);
+	__set_bit(EV_REL, md->input->evbit);
+	__set_bit(EV_KEY, md->input->evbit);
+#ifdef INPUT_PROP_DIRECT
+	__set_bit(INPUT_PROP_DIRECT, md->input->propbit);
+#endif
+
+	/* If virtualkeys enabled, don't use all screen */
+	if (md->pdata->flags & PT_MT_FLAG_VKEYS) {
+		max_x_tmp = md->pdata->vkeys_x;
+		max_y_tmp = md->pdata->vkeys_y;
+	} else {
+		max_x_tmp = md->si->sensing_conf_data.res_x;
+		max_y_tmp = md->si->sensing_conf_data.res_y;
+	}
+
+	/* get maximum values from the sysinfo data */
+	if (md->pdata->flags & PT_MT_FLAG_FLIP) {
+		max_x = max_y_tmp - 1;
+		max_y = max_x_tmp - 1;
+	} else {
+		max_x = max_x_tmp - 1;
+		max_y = max_y_tmp - 1;
+	}
+	max_p = md->si->sensing_conf_data.max_z;
+
+	/* set event signal capabilities */
+	for (i = 0; i < NUM_SIGNALS(md->pdata->frmwrk); i++) {
+		signal = MT_PARAM_SIGNAL(md, i);
+		if (signal != PT_IGNORE_VALUE) {
+			__set_bit(signal, md->input->absbit);
+
+			min = MT_PARAM_MIN(md, i);
+			max = MT_PARAM_MAX(md, i);
+			if (i == PT_ABS_ID_OST) {
+				/* shift track ids down to start at 0 */
+				max = max - min;
+				min = min - min;
+			} else if (i == PT_ABS_X_OST)
+				max = max_x;
+			else if (i == PT_ABS_Y_OST)
+				max = max_y;
+			else if (i == PT_ABS_P_OST)
+				max = max_p;
+
+			input_set_abs_params(md->input, signal, min, max,
+				MT_PARAM_FUZZ(md, i), MT_PARAM_FLAT(md, i));
+			pt_debug(dev, DL_INFO,
+				"%s: register signal=%02X min=%d max=%d\n",
+				__func__, signal, min, max);
+		}
+	}
+
+	md->or_min = MT_PARAM_MIN(md, PT_ABS_OR_OST);
+	md->or_max = MT_PARAM_MAX(md, PT_ABS_OR_OST);
+
+	md->t_min = MT_PARAM_MIN(md, PT_ABS_ID_OST);
+	md->t_max = MT_PARAM_MAX(md, PT_ABS_ID_OST);
+
+	rc = md->mt_function.input_register_device(md->input,
+			md->si->tch_abs[PT_TCH_T].max);
+	if (rc < 0)
+		pt_debug(dev, DL_ERROR, "%s: Error, failed register input device r=%d\n",
+			__func__, rc);
+	else
+		md->input_device_registered = true;
+
+#ifdef EASYWAKE_TSG6
+	input_set_capability(md->input, EV_KEY, KEY_F1);
+	input_set_capability(md->input, EV_KEY, KEY_F2);
+	input_set_capability(md->input, EV_KEY, KEY_F3);
+	input_set_capability(md->input, EV_KEY, KEY_F4);
+	input_set_capability(md->input, EV_KEY, KEY_F5);
+	input_set_capability(md->input, EV_KEY, KEY_F6);
+	input_set_capability(md->input, EV_KEY, KEY_F7);
+	input_set_capability(md->input, EV_KEY, KEY_F8);
+#endif
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_setup_input_attention
+ *
+ * SUMMARY: Wrapper function for pt_setup_input_device() register to TTDL
+ *  attention list.
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *     *dev - pointer to device structure
+ ******************************************************************************/
+static int pt_setup_input_attention(struct device *dev)
+{
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+	struct pt_mt_data *md = &cd->md;
+	int rc;
+
+	md->si = _pt_request_sysinfo(dev);
+	if (!md->si)
+		return -EINVAL;
+
+	rc = pt_setup_input_device(dev);
+
+	_pt_unsubscribe_attention(dev, PT_ATTEN_STARTUP, PT_MT_NAME,
+		pt_setup_input_attention, 0);
+
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_mt_probe
+ *
+ * SUMMARY: The probe function for touch input device
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *	*dev   - pointer to device structure
+ ******************************************************************************/
+int pt_mt_probe(struct device *dev)
+{
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+	struct pt_mt_data *md = &cd->md;
+	struct pt_platform_data *pdata = dev_get_platdata(dev);
+	struct pt_mt_platform_data *mt_pdata;
+	int rc = 0;
+
+	pt_debug(dev, DL_INFO,
+		"%s: >>>>>> Register MT <<<<<<\n", __func__);
+	if (!pdata || !pdata->mt_pdata) {
+		pt_debug(dev, DL_ERROR,
+			"%s: Missing platform data\n", __func__);
+		rc = -ENODEV;
+		goto error_no_pdata;
+	}
+	mt_pdata = pdata->mt_pdata;
+
+	pt_init_function_ptrs(md);
+
+	mutex_init(&md->mt_lock);
+	md->dev = dev;
+	md->pdata = mt_pdata;
+
+	/* Create the input device and register it. */
+	pt_debug(dev, DL_INFO,
+		"%s: Create the input device and register it\n", __func__);
+	md->input = input_allocate_device();
+	if (!md->input) {
+		pt_debug(dev, DL_ERROR, "%s: Error, failed to allocate input device\n",
+			__func__);
+		rc = -ENODEV;
+		goto error_alloc_failed;
+	} else
+		md->input_device_allocated = true;
+
+	if (md->pdata->inp_dev_name)
+		md->input->name = md->pdata->inp_dev_name;
+	else
+		md->input->name = PT_MT_NAME;
+	scnprintf(md->phys, sizeof(md->phys), "%s/input%d", dev_name(dev),
+			cd->phys_num++);
+	md->input->phys = md->phys;
+	md->input->dev.parent = md->dev;
+	md->input->open = pt_mt_open;
+	md->input->close = pt_mt_close;
+	input_set_drvdata(md->input, md);
+
+	/* get sysinfo */
+	md->si = _pt_request_sysinfo(dev);
+
+	if (md->si) {
+		rc = pt_setup_input_device(dev);
+		if (rc)
+			goto error_init_input;
+	} else {
+		pt_debug(dev, DL_ERROR, "%s: Fail get sysinfo pointer from core p=%p\n",
+			__func__, md->si);
+		_pt_subscribe_attention(dev, PT_ATTEN_STARTUP,
+			PT_MT_NAME, pt_setup_input_attention, 0);
+	}
+
+	return 0;
+
+error_init_input:
+	input_free_device(md->input);
+	md->input_device_allocated = false;
+error_alloc_failed:
+error_no_pdata:
+	pt_debug(dev, DL_ERROR, "%s failed.\n", __func__);
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_mt_release
+ *
+ * SUMMARY: The release function for touch input device
+ *
+ * RETURN:
+ *	 0 = success
+ *
+ * PARAMETERS:
+ *	*dev   - pointer to device structure
+ ******************************************************************************/
+int pt_mt_release(struct device *dev)
+{
+	struct pt_core_data *cd;
+	struct pt_mt_data *md;
+
+	/* Ensure valid pointers before de-referencing them */
+	if (dev) {
+		cd = dev_get_drvdata(dev);
+		if (cd)
+			md = &cd->md;
+		else
+			return 0;
+	} else {
+		return 0;
+	}
+
+	/*
+	 * Second call this function may cause kernel panic if probe fail.
+	 * Use input_device_registered & input_device_allocated variable to
+	 * avoid unregister or free unavailable devive.
+	 */
+	if (md && md->input_device_registered) {
+		md->input_device_registered = false;
+		input_unregister_device(md->input);
+		/* Unregistering device will free the device too */
+		md->input_device_allocated = false;
+	} else if (md && md->input_device_allocated) {
+		md->input_device_allocated = false;
+		input_free_device(md->input);
+		_pt_unsubscribe_attention(dev, PT_ATTEN_STARTUP,
+			PT_MT_NAME, pt_setup_input_attention, 0);
+	}
+
+	return 0;
+}

+ 143 - 0
pt/pt_mta.c

@@ -0,0 +1,143 @@
+/*
+ * pt_mta.c
+ * Parade TrueTouch(TM) Standard Product Multi-Touch Protocol A 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 <[email protected]>
+ */
+
+#include "pt_regs.h"
+
+/*******************************************************************************
+ * FUNCTION: pt_final_sync
+ *
+ * SUMMARY: Function to create SYN_REPORT
+ *
+ * PARAMETERS:
+ *        *input - pointer to input device structure
+ *     max_slots - max support touch number
+ * mt_sync_count - current valid touch number
+ *           ids - bit map value
+ ******************************************************************************/
+static void pt_final_sync(struct input_dev *input, int max_slots,
+		int mt_sync_count, unsigned long *ids)
+{
+	if (mt_sync_count)
+		input_sync(input);
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_input_sync
+ *
+ * SUMMARY: Function to create SYN_MT_REPORT
+ *
+ * PARAMETERS:
+ *        *input - pointer to input device structure
+ ******************************************************************************/
+static void pt_input_sync(struct input_dev *input)
+{
+	input_mt_sync(input);
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_input_report
+ *
+ * SUMMARY: Function to report coordinate information of touch point
+ * protocol
+ *
+ * PARAMETERS:
+ *        *input - pointer to input device structure
+ *           sig - track id to allow tracking of a touch
+ *             t - event id to indicate an event associated with touch instance
+ *          type - indicate the touch object
+ ******************************************************************************/
+static void pt_input_report(struct input_dev *input, int sig,
+		int t, int type)
+{
+	if (type == PT_OBJ_STANDARD_FINGER || type == PT_OBJ_GLOVE) {
+		input_report_key(input, BTN_TOOL_FINGER, PT_BTN_PRESSED);
+		input_report_key(input, BTN_TOOL_PEN, PT_BTN_RELEASED);
+	} else if (type == PT_OBJ_STYLUS) {
+		input_report_key(input, BTN_TOOL_PEN, PT_BTN_PRESSED);
+		input_report_key(input, BTN_TOOL_FINGER, PT_BTN_RELEASED);
+	}
+
+	input_report_key(input, BTN_TOUCH, PT_BTN_PRESSED);
+
+	input_report_abs(input, sig, t);
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_report_slot_liftoff
+ *
+ * SUMMARY: Function to report all touches are lifted
+ * protocol
+ *
+ * PARAMETERS:
+ *        *md - pointer to input device structure
+ *  max_slots - indicate max number of touch id
+ ******************************************************************************/
+static void pt_report_slot_liftoff(struct pt_mt_data *md,
+		int max_slots)
+{
+	input_report_key(md->input, BTN_TOUCH, PT_BTN_RELEASED);
+	input_report_key(md->input, BTN_TOOL_FINGER, PT_BTN_RELEASED);
+	input_report_key(md->input, BTN_TOOL_PEN, PT_BTN_RELEASED);
+
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_input_register_device
+ *
+ * SUMMARY: Function to register input device
+ * protocol
+ *
+ * PARAMETERS:
+ *     *input - pointer to input device structure
+ *  max_slots - indicate max number of touch id
+ ******************************************************************************/
+static int pt_input_register_device(struct input_dev *input, int max_slots)
+{
+	__set_bit(BTN_TOUCH, input->keybit);
+	__set_bit(BTN_TOOL_FINGER, input->keybit);
+	__set_bit(BTN_TOOL_PEN, input->keybit);
+	return input_register_device(input);
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_init_function_ptrs
+ *
+ * SUMMARY: Function to init function pointer
+ *
+ * PARAMETERS:
+ *     *md - pointer to touch data structure
+ ******************************************************************************/
+void pt_init_function_ptrs(struct pt_mt_data *md)
+{
+	md->mt_function.report_slot_liftoff = pt_report_slot_liftoff;
+	md->mt_function.final_sync = pt_final_sync;
+	md->mt_function.input_sync = pt_input_sync;
+	md->mt_function.input_report = pt_input_report;
+	md->mt_function.input_register_device = pt_input_register_device;
+}

+ 144 - 0
pt/pt_mtb.c

@@ -0,0 +1,144 @@
+/*
+ * pt_mtb.c
+ * Parade TrueTouch(TM) Standard Product Multi-Touch Protocol B 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 <[email protected]>
+ */
+
+#include "pt_regs.h"
+#include <linux/input/mt.h>
+#include <linux/version.h>
+
+/*******************************************************************************
+ * FUNCTION: pt_final_sync
+ *
+ * SUMMARY: Function to create SYN_REPORT
+ *
+ * PARAMETERS:
+ *        *input - pointer to input device structure
+ *     max_slots - max support touch number
+ * mt_sync_count - current valid touch number
+ *           ids - bit map value
+ ******************************************************************************/
+static void pt_final_sync(struct input_dev *input, int max_slots,
+		int mt_sync_count, unsigned long *ids)
+{
+	int t;
+
+	for (t = 0; t < max_slots; t++) {
+		if (test_bit(t, ids))
+			continue;
+		input_mt_slot(input, t);
+		input_mt_report_slot_state(input, MT_TOOL_FINGER, false);
+	}
+
+	input_sync(input);
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_input_report
+ *
+ * SUMMARY: Function to report coordinate information of touch point
+ * protocol
+ *
+ * PARAMETERS:
+ *        *input - pointer to input device structure
+ *           sig - track id to allow tracking of a touch
+ *             t - event id to indicate an event associated with touch instance
+ *          type - indicate the touch object
+ ******************************************************************************/
+static void pt_input_report(struct input_dev *input, int sig,
+		int t, int type)
+{
+	input_mt_slot(input, t);
+
+	if (type == PT_OBJ_STANDARD_FINGER || type == PT_OBJ_GLOVE)
+		input_mt_report_slot_state(input, MT_TOOL_FINGER, true);
+	else if (type == PT_OBJ_STYLUS)
+		input_mt_report_slot_state(input, MT_TOOL_PEN, true);
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_report_slot_liftoff
+ *
+ * SUMMARY: Function to report all touches are lifted
+ * protocol
+ *
+ * PARAMETERS:
+ *        *md - pointer to input device structure
+ *  max_slots - indicate max number of touch id
+ ******************************************************************************/
+static void pt_report_slot_liftoff(struct pt_mt_data *md,
+		int max_slots)
+{
+	int t;
+
+	if (md->num_prv_rec == 0)
+		return;
+
+	for (t = 0; t < max_slots; t++) {
+		input_mt_slot(md->input, t);
+		input_mt_report_slot_state(md->input,
+			MT_TOOL_FINGER, false);
+	}
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_input_register_device
+ *
+ * SUMMARY: Function to register input device
+ * protocol
+ *
+ * PARAMETERS:
+ *     *input - pointer to input device structure
+ *  max_slots - indicate max number of touch id
+ ******************************************************************************/
+static int pt_input_register_device(struct input_dev *input, int max_slots)
+{
+#if (KERNEL_VERSION(3, 7, 0) <= LINUX_VERSION_CODE)
+	input_mt_init_slots(input, max_slots, 0);
+#else
+	input_mt_init_slots(input, max_slots);
+#endif
+	return input_register_device(input);
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_init_function_ptrs
+ *
+ * SUMMARY: Function to init function pointer
+ *
+ * PARAMETERS:
+ *     *md - pointer to touch data structure
+ ******************************************************************************/
+void pt_init_function_ptrs(struct pt_mt_data *md)
+{
+	md->mt_function.report_slot_liftoff = pt_report_slot_liftoff;
+	md->mt_function.final_sync = pt_final_sync;
+	md->mt_function.input_sync = NULL;
+	md->mt_function.input_report = pt_input_report;
+	md->mt_function.input_register_device = pt_input_register_device;
+}
+

+ 572 - 0
pt/pt_pen.c

@@ -0,0 +1,572 @@
+#ifndef TTDL_KERNEL_SUBMISSION
+/*
+ * pt_pen.c
+ * Parade TrueTouch(TM) Standard Product CapSense Reports Module.
+ * For use with Parade touchscreen controllers.
+ * Supported parts include:
+ * TMA5XX
+ * TMA448
+ * TMA445A
+ * TT21XXX
+ * TT31XXX
+ * TT4XXXX
+ * TT7XXX
+ * TC3XXX
+ *
+ * Copyright (C) 2015-2021 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 <[email protected]>
+ */
+
+#include "pt_regs.h"
+
+/*******************************************************************************
+ * FUNCTION: pt_pen_lift_all
+ *
+ * SUMMARY: Reports pen liftoff action
+ *
+ * PARAMETERS:
+ *     *pend  - pointer to pen data structure
+ ******************************************************************************/
+static void pt_pen_lift_all(struct pt_pen_data *pend)
+{
+	input_report_key(pend->input, BTN_STYLUS, 0);
+	input_report_key(pend->input, BTN_STYLUS2, 0);
+	input_report_key(pend->input, BTN_TOUCH, 0);
+	input_report_key(pend->input, BTN_TOOL_PEN, 0);
+	input_sync(pend->input);
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_get_pen_data
+ *
+ * SUMMARY: Gets axis of pen report
+ *
+ * PARAMETERS:
+ *     *pend    - pointer to pen data structure
+ *     *xy_data - pointer to touch data
+ ******************************************************************************/
+static void pt_get_pen(struct pt_pen_data *pend,
+	struct pt_pen *pen, u8 *xy_data)
+{
+	struct device *dev = pend->dev;
+	struct pt_sysinfo *si = pend->si;
+	enum pt_pen_abs abs;
+
+	for (abs = PT_PEN_X; abs < PT_PEN_NUM_ABS; abs++) {
+		if (!si->pen_abs[abs].report)
+			continue;
+		pt_get_touch_field(dev, &pen->abs[abs],
+			si->pen_abs[abs].size,
+			si->pen_abs[abs].max,
+			xy_data + si->tch_abs[abs].ofs,
+			si->pen_abs[abs].bofs);
+		pt_debug(dev, DL_DEBUG, "%s: get %s=%04X(%d)\n",
+			__func__, pt_pen_abs_string[abs],
+			pen->abs[abs], pen->abs[abs]);
+	}
+}
+
+
+/*******************************************************************************
+ * FUNCTION: pt_xy_worker
+ *
+ * SUMMARY: Read xy_data for current pen touch
+ *
+ * RETURN:
+ *	 0 = success
+ *
+ * PARAMETERS:
+ *     *pend  - pointer to pen data structure
+ ******************************************************************************/
+static int pt_xy_worker(struct pt_pen_data *pend)
+{
+	struct pt_sysinfo *si = pend->si;
+	struct pt_pen pen;
+	bool tool;
+
+	pt_get_pen(pend, &pen, si->xy_data + 3);
+
+	tool = pen.abs[PT_PEN_IV] ?
+		BTN_TOOL_RUBBER : BTN_TOOL_PEN;
+
+	input_report_abs(pend->input, ABS_X, pen.abs[PT_PEN_X]);
+	input_report_abs(pend->input, ABS_Y, pen.abs[PT_PEN_Y]);
+	input_report_abs(pend->input, ABS_PRESSURE, pen.abs[PT_PEN_P]);
+	input_report_key(pend->input, BTN_STYLUS, pen.abs[PT_PEN_BS]);
+
+	if (si->pen_abs[PT_PEN_2ND_BS].report)
+		input_report_key(pend->input, BTN_STYLUS2,
+			pen.abs[PT_PEN_2ND_BS]);
+
+	input_report_key(pend->input, BTN_TOUCH, pen.abs[PT_PEN_TS]);
+	input_report_key(pend->input, tool, pen.abs[PT_PEN_IR]);
+
+	if (si->pen_abs[PT_PEN_X_TILT].report)
+		input_report_abs(pend->input, ABS_TILT_X,
+			pen.abs[PT_PEN_X_TILT]);
+
+	if (si->pen_abs[PT_PEN_Y_TILT].report)
+		input_report_abs(pend->input, ABS_TILT_Y,
+			pen.abs[PT_PEN_Y_TILT]);
+
+	input_sync(pend->input);
+
+	return 0;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_pen_attention
+ *
+ * SUMMARY: Wrapper function for pt_xy_worker() that register to TTDL attention
+ *  list.
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *     *dev - pointer to device structure
+ ******************************************************************************/
+static int pt_pen_attention(struct device *dev)
+{
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+	struct pt_pen_data *pend = &cd->pend;
+	int rc;
+
+	if (pend->si->xy_mode[2] != pend->si->desc.pen_report_id)
+		return 0;
+
+	/* core handles handshake */
+	mutex_lock(&pend->pen_lock);
+	rc = pt_xy_worker(pend);
+	mutex_unlock(&pend->pen_lock);
+	if (rc < 0)
+		pt_debug(dev, DL_ERROR,
+			"%s: xy_worker error r=%d\n", __func__, rc);
+
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_startup_attention
+ *
+ * SUMMARY: Wrapper function for pt_pen_lift_all() that register to TTDL
+ *  attention list.
+ *
+ * RETURN:
+ *	 0 = success
+ *
+ * PARAMETERS:
+ *     *dev - pointer to device structure
+ ******************************************************************************/
+static int pt_startup_attention(struct device *dev)
+{
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+	struct pt_pen_data *pend = &cd->pend;
+
+	mutex_lock(&pend->pen_lock);
+	pt_pen_lift_all(pend);
+	mutex_unlock(&pend->pen_lock);
+
+	return 0;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_pen_suspend_attention
+ *
+ * SUMMARY: Function for pen to enter suspend state that as following steps:
+ *          1) Lift pen touch
+ *          2) Set flag with suspend state
+ *          3) Decrese pm system count
+ *
+ * RETURN:
+ *	 0 = success
+ *
+ * PARAMETERS:
+ *     *dev - pointer to device structure
+ ******************************************************************************/
+static int pt_pen_suspend_attention(struct device *dev)
+{
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+	struct pt_pen_data *pend = &cd->pend;
+
+	mutex_lock(&pend->pen_lock);
+	pt_pen_lift_all(pend);
+	pend->is_suspended = true;
+	mutex_unlock(&pend->pen_lock);
+
+	pm_runtime_put(dev);
+
+	return 0;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_pen_resume_attention
+ *
+ * SUMMARY: Function for pen to leave suspend state that as following steps:
+ *          1) Increse pm system count
+ *          2) Clear suspend state flag
+ *
+ * RETURN:
+ *	 0 = success
+ *
+ * PARAMETERS:
+ *     *dev - pointer to device structure
+ ******************************************************************************/
+static int pt_pen_resume_attention(struct device *dev)
+{
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+	struct pt_pen_data *pend = &cd->pend;
+
+	pm_runtime_get(dev);
+
+	mutex_lock(&pend->pen_lock);
+	pend->is_suspended = false;
+	mutex_unlock(&pend->pen_lock);
+
+	return 0;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_pen_open
+ *
+ * SUMMARY: Open method for input device(pen) that sets up call back
+ *  functions to TTDL attention list
+ *
+ * RETURN:
+ *	 0 = success
+ *
+ * PARAMETERS:
+ *     *input - pointer to input_dev structure
+ ******************************************************************************/
+static int pt_pen_open(struct input_dev *input)
+{
+	struct device *dev = input->dev.parent;
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+	struct pt_pen_data *pend = &cd->pend;
+
+	pm_runtime_get_sync(dev);
+
+	mutex_lock(&pend->pen_lock);
+	pend->is_suspended = false;
+	mutex_unlock(&pend->pen_lock);
+
+	pt_debug(dev, DL_INFO, "%s: setup subscriptions\n", __func__);
+
+	/* set up touch call back */
+	_pt_subscribe_attention(dev, PT_ATTEN_IRQ, PT_PEN_NAME,
+		pt_pen_attention, PT_MODE_OPERATIONAL);
+
+	/* set up startup call back */
+	_pt_subscribe_attention(dev, PT_ATTEN_STARTUP, PT_PEN_NAME,
+		pt_startup_attention, 0);
+
+	/* set up suspend call back */
+	_pt_subscribe_attention(dev, PT_ATTEN_SUSPEND, PT_PEN_NAME,
+		pt_pen_suspend_attention, 0);
+
+	/* set up resume call back */
+	_pt_subscribe_attention(dev, PT_ATTEN_RESUME, PT_PEN_NAME,
+		pt_pen_resume_attention, 0);
+
+	return 0;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_pen_close
+ *
+ * SUMMARY: Close method for input device(pen) that clears call back
+ *  functions from TTDL attention list.
+ *
+ * PARAMETERS:
+ *     *input - pointer to input_dev structure
+ ******************************************************************************/
+static void pt_pen_close(struct input_dev *input)
+{
+	struct device *dev = input->dev.parent;
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+	struct pt_pen_data *pend = &cd->pend;
+
+	_pt_unsubscribe_attention(dev, PT_ATTEN_IRQ, PT_PEN_NAME,
+		pt_pen_attention, PT_MODE_OPERATIONAL);
+
+	_pt_unsubscribe_attention(dev, PT_ATTEN_STARTUP, PT_PEN_NAME,
+		pt_startup_attention, 0);
+
+	_pt_unsubscribe_attention(dev, PT_ATTEN_SUSPEND, PT_PEN_NAME,
+		pt_pen_suspend_attention, 0);
+
+	_pt_unsubscribe_attention(dev, PT_ATTEN_RESUME, PT_PEN_NAME,
+		pt_pen_resume_attention, 0);
+
+	mutex_lock(&pend->pen_lock);
+	if (!pend->is_suspended) {
+		pm_runtime_put(dev);
+		pend->is_suspended = true;
+	}
+	mutex_unlock(&pend->pen_lock);
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_setup_input_device
+ *
+ * SUMMARY: Set up resolution, event signal capabilities and register input
+ *  device for pen.
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *     *dev - pointer to device structure
+ ******************************************************************************/
+static int pt_setup_input_device(struct device *dev)
+{
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+	struct pt_pen_data *pend = &cd->pend;
+	int i;
+	int rc;
+	u32 usage;
+	int max_x, max_y, max_p;
+
+	pt_debug(dev, DL_INFO, "%s: Initialize event signals\n",
+		__func__);
+	__set_bit(EV_ABS, pend->input->evbit);
+	__set_bit(EV_KEY, pend->input->evbit);
+
+	for (i = PT_PEN_X; i < PT_PEN_NUM_ABS; i++) {
+		usage = pt_pen_abs_field_map[i];
+
+		switch (usage) {
+		case HID_GD_X:
+			max_x = pend->si->sensing_conf_data.res_x;
+			input_set_abs_params(pend->input,
+				ABS_X, 0, max_x, 0, 0);
+			break;
+		case HID_GD_Y:
+			max_y = pend->si->sensing_conf_data.res_y;
+			input_set_abs_params(pend->input,
+				ABS_Y, 0, max_y, 0, 0);
+			break;
+		case HID_DG_TIPPRESSURE:
+			max_p = pend->si->sensing_conf_data.max_z;
+			input_set_abs_params(pend->input,
+				ABS_PRESSURE, 0, max_p, 0, 0);
+			break;
+		case HID_DG_INRANGE:
+			input_set_capability(pend->input,
+				EV_KEY, BTN_TOOL_PEN);
+			break;
+		case HID_DG_INVERT:
+			input_set_capability(pend->input,
+				EV_KEY, BTN_TOOL_RUBBER);
+			break;
+		case HID_DG_TILT_X:
+			max_x = pend->si->pen_abs[i].max;
+			input_set_abs_params(pend->input,
+				ABS_TILT_X, 0, max_x, 0, 0);
+			break;
+		case HID_DG_TILT_Y:
+			max_x = pend->si->pen_abs[i].max;
+			input_set_abs_params(pend->input,
+				ABS_TILT_Y, 0, max_x, 0, 0);
+			break;
+		case HID_DG_ERASER:
+		case HID_DG_TIPSWITCH:
+			input_set_capability(pend->input,
+				EV_KEY, BTN_TOUCH);
+			break;
+		case HID_DG_BARRELSWITCH:
+			input_set_capability(pend->input,
+				EV_KEY, BTN_STYLUS);
+			break;
+		case HID_DG_BARRELSWITCH2:
+			input_set_capability(pend->input,
+				EV_KEY, BTN_STYLUS2);
+			break;
+		}
+	}
+
+	rc = input_register_device(pend->input);
+	if (rc < 0)
+		pt_debug(dev, DL_ERROR,
+			"%s: Error, failed register input device r=%d\n",
+			__func__, rc);
+	else
+		pend->input_device_registered = true;
+
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_setup_input_attention
+ *
+ * SUMMARY: Wrapper function for pt_setup_input_device() register to TTDL
+ *  attention list.
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *     *dev - pointer to device structure
+ ******************************************************************************/
+static int pt_setup_input_attention(struct device *dev)
+{
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+	struct pt_pen_data *pend = &cd->pend;
+	int rc;
+
+	pend->si = _pt_request_sysinfo(dev);
+	if (!pend->si)
+		return -1;
+
+	rc = pt_setup_input_device(dev);
+
+	_pt_unsubscribe_attention(dev, PT_ATTEN_STARTUP, PT_PEN_NAME,
+		pt_setup_input_attention, 0);
+
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_pen_probe
+ *
+ * SUMMARY: The probe function for pen input device
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *	*dev   - pointer to device structure
+ ******************************************************************************/
+int pt_pen_probe(struct device *dev)
+{
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+	struct pt_pen_data *pend = &cd->pend;
+	struct pt_platform_data *pdata = dev_get_platdata(dev);
+	struct pt_pen_platform_data *pen_pdata;
+	int rc = 0;
+
+	if (!pdata || !pdata->pen_pdata) {
+		pt_debug(dev, DL_ERROR,
+			"%s: Missing platform data\n", __func__);
+		rc = -ENODEV;
+		goto error_no_pdata;
+	}
+	pen_pdata = pdata->pen_pdata;
+
+	mutex_init(&pend->pen_lock);
+	pend->dev = dev;
+	pend->pdata = pen_pdata;
+
+	/* Create the input device and register it. */
+	pt_debug(dev, DL_INFO,
+		"%s: Create the input device and register it\n", __func__);
+	pend->input = input_allocate_device();
+	if (!pend->input) {
+		pt_debug(dev, DL_ERROR,
+			"%s: Error, failed to allocate input device\n",
+			__func__);
+		rc = -ENODEV;
+		goto error_alloc_failed;
+	} else
+		pend->input_device_allocated = true;
+
+	if (pend->pdata->inp_dev_name)
+		pend->input->name = pend->pdata->inp_dev_name;
+	else
+		pend->input->name = PT_PEN_NAME;
+	scnprintf(pend->phys, sizeof(pend->phys), "%s/input%d", dev_name(dev),
+			cd->phys_num++);
+	pend->input->phys = pend->phys;
+	pend->input->dev.parent = pend->dev;
+	pend->input->open = pt_pen_open;
+	pend->input->close = pt_pen_close;
+	input_set_drvdata(pend->input, pend);
+
+	/* get sysinfo */
+	pend->si = _pt_request_sysinfo(dev);
+
+	if (pend->si) {
+		rc = pt_setup_input_device(dev);
+		if (rc)
+			goto error_init_input;
+	} else {
+		pt_debug(dev, DL_ERROR,
+			"%s: Fail get sysinfo pointer from core p=%p\n",
+			__func__, pend->si);
+		_pt_subscribe_attention(dev, PT_ATTEN_STARTUP,
+			PT_PEN_NAME, pt_setup_input_attention, 0);
+	}
+
+	return 0;
+
+error_init_input:
+	input_free_device(pend->input);
+	pend->input_device_allocated = false;
+error_alloc_failed:
+error_no_pdata:
+	pt_debug(dev, DL_ERROR, "%s failed.\n", __func__);
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_pen_release
+ *
+ * SUMMARY: The release function for pen input device
+ *
+ * RETURN:
+ *	 0 = success
+ *
+ * PARAMETERS:
+ *	*dev   - pointer to device structure
+ ******************************************************************************/
+int pt_pen_release(struct device *dev)
+{
+	struct pt_core_data *cd;
+	struct pt_pen_data *pend;
+
+	/* Ensure valid pointers before de-referencing them */
+	if (dev) {
+		cd = dev_get_drvdata(dev);
+		if (cd)
+			pend = &cd->pend;
+		else
+			return 0;
+	} else {
+		return 0;
+	}
+
+	/*
+	 * Second call this function may cause kernel panic if probe fail.
+	 * Use input_device_registered & input_device_allocated variable to
+	 * avoid unregister or free unavailable devive.
+	 */
+	if (pend && pend->input_device_registered) {
+		pend->input_device_registered = false;
+		input_unregister_device(pend->input);
+		/* Unregistering device will free the device too */
+		pend->input_device_allocated = false;
+	} else if (pend && pend->input_device_allocated) {
+		pend->input_device_allocated = false;
+		input_free_device(pend->input);
+		_pt_unsubscribe_attention(dev, PT_ATTEN_STARTUP,
+			PT_PEN_NAME, pt_setup_input_attention, 0);
+	}
+
+	return 0;
+}
+#endif /*!TTDL_KERNEL_SUBMISSION */

+ 1281 - 0
pt/pt_platform.c

@@ -0,0 +1,1281 @@
+/*
+ * pt_platform.c
+ * Parade TrueTouch(TM) Standard Product Platform 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 <[email protected]>
+ */
+
+#include "pt_regs.h"
+#include <linux/pt_platform.h>
+#include <linux/regulator/consumer.h>
+#ifdef PT_PTSBC_SUPPORT
+#include <linux/init-input.h>
+#endif
+
+#ifdef CONFIG_TOUCHSCREEN_PARADE_PLATFORM_FW_UPGRADE
+/* FW for Panel ID = 0x00 */
+#include "pt_fw_pid00.h"
+static struct pt_touch_firmware pt_firmware_pid00 = {
+	.img = pt_img_pid00,
+	.size = ARRAY_SIZE(pt_img_pid00),
+	.ver = pt_ver_pid00,
+	.vsize = ARRAY_SIZE(pt_ver_pid00),
+	.panel_id = 0x00,
+};
+
+/* FW for Panel ID = 0x01 */
+#include "pt_fw_pid01.h"
+static struct pt_touch_firmware pt_firmware_pid01 = {
+	.img = pt_img_pid01,
+	.size = ARRAY_SIZE(pt_img_pid01),
+	.ver = pt_ver_pid01,
+	.vsize = ARRAY_SIZE(pt_ver_pid01),
+	.panel_id = 0x01,
+};
+
+/* FW for Panel ID not enabled (legacy) */
+#include "pt_fw.h"
+static struct pt_touch_firmware pt_firmware = {
+	.img = pt_img,
+	.size = ARRAY_SIZE(pt_img),
+	.ver = pt_ver,
+	.vsize = ARRAY_SIZE(pt_ver),
+};
+#else
+/* FW for Panel ID not enabled (legacy) */
+static struct pt_touch_firmware pt_firmware = {
+	.img = NULL,
+	.size = 0,
+	.ver = NULL,
+	.vsize = 0,
+};
+#endif
+
+#ifdef CONFIG_TOUCHSCREEN_PARADE_PLATFORM_TTCONFIG_UPGRADE
+/* TT Config for Panel ID = 0x00 */
+#include "pt_params_pid00.h"
+static struct touch_settings pt_sett_param_regs_pid00 = {
+	.data = (uint8_t *)&pt_param_regs_pid00[0],
+	.size = ARRAY_SIZE(pt_param_regs_pid00),
+	.tag = 0,
+};
+
+static struct touch_settings pt_sett_param_size_pid00 = {
+	.data = (uint8_t *)&pt_param_size_pid00[0],
+	.size = ARRAY_SIZE(pt_param_size_pid00),
+	.tag = 0,
+};
+
+static struct pt_touch_config pt_ttconfig_pid00 = {
+	.param_regs = &pt_sett_param_regs_pid00,
+	.param_size = &pt_sett_param_size_pid00,
+	.fw_ver = ttconfig_fw_ver_pid00,
+	.fw_vsize = ARRAY_SIZE(ttconfig_fw_ver_pid00),
+	.panel_id = 0x00,
+};
+
+/* TT Config for Panel ID = 0x01 */
+#include "pt_params_pid01.h"
+static struct touch_settings pt_sett_param_regs_pid01 = {
+	.data = (uint8_t *)&pt_param_regs_pid01[0],
+	.size = ARRAY_SIZE(pt_param_regs_pid01),
+	.tag = 0,
+};
+
+static struct touch_settings pt_sett_param_size_pid01 = {
+	.data = (uint8_t *)&pt_param_size_pid01[0],
+	.size = ARRAY_SIZE(pt_param_size_pid01),
+	.tag = 0,
+};
+
+static struct pt_touch_config pt_ttconfig_pid01 = {
+	.param_regs = &pt_sett_param_regs_pid01,
+	.param_size = &pt_sett_param_size_pid01,
+	.fw_ver = ttconfig_fw_ver_pid01,
+	.fw_vsize = ARRAY_SIZE(ttconfig_fw_ver_pid01),
+	.panel_id = 0x01,
+};
+
+/* TT Config for Panel ID not enabled (legacy)*/
+#include "pt_params.h"
+static struct touch_settings pt_sett_param_regs = {
+	.data = (uint8_t *)&pt_param_regs[0],
+	.size = ARRAY_SIZE(pt_param_regs),
+	.tag = 0,
+};
+
+static struct touch_settings pt_sett_param_size = {
+	.data = (uint8_t *)&pt_param_size[0],
+	.size = ARRAY_SIZE(pt_param_size),
+	.tag = 0,
+};
+
+static struct pt_touch_config pt_ttconfig = {
+	.param_regs = &pt_sett_param_regs,
+	.param_size = &pt_sett_param_size,
+	.fw_ver = ttconfig_fw_ver,
+	.fw_vsize = ARRAY_SIZE(ttconfig_fw_ver),
+};
+#else
+/* TT Config for Panel ID not enabled (legacy)*/
+static struct pt_touch_config pt_ttconfig = {
+	.param_regs = NULL,
+	.param_size = NULL,
+	.fw_ver = NULL,
+	.fw_vsize = 0,
+};
+#endif
+
+static struct pt_touch_firmware *pt_firmwares[] = {
+#ifdef CONFIG_TOUCHSCREEN_PARADE_PLATFORM_FW_UPGRADE
+	&pt_firmware_pid00,
+	&pt_firmware_pid01,
+#endif
+	NULL, /* Last item should always be NULL */
+};
+
+static struct pt_touch_config *pt_ttconfigs[] = {
+#ifdef CONFIG_TOUCHSCREEN_PARADE_PLATFORM_TTCONFIG_UPGRADE
+	&pt_ttconfig_pid00,
+	&pt_ttconfig_pid01,
+#endif
+	NULL, /* Last item should always be NULL */
+};
+
+struct pt_loader_platform_data _pt_loader_platform_data = {
+	.fw = &pt_firmware,
+	.ttconfig = &pt_ttconfig,
+	.fws = pt_firmwares,
+	.ttconfigs = pt_ttconfigs,
+	.flags = PT_LOADER_FLAG_NONE,
+};
+
+/*******************************************************************************
+ * FUNCTION: pt_xres
+ *
+ * SUMMARY: Toggles the reset gpio (TP_XRES) to perform a HW reset
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = fail
+ *
+ * PARAMETERS:
+ *  *pdata - pointer to the platform data structure
+ *  *dev   - pointer to Device structure
+ ******************************************************************************/
+int pt_xres(struct pt_core_platform_data *pdata,
+		struct device *dev)
+{
+	int rst_gpio = pdata->rst_gpio;
+	int rc = 0;
+	int ddi_rst_gpio = pdata->ddi_rst_gpio;
+
+	pt_debug(dev, DL_WARN, "%s: 20ms HARD RESET on gpio=%d\n",
+		__func__, pdata->rst_gpio);
+
+	/* Toggling only TP_XRES as DDI_XRES resets the entire part */
+	gpio_set_value(rst_gpio, 1);
+	if (ddi_rst_gpio)
+		gpio_set_value(ddi_rst_gpio, 1);
+	usleep_range(3000, 4000);
+	gpio_set_value(rst_gpio, 0);
+	usleep_range(6000, 7000);
+	gpio_set_value(rst_gpio, 1);
+	if (ddi_rst_gpio)
+		gpio_set_value(ddi_rst_gpio, 1);
+
+	/* Sleep to allow the DUT to boot */
+	usleep_range(3000, 4000);
+	return rc;
+}
+
+#ifdef PT_PINCTRL_EN
+/*******************************************************************************
+ * FUNCTION: pt_pinctrl_init
+ *
+ * SUMMARY: Pinctrl method to obtain pin state handler for TP_RST, IRQ
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = fail
+ *
+ * PARAMETERS:
+ *  *pdata - pointer to the platform data structure
+ *  *dev   - pointer to Device structure
+ ******************************************************************************/
+static int pt_pinctrl_init(struct pt_core_platform_data *pdata,
+			   struct device *dev)
+{
+	int ret = 0;
+
+	pdata->pinctrl = devm_pinctrl_get(dev);
+	if (IS_ERR_OR_NULL(pdata->pinctrl)) {
+		pt_debug(dev, DL_ERROR,
+			 "Failed to get pinctrl, please check dts");
+		ret = PTR_ERR(pdata->pinctrl);
+		goto err_pinctrl_get;
+	}
+
+	pdata->pins_active =
+	    pinctrl_lookup_state(pdata->pinctrl, "pmx_ts_active");
+	if (IS_ERR_OR_NULL(pdata->pins_active)) {
+		pt_debug(dev, DL_ERROR, "pmx_ts_active not found");
+		ret = PTR_ERR(pdata->pins_active);
+		goto err_pinctrl_lookup;
+	}
+
+	pdata->pins_suspend =
+	    pinctrl_lookup_state(pdata->pinctrl, "pmx_ts_suspend");
+	if (IS_ERR_OR_NULL(pdata->pins_suspend)) {
+		pt_debug(dev, DL_ERROR, "pmx_ts_suspend not found");
+		ret = PTR_ERR(pdata->pins_suspend);
+		goto err_pinctrl_lookup;
+	}
+
+	pdata->pins_release =
+	    pinctrl_lookup_state(pdata->pinctrl, "pmx_ts_release");
+	if (IS_ERR_OR_NULL(pdata->pins_release)) {
+		pt_debug(dev, DL_ERROR, "pmx_ts_release not found");
+		ret = PTR_ERR(pdata->pins_release);
+		goto err_pinctrl_lookup;
+	}
+
+	return 0;
+
+err_pinctrl_lookup:
+	devm_pinctrl_put(pdata->pinctrl);
+
+err_pinctrl_get:
+	pdata->pinctrl = NULL;
+	pdata->pins_release = NULL;
+	pdata->pins_suspend = NULL;
+	pdata->pins_active = NULL;
+	return ret;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_pinctrl_select_normal
+ *
+ * SUMMARY: Pinctrl method to configure drive mode for TP_RST, IRQ - normal
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = fail
+ *
+ * PARAMETERS:
+ *  *pdata - pointer to the platform data structure
+ *  *dev   - pointer to Device structure
+ ******************************************************************************/
+static int pt_pinctrl_select_normal(struct pt_core_platform_data *pdata,
+				    struct device *dev)
+{
+	int ret = 0;
+
+	if (pdata->pinctrl && pdata->pins_active) {
+		ret = pinctrl_select_state(pdata->pinctrl, pdata->pins_active);
+		if (ret < 0) {
+			pt_debug(dev, DL_ERROR, "Set normal pin state error=%d",
+				 ret);
+		}
+	}
+
+	return ret;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_pinctrl_select_suspend
+ *
+ * SUMMARY:  Pinctrl method to configure drive mode for TP_RST, IRQ - suspend
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = fail
+ *
+ * PARAMETERS:
+ *  *pdata - pointer to the platform data structure
+ *  *dev   - pointer to Device structure
+ ******************************************************************************/
+static int pt_pinctrl_select_suspend(struct pt_core_platform_data *pdata,
+				     struct device *dev)
+{
+	int ret = 0;
+
+	if (pdata->pinctrl && pdata->pins_suspend) {
+		ret = pinctrl_select_state(pdata->pinctrl, pdata->pins_suspend);
+		if (ret < 0) {
+			pt_debug(dev, DL_ERROR,
+				 "Set suspend pin state error=%d", ret);
+		}
+	}
+
+	return ret;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_pinctrl_select_release
+ *
+ * SUMMARY:  Pinctrl method to configure drive mode for TP_RST, IRQ - release
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = fail
+ *
+ * PARAMETERS:
+ *  *pdata - pointer to the platform data structure
+ *  *dev   - pointer to Device structure
+ ******************************************************************************/
+static int pt_pinctrl_select_release(struct pt_core_platform_data *pdata,
+				     struct device *dev)
+{
+	int ret = 0;
+
+	if (pdata->pinctrl) {
+		if (IS_ERR_OR_NULL(pdata->pins_release)) {
+			devm_pinctrl_put(pdata->pinctrl);
+			pdata->pinctrl = NULL;
+		} else {
+			ret = pinctrl_select_state(pdata->pinctrl,
+						   pdata->pins_release);
+			if (ret < 0)
+				pt_debug(dev, DL_ERROR,
+					 "Set gesture pin state error=%d", ret);
+		}
+	}
+
+	return ret;
+}
+#endif /* PT_PINCTRL_EN */
+
+#ifdef PT_REGULATOR_EN
+#define PT_VCC_MIN_UV (2800000)
+#define PT_VCC_MAX_UV (3300000)
+#define PT_VDDI_MIN_UV (1800000)
+#define PT_VDDI_MAX_UV (1900000)
+/*******************************************************************************
+ * FUNCTION: pt_regulator_init
+ *
+ * SUMMARY: With regulator framework to get regulator handle and setup voltage
+ *  level.
+ *
+ * NOTE: The function only contains setup for VCC and VDDI since AVDD AVEE is
+ *  usually used by TDDI products while the power is setup on other driver.
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = fail
+ *
+ * PARAMETERS:
+ *  *pdata - pointer to the platform data structure
+ *  *dev   - pointer to Device structure
+ ******************************************************************************/
+static int pt_regulator_init(struct pt_core_platform_data *pdata,
+			     struct device *dev)
+{
+	int rc = 0;
+
+	pdata->vcc = devm_regulator_get(dev, "vcc");
+	if (IS_ERR(pdata->vcc)) {
+		rc = PTR_ERR(pdata->vcc);
+		pt_debug(dev, DL_ERROR, "get vcc regulator failed,rc=%d", rc);
+		return rc;
+	}
+
+	if (regulator_count_voltages(pdata->vcc) > 0) {
+		rc = regulator_set_voltage(pdata->vcc, PT_VCC_MIN_UV,
+			      PT_VCC_MAX_UV);
+		if (rc) {
+			pt_debug(dev, DL_ERROR,
+				 "set vcc regulator failed rc=%d", rc);
+			goto error_set_vcc;
+		}
+	}
+
+	pdata->vddi = devm_regulator_get(dev, "vddi");
+	if (IS_ERR(pdata->vddi)) {
+		rc = PTR_ERR(pdata->vddi);
+		pt_debug(dev, DL_ERROR, "get vddi regulator failed,rc=%d", rc);
+		goto error_get_vcc;
+	}
+
+	if (regulator_count_voltages(pdata->vddi) > 0) {
+		rc = regulator_set_voltage(pdata->vddi, PT_VDDI_MIN_UV,
+					   PT_VDDI_MAX_UV);
+		if (rc) {
+			pt_debug(dev, DL_ERROR,
+				 "set vddi regulator failed rc=%d", rc);
+			goto error_set_vddi;
+		}
+	}
+
+	return 0;
+
+error_set_vddi:
+	devm_regulator_put(pdata->vddi);
+error_get_vcc:
+	/*
+	 * regulator_set_voltage always select minimum legal voltage between
+	 * min_uV and max_uV. To set the minuV to 0 means to restore the default
+	 * value of regulator. Since regulator_set_voltage is the part to
+	 * release regulator source, it's not necessary to check the returned
+	 * value of it.
+	 */
+	if (regulator_count_voltages(pdata->vcc) > 0)
+		regulator_set_voltage(pdata->vcc, 0, PT_VCC_MAX_UV);
+error_set_vcc:
+	devm_regulator_put(pdata->vcc);
+
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_setup_power_by_regulator
+ *
+ * SUMMARY:  With regulator framework to set power on/off.
+ *
+ * NOTE: The function only contains setup for VCC and VDDI since AVDD AVEE is
+ *  usually used by TDDI products while the power is setup on other driver.
+ *
+ * NOTE: The timing sequence is the EXAMPLE ONLY for TT7XXX:
+ *  power up order   : VDDI, VCC
+ *  power down order : VCC, VDDI
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = fail
+ *
+ * PARAMETERS:
+ *  *pdata - pointer to the platform data structure
+ *   on    - flag to decide power state,PT_MT_POWER_ON/PT_MT_POWER_OFF
+ *  *dev   - pointer to Device structure
+ ******************************************************************************/
+static int pt_setup_power_by_regulator(struct pt_core_platform_data *pdata,
+				       int on, struct device *dev)
+{
+	int rc = 0;
+
+	if (IS_ERR(pdata->vddi) || IS_ERR(pdata->vcc)) {
+		pt_debug(dev, DL_ERROR, "vddi or vcc is not valid\n");
+		return -EINVAL;
+	}
+
+	if (on == PT_MT_POWER_ON) {
+		rc = regulator_enable(pdata->vddi);
+		if (rc) {
+			pt_debug(dev, DL_ERROR,
+				 "enable vddi regulator failed,rc=%d", rc);
+		}
+		/* Ensure the power goes stable */
+		usleep_range(3000, 4000);
+
+		rc = regulator_enable(pdata->vcc);
+		if (rc) {
+			pt_debug(dev, DL_ERROR,
+				 "enable vcc regulator failed,rc=%d", rc);
+		}
+		/* Ensure the power goes stable */
+		usleep_range(3000, 4000);
+	} else {
+		rc = regulator_disable(pdata->vcc);
+		if (rc) {
+			pt_debug(dev, DL_ERROR,
+				 "disable vcc regulator failed,rc=%d", rc);
+		}
+		rc = regulator_disable(pdata->vddi);
+		if (rc) {
+			pt_debug(dev, DL_ERROR,
+				 "disable vddi regulator failed,rc=%d", rc);
+		}
+		/* Ensure the power ramp down completely */
+		usleep_range(10000, 12000);
+	}
+
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_regulator_release
+ *
+ * SUMMARY: With regulator framework to release regulator resource
+ *
+ * NOTE: The regulator MUST be disabled before this call.
+ * NOTE: The function only contains setup for VCC and VDDI since AVDD AVEE is
+ *  usually used by TDDI products while the power is setup on other driver.
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = fail
+ *
+ * PARAMETERS:
+ *  *pdata - pointer to the platform data structure
+ *  *dev   - pointer to Device structure
+ ******************************************************************************/
+static int pt_regulator_release(struct pt_core_platform_data *pdata,
+				struct device *dev)
+{
+	if (IS_ERR(pdata->vddi) || IS_ERR(pdata->vcc))
+		return -EINVAL;
+
+	/*
+	 * regulator_set_voltage always select minimum legal voltage between
+	 * min_uV and max_uV. To set the minuV to 0 means to restore the default
+	 * value of regulator. Since regulator_set_voltage is the part to
+	 * release regulator source, it's not necessary to check the returned
+	 * value of it.
+	 */
+	if (regulator_count_voltages(pdata->vddi) > 0)
+		regulator_set_voltage(pdata->vddi, 0, PT_VDDI_MAX_UV);
+	devm_regulator_put(pdata->vddi);
+
+	if (regulator_count_voltages(pdata->vcc) > 0)
+		regulator_set_voltage(pdata->vcc, 0, PT_VCC_MAX_UV);
+	devm_regulator_put(pdata->vcc);
+
+	return 0;
+}
+#endif /* PT_REGULATOR_EN */
+/*******************************************************************************
+ * FUNCTION: pt_init
+ *
+ * SUMMARY: Set up/free gpios for TP_RST, IRQ, DDI_RST.
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = fail
+ *
+ * PARAMETERS:
+ *  *pdata - pointer to the platform data structure
+ *   on    - flag to set up or free gpios(0:free; !0:set up)
+ *  *dev   - pointer to Device structure
+ ******************************************************************************/
+int pt_init(struct pt_core_platform_data *pdata,
+		int on, struct device *dev)
+{
+	int rst_gpio     = pdata->rst_gpio;
+	int irq_gpio     = pdata->irq_gpio;
+	int ddi_rst_gpio = pdata->ddi_rst_gpio;
+	int rc = 0;
+
+	if (on) {
+#ifdef PT_PINCTRL_EN
+		rc = pt_pinctrl_init(pdata, dev);
+		if (!rc) {
+			pt_pinctrl_select_normal(pdata, dev);
+		} else {
+			pt_debug(dev, DL_ERROR,
+				 "%s: Failed to request pinctrl\n", __func__);
+		}
+#endif
+#ifdef PT_REGULATOR_EN
+		rc = pt_regulator_init(pdata, dev);
+		if (rc) {
+			pt_debug(dev, DL_ERROR,
+				 "%s: Failed requesting regulator rc=%d",
+				 __func__, rc);
+		}
+#endif
+	}
+
+	if (on && rst_gpio) {
+		/* Configure RST GPIO */
+		pt_debug(dev, DL_WARN, "%s: Request RST GPIO %d",
+			__func__, rst_gpio);
+		rc = gpio_request(rst_gpio, NULL);
+		if (rc < 0) {
+			gpio_free(rst_gpio);
+			rc = gpio_request(rst_gpio, NULL);
+		}
+		if (rc < 0) {
+			pt_debug(dev, DL_ERROR,
+				"%s: Failed requesting RST GPIO %d\n",
+				__func__, rst_gpio);
+			goto fail_rst_gpio;
+		} else {
+			/*
+			 * Set the GPIO direction and the starting level
+			 * The start level is high because the DUT needs
+			 * to stay in reset during power up.
+			 */
+			rc = gpio_direction_output(rst_gpio, 1);
+			if (rc < 0) {
+				pt_debug(dev, DL_ERROR,
+					"%s: Output Setup ERROR: RST GPIO %d\n",
+					__func__, rst_gpio);
+				goto fail_rst_gpio;
+			}
+		}
+	}
+
+	if (on && irq_gpio) {
+		/* Configure IRQ GPIO */
+		pt_debug(dev, DL_WARN, "%s: Request IRQ GPIO %d",
+			__func__, irq_gpio);
+		rc = gpio_request(irq_gpio, NULL);
+		if (rc < 0) {
+			gpio_free(irq_gpio);
+			rc = gpio_request(irq_gpio, NULL);
+		}
+		if (rc < 0) {
+			pt_debug(dev, DL_ERROR,
+				"%s: Failed requesting IRQ GPIO %d\n",
+				__func__, irq_gpio);
+			goto fail_irq_gpio;
+		} else {
+			/* Set the GPIO direction */
+			rc = gpio_direction_input(irq_gpio);
+			if (rc < 0) {
+				pt_debug(dev, DL_ERROR,
+					"%s: Input Setup ERROR: IRQ GPIO %d\n",
+					__func__, irq_gpio);
+				goto fail_irq_gpio;
+			}
+		}
+	}
+
+	if (on && ddi_rst_gpio) {
+		/* Configure DDI RST GPIO */
+		pt_debug(dev, DL_WARN, "%s: Request DDI RST GPIO %d",
+			__func__, ddi_rst_gpio);
+		rc = gpio_request(ddi_rst_gpio, NULL);
+		if (rc < 0) {
+			gpio_free(ddi_rst_gpio);
+			rc = gpio_request(ddi_rst_gpio, NULL);
+		}
+		if (rc < 0) {
+			pt_debug(dev, DL_ERROR,
+				"%s: Failed requesting DDI RST GPIO %d\n",
+				__func__, ddi_rst_gpio);
+			goto fail_ddi_rst_gpio;
+		} else {
+			/* Set the GPIO direction and the starting level */
+			rc = gpio_direction_output(ddi_rst_gpio, 0);
+			if (rc < 0) {
+				pt_debug(dev, DL_ERROR,
+					"%s: Output Setup ERROR: RST GPIO %d\n",
+					__func__, ddi_rst_gpio);
+				goto fail_ddi_rst_gpio;
+			}
+		}
+	}
+
+	if (!on) {
+		/* "on" not set, therefore free all gpio's */
+		if (ddi_rst_gpio)
+			gpio_free(ddi_rst_gpio);
+		if (irq_gpio)
+			gpio_free(irq_gpio);
+		if (rst_gpio)
+			gpio_free(rst_gpio);
+#ifdef PT_REGULATOR_EN
+		pt_regulator_release(pdata, dev);
+#endif
+#ifdef PT_PINCTRL_EN
+		pt_pinctrl_select_release(pdata, dev);
+#endif
+	}
+
+	/* All GPIO's created successfully */
+	goto success;
+
+fail_ddi_rst_gpio:
+	pt_debug(dev, DL_ERROR,
+		"%s: ERROR - GPIO setup Failure, freeing DDI_XRES GPIO %d\n",
+		__func__, ddi_rst_gpio);
+	gpio_free(ddi_rst_gpio);
+fail_irq_gpio:
+	pt_debug(dev, DL_ERROR,
+		"%s: ERROR - GPIO setup Failure, freeing IRQ GPIO %d\n",
+		__func__, irq_gpio);
+	gpio_free(irq_gpio);
+fail_rst_gpio:
+	pt_debug(dev, DL_ERROR,
+		"%s: ERROR - GPIO setup Failure, freeing TP_XRES GPIO %d\n",
+		__func__, rst_gpio);
+	gpio_free(rst_gpio);
+
+success:
+	pt_debug(dev, DL_INFO,
+		"%s: SUCCESS - Configured DDI_XRES GPIO %d, IRQ GPIO %d, TP_XRES GPIO %d\n",
+		__func__, ddi_rst_gpio, irq_gpio, rst_gpio);
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_irq_stat
+ *
+ * SUMMARY: Obtain the level state of IRQ gpio.
+ *
+ * RETURN:
+ *	 level state of IRQ gpio
+ *
+ * PARAMETERS:
+ *  *pdata       - pointer to the platform data structure
+ *  *dev         - pointer to Device structure
+ ******************************************************************************/
+int pt_irq_stat(struct pt_core_platform_data *pdata,
+		struct device *dev)
+{
+	return gpio_get_value(pdata->irq_gpio);
+}
+
+#ifdef PT_DETECT_HW
+/*******************************************************************************
+ * FUNCTION: pt_detect
+ *
+ * SUMMARY: Detect the I2C device by reading one byte(FW sentiel) after the
+ *  reset operation.
+ *
+ * RETURN:
+ *	 0 - detected
+ *  !0 - undetected
+ *
+ * PARAMETERS:
+ *  *pdata - pointer to the platform data structure
+ *  *dev   - pointer to Device structure
+ *   read  - pointer to the function to perform a read operation
+ ******************************************************************************/
+int pt_detect(struct pt_core_platform_data *pdata,
+		struct device *dev, pt_platform_read read)
+{
+	int retry = 3;
+	int rc;
+	char buf[1];
+
+	while (retry--) {
+		/* Perform reset, wait for 100 ms and perform read */
+		pt_debug(dev, DL_WARN, "%s: Performing a reset\n",
+			__func__);
+		pdata->xres(pdata, dev);
+		msleep(100);
+		rc = read(dev, buf, 1);
+		if (!rc)
+			return 0;
+
+		pt_debug(dev, DL_ERROR, "%s: Read unsuccessful, try=%d\n",
+			__func__, 3 - retry);
+	}
+
+	return rc;
+}
+#endif
+
+#ifndef PT_REGULATOR_EN
+/*******************************************************************************
+ * FUNCTION: pt_setup_power_by_gpio
+ *
+ * SUMMARY:  With GPIOs to control LDO directly to set power on/off.
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = fail
+ *
+ * PARAMETERS:
+ *  *pdata - pointer to the platform data structure
+ *   on    - flag to decide power state,PT_MT_POWER_ON/PT_MT_POWER_OFF
+ *  *dev   - pointer to Device structure
+ ******************************************************************************/
+static int pt_setup_power_by_gpio(struct pt_core_platform_data *pdata,
+				       int on, struct device *dev)
+{
+	int rc = 0;
+	int en_vcc  = pdata->vcc_gpio;
+	int en_vddi = pdata->vddi_gpio;
+	int en_avdd = pdata->avdd_gpio;
+	int en_avee = pdata->avee_gpio;
+
+	if (on == PT_MT_POWER_ON) {
+		/*
+		 * Enable GPIOs to turn on voltage regulators to pwr up DUT
+		 * - TC device power up order: VDDI, VCC, AVDD, AVEE
+		 * - TT device power up order: VDDI, VCC
+		 * NOTE: VDDI must be stable for >10ms before XRES is released
+		 */
+		pt_debug(dev, DL_INFO,
+		"%s: Enable defined pwr: VDDI, VCC, AVDD, AVEE\n", __func__);
+
+		/* Turn on VDDI [Digital Interface] (+1.8v) */
+		if (pdata->vddi_gpio) {
+			rc = gpio_request(en_vddi, NULL);
+			if (rc < 0) {
+				gpio_free(en_vddi);
+				rc = gpio_request(en_vddi, NULL);
+			}
+			if (rc < 0) {
+				pr_err("%s: Failed requesting VDDI GPIO %d\n",
+					__func__, en_vddi);
+			}
+			rc = gpio_direction_output(en_vddi, 1);
+			if (rc)
+				pr_err("%s: setcfg for VDDI GPIO %d failed\n",
+					__func__, en_vddi);
+			gpio_free(en_vddi);
+			usleep_range(3000, 4000);
+		}
+
+		/* Turn on VCC */
+		if (pdata->vcc_gpio) {
+			rc = gpio_request(en_vcc, NULL);
+			if (rc < 0) {
+				gpio_free(en_vcc);
+				rc = gpio_request(en_vcc, NULL);
+			}
+			if (rc < 0) {
+				pr_err("%s: Failed requesting VCC GPIO %d\n",
+					__func__, en_vcc);
+			}
+			rc = gpio_direction_output(en_vcc, 1);
+			if (rc)
+				pr_err("%s: setcfg for VCC GPIO %d failed\n",
+					__func__, en_vcc);
+			gpio_free(en_vcc);
+			usleep_range(3000, 4000);
+		}
+
+		/* Turn on AVDD (+5.0v) */
+		if (pdata->avdd_gpio) {
+			rc = gpio_request(en_avdd, NULL);
+			if (rc < 0) {
+				gpio_free(en_avdd);
+				rc = gpio_request(en_avdd, NULL);
+			}
+			if (rc < 0) {
+				pr_err("%s: Failed requesting AVDD GPIO %d\n",
+					__func__, en_avdd);
+			}
+			rc = gpio_direction_output(en_avdd, 1);
+			if (rc)
+				pr_err("%s: setcfg for AVDD GPIO %d failed\n",
+					__func__, en_avdd);
+			gpio_free(en_avdd);
+			usleep_range(3000, 4000);
+		}
+
+		/* Turn on AVEE (-5.0v) */
+		if (pdata->avee_gpio) {
+			rc = gpio_request(en_avee, NULL);
+			if (rc < 0) {
+				gpio_free(en_avee);
+				rc = gpio_request(en_avee, NULL);
+			}
+			if (rc < 0) {
+				pr_err("%s: Failed requesting AVEE GPIO %d\n",
+					__func__, en_avee);
+			}
+			rc = gpio_direction_output(en_avee, 1);
+			if (rc)
+				pr_err("%s: setcfg for AVEE GPIO %d failed\n",
+					__func__, en_avee);
+			gpio_free(en_avee);
+			usleep_range(3000, 4000);
+		}
+	} else {
+		/*
+		 * Disable GPIOs to turn off voltage regulators to pwr down
+		 * TC device The power down order is: AVEE, AVDD, VDDI
+		 * TT device The power down order is: VCC, VDDI
+		 *
+		 * Note:Turn off some of regulators may effect display
+		 * parts for TDDI chip
+		 */
+		pt_debug(dev, DL_INFO,
+		"%s: Turn off defined pwr: VCC, AVEE, AVDD, VDDI\n", __func__);
+
+		/* Turn off VCC */
+		if (pdata->vcc_gpio) {
+			rc = gpio_request(en_vcc, NULL);
+			if (rc < 0) {
+				gpio_free(en_vcc);
+				rc = gpio_request(en_vcc, NULL);
+			}
+			if (rc < 0) {
+				pr_err("%s: Failed requesting VCC GPIO %d\n",
+					__func__, en_vcc);
+			}
+			rc = gpio_direction_output(en_vcc, 0);
+			if (rc)
+				pr_err("%s: setcfg for VCC GPIO %d failed\n",
+					__func__, en_vcc);
+			gpio_free(en_vcc);
+		}
+
+		/* Turn off AVEE (-5.0v) */
+		if (pdata->avee_gpio) {
+			rc = gpio_request(en_avee, NULL);
+			if (rc < 0) {
+				gpio_free(en_avee);
+				rc = gpio_request(en_avee, NULL);
+			}
+			if (rc < 0) {
+				pr_err("%s: Failed requesting AVEE GPIO %d\n",
+					__func__, en_avee);
+			}
+			rc = gpio_direction_output(en_avee, 0);
+			if (rc)
+				pr_err("%s: setcfg for AVEE GPIO %d failed\n",
+					__func__, en_avee);
+			gpio_free(en_avee);
+		}
+
+		/* Turn off AVDD (+5.0v) */
+		if (pdata->avdd_gpio) {
+			rc = gpio_request(en_avdd, NULL);
+			if (rc < 0) {
+				gpio_free(en_avdd);
+				rc = gpio_request(en_avdd, NULL);
+			}
+			if (rc < 0) {
+				pr_err("%s: Failed requesting AVDD GPIO %d\n",
+					__func__, en_avdd);
+			}
+			rc = gpio_direction_output(en_avdd, 0);
+			if (rc)
+				pr_err("%s: setcfg for AVDD GPIO %d failed\n",
+					__func__, en_avdd);
+			gpio_free(en_avdd);
+		}
+
+		/* Turn off VDDI [Digital Interface] (+1.8v) */
+		if (pdata->vddi_gpio) {
+			rc = gpio_request(en_vddi, NULL);
+			if (rc < 0) {
+				gpio_free(en_vddi);
+				rc = gpio_request(en_vddi, NULL);
+			}
+			if (rc < 0) {
+				pr_err("%s: Failed requesting VDDI GPIO %d\n",
+					__func__, en_vddi);
+			}
+			rc = gpio_direction_output(en_vddi, 0);
+			if (rc)
+				pr_err("%s: setcfg for VDDI GPIO %d failed\n",
+					__func__, en_vddi);
+			gpio_free(en_vddi);
+			usleep_range(10000, 12000);
+		}
+	}
+
+	return rc;
+}
+#endif /* PT_REGULATOR_EN */
+/*******************************************************************************
+ * FUNCTION: pt_setup_power
+ *
+ * SUMMARY: Turn on/turn off voltage regulator
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *  *pdata - pointer to  core platform data
+ *   on     - flag to decide power state,PT_MT_POWER_ON/PT_MT_POWER_OFF
+ *  *dev   - pointer to device
+ ******************************************************************************/
+int pt_setup_power(struct pt_core_platform_data *pdata, int on,
+		struct device *dev)
+{
+	int rc = 0;
+
+	/*
+	 * For TDDI parts, force part into RESET by holding DDI XRES
+	 * while powering it up
+	 */
+	if (pdata->ddi_rst_gpio)
+		gpio_set_value(pdata->ddi_rst_gpio, 0);
+
+	/*
+	 * Force part into RESET by holding XRES#(TP_XRES)
+	 * while powering it up
+	 */
+	if (pdata->rst_gpio)
+		gpio_set_value(pdata->rst_gpio, 0);
+
+#ifdef PT_REGULATOR_EN
+	rc = pt_setup_power_by_regulator(pdata, on, dev);
+	if (rc) {
+		pt_debug(dev, DL_ERROR,
+			 "%s: Failed setup power by regulator rc=%d",
+			 __func__, rc);
+	}
+#else /* PT_REGULATOR_EN */
+	rc = pt_setup_power_by_gpio(pdata, on, dev);
+	if (rc) {
+		pt_debug(dev, DL_ERROR,
+			 "%s: Failed setup power by gpio rc=%d",
+			 __func__, rc);
+	}
+#endif /* PT_REGULATOR_EN */
+
+	/* Force part out of RESET by releasing XRES#(TP_XRES) */
+	if (pdata->rst_gpio)
+		gpio_set_value(pdata->rst_gpio, 1);
+
+	/* Force part out of RESET by releasing DDI XRES */
+	if (pdata->ddi_rst_gpio)
+		gpio_set_value(pdata->ddi_rst_gpio, 1);
+
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_wakeup
+ *
+ * SUMMARY: Resume power for "power on/off" sleep strategy which against to
+ *  "deepsleep" strategy.
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = fail
+ *
+ * PARAMETERS:
+ *  *pdata       - pointer to the platform data structure
+ *  *dev         - pointer to Device structure
+ *  *ignore_irq  - pointer to atomic structure to allow the host ignoring false
+ *                 IRQ during power up
+ ******************************************************************************/
+static int pt_wakeup(struct pt_core_platform_data *pdata,
+		struct device *dev, atomic_t *ignore_irq)
+{
+	int rc = 0;
+
+#ifdef PT_PINCTRL_EN
+	pt_pinctrl_select_normal(pdata, dev);
+#endif
+	rc = pt_setup_power(pdata, PT_MT_POWER_ON, dev);
+	if (rc)
+		pt_debug(dev, DL_ERROR, "%s: Failed setup power\n", __func__);
+
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_sleep
+ *
+ * SUMMARY: Suspend power for "power on/off" sleep strategy which against to
+ *  "deepsleep" strategy.
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = fail
+ *
+ * PARAMETERS:
+ *  *pdata       - pointer to the platform data structure
+ *  *dev         - pointer to Device structure
+ *  *ignore_irq  - pointer to atomic structure to allow the host ignoring false
+ *                 IRQ during power down
+ ******************************************************************************/
+static int pt_sleep(struct pt_core_platform_data *pdata,
+		struct device *dev, atomic_t *ignore_irq)
+{
+	int rc = 0;
+
+	rc = pt_setup_power(pdata, PT_MT_POWER_OFF, dev);
+	if (rc)
+		pt_debug(dev, DL_ERROR, "%s: Failed setup power\n", __func__);
+
+#ifdef PT_PINCTRL_EN
+	pt_pinctrl_select_suspend(pdata, dev);
+#endif
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_power
+ *
+ * SUMMARY: Wrapper function to resume/suspend power with function
+ *  pt_wakeup()/pt_sleep().
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = fail
+ *
+ * PARAMETERS:
+ *  *pdata       - pointer to the platform data structure
+ *   on          - flag to remsume/suspend power(0:resume; 1:suspend)
+ *  *dev         - pointer to Device structure
+ *  *ignore_irq  - pointer to atomic structure to allow the host ignoring false
+ *                 IRQ during power up/down
+ ******************************************************************************/
+int pt_power(struct pt_core_platform_data *pdata,
+		int on, struct device *dev, atomic_t *ignore_irq)
+{
+	if (on)
+		return pt_wakeup(pdata, dev, ignore_irq);
+
+	return pt_sleep(pdata, dev, ignore_irq);
+}
+
+#ifdef PT_PTSBC_SUPPORT
+
+static struct workqueue_struct *parade_wq;
+static u32 int_handle;
+
+/*******************************************************************************
+ * FUNCTION: pt_irq_work_function
+ *
+ * SUMMARY: Work function for queued IRQ activity
+ *
+ * RETURN: Void
+ *
+ * PARAMETERS:
+ *	*work - pointer to work structure
+ ******************************************************************************/
+static void pt_irq_work_function(struct work_struct *work)
+{
+	struct pt_core_data *cd = container_of(work,
+			struct pt_core_data, irq_work);
+
+	pt_irq(cd->irq, (void *)cd);
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_irq_wrapper
+ *
+ * SUMMARY: Wrapper function for IRQ to queue the irq_work function
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *	*handle - void pointer to contain the core_data pointer
+ ******************************************************************************/
+peint_handle *pt_irq_wrapper(void *handle)
+{
+	struct pt_core_data *cd = (struct pt_core_data *)handle;
+
+	queue_work(parade_wq, &cd->irq_work);
+	return 0;
+}
+#endif /* PT_PTSBC_SUPPORT */
+
+/*******************************************************************************
+ * FUNCTION: pt_setup_irq
+ *
+ * SUMMARY: Configure the IRQ GPIO used by the TT DUT
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *	*pdata - pointer to core platform data
+ *	 on    - flag to setup interrupt process work(PT_MT_IRQ_FREE/)
+ *	*dev   - pointer to device
+ ******************************************************************************/
+int pt_setup_irq(struct pt_core_platform_data *pdata, int on,
+	struct device *dev)
+{
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+	unsigned long irq_flags;
+	int rc = 0;
+
+	if (on == PT_MT_IRQ_REG) {
+		/*
+		 * When TTDL has direct access to the GPIO the irq_stat function
+		 * will be defined and the gpio_to_irq conversion must be
+		 * performed. e.g. For CHROMEOS this is not the case, the irq is
+		 * passed in directly.
+		 */
+		if (pdata->irq_stat) {
+			/* Initialize IRQ */
+			dev_vdbg(dev, "%s: Value Passed to gpio_to_irq =%d\n",
+				__func__, pdata->irq_gpio);
+			cd->irq = gpio_to_irq(pdata->irq_gpio);
+			dev_vdbg(dev,
+				"%s: Value Returned from gpio_to_irq =%d\n",
+				__func__, cd->irq);
+		}
+		if (cd->irq < 0)
+			return -EINVAL;
+
+		cd->irq_enabled = true;
+
+		pt_debug(dev, DL_INFO, "%s: initialize threaded irq=%d\n",
+			__func__, cd->irq);
+
+		if (pdata->level_irq_udelay > 0)
+#ifdef PT_PTSBC_SUPPORT
+			/* use level triggered interrupts */
+			irq_flags = TRIG_LEVL_LOW;
+		else
+			/* use edge triggered interrupts */
+			irq_flags = TRIG_EDGE_NEGATIVE;
+#else
+			/* use level triggered interrupts */
+			irq_flags = IRQF_TRIGGER_LOW | IRQF_ONESHOT;
+		else
+			/* use edge triggered interrupts */
+			irq_flags = IRQF_TRIGGER_FALLING | IRQF_ONESHOT;
+#endif /* PT_PTSBC_SUPPORT */
+
+#ifdef PT_PTSBC_SUPPORT
+		/* Adding new work queue to cd struct */
+		INIT_WORK(&cd->irq_work, pt_irq_work_function);
+
+		parade_wq = create_singlethread_workqueue("parade_wq");
+		if (!parade_wq)
+			pt_debug(dev, DL_ERROR, "%s Create workqueue failed.\n",
+				__func__);
+
+		int_handle = sw_gpio_irq_request(pdata->irq_gpio, irq_flags,
+				(peint_handle)pt_irq_wrapper, cd);
+		if (!int_handle) {
+			pt_debug(dev, DL_ERROR,
+				"%s: PARADE could not request irq\n", __func__);
+			rc = -1;
+		} else {
+			rc = 0;
+			/* clk=0: 32Khz; clk=1: 24Mhz*/
+			ctp_set_int_port_rate(pdata->irq_gpio, 1);
+			/*
+			 * Debounce INT Line by clock divider: 2^n. E.g. The
+			 * para:0x03 means the period of interrupt controller is
+			 * 0.33 us = (2^3)/24. It has ability to measure the
+			 * high/low width of the pulse bigger than 1 us.
+			 */
+			ctp_set_int_port_deb(pdata->irq_gpio, 0x03);
+			pt_debug(cd->dev, DL_INFO,
+				"%s: Parade sw_gpio_irq_request SUCCESS\n",
+				__func__);
+		}
+#else
+		rc = request_threaded_irq(cd->irq, NULL, pt_irq,
+			irq_flags, dev_name(dev), cd);
+		if (rc < 0)
+			pt_debug(dev, DL_ERROR,
+				"%s: Error, could not request irq\n", __func__);
+#endif /* PT_PTSBC_SUPPORT */
+	} else {
+		disable_irq_nosync(cd->irq);
+#ifndef PT_PTSBC_SUPPORT
+		free_irq(cd->irq, cd);
+#else
+		sw_gpio_irq_free(int_handle);
+		cancel_work_sync(&cd->irq_work);
+		destroy_workqueue(parade_wq);
+#endif /* PT_PTSBC_SUPPORT */
+	}
+	return rc;
+}

+ 781 - 0
pt/pt_proximity.c

@@ -0,0 +1,781 @@
+#ifndef TTDL_KERNEL_SUBMISSION
+/*
+ * pt_proximity.c
+ * Parade TrueTouch(TM) Standard Product Proximity 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 <[email protected]>
+ *
+ */
+
+#include "pt_regs.h"
+
+#define PT_PROXIMITY_NAME "pt_proximity"
+
+/* Timeout value in ms. */
+#define PT_PROXIMITY_REQUEST_EXCLUSIVE_TIMEOUT		1000
+
+#define PT_PROXIMITY_ON 0
+#define PT_PROXIMITY_OFF 1
+
+/*******************************************************************************
+ * FUNCTION: get_prox_data
+ *
+ * SUMMARY: Gets pointer of proximity data from core data structure
+ *
+ * RETURN:
+ *	 pointer of pt_proximity_data structure in core data structure
+ *
+ * PARAMETERS:
+ *     *dev - pointer to device structure
+ ******************************************************************************/
+static inline struct pt_proximity_data *get_prox_data(struct device *dev)
+{
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+
+	return &cd->pd;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_report_proximity
+ *
+ * SUMMARY: Reports proximity event
+ *
+ * PARAMETERS:
+ *     *pd  - pointer to proximity data structure
+ *      on  - state of proximity(true:on; false:off)
+ ******************************************************************************/
+static void pt_report_proximity(struct pt_proximity_data *pd,
+	bool on)
+{
+	int val = on ? PT_PROXIMITY_ON : PT_PROXIMITY_OFF;
+
+	input_report_abs(pd->input, ABS_DISTANCE, val);
+	input_sync(pd->input);
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_get_touch_hdr
+ *
+ * SUMMARY: Gets header of touch report
+ *
+ * PARAMETERS:
+ *     *pd      - pointer to proximity data structure
+ *     *touch   - pointer to pt_touch structure
+ *     *xy_mode - pointer to input mode data
+ ******************************************************************************/
+static void pt_get_touch_hdr(struct pt_proximity_data *pd,
+	struct pt_touch *touch, u8 *xy_mode)
+{
+	struct device *dev = pd->dev;
+	struct pt_sysinfo *si = pd->si;
+	enum pt_tch_hdr hdr;
+
+	for (hdr = PT_TCH_TIME; hdr < PT_TCH_NUM_HDR; hdr++) {
+		if (!si->tch_hdr[hdr].report)
+			continue;
+		pt_get_touch_field(dev, &touch->hdr[hdr],
+			si->tch_hdr[hdr].size,
+			si->tch_hdr[hdr].max,
+			xy_mode + si->tch_hdr[hdr].ofs,
+			si->tch_hdr[hdr].bofs);
+		pt_debug(dev, DL_INFO, "%s: get %s=%04X(%d)\n",
+			__func__, pt_tch_hdr_string[hdr],
+			touch->hdr[hdr], touch->hdr[hdr]);
+	}
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_get_touch
+ *
+ * SUMMARY: Parse proximity touch event
+ *
+ * PARAMETERS:
+ *     *pd       - pointer to proximity data structure
+ *     *touch    - pointer to touch structure
+ *      xy_data  - pointer to touch data
+ ******************************************************************************/
+static void pt_get_touch(struct pt_proximity_data *pd,
+	struct pt_touch *touch, u8 *xy_data)
+{
+	struct device *dev = pd->dev;
+	struct pt_sysinfo *si = pd->si;
+	enum pt_tch_abs abs;
+
+	for (abs = PT_TCH_X; abs < PT_TCH_NUM_ABS; abs++) {
+		if (!si->tch_abs[abs].report)
+			continue;
+		pt_get_touch_field(dev, &touch->abs[abs],
+			si->tch_abs[abs].size,
+			si->tch_abs[abs].max,
+			xy_data + si->tch_abs[abs].ofs,
+			si->tch_abs[abs].bofs);
+		pt_debug(dev, DL_INFO, "%s: get %s=%04X(%d)\n",
+			__func__, pt_tch_abs_string[abs],
+			touch->abs[abs], touch->abs[abs]);
+	}
+
+	pt_debug(dev, DL_INFO, "%s: x=%04X(%d) y=%04X(%d)\n",
+		__func__, touch->abs[PT_TCH_X], touch->abs[PT_TCH_X],
+		touch->abs[PT_TCH_Y], touch->abs[PT_TCH_Y]);
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_get_proximity_touch
+ *
+ * SUMMARY: Parse and report proximity touch event
+ *
+ * PARAMETERS:
+ *     *pd      - pointer to proximity data structure
+ *     *touch   - pointer to pt_touch structure
+ ******************************************************************************/
+static void pt_get_proximity_touch(struct pt_proximity_data *pd,
+		struct pt_touch *tch, int num_cur_tch)
+{
+	struct pt_sysinfo *si = pd->si;
+	int i;
+
+	for (i = 0; i < num_cur_tch; i++) {
+		pt_get_touch(pd, tch, si->xy_data +
+			(i * si->desc.tch_record_size));
+
+		/* Check for proximity event */
+		if (tch->abs[PT_TCH_O] == PT_OBJ_PROXIMITY) {
+			if (tch->abs[PT_TCH_E] == PT_EV_TOUCHDOWN)
+				pt_report_proximity(pd, true);
+			else if (tch->abs[PT_TCH_E] == PT_EV_LIFTOFF)
+				pt_report_proximity(pd, false);
+			break;
+		}
+	}
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_xy_worker
+ *
+ * SUMMARY: Read xy_data for all current touches
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *     *pd - pointer to proximity data structure
+ ******************************************************************************/
+static int pt_xy_worker(struct pt_proximity_data *pd)
+{
+	struct device *dev = pd->dev;
+	struct pt_sysinfo *si = pd->si;
+	struct pt_touch tch;
+	u8 num_cur_tch;
+
+	pt_get_touch_hdr(pd, &tch, si->xy_mode + 3);
+
+	num_cur_tch = tch.hdr[PT_TCH_NUM];
+	if (num_cur_tch > si->sensing_conf_data.max_tch) {
+		pt_debug(dev, DL_ERROR, "%s: Num touch err detected (n=%d)\n",
+			__func__, num_cur_tch);
+		num_cur_tch = si->sensing_conf_data.max_tch;
+	}
+
+	if (tch.hdr[PT_TCH_LO])
+		pt_debug(dev, DL_WARN, "%s: Large area detected\n",
+		__func__);
+
+	/* extract xy_data for all currently reported touches */
+	pt_debug(dev, DL_INFO, "%s: extract data num_cur_rec=%d\n",
+		__func__, num_cur_tch);
+	if (num_cur_tch)
+		pt_get_proximity_touch(pd, &tch, num_cur_tch);
+	else
+		pt_report_proximity(pd, false);
+
+	return 0;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_mt_attention
+ *
+ * SUMMARY: Wrapper function for pt_xy_worker() that subscribe into the TTDL
+ *  attention list.
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *     *dev - pointer to device structure
+ ******************************************************************************/
+static int pt_proximity_attention(struct device *dev)
+{
+	struct pt_proximity_data *pd = get_prox_data(dev);
+	int rc = 0;
+
+	if (pd->si->xy_mode[2] != pd->si->desc.tch_report_id)
+		return 0;
+
+	mutex_lock(&pd->prox_lock);
+	rc = pt_xy_worker(pd);
+	mutex_unlock(&pd->prox_lock);
+	if (rc < 0)
+		pt_debug(dev, DL_ERROR, "%s: xy_worker error r=%d\n",
+			__func__, rc);
+
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_startup_attention
+ *
+ * SUMMARY: Wrapper function for pt_report_proximity() that subcribe into the
+ *  TTDL attention list.
+ *
+ * RETURN:
+ *	 0 = success
+ *
+ * PARAMETERS:
+ *     *dev - pointer to device structure
+ ******************************************************************************/
+static int pt_startup_attention(struct device *dev)
+{
+	struct pt_proximity_data *pd = get_prox_data(dev);
+
+	mutex_lock(&pd->prox_lock);
+	pt_report_proximity(pd, false);
+	mutex_unlock(&pd->prox_lock);
+
+	return 0;
+}
+
+/*******************************************************************************
+ * FUNCTION: _pt_set_proximity_via_touchmode_enabled
+ *
+ * SUMMARY: Enable/Disable proximity via touchmode parameter
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *     *pd     - pointer to proximity data structure
+ *      enable - enable or disable proximity(true:enable; false:disable)
+ ******************************************************************************/
+static int _pt_set_proximity_via_touchmode_enabled(
+		struct pt_proximity_data *pd, bool enable)
+{
+	struct device *dev = pd->dev;
+	u32 touchmode_enabled;
+	int rc;
+
+	rc = _pt_request_pip_get_param(dev, 0,
+			PT_RAM_ID_TOUCHMODE_ENABLED, &touchmode_enabled);
+	if (rc)
+		return rc;
+
+	if (enable)
+		touchmode_enabled |= 0x80;
+	else
+		touchmode_enabled &= 0x7F;
+
+	rc = _pt_request_pip_set_param(dev, 0,
+			PT_RAM_ID_TOUCHMODE_ENABLED, touchmode_enabled,
+			PT_RAM_ID_TOUCHMODE_ENABLED_SIZE);
+
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: _pt_set_proximity_via_proximity_enable
+ *
+ * SUMMARY: Enable/Disable proximity via proximity parameter
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *     *pd     - pointer to proximity data structure
+ *      enable - enable or disable proximity(true:enable; false:disable)
+ ******************************************************************************/
+static int _pt_set_proximity_via_proximity_enable(
+		struct pt_proximity_data *pd, bool enable)
+{
+	struct device *dev = pd->dev;
+	u32 proximity_enable;
+	int rc;
+
+	rc = _pt_request_pip_get_param(dev, 0,
+			PT_RAM_ID_PROXIMITY_ENABLE, &proximity_enable);
+	if (rc)
+		return rc;
+
+	if (enable)
+		proximity_enable |= 0x01;
+	else
+		proximity_enable &= 0xFE;
+
+	rc = _pt_request_pip_set_param(dev, 0,
+			PT_RAM_ID_PROXIMITY_ENABLE, proximity_enable,
+			PT_RAM_ID_PROXIMITY_ENABLE_SIZE);
+
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: _pt_set_proximity
+ *
+ * SUMMARY: Set proximity mode via touchmode parameter or proximity parameter.
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *     *pd     - pointer to proximity data structure
+ *      enable - enable or disable proximity(true:enable; false:disable)
+ ******************************************************************************/
+static int _pt_set_proximity(struct pt_proximity_data *pd,
+		bool enable)
+{
+	if (!IS_PIP_VER_GE(pd->si, 1, 4))
+		return _pt_set_proximity_via_touchmode_enabled(pd,
+				enable);
+
+	return _pt_set_proximity_via_proximity_enable(pd, enable);
+}
+
+/*******************************************************************************
+ * FUNCTION: _pt_set_proximity
+ *
+ * SUMMARY: Enable proximity mode and subscribe into IRQ and STARTUP TTDL
+ *  attention list.
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *     *pd     - pointer to proximity data structure
+ ******************************************************************************/
+static int _pt_proximity_enable(struct pt_proximity_data *pd)
+{
+	struct device *dev = pd->dev;
+	int rc = 0;
+
+	pm_runtime_get_sync(dev);
+
+	rc = pt_request_exclusive(dev,
+			PT_PROXIMITY_REQUEST_EXCLUSIVE_TIMEOUT);
+	if (rc < 0) {
+		pt_debug(dev, DL_ERROR, "%s: Error on request exclusive r=%d\n",
+				__func__, rc);
+		goto exit;
+	}
+
+	rc = _pt_set_proximity(pd, true);
+	if (rc < 0) {
+		pt_debug(dev, DL_ERROR, "%s: Error on request enable proximity scantype r=%d\n",
+				__func__, rc);
+		goto exit_release;
+	}
+
+	pt_debug(dev, DL_INFO, "%s: setup subscriptions\n", __func__);
+
+	/* set up touch call back */
+	_pt_subscribe_attention(dev, PT_ATTEN_IRQ, PT_PROXIMITY_NAME,
+		pt_proximity_attention, PT_MODE_OPERATIONAL);
+
+	/* set up startup call back */
+	_pt_subscribe_attention(dev, PT_ATTEN_STARTUP,
+		PT_PROXIMITY_NAME, pt_startup_attention, 0);
+
+exit_release:
+	pt_release_exclusive(dev);
+exit:
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: _pt_proximity_disable
+ *
+ * SUMMARY: Disable proximity mode and unsubscribe from IRQ and STARTUP TTDL
+ *  attention list.
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *     *pd     - pointer to proximity data structure
+ ******************************************************************************/
+static int _pt_proximity_disable(struct pt_proximity_data *pd,
+		bool force)
+{
+	struct device *dev = pd->dev;
+	int rc = 0;
+
+	rc = pt_request_exclusive(dev,
+			PT_PROXIMITY_REQUEST_EXCLUSIVE_TIMEOUT);
+	if (rc < 0) {
+		pt_debug(dev, DL_ERROR, "%s: Error on request exclusive r=%d\n",
+				__func__, rc);
+		goto exit;
+	}
+
+	rc = _pt_set_proximity(pd, false);
+	if (rc < 0) {
+		pt_debug(dev, DL_ERROR, "%s: Error on request disable proximity scan r=%d\n",
+				__func__, rc);
+		goto exit_release;
+	}
+
+exit_release:
+	pt_release_exclusive(dev);
+
+exit:
+	if (!rc || force) {
+		_pt_unsubscribe_attention(dev, PT_ATTEN_IRQ,
+			PT_PROXIMITY_NAME, pt_proximity_attention,
+			PT_MODE_OPERATIONAL);
+
+		_pt_unsubscribe_attention(dev, PT_ATTEN_STARTUP,
+			PT_PROXIMITY_NAME, pt_startup_attention, 0);
+	}
+
+	pm_runtime_put(dev);
+
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_proximity_enable_show
+ *
+ * SUMMARY: Show method for the prox_enable sysfs node that will show the
+ *	enable_count of proximity
+ *
+ * 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_proximity_enable_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct pt_proximity_data *pd = get_prox_data(dev);
+	int val = 0;
+
+	mutex_lock(&pd->sysfs_lock);
+	val = pd->enable_count;
+	mutex_unlock(&pd->sysfs_lock);
+
+	return scnprintf(buf, PT_MAX_PRBUF_SIZE, "%d\n", val);
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_proximity_enable_store
+ *
+ * SUMMARY: The store method for the prox_enable sysfs node that allows to
+ *  enable or disable proxmity mode.
+ *
+ * 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_proximity_enable_store(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t size)
+{
+	struct pt_proximity_data *pd = get_prox_data(dev);
+	unsigned long value;
+	int rc;
+
+	rc = kstrtoul(buf, 10, &value);
+	if (rc < 0 || (value != 0 && value != 1)) {
+		pt_debug(dev, DL_ERROR, "%s: Invalid value\n", __func__);
+		return -EINVAL;
+	}
+
+	mutex_lock(&pd->sysfs_lock);
+	if (value) {
+		if (pd->enable_count++) {
+			pt_debug(dev, DL_WARN, "%s: '%s' already enabled\n",
+				__func__, pd->input->name);
+		} else {
+			rc = _pt_proximity_enable(pd);
+			if (rc)
+				pd->enable_count--;
+		}
+	} else {
+		if (--pd->enable_count) {
+			if (pd->enable_count < 0) {
+				pt_debug(dev, DL_ERROR, "%s: '%s' unbalanced disable\n",
+					__func__, pd->input->name);
+				pd->enable_count = 0;
+			}
+		} else {
+			rc = _pt_proximity_disable(pd, false);
+			if (rc)
+				pd->enable_count++;
+		}
+	}
+	mutex_unlock(&pd->sysfs_lock);
+
+	if (rc)
+		return rc;
+
+	return size;
+}
+
+static DEVICE_ATTR(prox_enable, 0600,
+		pt_proximity_enable_show,
+		pt_proximity_enable_store);
+
+/*******************************************************************************
+ * FUNCTION: pt_setup_input_device_and_sysfs
+ *
+ * SUMMARY: Create sysnode, set event signal capabilities and register input
+ *  device for proximity.
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *     *dev - pointer to device structure
+ ******************************************************************************/
+static int pt_setup_input_device_and_sysfs(struct device *dev)
+{
+	struct pt_proximity_data *pd = get_prox_data(dev);
+	int signal = PT_IGNORE_VALUE;
+	int i;
+	int rc;
+
+	rc = device_create_file(dev, &dev_attr_prox_enable);
+	if (rc) {
+		pt_debug(dev, DL_ERROR, "%s: Error, could not create enable\n",
+				__func__);
+		goto exit;
+	}
+
+	pt_debug(dev, DL_INFO, "%s: Initialize event signals\n",
+				__func__);
+
+	__set_bit(EV_ABS, pd->input->evbit);
+
+	/* set event signal capabilities */
+	for (i = 0; i < NUM_SIGNALS(pd->pdata->frmwrk); i++) {
+		signal = PARAM_SIGNAL(pd->pdata->frmwrk, i);
+		if (signal != PT_IGNORE_VALUE) {
+			input_set_abs_params(pd->input, signal,
+				PARAM_MIN(pd->pdata->frmwrk, i),
+				PARAM_MAX(pd->pdata->frmwrk, i),
+				PARAM_FUZZ(pd->pdata->frmwrk, i),
+				PARAM_FLAT(pd->pdata->frmwrk, i));
+		}
+	}
+
+	rc = input_register_device(pd->input);
+	if (rc) {
+		pt_debug(dev, DL_ERROR, "%s: Error, failed register input device r=%d\n",
+			__func__, rc);
+		goto unregister_enable;
+	}
+
+	pd->input_device_registered = true;
+	return rc;
+
+unregister_enable:
+	device_remove_file(dev, &dev_attr_prox_enable);
+exit:
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_setup_input_attention
+ *
+ * SUMMARY: Wrapper function for pt_setup_input_device_and_sysfs() that
+ *  subscribe into TTDL attention list.
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *     *dev - pointer to device structure
+ ******************************************************************************/
+static int pt_setup_input_attention(struct device *dev)
+{
+	struct pt_proximity_data *pd = get_prox_data(dev);
+	int rc;
+
+	pd->si = _pt_request_sysinfo(dev);
+	if (!pd->si)
+		return -EINVAL;
+
+	rc = pt_setup_input_device_and_sysfs(dev);
+	if (!rc)
+		rc = _pt_set_proximity(pd, false);
+
+	_pt_unsubscribe_attention(dev, PT_ATTEN_STARTUP,
+		PT_PROXIMITY_NAME, pt_setup_input_attention, 0);
+
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_proximity_probe
+ *
+ * SUMMARY: The probe function for proximity input device
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *	*dev   - pointer to device structure
+ ******************************************************************************/
+int pt_proximity_probe(struct device *dev)
+{
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+	struct pt_proximity_data *pd = &cd->pd;
+	struct pt_platform_data *pdata = dev_get_platdata(dev);
+	struct pt_proximity_platform_data *prox_pdata;
+	int rc = 0;
+
+	if (!pdata ||  !pdata->prox_pdata) {
+		pt_debug(dev, DL_ERROR,
+			"%s: Missing platform data\n", __func__);
+		rc = -ENODEV;
+		goto error_no_pdata;
+	}
+	prox_pdata = pdata->prox_pdata;
+
+	mutex_init(&pd->prox_lock);
+	mutex_init(&pd->sysfs_lock);
+	pd->dev = dev;
+	pd->pdata = prox_pdata;
+
+	/* Create the input device and register it. */
+	pt_debug(dev, DL_INFO,
+		"%s: Create the input device and register it\n", __func__);
+	pd->input = input_allocate_device();
+	if (!pd->input) {
+		pt_debug(dev, DL_ERROR, "%s: Error, failed to allocate input device\n",
+			__func__);
+		rc = -ENODEV;
+		goto error_alloc_failed;
+	} else
+		pd->input_device_allocated = true;
+
+	if (pd->pdata->inp_dev_name)
+		pd->input->name = pd->pdata->inp_dev_name;
+	else
+		pd->input->name = PT_PROXIMITY_NAME;
+	scnprintf(pd->phys, sizeof(pd->phys), "%s/input%d", dev_name(dev),
+			cd->phys_num++);
+	pd->input->phys = pd->phys;
+	pd->input->dev.parent = pd->dev;
+	input_set_drvdata(pd->input, pd);
+
+	/* get sysinfo */
+	pd->si = _pt_request_sysinfo(dev);
+
+	if (pd->si) {
+		rc = pt_setup_input_device_and_sysfs(dev);
+		if (rc)
+			goto error_init_input;
+
+		rc = _pt_set_proximity(pd, false);
+	} else {
+		pt_debug(dev, DL_ERROR, "%s: Fail get sysinfo pointer from core p=%p\n",
+			__func__, pd->si);
+		_pt_subscribe_attention(dev, PT_ATTEN_STARTUP,
+			PT_PROXIMITY_NAME, pt_setup_input_attention,
+			0);
+	}
+
+	return 0;
+
+error_init_input:
+	input_free_device(pd->input);
+	pd->input_device_allocated = false;
+error_alloc_failed:
+error_no_pdata:
+	pt_debug(dev, DL_ERROR, "%s failed.\n", __func__);
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_proximity_release
+ *
+ * SUMMARY: The release function for proximity input device
+ *
+ * RETURN:
+ *	 0 = success
+ *
+ * PARAMETERS:
+ *	*dev   - pointer to device structure
+ ******************************************************************************/
+int pt_proximity_release(struct device *dev)
+{
+	struct pt_proximity_data *pd;
+
+	/* Ensure valid pointers before de-referencing them */
+	if (dev)
+		pd = get_prox_data(dev);
+	else
+		return 0;
+
+	/*
+	 * Second call this function may cause kernel panic if probe fail.
+	 * Use input_device_registered & input_device_allocated variable to
+	 * avoid unregister or free unavailable devive.
+	 */
+	if (pd && pd->input_device_registered) {
+		/* Disable proximity sensing */
+		pd->input_device_registered = false;
+		mutex_lock(&pd->sysfs_lock);
+		if (pd->enable_count)
+			_pt_proximity_disable(pd, true);
+		mutex_unlock(&pd->sysfs_lock);
+		device_remove_file(dev, &dev_attr_prox_enable);
+		input_unregister_device(pd->input);
+		/* Unregistering device will free the device too */
+		pd->input_device_allocated = false;
+	} else if (pd && pd->input_device_allocated) {
+		pd->input_device_allocated = false;
+		input_free_device(pd->input);
+		_pt_unsubscribe_attention(dev, PT_ATTEN_STARTUP,
+			PT_PROXIMITY_NAME, pt_setup_input_attention,
+			0);
+	}
+
+	return 0;
+}
+#endif /* !TTDL_KERNEL_SUBMISSION */

+ 1875 - 0
pt/pt_regs.h

@@ -0,0 +1,1875 @@
+/*
+ * pt_regs.h
+ * Parade TrueTouch(TM) Standard Product Registers.
+ * 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 <[email protected]>
+ *
+ */
+
+#ifndef _PT_REGS_H
+#define _PT_REGS_H
+
+#define PT_PANEL_ID_DEFAULT     0
+
+#define PT_MAX_PATH_SIZE 128
+#define PT_PIP2_BIN_FILE_PATH "/data/ttdl/pt_fw.bin"
+#define PT_SUPPRESS_AUTO_BL 0
+#define PT_ALLOW_AUTO_BL    1
+
+#define PT_PIP2_MAX_FILE_SIZE           0x18000
+#define PT_PIP2_FILE_SECTOR_SIZE        0x1000
+
+#include <linux/device.h>
+#ifdef CONFIG_HAS_EARLYSUSPEND
+#include <linux/earlysuspend.h>
+#elif defined(CONFIG_FB)
+#include <linux/notifier.h>
+#include <linux/fb.h>
+#endif
+
+#include <asm/unaligned.h>
+#include <linux/debugfs.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/gpio.h>
+#include <linux/hid.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/kernel.h>
+#include <linux/limits.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/of.h>
+#include <linux/pm_runtime.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/suspend.h>
+#include <linux/stringify.h>
+#include <linux/types.h>
+#include <linux/uaccess.h>
+#include <linux/workqueue.h>
+#include <linux/version.h>
+#include <linux/pt_core.h>
+
+#include <linux/timer.h>
+#include <linux/timex.h>
+#include <linux/rtc.h>
+
+#define STATUS_SUCCESS   0
+#define STATUS_FAIL     -1
+
+#define PT_FW_FILE_PREFIX	"tt_fw"
+#define PT_FW_FILE_SUFFIX	".bin"
+#define PT_FW_FILE_NAME		"tt_fw.bin"
+#define PT_FW_RAM_FILE_NAME	"tt_fw_ram.bin"
+#ifndef TTDL_KERNEL_SUBMISSION
+/* Enable special TTDL features */
+#ifndef TTHE_TUNER_SUPPORT
+#define TTHE_TUNER_SUPPORT
+#endif
+
+#ifndef TTDL_DIAGNOSTICS
+#define TTDL_DIAGNOSTICS
+#endif
+
+#ifndef EASYWAKE_TSG6
+#define EASYWAKE_TSG6
+#endif
+#endif /* !TTDL_KERNEL_SUBMISSION */
+
+#ifdef TTHE_TUNER_SUPPORT
+#define PT_TTHE_TUNER_FILE_NAME "tthe_tuner"
+#endif
+#define PT_MAX_PRBUF_SIZE       PIPE_BUF
+#define PT_PR_TRUNCATED         " truncated..."
+
+#define PT_DEFAULT_CORE_ID      "pt_core0"
+#define PT_MAX_NUM_CORE_DEVS    5
+#define PT_IRQ_ASSERTED_VALUE	0
+
+#ifdef PT_ENABLE_MAX_ELEN
+#define PT_MAX_ELEN 100
+#endif
+
+/*
+ * The largest PIP message is the PIP2 FILE_WRITE which has:
+ *     2 byte register
+ *     4 byte header
+ *     1 byte file handle
+ *   256 byte payload
+ *     2 byte CRC
+ */
+#define PT_MAX_PIP2_MSG_SIZE      265
+#define PT_MAX_PIP1_MSG_SIZE      255
+#define PT_HID_DESC_SIZE           30
+
+/*
+ * The minimun size of PIP2 packet includes:
+ *     2 byte length
+ *     1 byte sequence
+ *     1 byte command ID
+ *     2 byte CRC
+ */
+#define PT_MIN_PIP2_PACKET_SIZE   6
+
+static const u8 pt_data_block_security_key[] = {
+	0xA5, 0x01, 0x02, 0x03, 0xFF, 0xFE, 0xFD, 0x5A
+};
+
+/* Enum for debug reporting levels */
+enum PT_DEBUG_LEVEL {
+	DL_QUIET	= 0,
+	DL_ERROR	= 1,
+	DL_WARN		= 2,
+	DL_INFO		= 3,
+	DL_DEBUG	= 4,
+	DL_MAX
+};
+#define PT_INITIAL_DEBUG_LEVEL DL_WARN
+
+/* Startup DUT enum status bitmask */
+enum PT_STARTUP_STATUS {
+	STARTUP_STATUS_START              = 0,
+	STARTUP_STATUS_BL_RESET_SENTINEL  = 0x001,
+	STARTUP_STATUS_FW_RESET_SENTINEL  = 0x002,
+	STARTUP_STATUS_GET_DESC           = 0x004,
+	STARTUP_STATUS_FW_OUT_OF_BOOT     = 0x008,
+	STARTUP_STATUS_GET_RPT_DESC       = 0x010,
+	STARTUP_STATUS_GET_SYS_INFO       = 0x020,
+	STARTUP_STATUS_GET_CFG_CRC        = 0x040,
+	STARTUP_STATUS_RESTORE_PARM       = 0x080,
+	STARTUP_STATUS_COMPLETE           = 0x100,
+	STARTUP_STATUS_FULL               = 0x1FF
+};
+
+#define SLAVE_DETECT_MASK                   0x01
+/* TTDL Built In Self Test selection bit masks */
+enum PT_TTDL_BIST_TESTS {
+	PT_BIST_BUS_TEST                  = 0x01,
+	PT_BIST_IRQ_TEST                  = 0x02,
+	PT_BIST_TP_XRES_TEST              = 0x04,
+	PT_BIST_SLAVE_BUS_TEST            = 0x08,
+	PT_BIST_SLAVE_IRQ_TEST            = 0x10,
+	PT_BIST_SLAVE_XRES_TEST           = 0x20
+};
+
+/* tthe_tuner node format options */
+enum PT_TTHE_TUNER_FORMAT {
+	PT_TTHE_TUNER_FORMAT_HID_USB                   = 0x01,
+	PT_TTHE_TUNER_FORMAT_HID_I2C                   = 0x02,
+	PT_TTHE_TUNER_FORMAT_HID_FINGER_TO_PIP         = 0x03,
+	PT_TTHE_TUNER_FORMAT_HID_FINGER_AND_PEN_TO_PIP = 0x04,
+	PT_TTHE_TUNER_FORMAT_RESERVED                  = 0xFE,
+};
+
+#define PT_INITIAL_SHOW_TIME_STAMP 0
+
+/*
+ * Print out all debug prints that are less then or equal to set level.
+ */
+#define pt_debug(dev, dlevel, format, arg...)	 \
+	do { \
+		struct pt_core_data *cd_tmp = dev_get_drvdata(dev);\
+		if (cd_tmp->debug_level >= dlevel) {\
+			if (dlevel == DL_ERROR)\
+				dev_err(dev, "[%d] "format, dlevel, ##arg);\
+			else\
+				dev_info(dev, "[%d] "format, dlevel, ##arg);\
+		} \
+	} while (0)
+
+enum PT_PIP_REPORT_ID {
+	PT_PIP_INVALID_REPORT_ID           = 0x00,
+	PT_PIP_TOUCH_REPORT_ID             = 0x01,
+	PT_PIP_TOUCH_REPORT_WIN8_ID        = 0x02,
+	PT_PIP_CAPSENSE_BTN_REPORT_ID      = 0x03,
+	PT_PIP_WAKEUP_REPORT_ID            = 0x04,
+	PT_PIP_NOISE_METRIC_REPORT_ID      = 0x05,
+	PT_PIP_PUSH_BUTTON_REPORT_ID       = 0x06,
+	PT_PIP_SELFCAP_INPUT_REPORT_ID     = 0x0D,
+	PT_PIP_TRACKING_HEATMAP_REPORT_ID  = 0x0E,
+	PT_PIP_SENSOR_DATA_REPORT_ID       = 0x0F,
+	PT_PIP_NON_HID_RESPONSE_ID         = 0x1F,
+	PT_PIP_NON_HID_COMMAND_ID          = 0x2F,
+	PT_PIP_BL_RESPONSE_REPORT_ID       = 0x30,
+	PT_PIP_BL_COMMAND_REPORT_ID        = 0x40
+};
+
+enum PT_HID_REPORT_ID {
+	PT_HID_FINGER_REPORT_ID            = 0x01,
+	PT_HID_PEN_REPORT_ID               = 0x02,
+	PT_HID_VS_FINGER_REPORT_ID         = 0x41, /* Vendor Specific ID */
+	PT_HID_VS_PEN_REPORT_ID            = 0x42  /* Vendor Specific ID */
+};
+
+
+/* HID IDs and commands */
+#define HID_VENDOR_ID                         0x04B4
+#define HID_APP_PRODUCT_ID                    0xC101
+#define HID_VERSION                           0x0100
+#define HID_REPORT_DESC_ID                      0xF6
+#define HID_APP_REPORT_ID                       0xF7
+#define HID_BL_REPORT_ID                        0xFF
+#define HID_RESPONSE_REPORT_ID                  0xF0
+#define HID_POWER_ON                             0x0
+#define HID_POWER_SLEEP                          0x1
+#define HID_POWER_STANDBY                        0x2
+
+/* PIP1 offsets and masks */
+#define PIP1_RESP_REPORT_ID_OFFSET                 2
+#define PIP1_RESP_COMMAND_ID_OFFSET                4
+#define PIP1_RESP_COMMAND_ID_MASK               0x7F
+#define PIP1_CMD_COMMAND_ID_OFFSET                 6
+#define PIP1_CMD_COMMAND_ID_MASK                0x7F
+
+#define PIP1_SYSINFO_TTDATA_OFFSET                 5
+#define PIP1_SYSINFO_SENSING_OFFSET               33
+#define PIP1_SYSINFO_BTN_OFFSET                   48
+#define PIP1_SYSINFO_BTN_MASK                   0xFF
+#define PIP1_SYSINFO_MAX_BTN                       8
+
+/*  Timeouts in ms */
+#define PT_PTSBC_INIT_WATCHDOG_TIMEOUT         20000
+#define PT_REQUEST_EXCLUSIVE_TIMEOUT            8000
+#define PT_WATCHDOG_TIMEOUT                     2000
+#define PT_FW_EXIT_BOOT_MODE_TIMEOUT            1000
+#define PT_BL_WAIT_FOR_SENTINEL                  500
+#define PT_REQUEST_ENUM_TIMEOUT                 4000
+#define PT_GET_HID_DESCRIPTOR_TIMEOUT            500
+#define PT_HID_GET_REPORT_DESCRIPTOR_TIMEOUT     500
+#define PT_HID_CMD_DEFAULT_TIMEOUT               500
+#define PT_PIP_CMD_DEFAULT_TIMEOUT              2000
+#define PT_PIP1_CMD_DEFAULT_TIMEOUT             1000
+#define PT_PIP1_START_BOOTLOADER_TIMEOUT        2000
+#define PT_PIP1_CMD_GET_SYSINFO_TIMEOUT          500
+#define PT_PIP1_CMD_CALIBRATE_IDAC_TIMEOUT      5000
+#define PT_PIP1_CMD_CALIBRATE_EXT_TIMEOUT       5000
+#define PT_PIP1_CMD_WRITE_CONF_BLOCK_TIMEOUT     400
+#define PT_PIP1_CMD_RUN_SELF_TEST_TIMEOUT      10000
+#define PT_PIP1_CMD_INITIATE_BL_TIMEOUT        20000
+#define PT_PIP1_CMD_PROGRAM_AND_VERIFY_TIMEOUT   400
+#define PT_PIP2_CMD_FILE_ERASE_TIMEOUT          3000
+/*
+ * BL internal timeout is 500 ms and here it is slightly larger to consider
+ * the BUS communication cost. (Bugz#92376)
+ */
+#define PT_PIP2_CMD_FILE_SECTOR_ERASE_TIMEOUT   600
+
+/* Max counts */
+#define PT_WATCHDOG_RETRY_COUNT                   30
+#define PT_BUS_READ_INPUT_RETRY_COUNT              2
+
+#define PT_FLUSH_BUS_BASED_ON_LEN                  0
+#define PT_FLUSH_BUS_FULL_256_READ                 1
+
+/* maximum number of concurrent tracks */
+#define TOUCH_REPORT_SIZE                         10
+#define TOUCH_INPUT_HEADER_SIZE                    7
+#define TOUCH_COUNT_BYTE_OFFSET                    5
+#define BTN_REPORT_SIZE                            9
+#define BTN_INPUT_HEADER_SIZE                      5
+#define SENSOR_REPORT_SIZE                       150
+#define SENSOR_HEADER_SIZE                         4
+#define MAX_TOUCH_NUM                              6
+
+/* helpers */
+#define GET_NUM_TOUCHES(x)          ((x) & 0x1F)
+#define IS_LARGE_AREA(x)            ((x) & 0x20)
+#define IS_BAD_PKT(x)               ((x) & 0x20)
+#define IS_TMO(t)                   ((t) == 0)
+#define HI_BYTE(x)                  (u8)(((x) >> 8) & 0xFF)
+#define LOW_BYTE(x)                 (u8)((x) & 0xFF)
+#define SET_CMD_LOW(byte, bits)	\
+	((byte) = (((byte) & 0xF0) | ((bits) & 0x0F)))
+#define SET_CMD_HIGH(byte, bits)\
+	((byte) = (((byte) & 0x0F) | ((bits) & 0xF0)))
+
+#define GET_MASK(length) \
+	((1 << length) - 1)
+#define GET_FIELD(name, length, shift) \
+	((name >> shift) & GET_MASK(length))
+
+#define _base(x) ((x >= '0' && x <= '9') ? '0' : \
+	(x >= 'a' && x <= 'f') ? 'a' - 10 : \
+	(x >= 'A' && x <= 'F') ? 'A' - 10 : \
+	'\255')
+#define HEXOF(x) (x - _base(x))
+
+#define HID_ITEM_SIZE_MASK	0x03
+#define HID_ITEM_TYPE_MASK	0x0C
+#define HID_ITEM_TAG_MASK	0xF0
+
+#define HID_ITEM_SIZE_SHIFT	0
+#define HID_ITEM_TYPE_SHIFT	2
+#define HID_ITEM_TAG_SHIFT	4
+
+#define HID_GET_ITEM_SIZE(x)  \
+	((x & HID_ITEM_SIZE_MASK) >> HID_ITEM_SIZE_SHIFT)
+#define HID_GET_ITEM_TYPE(x) \
+	((x & HID_ITEM_TYPE_MASK) >> HID_ITEM_TYPE_SHIFT)
+#define HID_GET_ITEM_TAG(x) \
+	((x & HID_ITEM_TAG_MASK) >> HID_ITEM_TAG_SHIFT)
+
+
+#define IS_EASY_WAKE_CONFIGURED(x) \
+		((x) != 0 && (x) != 0xFF)
+
+#define IS_PIP_VER_GE(p, maj, min) \
+		((p)->ttdata.pip_ver_major > (maj) ? \
+			1 : \
+			(((p)->ttdata.pip_ver_major == (maj) ? \
+				((p)->ttdata.pip_ver_minor >= (min) ? \
+				1 : 0) : \
+				0)))
+#define IS_PIP_VER_EQ(p, maj, min) \
+		((p)->ttdata.pip_ver_major == (maj) ? \
+			((p)->ttdata.pip_ver_minor == (min) ? \
+			1 : \
+				0 : \
+				0))
+#define PT_PANEL_ID_BITMASK  0x0000000C
+#define PT_PANEL_ID_SHIFT    2
+
+#define TTDL_PTVIRTDUT_SUPPORT  1
+
+/* DUT Debug commands (dut_debug sysfs) */
+#define PT_DUT_DBG_HID_RESET                  50
+#define PT_DUT_DBG_HID_GET_REPORT             51 /* depricated */
+#define PT_DUT_DBG_HID_SET_REPORT             52 /* depricated */
+#define PT_DUT_DBG_HID_SET_POWER_ON           53
+#define PT_DUT_DBG_HID_SET_POWER_SLEEP        54
+#define PT_DUT_DBG_HID_SET_POWER_STANDBY      55
+#define PT_DUT_DBG_PIP_SOFT_RESET             97
+#define PT_DUT_DBG_RESET                      98
+#define PT_DUT_DBG_PIP_NULL                  100
+#define PT_DUT_DBG_PIP_ENTER_BL              101
+#define PT_DUT_DBG_HID_SYSINFO               102
+#define PT_DUT_DBG_PIP_SUSPEND_SCAN          103
+#define PT_DUT_DBG_PIP_RESUME_SCAN           104
+#define	PT_DUT_DBG_HID_DESC                  109
+
+/* Driver Debug commands (drv_debug sysfs) */
+#define PT_DRV_DBG_SUSPEND                     4
+#define PT_DRV_DBG_RESUME                      5
+#define PT_DRV_DBG_STOP_WD                   105
+#define PT_DRV_DBG_START_WD                  106
+#define PT_DRV_DBG_TTHE_TUNER_EXIT           107
+#define	PT_DRV_DBG_TTHE_BUF_CLEAN            108
+#define	PT_DRV_DBG_CLEAR_PARM_LIST           110
+#define PT_DRV_DBG_FORCE_BUS_READ            111
+#define PT_DRV_DBG_CLEAR_CAL_DATA            112
+#define PT_DUT_DBG_REPORT_DESC               113
+
+/*
+ * Commands that require additional parameters
+ * will be in the 200 range. Commands that do not
+ * require additional parameters remain below 200.
+ */
+#define PT_DRV_DBG_REPORT_LEVEL              200
+#define PT_DRV_DBG_WATCHDOG_INTERVAL         201
+#define PT_DRV_DBG_SHOW_TIMESTAMP            202
+#define PT_DRV_DBG_SET_GENERATION            210
+
+#ifdef TTDL_DIAGNOSTICS
+#define PT_DRV_DBG_FLUSH_BUS                 204 /* deprecated */
+#define PT_DRV_DBG_SETUP_PWR                 205
+#define PT_DRV_DBG_GET_PUT_SYNC              206
+#define PT_DRV_DBG_SET_PIP2_LAUNCH_APP       207 /* deprecated */
+#define PT_DRV_DBG_SET_TT_DATA               208
+#define PT_DRV_DBG_SET_RUN_FW_PIN            209 /* deprecated */
+#define PT_DRV_DBG_SET_BRIDGE_MODE           211
+#define PT_DRV_DBG_SET_I2C_ADDRESS           212
+#define PT_DRV_DBG_SET_FLASHLESS_DUT         213
+#define PT_DRV_DBG_SET_FORCE_SEQ             214
+#define PT_DRV_DBG_BL_WITH_NO_INT            215
+#define PT_DRV_DBG_CAL_CACHE_IN_HOST         216
+#define PT_DRV_DBG_NUM_DEVICES               217
+#define PT_DRV_DBG_SET_PANEL_ID_TYPE         218
+#define PT_DRV_DBG_PIP_TIMEOUT               219
+#define PT_DRV_DBG_CORE_PLATFORM_FLAG        220
+#ifdef TTDL_PTVIRTDUT_SUPPORT
+#define PT_DRV_DBG_SET_HW_DETECT             298
+#define PT_DRV_DBG_VIRTUAL_I2C_DUT           299
+#endif /* TTDL_PTVIRTDUT_SUPPORT */
+
+#define VIRT_MAX_IRQ_RELEASE_TIME_US    500000
+#endif /* TTDL DIAGNOSTICS */
+
+/* Recognized usages */
+/* undef them first for possible redefinition in Linux */
+#undef HID_DI_PRESSURE
+#undef HID_DI_TIP
+#undef HID_DI_CONTACTID
+#undef HID_DI_CONTACTCOUNT
+#undef HID_DI_SCANTIME
+#define HID_DI_PRESSURE                   0x000d0030
+#define HID_DI_TIP                        0x000d0042
+#define HID_DI_CONTACTID                  0x000d0051
+#define HID_DI_CONTACTCOUNT               0x000d0054
+#define HID_DI_SCANTIME                   0x000d0056
+
+/* Parade vendor specific usages */
+#define HID_PT_UNDEFINED                  0xff010000
+#define HID_PT_BOOTLOADER                 0xff010001
+#define HID_PT_TOUCHAPPLICATION           0xff010002
+#define HID_PT_BUTTONS                    0xff010020
+#define HID_PT_GENERICITEM                0xff010030
+#define HID_PT_LARGEOBJECT                0xff010040
+#define HID_PT_NOISEEFFECTS               0xff010041
+#define HID_PT_REPORTCOUNTER              0xff010042
+#define HID_PT_TOUCHTYPE                  0xff010060
+#define HID_PT_EVENTID                    0xff010061
+#define HID_PT_MAJORAXISLENGTH            0xff010062
+#define HID_PT_MINORAXISLENGTH            0xff010063
+#define HID_PT_ORIENTATION                0xff010064
+#define HID_PT_BUTTONSIGNAL               0xff010065
+#define HID_PT_MAJOR_CONTACT_AXIS_LENGTH  0xff010066
+#define HID_PT_MINOR_CONTACT_AXIS_LENGTH  0xff010067
+#define HID_PT_TCH_COL_USAGE_PG           0x000D0004
+#define HID_PT_BTN_COL_USAGE_PG           0xFF010020
+#define HID_PT_PEN_COL_USAGE_PG           0x000D0020
+
+#define PANEL_ID_NOT_ENABLED	0xFF
+
+#ifdef EASYWAKE_TSG6
+#define GESTURE_DOUBLE_TAP         (1)
+#define GESTURE_TWO_FINGERS_SLIDE  (2)
+#define GESTURE_TOUCH_DETECTED     (3)
+#define GESTURE_PUSH_BUTTON        (4)
+#define GESTURE_SINGLE_SLIDE_DE_TX (5)
+#define GESTURE_SINGLE_SLIDE_IN_TX (6)
+#define GESTURE_SINGLE_SLIDE_DE_RX (7)
+#define GESTURE_SINGLE_SLIDE_IN_RX (8)
+#endif
+
+/* FW RAM parameters */
+#define PT_RAM_ID_TOUCHMODE_ENABLED	0x02
+#define PT_RAM_ID_PROXIMITY_ENABLE	0x20
+#define PT_RAM_ID_TOUCHMODE_ENABLED_SIZE	1
+#define PT_RAM_ID_PROXIMITY_ENABLE_SIZE	1
+
+/* abs signal capabilities offsets in the frameworks array */
+enum pt_sig_caps {
+	PT_SIGNAL_OST,
+	PT_MIN_OST,
+	PT_MAX_OST,
+	PT_FUZZ_OST,
+	PT_FLAT_OST,
+	PT_NUM_ABS_SET	/* number of signal capability fields */
+};
+
+/* helpers */
+#define NUM_SIGNALS(frmwrk)		((frmwrk)->size / PT_NUM_ABS_SET)
+#define PARAM(frmwrk, sig_ost, cap_ost) \
+		((frmwrk)->abs[((sig_ost) * PT_NUM_ABS_SET) + (cap_ost)])
+
+#define PARAM_SIGNAL(frmwrk, sig_ost)	PARAM(frmwrk, sig_ost, PT_SIGNAL_OST)
+#define PARAM_MIN(frmwrk, sig_ost)	PARAM(frmwrk, sig_ost, PT_MIN_OST)
+#define PARAM_MAX(frmwrk, sig_ost)	PARAM(frmwrk, sig_ost, PT_MAX_OST)
+#define PARAM_FUZZ(frmwrk, sig_ost)	PARAM(frmwrk, sig_ost, PT_FUZZ_OST)
+#define PARAM_FLAT(frmwrk, sig_ost)	PARAM(frmwrk, sig_ost, PT_FLAT_OST)
+
+/* abs axis signal offsets in the framworks array  */
+enum pt_sig_ost {
+	PT_ABS_X_OST,
+	PT_ABS_Y_OST,
+	PT_ABS_P_OST,
+	PT_ABS_W_OST,
+	PT_ABS_ID_OST,
+	PT_ABS_MAJ_OST,
+	PT_ABS_MIN_OST,
+	PT_ABS_OR_OST,
+	PT_ABS_TOOL_OST,
+	PT_ABS_D_OST,
+	PT_NUM_ABS_OST	/* number of abs signals */
+};
+
+enum hid_command {
+	HID_CMD_RESERVED       = 0x0,
+	HID_CMD_RESET          = 0x1,
+	HID_CMD_GET_REPORT     = 0x2,
+	HID_CMD_SET_REPORT     = 0x3,
+	HID_CMD_GET_IDLE       = 0x4,
+	HID_CMD_SET_IDLE       = 0x5,
+	HID_CMD_GET_PROTOCOL   = 0x6,
+	HID_CMD_SET_PROTOCOL   = 0x7,
+	HID_CMD_SET_POWER      = 0x8,
+	HID_CMD_VENDOR         = 0xE,
+};
+
+enum PIP1_cmd_type {
+	PIP1_CMD_TYPE_FW,
+	PIP1_CMD_TYPE_BL,
+};
+
+/* PIP BL cmd IDs and input for dut_debug sysfs */
+enum pip1_bl_cmd_id {
+	PIP1_BL_CMD_ID_VERIFY_APP_INTEGRITY     = 0x31, /* 49 */
+	PIP1_BL_CMD_ID_GET_INFO                 = 0x38, /* 56 */
+	PIP1_BL_CMD_ID_PROGRAM_AND_VERIFY       = 0x39, /* 57 */
+	PIP1_BL_CMD_ID_LAUNCH_APP               = 0x3B, /* 59 */
+	PIP1_BL_CMD_ID_GET_PANEL_ID             = 0x3E, /* 62 */
+	PIP1_BL_CMD_ID_INITIATE_BL              = 0x48, /* 72 */
+	PIP1_BL_CMD_ID_LAST,
+};
+#define PIP1_BL_SOP	0x1
+#define PIP1_BL_EOP	0x17
+
+/* PIP1 Command/Response IDs */
+enum PIP1_CMD_ID {
+	PIP1_CMD_ID_NULL                        = 0x00,
+	PIP1_CMD_ID_START_BOOTLOADER            = 0x01,
+	PIP1_CMD_ID_GET_SYSINFO                 = 0x02,
+	PIP1_CMD_ID_SUSPEND_SCANNING            = 0x03,
+	PIP1_CMD_ID_RESUME_SCANNING             = 0x04,
+	PIP1_CMD_ID_GET_PARAM                   = 0x05,
+	PIP1_CMD_ID_SET_PARAM                   = 0x06,
+	PIP1_CMD_ID_GET_NOISE_METRICS           = 0x07,
+	PIP1_CMD_ID_RESERVED                    = 0x08,
+	PIP1_CMD_ID_ENTER_EASYWAKE_STATE        = 0x09,
+	PIP1_CMD_ID_VERIFY_CONFIG_BLOCK_CRC     = 0x20,
+	PIP1_CMD_ID_GET_CONFIG_ROW_SIZE         = 0x21,
+	PIP1_CMD_ID_READ_DATA_BLOCK             = 0x22,
+	PIP1_CMD_ID_WRITE_DATA_BLOCK            = 0x23,
+	PIP1_CMD_ID_GET_DATA_STRUCTURE          = 0x24,
+	PIP1_CMD_ID_LOAD_SELF_TEST_PARAM        = 0x25,
+	PIP1_CMD_ID_RUN_SELF_TEST               = 0x26,
+	PIP1_CMD_ID_GET_SELF_TEST_RESULT        = 0x27,
+	PIP1_CMD_ID_CALIBRATE_IDACS             = 0x28,
+	PIP1_CMD_ID_INITIALIZE_BASELINES        = 0x29,
+	PIP1_CMD_ID_EXEC_PANEL_SCAN             = 0x2A,
+	PIP1_CMD_ID_RETRIEVE_PANEL_SCAN         = 0x2B,
+	PIP1_CMD_ID_START_SENSOR_DATA_MODE      = 0x2C,
+	PIP1_CMD_ID_STOP_SENSOR_DATA_MODE       = 0x2D,
+	PIP1_CMD_ID_START_TRACKING_HEATMAP_MODE = 0x2E,
+	PIP1_CMD_ID_START_SELF_CAP_RPT_MODE     = 0x2F,
+	PIP1_CMD_ID_CALIBRATE_DEVICE_EXTENDED   = 0x30,
+	PIP1_CMD_ID_INT_PIN_OVERRIDE            = 0x40,
+	PIP1_CMD_ID_STORE_PANEL_SCAN            = 0x60,
+	PIP1_CMD_ID_PROCESS_PANEL_SCAN          = 0x61,
+	PIP1_CMD_ID_DISCARD_INPUT_REPORT,
+	PIP1_CMD_ID_LAST,
+	PIP1_CMD_ID_USER_CMD,
+};
+
+/* PIP2 Command/Response data and structures */
+enum PIP2_CMD_ID {
+	PIP2_CMD_ID_PING                = 0x00,
+	PIP2_CMD_ID_STATUS              = 0x01,
+	PIP2_CMD_ID_CTRL                = 0x02,
+	PIP2_CMD_ID_CONFIG              = 0x03,
+	PIP2_CMD_ID_RESERVE             = 0x04,
+	PIP2_CMD_ID_CLEAR               = 0x05,
+	PIP2_CMD_ID_RESET               = 0x06,
+	PIP2_CMD_ID_VERSION             = 0x07,
+	PIP2_CMD_ID_FILE_OPEN           = 0x10,
+	PIP2_CMD_ID_FILE_CLOSE          = 0x11,
+	PIP2_CMD_ID_FILE_READ           = 0x12,
+	PIP2_CMD_ID_FILE_WRITE          = 0x13,
+	PIP2_CMD_ID_FILE_IOCTL          = 0x14,
+	PIP2_CMD_ID_FLASH_INFO          = 0x15,
+	PIP2_CMD_ID_EXECUTE             = 0x16,
+	PIP2_CMD_ID_GET_LAST_ERRNO      = 0x17,
+	PIP2_CMD_ID_EXIT_HOST_MODE      = 0x18,
+	PIP2_CMD_ID_READ_GPIO           = 0x19,
+	PIP2_CMD_EXECUTE_SCAN           = 0x21,
+	PIP2_CMD_SET_PARAMETER          = 0x40,
+	PIP2_CMD_GET_PARAMETER          = 0x41,
+	PIP2_CMD_SET_DDI_REG            = 0x42,
+	PIP2_CMD_GET_DDI_REG            = 0x43,
+	PIP2_CMD_ID_END                 = 0x7F
+};
+
+enum PIP2_STATUS_EXEC_RUNNING {
+	PIP2_STATUS_BOOT_EXEC           = 0x00,
+	PIP2_STATUS_APP_EXEC            = 0x01,
+};
+
+/* FW_SYS_MODE_UNDEFINED must be 1 greater than FW_SYS_MODE_MAX */
+enum PIP2_FW_SYSTEM_MODE {
+	FW_SYS_MODE_BOOT                = 0x00,
+	FW_SYS_MODE_SCANNING            = 0x01,
+	FW_SYS_MODE_DEEP_SLEEP          = 0x02,
+	FW_SYS_MODE_TEST                = 0x03,
+	FW_SYS_MODE_DEEP_STANDBY        = 0x04,
+	FW_SYS_MODE_MAX                 = FW_SYS_MODE_DEEP_STANDBY,
+	FW_SYS_MODE_UNDEFINED           = FW_SYS_MODE_MAX + 1,
+};
+
+/* PIP2 Command/Response data and structures */
+enum PIP2_FILE_ID {
+	PIP2_RAM_FILE                   = 0x00,
+	PIP2_FW_FILE                    = 0x01,
+	PIP2_CONFIG_FILE                = 0x02,
+	PIP2_FILE_3                     = 0x03,
+	PIP2_FILE_4                     = 0x04,
+	PIP2_FILE_5                     = 0x05,
+	PIP2_FILE_6                     = 0x06,
+	PIP2_FILE_7                     = 0x07,
+	PIP2_FILE_8                     = 0x08,
+	PIP2_FILE_RESERVED              = 0x0F,
+	PIP2_FILE_MAX                   = PIP2_FILE_RESERVED,
+};
+
+/* Optimize packet sizes per Allwinner H3 bus drivers */
+#define PIP2_FILE_WRITE_LEN_PER_PACKET          (245)
+#define PIP2_BL_I2C_FILE_WRITE_LEN_PER_PACKET   (245)
+#define PIP2_BL_SPI_FILE_WRITE_LEN_PER_PACKET   (256)
+#define PIP2_FILE_WRITE_MAX_LEN_PER_PACKET      \
+				PIP2_BL_SPI_FILE_WRITE_LEN_PER_PACKET
+
+enum DUT_GENERATION {
+	DUT_UNKNOWN                     = 0x00,
+	DUT_PIP1_ONLY                   = 0x01,
+	DUT_PIP2_CAPABLE                = 0x02,
+};
+
+enum PIP2_RSP_ERR {
+	PIP2_RSP_ERR_NONE               = 0x00,
+	PIP2_RSP_ERR_BUSY               = 0x01,
+	PIP2_RSP_ERR_INIT_FAILURE       = 0x02,
+	PIP2_RSP_ERR_ALREADY_OPEN       = 0x03,
+	PIP2_RSP_ERR_NOT_OPEN           = 0x04,
+	PIP2_RSP_ERR_IO_FAILURE         = 0x05,
+	PIP2_RSP_ERR_UNKNOWN_IOCTL      = 0x06,
+	PIP2_RSP_ERR_BAD_ADDRESS        = 0x07,
+	PIP2_RSP_ERR_BAD_FILE           = 0x08,
+	PIP2_RSP_ERR_END_OF_FILE        = 0x09,
+	PIP2_RSP_ERR_TOO_MANY_FILES     = 0x0A,
+	PIP2_RSP_ERR_TIMEOUT            = 0x0B,
+	PIP2_RSP_ERR_ABORTED            = 0x0C,
+	PIP2_RSP_ERR_CRC                = 0x0D,
+	PIP2_RSP_ERR_UNKNOWN_REC_TYPE   = 0x0E,
+	PIP2_RSP_ERR_BAD_FRAME          = 0x0F,
+	PIP2_RSP_ERR_NO_PERMISSION      = 0x10,
+	PIP2_RSP_ERR_UNKNOWN_COMMAND    = 0x11,
+	PIP2_RSP_ERR_INVALID_PARAM      = 0x12,
+	PIP2_RSP_ERR_IO_ALREADY_ACTIVE  = 0x13,
+	PIP2_RSP_ERR_SHUTDOWN           = 0x14,
+	PIP2_RSP_ERR_INVALID_IMAGE      = 0x15,
+	PIP2_RSP_ERR_UNKNOWN_REGISTER   = 0x16,
+	PIP2_RSP_ERR_BAD_LENGTH         = 0x17,
+	PIP2_RSP_ERR_TRIM_FAILURE       = 0x18,
+	PIP2_RSP_ERR_BAD_SEQ            = 0x19,
+	PIP2_RSP_ERR_BUF_TOO_SMALL      = 0x1A,
+	PIP2_RSP_ERR_ASYNC_SEQ          = 0x1B,
+	PIP2_RSP_ERR_EXEC_IMAGE         = 0x1C,
+};
+
+/*
+ * Extra bytes for PIP2 = 4 + 2:
+ * 4 byte header - (len_lsb, len_msb, report ID, Tag, Sequence)
+ * 2 byte footer - (crc_lsb, crc_msb)
+ */
+#define PIP2_CMD_COMMAND_ID_OFFSET           5
+#define PIP2_CMD_COMMAND_ID_MASK          0x7F
+#define PIP2_RESP_COMMAND_ID_OFFSET          3
+#define PIP2_RESP_SEQUENCE_OFFSET            2
+#define PIP2_RESP_SEQUENCE_MASK           0x0F
+#define PIP2_RESP_REPORT_ID_OFFSET           3
+#define PIP2_RESP_STATUS_OFFSET              4
+#define PIP2_RESP_BODY_OFFSET                5
+#define PIP2_CRC_SIZE                        2
+#define PIP2_LEN_FIELD_SIZE                  2
+#define PIP2_VERSION_CHIP_REV_OFFSET        14
+#define PIP2_EXTRA_BYTES_NUM (PIP2_RESP_STATUS_OFFSET + PIP2_CRC_SIZE)
+
+/* File IOCTL commands */
+#define PIP2_FILE_IOCTL_CODE_ERASE_FILE		0
+#define PIP2_FILE_IOCTL_CODE_SEEK_POINTER	1
+#define PIP2_FILE_IOCTL_CODE_AES_CONTROL	2
+#define PIP2_FILE_IOCTL_CODE_FILE_STATS		3
+#define PIP2_FILE_IOCTL_CODE_FILE_CRC		4
+
+struct pip2_cmd_structure {
+	u8 reg[2];
+	u16 len;
+	u8 id;
+	u8 seq;
+	u8 *data;
+	u8 crc[2]; /* MSB:crc[0], LSB:crc[1] */
+};
+
+struct pip2_cmd_response_structure {
+	u8 id;
+	u16 response_len;
+	u32 response_time_min;
+	u32 response_time_max;
+};
+
+enum pip1_bl_status {
+	ERROR_SUCCESS,
+	ERROR_KEY,
+	ERROR_VERIFICATION,
+	ERROR_LENGTH,
+	ERROR_DATA,
+	ERROR_COMMAND,
+	ERROR_CRC                = 8,
+	ERROR_FLASH_ARRAY,
+	ERROR_FLASH_ROW,
+	ERROR_FLASH_PROTECTION,
+	ERROR_UKNOWN             = 15,
+	ERROR_INVALID,
+};
+
+enum pt_mode {
+	PT_MODE_UNKNOWN      = 0,
+	PT_MODE_BOOTLOADER   = 1,
+	PT_MODE_OPERATIONAL  = 2,
+	PT_MODE_IGNORE       = 255,
+};
+
+enum pt_protocol_mode {
+	PT_PROTOCOL_MODE_PIP         = 0,
+	PT_PROTOCOL_MODE_HID         = 1,
+	PT_PROTOCOL_MODE_IGNORE      = 255,
+};
+
+struct pt_dut_status {
+	enum PIP2_FW_SYSTEM_MODE fw_system_mode;
+	enum pt_mode mode;
+	enum pt_protocol_mode protocol_mode;
+} __packed;
+
+enum PT_ENTER_BL_RESULT {
+	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,
+};
+
+enum TTDL_EXTENDED_ERROR_CODES {
+	EX_ERR_FREAD            = 400,
+	EX_ERR_FWRITE           = 401,
+	EX_ERR_FOPEN            = 402,
+	EX_ERR_FCLOSE           = 403,
+	EX_ERR_FLEN             = 404,
+};
+
+enum pt_cmd_status {
+	PT_CMD_STATUS_SUCCESS        = 0,
+	PT_CMD_STATUS_FAILURE        = 1,
+};
+
+enum {
+	PT_IC_GRPNUM_RESERVED,
+	PT_IC_GRPNUM_CMD_REGS,
+	PT_IC_GRPNUM_TCH_REP,
+	PT_IC_GRPNUM_DATA_REC,
+	PT_IC_GRPNUM_TEST_REC,
+	PT_IC_GRPNUM_PCFG_REC,
+	PT_IC_GRPNUM_TCH_PARM_VAL,
+	PT_IC_GRPNUM_TCH_PARM_SIZE,
+	PT_IC_GRPNUM_RESERVED1,
+	PT_IC_GRPNUM_RESERVED2,
+	PT_IC_GRPNUM_OPCFG_REC,
+	PT_IC_GRPNUM_DDATA_REC,
+	PT_IC_GRPNUM_MDATA_REC,
+	PT_IC_GRPNUM_TEST_REGS,
+	PT_IC_GRPNUM_BTN_KEYS,
+	PT_IC_GRPNUM_TTHE_REGS,
+	PT_IC_GRPNUM_SENSING_CONF,
+	PT_IC_GRPNUM_NUM,
+};
+
+enum pt_event_id {
+	PT_EV_NO_EVENT,
+	PT_EV_TOUCHDOWN,
+	PT_EV_MOVE,		/* significant displacement (> act dist) */
+	PT_EV_LIFTOFF,		/* record reports last position */
+};
+
+enum pt_object_id {
+	PT_OBJ_STANDARD_FINGER,
+	PT_OBJ_PROXIMITY,
+	PT_OBJ_STYLUS,
+	PT_OBJ_GLOVE,
+};
+
+enum pt_self_test_result {
+	PT_ST_RESULT_PASS                = 0,
+	PT_ST_RESULT_FAIL                = 1,
+	PT_ST_RESULT_ABORTED             = 2,
+	PT_ST_RESULT_PARAM_ERR           = 3,
+	PT_ST_RESULT_CFG_ERR             = 4,
+	PT_ST_RESULT_CAL_ERR             = 5,
+	PT_ST_RESULT_DDI_STATE_ERR       = 6,
+	PT_ST_RESULT_HOST_MUST_INTERPRET = 0xFF,
+};
+#define PT_ST_PRINT_RESULTS    true
+#define PT_ST_NOPRINT          false
+
+enum pt_st_get_result {
+	PT_ST_DONT_GET_RESULTS          = 0,
+	PT_ST_GET_RESULTS               = 1,
+	PT_ST_GET_RESULTS_BASED_ON_DATA = 2,
+};
+
+/*
+ * Maximum number of parameters for the fw_self_test sysfs (255 - 12 + 2)
+ * 255 - Max PIP message size
+ *  12 - Header size for PIP message 0x25 (Load Self Test Parameters)
+ *   2 - Additional parameters for fw_self_test for test_id and format
+ */
+#define PT_FW_SELF_TEST_MAX_PARM 245
+
+enum pt_self_test_id {
+	PT_ST_ID_NULL                        = 0,
+	PT_ST_ID_BIST                        = 1,
+	PT_ST_ID_SHORTS                      = 2,
+	PT_ST_ID_OPENS                       = 3,
+	PT_ST_ID_AUTOSHORTS                  = 4,
+	PT_ST_ID_CM_PANEL                    = 5,
+	PT_ST_ID_CP_PANEL                    = 6,
+	PT_ST_ID_CM_BUTTON                   = 7,
+	PT_ST_ID_CP_BUTTON                   = 8,
+	PT_ST_ID_FORCE                       = 9,
+	PT_ST_ID_OPENS_HIZ                   = 10,
+	PT_ST_ID_OPENS_GND                   = 11,
+	PT_ST_ID_CP_LFT                      = 12,
+	PT_ST_ID_SC_NOISE                    = 13,
+	PT_ST_ID_LFT_NOISE                   = 14,
+	PT_ST_ID_CP_CHIP_ROUTE_PARASITIC_CAP = 15,
+	PT_ST_ID_NORMALIZED_RAW_CNT_PANEL    = 16,
+	PT_ST_ID_NORMALIZED_RAW_CNT_LFT      = 17,
+	PT_ST_ID_INVALID                     = 255
+};
+
+enum pt_scan_state {
+	PT_SCAN_STATE_UNKNOWN    = 0,
+	PT_SCAN_STATE_ACTIVE     = 1,
+	PT_SCAN_STATE_INACTIVE   = 2,
+};
+
+#define PT_CAL_DATA_MAX_SIZE    2048
+#define PT_CAL_DATA_ROW_SIZE     128
+#define PT_WAFER_LOT_SIZE          5
+#define PT_UID_SIZE               12
+
+enum pt_cal_data_actions {
+	PT_CAL_DATA_SAVE         = 0,
+	PT_CAL_DATA_RESTORE      = 1,
+	PT_CAL_DATA_CLEAR        = 2,
+	PT_CAL_DATA_INFO         = 3
+};
+
+enum pt_feature_enable_state {
+	PT_FEATURE_DISABLE     = 0,
+	PT_FEATURE_ENABLE      = 1
+};
+
+#define PT_NUM_MFGID               8
+/* System Information interface definitions */
+struct pt_ttdata_dev {
+	u8 pip_ver_major;
+	u8 pip_ver_minor;
+	__le16 fw_pid;
+	u8 fw_ver_major;
+	u8 fw_ver_minor;
+	__le32 revctrl;
+	__le16 fw_ver_conf;
+	u8 bl_ver_major;
+	u8 bl_ver_minor;
+	__le16 jtag_si_id_l;
+	__le16 jtag_si_id_h;
+	u8 mfg_id[PT_NUM_MFGID];
+	__le16 post_code;
+} __packed;
+
+/* Struct to cast over PIP2 VERSION response */
+struct pt_pip2_version_full {
+	u8 status_code;
+	u8 pip2_version_lsb;
+	u8 pip2_version_msb;
+	u8 fw_version_lsb;
+	u8 fw_version_msb;
+	u8 bl_version_lsb;
+	u8 bl_version_msb;
+	__le16 chip_rev;
+	__le16 chip_id;
+	u8 uid[PT_UID_SIZE];
+} __packed;
+
+struct pt_pip2_version {
+	u8 status_code;
+	u8 pip2_version_lsb;
+	u8 pip2_version_msb;
+	u8 bl_version_lsb;
+	u8 bl_version_msb;
+	u8 fw_version_lsb;
+	u8 fw_version_msb;
+	__le16 chip_id;
+	__le16 chip_rev;
+} __packed;
+
+struct pt_sensing_conf_data_dev {
+	u8 electrodes_x;
+	u8 electrodes_y;
+	__le16 len_x;
+	__le16 len_y;
+	__le16 res_x;
+	__le16 res_y;
+	__le16 max_z;
+	u8 origin_x;
+	u8 origin_y;
+	u8 panel_id;
+	u8 btn;
+	u8 scan_mode;
+	u8 max_num_of_tch_per_refresh_cycle;
+} __packed;
+
+struct pt_ttdata {
+	u8 pip_ver_major;
+	u8 pip_ver_minor;
+	u8 bl_ver_major;
+	u8 bl_ver_minor;
+	u8 fw_ver_major;
+	u8 fw_ver_minor;
+	u16 fw_pid;
+	u16 fw_ver_conf;
+	u16 post_code;
+	u32 revctrl;
+	u16 jtag_id_l;
+	u16 jtag_id_h;
+	u8 mfg_id[PT_NUM_MFGID];
+	u16 chip_rev;
+	u16 chip_id;
+	u8 uid[PT_UID_SIZE];
+};
+
+struct pt_sensing_conf_data {
+	u16 res_x;
+	u16 res_y;
+	u16 max_z;
+	u16 len_x;
+	u16 len_y;
+	u8 electrodes_x;
+	u8 electrodes_y;
+	u8 origin_x;
+	u8 origin_y;
+	u8 panel_id;
+	u8 btn;
+	u8 scan_mode;
+	u8 max_tch;
+	u8 rx_num;
+	u8 tx_num;
+};
+
+enum pt_tch_abs {	/* for ordering within the extracted touch data array */
+	PT_TCH_X,	/* X */
+	PT_TCH_Y,	/* Y */
+	PT_TCH_P,	/* P (Z) */
+	PT_TCH_T,	/* TOUCH ID */
+	PT_TCH_E,	/* EVENT ID */
+	PT_TCH_O,	/* OBJECT ID */
+	PT_TCH_TIP,	/* OBJECT ID */
+	PT_TCH_MAJ,	/* TOUCH_MAJOR */
+	PT_TCH_MIN,	/* TOUCH_MINOR */
+	PT_TCH_OR,	/* ORIENTATION */
+	PT_TCH_NUM_ABS,
+};
+
+enum pt_tch_hdr {
+	PT_TCH_TIME,	/* SCAN TIME */
+	PT_TCH_NUM,	/* NUMBER OF RECORDS */
+	PT_TCH_LO,	/* LARGE OBJECT */
+	PT_TCH_NOISE,	/* NOISE EFFECT */
+	PT_TCH_COUNTER,	/* REPORT_COUNTER */
+	PT_TCH_NUM_HDR,
+};
+
+static const char * const pt_tch_abs_string[] = {
+	[PT_TCH_X]	= "X",
+	[PT_TCH_Y]	= "Y",
+	[PT_TCH_P]	= "P",
+	[PT_TCH_T]	= "T",
+	[PT_TCH_E]	= "E",
+	[PT_TCH_O]	= "O",
+	[PT_TCH_TIP]	= "TIP",
+	[PT_TCH_MAJ]	= "MAJ",
+	[PT_TCH_MIN]	= "MIN",
+	[PT_TCH_OR]	= "OR",
+	[PT_TCH_NUM_ABS] = "INVALID",
+};
+
+static const char * const pt_tch_hdr_string[] = {
+	[PT_TCH_TIME]	= "SCAN TIME",
+	[PT_TCH_NUM]	= "NUMBER OF RECORDS",
+	[PT_TCH_LO]	= "LARGE OBJECT",
+	[PT_TCH_NOISE]	= "NOISE EFFECT",
+	[PT_TCH_COUNTER] = "REPORT_COUNTER",
+	[PT_TCH_NUM_HDR] = "INVALID",
+};
+
+static const int pt_tch_abs_field_map[] = {
+	[PT_TCH_X]	= 0x00010030 /* HID_GD_X */,
+	[PT_TCH_Y]	= 0x00010031 /* HID_GD_Y */,
+	[PT_TCH_P]	= HID_DI_PRESSURE,
+	[PT_TCH_T]	= HID_DI_CONTACTID,
+	[PT_TCH_E]	= HID_PT_EVENTID,
+	[PT_TCH_O]	= HID_PT_TOUCHTYPE,
+	[PT_TCH_TIP]	= HID_DI_TIP,
+	[PT_TCH_MAJ]	= HID_PT_MAJORAXISLENGTH,
+	[PT_TCH_MIN]	= HID_PT_MINORAXISLENGTH,
+	[PT_TCH_OR]	= HID_PT_ORIENTATION,
+	[PT_TCH_NUM_ABS] = 0,
+};
+
+static const int pt_tch_hdr_field_map[] = {
+	[PT_TCH_TIME]	= HID_DI_SCANTIME,
+	[PT_TCH_NUM]	= HID_DI_CONTACTCOUNT,
+	[PT_TCH_LO]	= HID_PT_LARGEOBJECT,
+	[PT_TCH_NOISE]	= HID_PT_NOISEEFFECTS,
+	[PT_TCH_COUNTER] = HID_PT_REPORTCOUNTER,
+	[PT_TCH_NUM_HDR] = 0,
+};
+
+#define PT_TOUCH_ID_MAX         32
+#define PT_NUM_EXT_TCH_FIELDS   3
+
+struct pt_tch_abs_params {
+	size_t ofs;	/* abs byte offset */
+	size_t size;	/* size in bits */
+	size_t min;	/* min value */
+	size_t max;	/* max value */
+	size_t bofs;	/* bit offset */
+	u8 report;	/* non-zero: valid; 0: invalid */
+	size_t logical_max;	/* logical max value */
+};
+
+struct pt_touch {
+	int hdr[PT_TCH_NUM_HDR];
+	int abs[PT_TCH_NUM_ABS];
+};
+
+enum pt_pen_abs {	/* for ordering within the extracted pen data array */
+	PT_PEN_X,	/* X */
+	PT_PEN_Y,	/* Y */
+	PT_PEN_P,	/* P (Z) */
+	PT_PEN_X_TILT,	/* X TILT */
+	PT_PEN_Y_TILT,	/* Y TILT */
+	PT_PEN_TS,   /* Tip Switch */
+	PT_PEN_BS,   /* Barrel Switch */
+	PT_PEN_IV,   /* Invert */
+	PT_PEN_ER,   /* Eraser */
+	PT_PEN_2ND_BS,  /* 2nd Barrel Switch */
+	PT_PEN_IR,   /* In Range */
+	PT_PEN_NUM_ABS,
+};
+
+static const int pt_pen_abs_field_map[] = {
+	[PT_PEN_X]       = 0x00010030 /* HID_GD_X */,
+	[PT_PEN_Y]       = 0x00010031 /* HID_GD_Y */,
+	[PT_PEN_P]       = 0x000D0030,
+	[PT_PEN_X_TILT]  = 0x000D003D,
+	[PT_PEN_Y_TILT]  = 0x000D003E,
+	[PT_PEN_TS]      = 0x000D0042,
+	[PT_PEN_BS]      = 0x000D0044,
+	[PT_PEN_IV]      = 0x000D003C,
+	[PT_PEN_ER]      = 0x000D0045,
+	[PT_PEN_2ND_BS]  = 0x000D005A,
+	[PT_PEN_IR]      = 0x000D0032,
+	[PT_PEN_NUM_ABS] = 0,
+};
+
+static const char * const pt_pen_abs_string[] = {
+	[PT_PEN_X]       = "X",
+	[PT_PEN_Y]       = "Y",
+	[PT_PEN_P]       = "P",
+	[PT_PEN_X_TILT]  = "X_TILT",
+	[PT_PEN_Y_TILT]  = "Y_TILT",
+	[PT_PEN_TS]      = "Tip_Switch",
+	[PT_PEN_BS]      = "Barrel_Switch",
+	[PT_PEN_IV]      = "Invert",
+	[PT_PEN_ER]      = "Eraser",
+	[PT_PEN_2ND_BS]  = "2nd Barrel_Switch",
+	[PT_PEN_IR]      = "In_Range",
+	[PT_PEN_NUM_ABS] = "INVALID",
+};
+
+struct pt_pen {
+	int abs[PT_PEN_NUM_ABS];
+};
+
+/* button to keycode support */
+#define PT_BITS_PER_BTN		1
+#define PT_NUM_BTN_EVENT_ID	((1 << PT_BITS_PER_BTN) - 1)
+
+enum pt_btn_state {
+	PT_BTN_RELEASED = 0,
+	PT_BTN_PRESSED = 1,
+	PT_BTN_NUM_STATE
+};
+
+struct pt_btn {
+	bool enabled;
+	int state;	/* PT_BTN_PRESSED, PT_BTN_RELEASED */
+	int key_code;
+};
+
+enum pt_ic_ebid {
+	PT_TCH_PARM_EBID  = 0x00,
+	PT_MDATA_EBID     = 0x01,
+	PT_DDATA_EBID     = 0x02,
+	PT_CAL_EBID       = 0xF0,
+};
+
+/* ttconfig block */
+#define PT_TTCONFIG_VERSION_OFFSET	8
+#define PT_TTCONFIG_VERSION_SIZE	2
+#define PT_TTCONFIG_VERSION_ROW		0
+
+struct pt_ttconfig {
+	u16 version;
+	u16 crc;
+};
+
+struct pt_report_desc_data {
+	u16 tch_report_id;
+	u16 tch_record_size;
+	u16 tch_header_size;
+	u16 btn_report_id;
+	u16 pen_report_id;
+	u8 max_touch_num;
+	u8 max_tch_per_packet;
+};
+
+struct pt_sysinfo {
+	bool ready;
+	struct pt_ttdata ttdata;
+	struct pt_sensing_conf_data sensing_conf_data;
+	struct pt_report_desc_data desc;
+	int num_btns;
+	struct pt_btn *btn;
+	struct pt_ttconfig ttconfig;
+	struct pt_tch_abs_params tch_hdr[PT_TCH_NUM_HDR];
+	struct pt_tch_abs_params tch_abs[PT_TCH_NUM_ABS];
+	struct pt_tch_abs_params pen_abs[PT_PEN_NUM_ABS];
+	u8 *xy_mode;
+	u8 *xy_data;
+};
+
+struct pt_bl_info {
+	bool ready;
+	u16 chip_id;
+};
+
+enum pt_atten_type {
+	PT_ATTEN_IRQ,
+	PT_ATTEN_STARTUP,
+	PT_ATTEN_EXCLUSIVE,
+	PT_ATTEN_WAKE,
+	PT_ATTEN_LOADER,
+	PT_ATTEN_SUSPEND,
+	PT_ATTEN_RESUME,
+	PT_ATTEN_CANCEL_LOADER,
+	PT_ATTEN_NUM_ATTEN,
+};
+
+enum pt_sleep_state {
+	SS_SLEEP_OFF,
+	SS_SLEEP_ON,
+	SS_SLEEPING,
+	SS_WAKING,
+};
+
+enum pt_fb_state {
+	FB_ON,
+	FB_OFF,
+};
+
+enum pt_startup_state {
+	STARTUP_NONE,
+	STARTUP_QUEUED,
+	STARTUP_RUNNING,
+	STARTUP_ILLEGAL,
+};
+
+struct pt_hid_desc {
+	__le16 hid_desc_len;
+	u8 packet_id;
+	u8 reserved_byte;
+	__le16 bcd_version;
+	__le16 report_desc_len;
+	__le16 report_desc_register;
+	__le16 input_register;
+	__le16 max_input_len;
+	__le16 output_register;
+	__le16 max_output_len;
+	__le16 command_register;
+	__le16 data_register;
+	__le16 vendor_id;
+	__le16 product_id;
+	__le16 version_id;
+	u8 reserved[4];
+} __packed;
+
+struct pt_hid_core {
+	u16 hid_vendor_id;
+	u16 hid_product_id;
+	__le16 hid_desc_register;
+	u16 hid_report_desc_len;
+	u16 hid_max_input_len;
+	u16 hid_max_output_len;
+};
+
+#define PT_HID_MAX_REPORTS                   20
+#define PT_HID_MAX_FIELDS                    128
+#define PT_HID_MAX_COLLECTIONS               3
+#define PT_HID_MAX_NESTED_COLLECTIONS        PT_HID_MAX_COLLECTIONS
+#define PT_HID_MAX_CONTINUOUS_USAGES         8
+
+/* Max input is for ASCII representation of hex characters */
+#define PT_MAX_INPUT            2048
+#define PT_PIP_1P7_EMPTY_BUF    0xFF00
+
+enum pt_module_id {
+	PT_MODULE_MT,
+	PT_MODULE_BTN,
+	PT_MODULE_PROX,
+	PT_MODULE_LAST,
+};
+
+struct pt_mt_data;
+struct pt_mt_function {
+	int (*mt_release)(struct device *dev);
+	int (*mt_probe)(struct device *dev, struct pt_mt_data *md);
+	void (*report_slot_liftoff)(struct pt_mt_data *md, int max_slots);
+	void (*input_sync)(struct input_dev *input);
+	void (*input_report)(struct input_dev *input, int sig, int t, int type);
+	void (*final_sync)(struct input_dev *input, int max_slots,
+			int mt_sync_count, unsigned long *ids);
+	int (*input_register_device)(struct input_dev *input, int max_slots);
+};
+
+struct pt_mt_data {
+	struct device *dev;
+	struct pt_mt_platform_data *pdata;
+	struct pt_sysinfo *si;
+	struct input_dev *input;
+	struct pt_mt_function mt_function;
+	struct mutex mt_lock;
+	bool is_suspended;
+	bool input_device_registered;
+	bool input_device_allocated;
+	char phys[NAME_MAX];
+	int num_prv_rec;
+	int or_min;
+	int or_max;
+	int t_min;
+	int t_max;
+};
+
+struct pt_pen_data {
+	struct device *dev;
+	struct pt_pen_platform_data *pdata;
+	struct pt_sysinfo *si;
+	struct input_dev *input;
+	struct mutex pen_lock;
+	bool is_suspended;
+	bool input_device_registered;
+	bool input_device_allocated;
+	char phys[NAME_MAX];
+};
+
+struct pt_btn_data {
+	struct device *dev;
+	struct pt_btn_platform_data *pdata;
+	struct pt_sysinfo *si;
+	struct input_dev *input;
+	struct mutex btn_lock;
+	bool is_suspended;
+	bool input_device_registered;
+	bool input_device_allocated;
+	char phys[NAME_MAX];
+};
+
+struct pt_proximity_data {
+	struct device *dev;
+	struct pt_proximity_platform_data *pdata;
+	struct pt_sysinfo *si;
+	struct input_dev *input;
+	struct mutex prox_lock;
+	struct mutex sysfs_lock;
+	int enable_count;
+	bool input_device_registered;
+	bool input_device_allocated;
+	char phys[NAME_MAX];
+};
+
+enum pt_calibrate_idacs_sensing_mode {
+	PT_CI_SM_MUTCAP_FINE,
+	PT_CI_SM_MUTCAP_BUTTON,
+	PT_CI_SM_SELFCAP,
+};
+
+enum pt_initialize_baselines_sensing_mode {
+	PT_IB_SM_MUTCAP = 1,
+	PT_IB_SM_BUTTON = 2,
+	PT_IB_SM_SELFCAP = 4,
+	PT_IB_SM_BALANCED = 8,
+};
+
+/* parameters for extended calibrate command(0x30)*/
+struct pt_cal_ext_data {
+	u8 mode;
+	u8 data0;
+	u8 data1;
+	u8 data2;
+} __packed;
+#define PT_CAL_EXT_MODE_UNDEFINED  0xFF
+
+#define PT_BIN_FILE_MIN_HDR_LENGTH 14
+#define PT_BIN_FILE_MAX_HDR_LENGTH 18
+struct pt_bin_file_hdr {
+	u8 length;
+	u16 ttpid;
+	u8  fw_major;
+	u8  fw_minor;
+	u32 fw_rev_ctrl;
+	u32 fw_crc;
+	u16 si_rev;
+	u16 si_id;
+	u16 config_ver;
+	u32 hex_file_size;
+};
+
+struct pt_core_nonhid_cmd {
+	int (*start_bl)(struct device *dev, int protect);
+	int (*suspend_scanning)(struct device *dev, int protect);
+	int (*resume_scanning)(struct device *dev, int protect);
+	int (*get_param)(struct device *dev, int protect, u8 param_id,
+			u32 *value);
+	int (*set_param)(struct device *dev, int protect, u8 param_id,
+			u32 value, u8 size);
+	int (*verify_cfg_block_crc)(struct device *dev, int protect,
+			u8 ebid, u8 *status, u16 *calculated_crc,
+			u16 *stored_crc);
+	int (*get_config_row_size)(struct device *dev, int protect,
+			u16 *row_size);
+	int (*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);
+	int (*run_selftest)(struct device *dev, int protect, u8 test_id,
+		u8 write_idacs_to_flash, u8 *status, u8 *summary_result,
+		u8 *results_available);
+	int (*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);
+	int (*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);
+	int (*calibrate_idacs)(struct device *dev, int protect, u8 mode,
+			u8 *status);
+	int (*calibrate_ext)(struct device *dev,
+		int protect, struct pt_cal_ext_data *cal_data, u8 *status);
+	int (*initialize_baselines)(struct device *dev, int protect,
+			u8 test_id, u8 *status);
+	int (*exec_panel_scan)(struct device *dev, int protect, u8 scan_type);
+	int (*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);
+	int (*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);
+	int (*write_data_block)(struct device *dev, u16 row_number,
+			u16 write_length, u8 ebid, u8 *write_buf,
+			u8 *security_key, u16 *actual_write_len);
+	int (*user_cmd)(struct device *dev, int protect, u16 read_len,
+			u8 *read_buf, u16 write_len, u8 *write_buf,
+			u16 *actual_read_len);
+	int (*get_bl_info)(struct device *dev, int protect, u8 *return_data);
+	int (*initiate_bl)(struct device *dev, int protect, u16 key_size,
+			u8 *key_buf, u16 row_size, u8 *metadata_row_buf);
+	int (*launch_app)(struct device *dev, int protect);
+	int (*prog_and_verify)(struct device *dev, int protect, u16 data_len,
+			u8 *data_buf);
+	int (*verify_app_integrity)(struct device *dev, int protect,
+			u8 *result);
+	int (*get_panel_id)(struct device *dev, int protect, u8 *panel_id);
+	int (*pip2_send_cmd)(struct device *dev, int protect,
+			u8 id, u8 *data, u16 report_body_len, u8 *read_buf,
+			u16 *actual_read_len);
+	int (*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 (*get_bl_pip2_version)(struct device *dev);
+	int (*pip2_file_open)(struct device *dev, u8 file_no);
+	int (*pip2_file_close)(struct device *dev, u8 file_no);
+	int (*pip2_file_erase)(struct device *dev, u8 file_no, u16 file_sector,
+			int *status);
+	int (*read_us_file)(struct device *dev, u8 *file_path, u8 *buf,
+			int *size);
+	int (*pip2_file_read)(struct device *dev, u8 file_no,
+			u16 num_bytes, u8 *read_buf);
+	int (*pip2_file_seek_offset)(struct device *dev, u8 file_no,
+			u32 read_offset, u32 write_offset);
+	int (*pip2_file_get_stats)(struct device *dev, u8 file_no,
+			u32 *address, u32 *file_size);
+	int (*pip2_file_crc)(struct device *dev, u8 file_no,
+			u32 offset, u32 length, u8 *read_buf);
+	int (*manage_cal_data)(struct device *dev, u8 action, u16 *size,
+			unsigned short *crc);
+	unsigned short (*calc_crc)(unsigned char *q, int len);
+};
+
+typedef int (*pt_atten_func) (struct device *);
+
+struct pt_core_commands {
+	int (*subscribe_attention)(struct device *dev,
+			enum pt_atten_type type, char *id,
+			pt_atten_func func, int flags);
+	int (*unsubscribe_attention)(struct device *dev,
+			enum pt_atten_type type, char *id,
+			pt_atten_func func, int flags);
+	int (*request_exclusive)(struct device *dev, int timeout_ms);
+	int (*release_exclusive)(struct device *dev);
+	int (*request_reset)(struct device *dev, int protect);
+	int (*request_pip2_launch_app)(struct device *dev, int protect);
+	int (*request_enum)(struct device *dev, bool wait);
+	struct pt_sysinfo * (*request_sysinfo)(struct device *dev);
+	struct pt_loader_platform_data
+		*(*request_loader_pdata)(struct device *dev);
+	int (*request_stop_wd)(struct device *dev);
+	int (*request_start_wd)(struct device *dev);
+	int (*request_get_mode)(struct device *dev, int protect, u8 *mode);
+	int (*request_pip2_get_mode_sysmode)(struct device *dev, int protect,
+		u8 *mode, u8 *sys_mode);
+	int (*request_active_pip_prot)(struct device *dev, int protect,
+		u8 *pip_version_major, u8 *pip_version_minor);
+	int (*request_enable_scan_type)(struct device *dev, u8 scan_type);
+	int (*request_disable_scan_type)(struct device *dev, u8 scan_type);
+	int (*request_pip2_enter_bl)(struct device *dev, u8 *start_mode,
+		int *result);
+	int (*request_pip2_bin_hdr)(struct device *dev,
+		struct pt_bin_file_hdr *hdr);
+	int (*request_dut_generation)(struct device *dev);
+	int (*request_hw_version)(struct device *dev, char *hw_version);
+#ifndef TTDL_KERNEL_SUBMISSION
+	int (*parse_sysfs_input)(struct device *dev,
+		const char *buf, size_t buf_size,
+		u32 *out_buf, size_t out_buf_size);
+#endif
+#ifdef TTHE_TUNER_SUPPORT
+	int (*request_tthe_print)(struct device *dev, u8 *buf, int buf_len,
+			const u8 *data_name);
+#endif
+#ifdef TTDL_DIAGNOSTICS
+	void (*request_toggle_err_gpio)(struct device *dev, u8 type);
+#endif
+	struct pt_core_nonhid_cmd *nonhid_cmd;
+	int (*request_get_fw_mode)(struct device *dev, int protect,
+		u8 *sys_mode, u8 *mode);
+};
+
+enum core_command_protected_status {
+	PT_CORE_CMD_UNPROTECTED = 0,
+	PT_CORE_CMD_PROTECTED = 1
+};
+
+enum pt_err_gpio_type {
+	PT_ERR_GPIO_NONE             = 0,
+	PT_ERR_GPIO_I2C_TRANS        = 1,
+	PT_ERR_GPIO_IRQ_STUCK        = 2,
+	PT_ERR_GPIO_EXCLUSIVE_ACCESS = 3,
+	PT_ERR_GPIO_EMPTY_PACKET     = 4,
+	PT_ERR_GPIO_BL_RETRY_PACKET  = 5,
+	PT_ERR_GPIO_MAX_TYPE         = PT_ERR_GPIO_BL_RETRY_PACKET,
+};
+
+struct pt_features {
+	uint8_t easywake;
+	uint8_t noise_metric;
+	uint8_t tracking_heatmap;
+	uint8_t sensor_data;
+};
+
+#if defined(CONFIG_PM_SLEEP) && defined(CONFIG_PM_RUNTIME)
+#if (KERNEL_VERSION(3, 3, 0) > LINUX_VERSION_CODE)
+#define NEED_SUSPEND_NOTIFIER
+#endif /* CONFIG_PM_SLEEP && CONFIG_PM_RUNTIME */
+#endif /* LINUX_VERSION_CODE */
+
+struct pt_module {
+	struct list_head node;
+	char *name;
+	int (*probe)(struct device *dev, void **data);
+	void (*release)(struct device *dev, void *data);
+};
+
+struct pt_bus_ops {
+	u16 bustype;
+	int (*read_default)(struct device *dev, void *buf, int size);
+	int (*read_default_nosize)(struct device *dev, u8 *buf, u32 max);
+	int (*write_read_specific)(struct device *dev, u16 write_len,
+			u8 *write_buf, u8 *read_buf, u16 read_len);
+};
+
+#define PT_MAX_DEVICES   3
+struct pt_bist_data {
+	u8 detected;
+	u8 mask;
+	u8 boot_err;
+	u8 bus_toggled;
+	u8 irq_toggled;
+	u8 xres_toggled;
+	char *bus_err_str;
+	char *irq_err_str;
+	char *xres_err_str;
+	char *print_buf;
+	u16 pr_index;
+	int status;
+};
+
+struct pt_core_data {
+	struct list_head node;
+	struct list_head module_list; /* List of probed modules */
+	char core_id[20];
+	struct device *dev;
+	struct list_head atten_list[PT_ATTEN_NUM_ATTEN];
+	struct list_head param_list;
+	struct mutex module_list_lock;
+	struct mutex system_lock;
+	struct mutex sysfs_lock;
+	struct mutex ttdl_restart_lock;
+	struct mutex firmware_class_lock;
+	struct mutex hid_report_lock;
+	enum pt_mode mode;
+	spinlock_t spinlock;
+	struct pt_mt_data md;
+	struct pt_pen_data pend;
+	struct pt_btn_data bd;
+	struct pt_proximity_data pd;
+	int phys_num;
+	int pip_cmd_timeout;
+	int pip_cmd_timeout_default;
+	void *pt_dynamic_data[PT_MODULE_LAST];
+	struct pt_platform_data *pdata;
+	struct pt_core_platform_data *cpdata;
+	const struct pt_bus_ops *bus_ops;
+	wait_queue_head_t wait_q;
+	enum pt_sleep_state sleep_state;
+	enum pt_startup_state startup_state;
+	int irq;
+	bool irq_enabled;
+	bool irq_wake;
+	bool irq_disabled;
+	bool hw_detected;
+	u8 easy_wakeup_gesture;
+#ifdef EASYWAKE_TSG6
+	u8 gesture_id;
+	u8 gesture_data_length;
+	u8 gesture_data[80];
+#endif
+	bool wait_until_wake;
+	u8 pid_for_loader;
+	char hw_version[13];
+#ifdef NEED_SUSPEND_NOTIFIER
+	/*
+	 * This notifier is used to receive suspend prepare events
+	 * When device is PM runtime suspended, pm_generic_suspend()
+	 * does not call our PM suspend callback for kernels with
+	 * version less than 3.3.0.
+	 */
+	struct notifier_block pm_notifier;
+#endif
+	struct pt_sysinfo sysinfo;
+	struct pt_bl_info bl_info;
+	struct pt_dut_status dut_status;
+	void *exclusive_dev;
+	int exclusive_waits;
+	struct timer_list watchdog_timer;
+	struct work_struct watchdog_work;
+	struct work_struct enum_work;
+	struct work_struct ttdl_restart_work;
+#ifdef PT_PTSBC_SUPPORT
+	struct work_struct irq_work;
+	struct work_struct probe_work;
+	struct timer_list probe_timer;
+#endif
+	u16 startup_retry_count;
+	struct pt_hid_core hid_core;
+	int hid_cmd_state;
+	int hid_reset_cmd_state; /* reset can happen any time */
+	struct pt_hid_desc hid_desc;
+	struct pt_hid_report *hid_reports[PT_HID_MAX_REPORTS];
+	int num_hid_reports;
+	struct pt_features features;
+#define PT_PREALLOCATED_CMD_BUFFER 32
+	u8 cmd_buf[PT_PREALLOCATED_CMD_BUFFER];
+	u8 input_buf[PT_MAX_INPUT];
+	u8 response_buf[PT_MAX_INPUT];
+	u8 cmd_rsp_buf[PT_MAX_INPUT];
+	u8 touch_buf[PT_MAX_INPUT];
+	u16 cmd_rsp_buf_len;
+	int raw_cmd_status;
+#ifdef CONFIG_HAS_EARLYSUSPEND
+	struct early_suspend es;
+#elif defined(CONFIG_FB)
+	struct notifier_block fb_notifier;
+	enum pt_fb_state fb_state;
+#endif
+#ifdef TTHE_TUNER_SUPPORT
+	struct dentry *tthe_debugfs;
+	u8 *tthe_buf;
+	u32 tthe_buf_len;
+	u32 tthe_buf_size;
+	struct mutex tthe_lock;
+	u8 tthe_exit;
+#endif
+	u8 debug_level;
+	u8 watchdog_enabled;
+	bool watchdog_force_stop;
+	u32 watchdog_interval;
+	u8 show_timestamp;
+	u32 startup_status;
+	u8 pip2_cmd_tag_seq;
+	u8 pip2_prot_active;
+	u8 pip2_send_user_cmd;
+	u8 get_param_id;
+	bool bl_pip_ver_ready;
+	bool app_pip_ver_ready;
+	u8 core_probe_complete;
+	u8 active_dut_generation;
+	bool set_dut_generation;
+	u8 fw_system_mode;
+	u8 flashless_dut;
+	u8 bl_with_no_int;
+	u8 cal_cache_in_host;
+	u8 num_devices;
+	u8 tthe_hid_usb_format;
+	u8 flashless_auto_bl;
+	u8 pip2_us_file_path[PT_MAX_PATH_SIZE];
+	bool fw_updating;
+	bool fw_sys_mode_in_standby_state;
+#ifdef TTDL_PTVIRTDUT_SUPPORT
+	u8 route_bus_virt_dut;
+#endif /* TTDL_PTVIRTDUT_SUPPORT */
+	u8 panel_id_support;
+#ifdef TTDL_DIAGNOSTICS
+	u8 t_refresh_active;
+	u8 flush_bus_type;
+	u8 ttdl_bist_select;
+	u8 force_pip2_seq;
+	u16 ping_test_size;
+	u16 pip2_crc_error_count;
+	u16 t_refresh_count;
+	u16 t_refresh_total;
+	u16 wd_xres_count;
+	u32 watchdog_count;
+	u32 watchdog_irq_stuck_count;
+	u32 watchdog_failed_access_count;
+	u32 bus_transmit_error_count;
+	u32 irq_count;
+	u32 bl_retry_packet_count;
+	u32 file_erase_timeout_count;
+	unsigned long t_refresh_time;
+	u16 err_gpio;
+	u16 err_gpio_type;
+	bool show_tt_data;
+	bool bridge_mode;
+	bool hw_detect_enabled;
+#endif
+};
+
+struct gd_sensor {
+	int32_t cm_min;
+	int32_t cm_max;
+	int32_t cm_ave;
+	int32_t cm_min_exclude_edge;
+	int32_t cm_max_exclude_edge;
+	int32_t cm_ave_exclude_edge;
+	int32_t gradient_val;
+};
+
+#ifdef TTHE_TUNER_SUPPORT
+#define PT_CMD_RET_PANEL_IN_DATA_OFFSET	0
+#define PT_CMD_RET_PANEL_ELMNT_SZ_MASK	0x07
+#define PT_CMD_RET_PANEL_HDR		0x0A
+#define PT_CMD_RET_PANEL_ELMNT_SZ_MAX	0x2
+
+enum scan_data_type_list {
+	PT_MUT_RAW,
+	PT_MUT_BASE,
+	PT_MUT_DIFF,
+	PT_SELF_RAW,
+	PT_SELF_BASE,
+	PT_SELF_DIFF,
+	PT_BAL_RAW,
+	PT_BAL_BASE,
+	PT_BAL_DIFF,
+};
+#endif
+
+static inline int pt_adap_read_default(struct pt_core_data *cd,
+		void *buf, int size)
+{
+	return cd->bus_ops->read_default(cd->dev, buf, size);
+}
+
+static inline int pt_adap_read_default_nosize(struct pt_core_data *cd,
+		void *buf, int max)
+{
+	return cd->bus_ops->read_default_nosize(cd->dev, buf, max);
+}
+
+static inline int pt_adap_write_read_specific(struct pt_core_data *cd,
+		u16 write_len, u8 *write_buf, u8 *read_buf, u16 read_len)
+{
+	return cd->bus_ops->write_read_specific(cd->dev, write_len, write_buf,
+			read_buf, read_len);
+}
+
+static inline void *pt_get_dynamic_data(struct device *dev, int id)
+{
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+
+	return cd->pt_dynamic_data[id];
+}
+
+int request_exclusive(struct pt_core_data *cd, void *ownptr,
+		int timeout_ms);
+int release_exclusive(struct pt_core_data *cd, void *ownptr);
+int _pt_request_pip_get_param(struct device *dev,
+		int protect, u8 param_id, u32 *value);
+int _pt_request_pip_set_param(struct device *dev,
+		int protect, u8 param_id, u32 value, u8 size);
+
+static inline int pt_request_exclusive(struct device *dev, int timeout_ms)
+{
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+
+	return request_exclusive(cd, dev, timeout_ms);
+}
+
+static inline int pt_release_exclusive(struct device *dev)
+{
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+
+	return release_exclusive(cd, dev);
+}
+
+static inline int pt_request_nonhid_get_param(struct device *dev,
+		int protect, u8 param_id, u32 *value)
+{
+	return _pt_request_pip_get_param(dev, protect, param_id,
+			value);
+}
+
+static inline int pt_request_nonhid_set_param(struct device *dev,
+		int protect, u8 param_id, u32 value, u8 size)
+{
+	return _pt_request_pip_set_param(dev, protect, param_id,
+			value, size);
+}
+
+void pt_pr_buf(struct device *dev, u8 debug_level, u8 *buf,
+	u16 buf_len, const char *data_name);
+
+#ifdef CONFIG_TOUCHSCREEN_PARADE_DEVICETREE_SUPPORT
+int pt_devtree_create_and_get_pdata(struct device *adap_dev);
+int pt_devtree_clean_pdata(struct device *adap_dev);
+#else
+static inline int pt_devtree_create_and_get_pdata(struct device *adap_dev)
+{
+	return 0;
+}
+
+static inline int pt_devtree_clean_pdata(struct device *adap_dev)
+{
+	return 0;
+}
+#endif
+
+int pt_probe(const struct pt_bus_ops *ops, struct device *dev,
+		u16 irq, size_t xfer_buf_size);
+int pt_release(struct pt_core_data *cd);
+
+struct pt_core_commands *pt_get_commands(void);
+struct pt_core_data *pt_get_core_data(char *id);
+
+#ifdef PT_AUX_BRIDGE_ENABLED
+int pt_trigger_ttdl_irq(void);
+#endif
+
+int pt_mt_release(struct device *dev);
+int pt_mt_probe(struct device *dev);
+
+#ifdef CONFIG_TOUCHSCREEN_PARADE_BUTTON
+int pt_btn_probe(struct device *dev);
+int pt_btn_release(struct device *dev);
+#else
+static inline int pt_btn_probe(struct device *dev) { return 0; }
+static inline int pt_btn_release(struct device *dev) { return 0; }
+#endif
+
+#ifdef CONFIG_TOUCHSCREEN_PARADE_PROXIMITY
+int pt_proximity_probe(struct device *dev);
+int pt_proximity_release(struct device *dev);
+#else
+static inline int pt_proximity_probe(struct device *dev) { return 0; }
+static inline int pt_proximity_release(struct device *dev) { return 0; }
+#endif
+
+static inline unsigned int pt_get_time_stamp(void)
+{
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 0, 0))
+	struct timespec64 ts;
+
+	ktime_get_real_ts64(&ts);
+	return (ts.tv_sec*1000 + ts.tv_nsec/1000000);
+#else
+	struct timeval tv;
+
+	do_gettimeofday(&tv);
+	return (tv.tv_sec*1000 + tv.tv_usec/1000);
+#endif
+}
+
+void pt_init_function_ptrs(struct pt_mt_data *md);
+void pt_get_touch_field(struct device *dev,
+	int *field, int size, int max, u8 *data, int bofs);
+int _pt_subscribe_attention(struct device *dev,
+	enum pt_atten_type type, char *id, int (*func)(struct device *),
+	int mode);
+int _pt_unsubscribe_attention(struct device *dev,
+	enum pt_atten_type type, char *id, int (*func)(struct device *),
+	int mode);
+struct pt_sysinfo *_pt_request_sysinfo(struct device *dev);
+
+extern const struct dev_pm_ops pt_pm_ops;
+
+int pt_register_module(struct pt_module *module);
+void pt_unregister_module(struct pt_module *module);
+
+void *pt_get_module_data(struct device *dev,
+		struct pt_module *module);
+
+#endif /* _PT_REGS_H */

+ 657 - 0
pt/pt_spi.c

@@ -0,0 +1,657 @@
+/*
+ * pt_spi.c
+ * Parade TrueTouch(TM) Standard Product SPI 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 <[email protected]>
+ */
+
+#include "pt_regs.h"
+
+#include <linux/spi/spi.h>
+#include <linux/version.h>
+
+/* TC3315 - DUT Address (0x24 & 0x07) << 1 = 0x08 for write and 0x09 for read */
+#define PT_SPI_WR_OP		0x08 /* r/~w */
+#define PT_SPI_RD_OP		0x09
+#define PT_SPI_BITS_PER_WORD	8
+#define PT_SPI_SYNC_ACK         0x62
+
+#define PT_SPI_CMD_BYTES	0
+#define PT_SPI_DATA_SIZE	(2 * 256)
+#define PT_SPI_DATA_BUF_SIZE	(PT_SPI_CMD_BYTES + PT_SPI_DATA_SIZE)
+
+#define PT_SPI_OP_SIZE     (1)
+#define PT_SPI_DUMMY_READ  (1)
+#define PT_SPI_BUFFER_SIZE  \
+	(PT_MAX_PIP2_MSG_SIZE + PT_SPI_OP_SIZE + PT_SPI_DUMMY_READ)
+
+static u8 *tmp_rbuf;
+static u8 *tmp_wbuf;
+DEFINE_MUTEX(pt_spi_bus_lock);
+
+#ifdef TTDL_PTVIRTDUT_SUPPORT
+#define VIRT_DUT_BUF_SIZE  300
+static unsigned char pt_dut_cmd_buf[VIRT_DUT_BUF_SIZE];
+static unsigned char pt_dut_out_buf[VIRT_DUT_BUF_SIZE];
+static int pt_dut_cmd_len;
+static int pt_dut_out_len;
+DEFINE_MUTEX(virt_spi_lock);
+
+/*******************************************************************************
+ * FUNCTION: virt_spi_transfer
+ *
+ * SUMMARY: Copies the current spi output message to the temporary buffer
+ *	used by the dut_cmd sysfs node
+ *
+ * RETURN VALUE:
+ *	Number of messages transferred which in this function will be 1
+ *
+ * PARAMETERS:
+ *      *buf - pointer to spi command
+ *	 len - length of command in the buffer
+ ******************************************************************************/
+static int virt_spi_transfer(u8 *buf, int len)
+{
+	int rc = 0;
+
+	mutex_lock(&virt_spi_lock);
+	if (len <= sizeof(pt_dut_cmd_buf)) {
+		memcpy(pt_dut_cmd_buf, buf, len);
+		pt_dut_cmd_len = len;
+		rc = 1;
+	} else
+		rc = 0;
+	mutex_unlock(&virt_spi_lock);
+
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: virt_spi_master_recv
+ *
+ * SUMMARY: Copies the spi input message from the dut_out sysfs node into a
+ *	temporary buffer.
+ *
+ * RETURN VALUE:
+ *	Length of data transferred
+ *
+ * PARAMETERS:
+ *      *dev  - pointer to device struct
+ *      *buf  - pointer to spi incoming report
+ *       size - size to be read
+ ******************************************************************************/
+static int virt_spi_master_recv(struct device *dev, u8 *buf, int size)
+{
+#ifndef PT_POLL_RESP_BY_BUS
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+	int i = 0;
+#endif
+
+	mutex_lock(&virt_spi_lock);
+	memcpy(buf, pt_dut_out_buf, size);
+
+	/* Set "empty buffer" */
+	pt_dut_out_buf[1] = 0xFF;
+	pt_dut_out_len = 0;
+	mutex_unlock(&virt_spi_lock);
+
+#ifndef PT_POLL_RESP_BY_BUS
+	if (cd->bl_with_no_int) {
+		/*
+		 * Wait for IRQ gpio to be released, make read operation
+		 * synchronize with PtVirtDut tool.
+		 * Safety net: Exit after 500ms (50us * 10000 loops = 500ms)
+		 */
+		while (i < VIRT_MAX_IRQ_RELEASE_TIME_US &&
+		       !gpio_get_value(cd->cpdata->irq_gpio)) {
+			pt_debug(dev, DL_INFO, "%s: %d IRQ still Enabled\n",
+				__func__, i);
+			usleep_range(50, 60);
+			i += 50;
+		}
+	}
+#endif
+
+	pt_debug(dev, DL_INFO,
+		"%s: Copy msg from dut_out to spi buffer, size=%d\n",
+		__func__, size);
+
+	return size;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_dut_cmd_show
+ *
+ * SUMMARY: The show function for the dut_cmd sysfs node. Provides read access
+ *	to the pt_dut_cmd_buf and clears it after it has been read.
+ *
+ * RETURN VALUE:
+ *	Number of bytes transferred
+ *
+ * PARAMETERS:
+ *      *dev  - pointer to device structure
+ *      *attr - pointer to device attributes
+ *	*buf  - pointer to output buffer
+ ******************************************************************************/
+static ssize_t pt_dut_cmd_show(struct device *dev,
+	struct device_attribute *attr, char *buf)
+{
+	int i;
+	int index = 0;
+
+	/* Only print to sysfs if the buffer has data */
+	mutex_lock(&virt_spi_lock);
+	if (pt_dut_cmd_len > 0) {
+		for (i = 0; i < pt_dut_cmd_len; i++)
+			index += scnprintf(buf + index, strlen(buf), "%02X",
+				pt_dut_cmd_buf[i]);
+		index += scnprintf(buf + index, strlen(buf), "\n");
+	}
+	pt_dut_cmd_len = 0;
+	mutex_unlock(&virt_spi_lock);
+	return index;
+}
+static DEVICE_ATTR(dut_cmd, 0444, pt_dut_cmd_show, NULL);
+
+/*******************************************************************************
+ * FUNCTION: pt_dut_out_store
+ *
+ * SUMMARY: The store function for the dut_out sysfs node. Provides write
+ *	access to the pt_dut_out_buf. The smallest valid PIP response is 2
+ *	bytes so don't update buffer if only 1 byte passed in.
+ *
+ * RETURN VALUE:
+ *	Number of bytes read from virtual DUT
+ *
+ * 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_dut_out_store(struct device *dev,
+	struct device_attribute *attr, const char *buf, size_t size)
+{
+	int loop_max = ARRAY_SIZE(pt_dut_out_buf);
+	int hex_str_len = strlen(buf)/2;
+	int i;
+	const char *pos = buf;
+
+	/* Clear out the last message */
+	mutex_lock(&virt_spi_lock);
+	memset(pt_dut_out_buf, 0, VIRT_DUT_BUF_SIZE);
+	pt_dut_out_len = 0;
+
+	/* Only update the dut_out buffer if at least 2 byte payload */
+	if (size >= 2 && hex_str_len <= loop_max) {
+		/* Convert string of hex values to byte array */
+		for (i = 0; i < hex_str_len; i++) {
+			pt_dut_out_buf[i] = ((HEXOF(*pos)) << 4) +
+					     HEXOF(*(pos + 1));
+			pos += 2;
+		}
+		pt_dut_out_len = get_unaligned_le16(&pt_dut_out_buf[0]);
+	} else if (size >= PT_PIP_1P7_EMPTY_BUF) {
+		/* Message too large, set to 'empty buffer' message */
+		pt_dut_out_buf[1] = 0xFF;
+		pt_dut_out_len = 0;
+	}
+	mutex_unlock(&virt_spi_lock);
+
+	return size;
+}
+static DEVICE_ATTR(dut_out, 0200, NULL, pt_dut_out_store);
+#endif /* TTDL_PTVIRTDUT_SUPPORT */
+
+/*******************************************************************************
+ * FUNCTION: pt_spi_xfer
+ *
+ * SUMMARY: Read or write date for SPI device.
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *      *dev - pointer to device structure
+ *        op - flag to write or read data
+ *      *buf - pointer to data buffer
+ *    length - data length
+ ******************************************************************************/
+static int pt_spi_xfer(struct device *dev, u8 op, u8 *buf, int length)
+{
+	struct spi_device *spi = to_spi_device(dev);
+	struct spi_message msg;
+	struct spi_transfer xfer;
+	int rc;
+
+	memset(&xfer, 0, sizeof(xfer));
+	spi_message_init(&msg);
+
+	switch (op) {
+	case PT_SPI_RD_OP:
+		/* Clear tmp_wbuf with additional OP Code and dummy byte */
+		memset(tmp_wbuf, 0, length + 2);
+		tmp_wbuf[0] = op;
+		/* Total read/write = Read length + Op code + dummy byte */
+		xfer.tx_buf = tmp_wbuf;
+		xfer.rx_buf = tmp_rbuf;
+		xfer.len = length + 2;
+		break;
+	case PT_SPI_WR_OP:
+		memcpy(&tmp_wbuf[1], buf, length);
+		tmp_wbuf[0] = op;
+		/* Write length + size of Op code */
+		xfer.tx_buf = tmp_wbuf;
+		xfer.len = length + 1;
+		break;
+	default:
+		rc = -EIO;
+		goto exit;
+	}
+
+	spi_message_add_tail(&xfer, &msg);
+	rc = spi_sync(spi, &msg);
+
+	/* On reads copy only the data content back into the passed in buf */
+	if (op == PT_SPI_RD_OP)
+		memcpy(buf, &tmp_rbuf[2], length);
+exit:
+	if (rc < 0)
+		pt_debug(dev, DL_ERROR, "%s: spi_sync() error %d\n",
+			__func__, rc);
+
+#if 0 /* TODO TC3315 - need to verify the ACK byte */
+	if (tmp_rbuf[0] != PT_SPI_SYNC_ACK) {
+		pt_debug(dev, DL_ERROR, "%s: r_header = 0x%02X\n", __func__,
+			r_header[0]);
+		return -EIO;
+	}
+#endif
+
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_spi_read_default
+ *
+ * SUMMARY: Read a certain number of bytes from the SPI bus
+ *	NOTE: For TC3315 every response includes a "dummy" prefix byte that
+ *		needs to be stipped off before returning buf.
+ *
+ * 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_spi_read_default(struct device *dev, void *buf, int size)
+{
+#ifdef TTDL_PTVIRTDUT_SUPPORT
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+#endif /* TTDL_PTVIRTDUT_SUPPORT */
+	int rc = 0;
+
+	if (!buf || !size || size > PT_MAX_PIP2_MSG_SIZE)
+		return -EINVAL;
+
+	mutex_lock(&pt_spi_bus_lock);
+#ifdef TTDL_PTVIRTDUT_SUPPORT
+	if (cd->route_bus_virt_dut)
+		virt_spi_master_recv(dev, buf, size);
+	else
+		rc = pt_spi_xfer(dev, PT_SPI_RD_OP, buf, size);
+#else
+	rc = pt_spi_xfer(dev, PT_SPI_RD_OP, buf, size);
+#endif /* TTDL_PTVIRTDUT_SUPPORT */
+	mutex_unlock(&pt_spi_bus_lock);
+
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_spi_read_default_nosize
+ *
+ * SUMMARY: Read from the SPI bus in two transactions first reading the HID
+ *	packet size (2 bytes) followed by reading the rest of the packet based
+ *	on the size initially read.
+ *	NOTE: The empty buffer 'size' was redefined in PIP version 1.7.
+ *	NOTE: For TC3315 every response includes a "dummy" prefix byte that
+ *		needs to be stipped off before returning buf.
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *      *dev  - pointer to Device structure
+ *      *buf  - pointer to buffer where the data read will be stored
+ *       max  - max size that can be read
+ ******************************************************************************/
+static int pt_spi_read_default_nosize(struct device *dev, u8 *buf, u32 max)
+{
+	u32 size;
+	int rc = 0;
+#ifdef TTDL_PTVIRTDUT_SUPPORT
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+
+	if (cd->route_bus_virt_dut) {
+		mutex_lock(&virt_spi_lock);
+		size = pt_dut_out_len;
+		mutex_unlock(&virt_spi_lock);
+		/* Only copy 2 bytes for "empty buffer" or "FW sentinel" */
+		if (!size || size == 2 || size >= PT_PIP_1P7_EMPTY_BUF)
+			size = 2;
+		virt_spi_master_recv(dev, buf, size);
+		return 0;
+	}
+
+#endif /* TTDL_PTVIRTDUT_SUPPORT */
+
+	if (!buf)
+		return 0;
+
+	mutex_lock(&pt_spi_bus_lock);
+
+	/* Separate transaction to retrieve only the length to read */
+	rc = pt_spi_xfer(dev, PT_SPI_RD_OP, buf, 2);
+	if (rc < 0) {
+		pt_debug(dev, DL_ERROR, "%s: SPI transfer error rc = %d\n",
+			 __func__, rc);
+		goto exit;
+	}
+
+	size = get_unaligned_le16(&buf[0]);
+	if (!size || size == 2 || size >= PT_PIP_1P7_EMPTY_BUF)
+		goto exit;
+
+	if (size > max || size > PT_MAX_PIP2_MSG_SIZE) {
+		pt_debug(dev, DL_ERROR, "%s: Invalid size %d !\n", __func__,
+			 size);
+		rc = -EINVAL;
+		goto exit;
+	}
+
+	rc = pt_spi_xfer(dev, PT_SPI_RD_OP, buf, size);
+	if (rc)
+		pt_debug(dev, DL_ERROR, "%s: SPI transfer error rc = %d\n",
+		 __func__, rc);
+
+exit:
+	mutex_unlock(&pt_spi_bus_lock);
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_spi_write_read_specific
+ *
+ * SUMMARY: Write the contents of write_buf to the SPI device and then read
+ *	the response using pt_spi_read_default_nosize()
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *  *dev       - pointer to Device structure
+ *   write_len - length of data buffer write_buf
+ *  *write_buf - pointer to buffer to write
+ *  *read_buf  - pointer to buffer to read response into
+ *   read_len  - length to read, 0 to use pt_spi_read_default_nosize
+ ******************************************************************************/
+static int pt_spi_write_read_specific(struct device *dev, u16 write_len,
+		u8 *write_buf, u8 *read_buf, u16 read_len)
+{
+#ifdef TTDL_PTVIRTDUT_SUPPORT
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+#endif /* TTDL_PTVIRTDUT_SUPPORT */
+	int rc = 0;
+
+	/* Ensure no packet larger than what the PIP spec allows */
+	if (write_len > PT_MAX_PIP2_MSG_SIZE)
+		return -EINVAL;
+
+	if (!write_buf || !write_len) {
+		if (!write_buf)
+			pt_debug(dev, DL_ERROR,
+				"%s write_buf is NULL", __func__);
+		if (!write_len)
+			pt_debug(dev, DL_ERROR,
+				"%s write_len is NULL", __func__);
+		return -EINVAL;
+	}
+
+	mutex_lock(&pt_spi_bus_lock);
+#ifdef TTDL_PTVIRTDUT_SUPPORT
+	if (cd->route_bus_virt_dut) {
+		virt_spi_transfer(write_buf, write_len);
+		pt_debug(dev, DL_DEBUG, "%s: Virt transfer size = %d",
+			__func__, write_len);
+	} else {
+		rc = pt_spi_xfer(dev, PT_SPI_WR_OP, write_buf, write_len);
+		if (rc < 0)
+			goto error;
+	}
+#else
+	rc = pt_spi_xfer(dev, PT_SPI_WR_OP, write_buf, write_len);
+	if (rc < 0)
+		goto error;
+#endif /* TTDL_PTVIRTDUT_SUPPORT */
+	mutex_unlock(&pt_spi_bus_lock);
+
+	if (read_buf)
+		rc = pt_spi_read_default_nosize(dev, read_buf,
+				PT_SPI_DATA_SIZE);
+	return rc;
+
+error:
+	mutex_unlock(&pt_spi_bus_lock);
+	return rc;
+}
+
+static struct pt_bus_ops pt_spi_bus_ops = {
+	.bustype = BUS_SPI,
+	.read_default = pt_spi_read_default,
+	.read_default_nosize = pt_spi_read_default_nosize,
+	.write_read_specific = pt_spi_write_read_specific,
+};
+
+#ifdef CONFIG_TOUCHSCREEN_PARADE_DEVICETREE_SUPPORT
+static const struct of_device_id pt_spi_of_match[] = {
+	{ .compatible = "parade,pt_spi_adapter", },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, pt_spi_of_match);
+#endif
+
+/*******************************************************************************
+ * FUNCTION: pt_spi_probe
+ *
+ * SUMMARY: Probe functon for the SPI module
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *      *spi - pointer to spi device structure
+ ******************************************************************************/
+static int pt_spi_probe(struct spi_device *spi)
+{
+	struct device *dev = &spi->dev;
+#ifdef CONFIG_TOUCHSCREEN_PARADE_DEVICETREE_SUPPORT
+	const struct of_device_id *match;
+#endif
+	int rc;
+
+	/* Set up SPI*/
+	spi->bits_per_word = PT_SPI_BITS_PER_WORD;
+	spi->mode = SPI_MODE_0;
+	rc = spi_setup(spi);
+	if (rc < 0) {
+		pt_debug(dev, DL_ERROR, "%s: SPI setup error %d\n",
+			__func__, rc);
+		return rc;
+	}
+
+#ifdef CONFIG_TOUCHSCREEN_PARADE_DEVICETREE_SUPPORT
+	match = of_match_device(of_match_ptr(pt_spi_of_match), dev);
+	if (match) {
+		rc = pt_devtree_create_and_get_pdata(dev);
+		if (rc < 0)
+			return rc;
+	}
+#endif
+
+	/* Add 2 to the length for the 'OP Code' & 'Dummy' prefix bytes */
+	tmp_wbuf = kzalloc(PT_SPI_BUFFER_SIZE, GFP_KERNEL);
+	tmp_rbuf = kzalloc(PT_SPI_BUFFER_SIZE, GFP_KERNEL);
+	if (!tmp_wbuf || !tmp_rbuf)
+		return -ENOMEM;
+
+#ifdef TTDL_PTVIRTDUT_SUPPORT
+	device_create_file(dev, &dev_attr_dut_cmd);
+	device_create_file(dev, &dev_attr_dut_out);
+#endif /* TTDL_PTVIRTDUT_SUPPORT */
+
+	rc = pt_probe(&pt_spi_bus_ops, &spi->dev, spi->irq,
+			  PT_SPI_DATA_BUF_SIZE);
+
+#ifdef CONFIG_TOUCHSCREEN_PARADE_DEVICETREE_SUPPORT
+	if (rc && match)
+		pt_devtree_clean_pdata(dev);
+#endif
+
+#ifdef TTDL_PTVIRTDUT_SUPPORT
+	if (rc) {
+		device_remove_file(dev, &dev_attr_dut_cmd);
+		device_remove_file(dev, &dev_attr_dut_out);
+	}
+#endif /* TTDL_PTVIRTDUT_SUPPORT */
+
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_spi_remove
+ *
+ * SUMMARY: Remove functon for the SPI module
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *      *spi - pointer to spi device structure
+ ******************************************************************************/
+static int pt_spi_remove(struct spi_device *spi)
+{
+#ifdef CONFIG_TOUCHSCREEN_PARADE_DEVICETREE_SUPPORT
+	const struct of_device_id *match;
+#endif
+	struct device *dev = &spi->dev;
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+
+	kfree(tmp_rbuf);
+	kfree(tmp_wbuf);
+
+#ifdef TTDL_PTVIRTDUT_SUPPORT
+	device_remove_file(dev, &dev_attr_dut_cmd);
+	device_remove_file(dev, &dev_attr_dut_out);
+#endif /* TTDL_PTVIRTDUT_SUPPORT */
+
+	pt_release(cd);
+
+#ifdef CONFIG_TOUCHSCREEN_PARADE_DEVICETREE_SUPPORT
+	match = of_match_device(of_match_ptr(pt_spi_of_match), dev);
+	if (match)
+		pt_devtree_clean_pdata(dev);
+#endif
+
+	return 0;
+}
+
+static const struct spi_device_id pt_spi_id[] = {
+	{ PT_SPI_NAME, 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(spi, pt_spi_id);
+
+static struct spi_driver pt_spi_driver = {
+	.driver = {
+		.name = PT_SPI_NAME,
+		.bus = &spi_bus_type,
+		.owner = THIS_MODULE,
+		.pm = &pt_pm_ops,
+#ifdef CONFIG_TOUCHSCREEN_PARADE_DEVICETREE_SUPPORT
+		.of_match_table = pt_spi_of_match,
+#endif
+	},
+	.probe = pt_spi_probe,
+	.remove = (pt_spi_remove),
+	.id_table = pt_spi_id,
+};
+
+#if (KERNEL_VERSION(3, 3, 0) <= LINUX_VERSION_CODE)
+module_spi_driver(pt_spi_driver);
+#else
+/*******************************************************************************
+ * FUNCTION: pt_spi_init
+ *
+ * SUMMARY: Initialize function to register spi module to kernel.
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ ******************************************************************************/
+static int __init pt_spi_init(void)
+{
+	int err = spi_register_driver(&pt_spi_driver);
+
+	pr_info("%s: Parade TTDL SPI Driver (Build %s) rc=%d\n",
+		 __func__, PT_DRIVER_VERSION, err);
+	return err;
+}
+module_init(pt_spi_init);
+
+/*******************************************************************************
+ * FUNCTION: pt_spi_exit
+ *
+ * SUMMARY: Exit function to unregister spi module from kernel.
+ *
+ ******************************************************************************/
+static void __exit pt_spi_exit(void)
+{
+	spi_unregister_driver(&pt_spi_driver);
+}
+module_exit(pt_spi_exit);
+#endif
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Parade TrueTouch(R) Standard Product SPI Driver");
+MODULE_AUTHOR("Parade Technologies <[email protected]>");