diff --git a/Documentation/devicetree/bindings/input/touchscreen/parade-touchscreen.txt b/Documentation/devicetree/bindings/input/touchscreen/parade-touchscreen.txt new file mode 100755 index 000000000000..cfc2eb7278da --- /dev/null +++ b/Documentation/devicetree/bindings/input/touchscreen/parade-touchscreen.txt @@ -0,0 +1,135 @@ +pt_i2c_adaptor bindings + +Required properties: +- compatible: "parade,pt_i2c_adaptor" +- reg: i2c address of the chip +- interrupt-parent: a phandle for the interrupt controller +- interrupts: Interrupt GPIO +- parade,adapter_id: adaptor name +- core,name: name of core +- core,irq_gpio: IRQ GPIO +- core,hid_desc_register: Default HID register +- core,flags: Special flags +- core,mt,name: name of multi-touch module +- core,mt,inp_dev_name: input device name +- core,mt,flags: MT flags +- core,mt,abs: ABS_MT_* event names + +Optional properties: +- core,rst_gpio: Reset GPIO +- core,easy_wakeup_gesture: Default wakeup gesture +- core,panel_id_support: panel ID support +- core,btn_keys: Virtual button key codes +- core,btn_keys-tag: Button Tag +- core,mt,vkeys-x: X coordinate for virtual button area +- core,mt,vkeys-y: Y coordinate for virtual button area +- core,mt,virtual_keys: Keycode CenterX CenterY Width Height + +[1]: Documentation/devicetree/bindings/input/parade-touchscreen.txt + +Example: + i2c@0 { + status = "ok"; + tsc@24 { + compatible = "parade,pt_i2c_adapter"; + reg = <0x24>; + interrupt-parent = <&msmgpio>; + interrupts = <59 0x2>; + parade,adapter_id = "pt_i2c_adapter"; + pinctrl-names = "pmx_ts_active","pmx_ts_suspend","pmx_ts_release"; + pinctrl-0 = <&ts_int_active>, <&ts_reset_active>; + pinctrl-1 = <&ts_int_suspend>, <&ts_reset_suspend>; + pinctrl-2 = <&ts_release>; + + parade,core { + parade,name = "pt_core"; + + parade,irq_gpio = <59>; + parade,rst_gpio = <13>; + parade,runfw_gpio = <64>; + parade,hid_desc_register = <1>; + /* + * PT_CORE_FLAG_NONE = 0x00 + * PT_CORE_FLAG_POWEROFF_ON_SLEEP = 0x02 + * PT_CORE_FLAG_RESTORE_PARAMETERS = 0x04 + * PT_CORE_FLAG_DEEP_STANDBY = 0x08 + * PT_CORE_FLAG_SKIP_SYS_SLEEP = 0x10 + * PT_CORE_FLAG_SKIP_RUNTIME = 0x20 + * PT_CORE_FLAG_SKIP_RESUME = 0x40 + */ + parade,flags = <4>; + /* PT_CORE_EWG_NONE */ + parade,easy_wakeup_gesture = <0>; + /* 0:AUTO 1:PIP1_ONLY 2:PIP2_CAPABLE*/ + parade,config_dut_generation = <2>; + /* 0:False 1:True*/ + parade,watchdog_force_stop = <0>; + /* + * PT_PANEL_ID_DISABLE = 0x00 + * PT_PANEL_ID_BY_BL = 0x01 + * PT_PANEL_ID_BY_SYS_INFO = 0x02 + * PT_PANEL_ID_BY_MFG_DATA = 0x04 + */ + parade,panel_id_support = <0>; + parade,btn_keys = <172 /* KEY_HOMEPAGE */ + /* previously was KEY_HOME, new Android versions use KEY_HOMEPAGE */ + 139 /* KEY_MENU */ + 158 /* KEY_BACK */ + 217 /* KEY_SEARCH */ + 114 /* KEY_VOLUMEDOWN */ + 115 /* KEY_VOLUMEUP */ + 212 /* KEY_CAMERA */ + 116>; /* KEY_POWER */ + parade,btn_keys-tag = <0>; + + parade,mt { + parade,name = "pt_mt"; + + parade,inp_dev_name = "pt_mt"; + /* + * PT_MT_FLAG_NONE = 0x00 + * PT_MT_FLAG_FLIP = 0x08 + * PT_MT_FLAG_INV_X = 0x10 + * PT_MT_FLAG_INV_Y = 0x20 + * PT_MT_FLAG_VKEYS = 0x40 + */ + parade,flags = <0x00>; + parade,abs = + /* ABS_MT_POSITION_X, PT_ABS_MIN_X, PT_ABS_MAX_X, 0, 0 */ + <0x35 0 880 0 0 + /* ABS_MT_POSITION_Y, PT_ABS_MIN_Y, PT_ABS_MAX_Y, 0, 0 */ + 0x36 0 1280 0 0 + /* ABS_MT_PRESSURE, PT_ABS_MIN_P, PT_ABS_MAX_P, 0, 0 */ + 0x3a 0 255 0 0 + /* PT_IGNORE_VALUE, PT_ABS_MIN_W, PT_ABS_MAX_W, 0, 0 */ + 0xffff 0 255 0 0 + /* ABS_MT_TRACKING_ID, PT_ABS_MIN_T, PT_ABS_MAX_T, 0, 0 */ + 0x39 0 15 0 0 + /* ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0 */ + 0x30 0 255 0 0 + /* ABS_MT_TOUCH_MINOR, 0, 255, 0, 0 */ + 0x31 0 255 0 0 + /* ABS_MT_ORIENTATION, -127, 127, 0, 0 */ + 0x34 0xffffff81 127 0 0 + /* ABS_MT_TOOL_TYPE, 0, MT_TOOL_MAX, 0, 0 */ + 0x37 0 1 0 0 + /* ABS_DISTANCE, 0, 255, 0, 0 */ + 0x19 0 255 0 0>; + + parade,vkeys_x = <720>; + parade,vkeys_y = <1280>; + + parade,virtual_keys = /* KeyCode CenterX CenterY Width Height */ + /* KEY_BACK */ + <158 90 1360 160 180 + /* KEY_MENU */ + 139 270 1360 160 180 + /* KEY_HOMEPAGE */ + 172 450 1360 160 180 + /* KEY SEARCH */ + 217 630 1360 160 180>; + }; + }; + }; + }; + diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index 5b429a59d97f..de68b3d6de7e 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -252,7 +252,7 @@ config TOUCHSCREEN_CYTTSP4_SPI module will be called cyttsp4_spi. config TOUCHSCREEN_CYPRESS_CYTTSP5 - tristate "Parade TrueTouch Gen5 Touchscreen Driver" + tristate "Parade TrueTouch Gen5 Touchscreen Driver (cyttsp5)" help Core driver for Parade TrueTouch(tm) Standard Product Geneartion5 touchscreen controllers. @@ -470,6 +470,202 @@ config TOUCHSCREEN_CYPRESS_CYTTSP5_DEBUG_MDL module will be called cyttsp5_debug. +config TOUCHSCREEN_PARADE + tristate "Parade TrueTouch Gen5 Touchscreen Driver" + help + Core driver for Parade TrueTouch(tm) Standard Product + Geneartion5 touchscreen controllers. + + Say Y here if you have a Parade Gen5 touchscreen. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called pt. + +config TOUCHSCREEN_PARADE_DEVICETREE_SUPPORT + bool "Enable Device Tree support" + depends on TOUCHSCREEN_PARADE && OF + help + Say Y here to enable support for device tree. + + If unsure, say N. + +config TOUCHSCREEN_PARADE_DEBUG + bool "Enable debug output" + depends on TOUCHSCREEN_PARADE + help + Say Y here to enable debug output for Parade TrueTouch(tm) + Standard Product Generation5 drivers set. + + If unsure, say N. + +config TOUCHSCREEN_PARADE_VDEBUG + bool "Enable verbose debug output" + depends on TOUCHSCREEN_PARADE_DEBUG + help + Say Y here to enable verbose debug output for Parade TrueTouch(tm) + Standard Product Generation5 drivers set. + + If unsure, say N. + +config TOUCHSCREEN_PARADE_I2C + tristate "Parade TrueTouch Gen5 I2C" + depends on TOUCHSCREEN_PARADE + select I2C + help + Say Y here to enable I2C bus interface to Parade TrueTouch(tm) + Standard Product Generation5 touchscreen controller. + + If unsure, say Y. + + To compile this driver as a module, choose M here: the + module will be called pt_i2c. + +config TOUCHSCREEN_PARADE_SPI + tristate "Parade TrueTouch Gen5 SPI" + depends on TOUCHSCREEN_PARADE + select SPI + help + Say Y here to enable SPI bus interface to Parade TrueTouch(tm) + Standard Product Generation5 touchscreen controller. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called pt_spi. + +choice + bool "Parade TrueTouch Gen5 MultiTouch Protocol" + depends on TOUCHSCREEN_PARADE + default TOUCHSCREEN_PARADE_MT_B + help + This option controls which MultiTouch protocol will be used to + report the touch events. + +config TOUCHSCREEN_PARADE_MT_A + bool "Protocol A" + help + Select to enable MultiTouch touch reporting using protocol A + on Parade TrueTouch(tm) Standard Product Generation4 touchscreen + controller. + +config TOUCHSCREEN_PARADE_MT_B + bool "Protocol B" + help + Select to enable MultiTouch touch reporting using protocol B + on Parade TrueTouch(tm) Standard Product Generation4 touchscreen + controller. + +endchoice + +config TOUCHSCREEN_PARADE_BUTTON + bool "Parade TrueTouch Gen5 MultiTouch CapSense Button" + depends on TOUCHSCREEN_PARADE + help + Say Y here to enable CapSense reporting on Parade TrueTouch(tm) + Standard Product Generation5 touchscreen controller. + + If unsure, say N. + +config TOUCHSCREEN_PARADE_PROXIMITY + bool "Parade TrueTouch Gen5 Proximity" + depends on TOUCHSCREEN_PARADE + help + Say Y here to enable proximity reporting on Parade TrueTouch(tm) + Standard Product Generation5 touchscreen controller. + + If unsure, say N. + +config TOUCHSCREEN_PARADE_DEVICE_ACCESS + tristate "Parade TrueTouch Gen5 MultiTouch Device Access" + depends on TOUCHSCREEN_PARADE + help + Say Y here to enable Parade TrueTouch(tm) Standard Product + Generation5 touchscreen controller device access module. + + This modules adds an interface to access touchscreen + controller using driver sysfs nodes. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called pt_device_access. + +config TOUCHSCREEN_PARADE_LOADER + tristate "Parade TrueTouch Gen5 MultiTouch Loader" + depends on TOUCHSCREEN_PARADE + help + Say Y here to enable Parade TrueTouch(tm) Standard Product + Generation5 touchscreen controller FW Loader module. + + This module enables support for Firmware upgrade. + + If unsure, say Y. + + To compile this driver as a module, choose M here: the + module will be called pt_loader. + +config TOUCHSCREEN_PARADE_PLATFORM_FW_UPGRADE + bool "FW upgrade from header file" + depends on TOUCHSCREEN_PARADE_LOADER + help + Say Y here to include Parade TrueTouch(tm) Standard Product + Generation5 device Firmware into driver. + + Need proper header file for this. + + If unsure, say N. + +config TOUCHSCREEN_PARADE_BINARY_FW_UPGRADE + bool "FW upgrade from binary file" + depends on TOUCHSCREEN_PARADE_LOADER + help + Say Y here to include Parade TrueTouch(tm) Standard Product + Generation5 device Firmware into kernel as binary blob. + + This should be enabled for manual FW upgrade support. + + If unsure, say Y. + +config TOUCHSCREEN_PARADE_PLATFORM_TTCONFIG_UPGRADE + bool "TT Configuration upgrade from header file" + depends on TOUCHSCREEN_PARADE_LOADER + help + Say Y here to include Parade TrueTouch(tm) Standard Product + Generation5 device TrueTouch Configuration into kernel itself. + + Need proper header file for this. + + If unsure, say N. + +config TOUCHSCREEN_PARADE_MANUAL_TTCONFIG_UPGRADE + bool "TT Configuration upgrade via SysFs" + depends on TOUCHSCREEN_PARADE_LOADER + help + Say Y here to provide a SysFs interface to upgrade TrueTouch + Configuration with a binary configuration file. + + Need proper binary version of config file for this + feature. + + If unsure, say Y. + +config TOUCHSCREEN_PARADE_DEBUG_MDL + tristate "Parade TrueTouch Gen5 MultiTouch Debug Module" + depends on TOUCHSCREEN_PARADE + help + Say Y here to enable Parade TrueTouch(tm) Standard Product + Generation5 Debug module. + + This module adds support for verbose printing touch + information. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called pt_debug. + config TOUCHSCREEN_DA9034 tristate "Touchscreen support for Dialog Semiconductor DA9034" depends on PMIC_DA903X diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile index d7bd0d131a84..7051eab781b8 100644 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile @@ -157,3 +157,53 @@ CFLAGS_cyttsp5_devtree.o += -DVERBOSE_DEBUG CFLAGS_cyttsp5_platform.o += -DVERBOSE_DEBUG endif +GCOV_PROFILE := y + +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_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_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_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 + diff --git a/drivers/input/touchscreen/pt_btn.c b/drivers/input/touchscreen/pt_btn.c new file mode 100755 index 000000000000..9cd894661a2e --- /dev/null +++ b/drivers/input/touchscreen/pt_btn.c @@ -0,0 +1,522 @@ +/* + * 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 + */ + +#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; +} diff --git a/drivers/input/touchscreen/pt_core.c b/drivers/input/touchscreen/pt_core.c new file mode 100755 index 000000000000..70795de29b6e --- /dev/null +++ b/drivers/input/touchscreen/pt_core.c @@ -0,0 +1,17267 @@ +/* + * 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 + */ + +#include "pt_regs.h" +#include +#include +#include + + +#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; +}; + +struct atten_node { + struct list_head node; + char *id; + struct device *dev; + + int (*func)(struct device *dev); + int mode; +}; + +struct param_node { + struct list_head node; + u8 id; + u32 value; + u8 size; +}; + +struct module_node { + struct list_head node; + struct pt_module *module; + void *data; +}; + +struct pt_hid_cmd { + u8 opcode; + u8 report_type; + union { + u8 report_id; + u8 power_state; + }; + u8 has_data_register; + size_t write_length; + u8 *write_buf; + u8 *read_buf; + u8 wait_interrupt; + u8 reset_cmd; + u16 timeout_ms; +}; + +struct pt_hid_output { + u8 cmd_type; + u16 length; + u8 command_code; + size_t write_length; + u8 *write_buf; + u8 novalidate; + u8 reset_expected; + u16 timeout_ms; +}; + +#define SET_CMD_OPCODE(byte, opcode) SET_CMD_LOW(byte, opcode) +#define SET_CMD_REPORT_TYPE(byte, type) SET_CMD_HIGH(byte, ((type) << 4)) +#define SET_CMD_REPORT_ID(byte, id) SET_CMD_LOW(byte, id) + +#define CREATE_PIP1_FW_CMD(command) \ + .cmd_type = PIP1_CMD_TYPE_FW, \ + .command_code = command + +#define CREATE_PIP1_BL_CMD(command) \ + .cmd_type = PIP1_CMD_TYPE_BL, \ + .command_code = command + +#define PT_MAX_PR_BUF_SIZE 2048 +/******************************************************************************* + * FUNCTION: pt_pr_buf + * + * SUMMARY: Print out the contents of a buffer to kmsg based on the debug level + * + * RETURN: Void + * + * PARAMETERS: + * *dev - pointer to Device structure + * debug_level - requested debug level to print at + * *buf - pointer to buffer to print + * buf_len - size of buf + * *data_name - Descriptive name of data prefixed to data + ******************************************************************************/ +void pt_pr_buf(struct device *dev, u8 debug_level, u8 *buf, + u16 buf_len, const char *data_name) +{ + struct pt_core_data *cd = dev_get_drvdata(dev); + int i; + 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 a space each printed char takes 3 bytes, subtract + * the length of the data_name prefix as well as 11 bytes + * for the " [0..xxx]: " printed before the data. + */ + max_size = (PT_MAX_PR_BUF_SIZE - sizeof(data_name) - 11) / 3; + + /* Ensure pr_buf_index stays within the 1018 size */ + pr_buf_index += sprintf(pr_buf, "%s [0..%d]: ", + data_name, buf_len); + for (i = 0; i < buf_len && i < max_size; i++) + pr_buf_index += sprintf(pr_buf + pr_buf_index, + "%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) { + sprintf(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(¶m_new->node, &cd->param_list); +exit_unlock: + spin_unlock(&cd->spinlock); + + return 0; +} + +#ifdef TTDL_DIAGNOSTICS +/******************************************************************************* + * FUNCTION: pt_erase_parameter_list + * + * SUMMARY: Empty out the entire parameter linked list of all parameter/value + * pairs. In some test cases this functionality is needed to ensure DUT + * returns to a virgin state after a reset and no parameters are restored. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data + ******************************************************************************/ +static int pt_erase_parameter_list(struct pt_core_data *cd) +{ + struct param_node *pos, *temp; + + spin_lock(&cd->spinlock); + list_for_each_entry_safe(pos, temp, &cd->param_list, node) { + pt_debug(cd->dev, DL_INFO, + "%s: Parameter Restore List - remove 0x%02x\n", + __func__, pos->id); + list_del(&pos->node); + kfree(pos); + } + spin_unlock(&cd->spinlock); + + return 0; +} + +/******************************************************************************* + * FUNCTION: pt_count_parameter_list + * + * SUMMARY: Count the items in the RAM parameter restor list + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data + ******************************************************************************/ +static int pt_count_parameter_list(struct pt_core_data *cd) +{ + struct param_node *pos, *temp; + int entries = 0; + + spin_lock(&cd->spinlock); + list_for_each_entry_safe(pos, temp, &cd->param_list, node) + entries++; + spin_unlock(&cd->spinlock); + + return entries; +} +#endif /* TTDL_DIAGNOSTICS */ + +/******************************************************************************* + * FUNCTION: request_exclusive + * + * SUMMARY: Request exclusive access to the DUT + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data + * *ownptr - pointer to device + * timeout_ms - Timeout value + ******************************************************************************/ +int request_exclusive(struct pt_core_data *cd, void *ownptr, + int timeout_ms) +{ + int t = msecs_to_jiffies(timeout_ms); + bool with_timeout = (timeout_ms != 0); + + pt_debug(cd->dev, DL_INFO, "%s: Attempt to Request EXCLUSIVE t=%d\n", + __func__, timeout_ms); + + mutex_lock(&cd->system_lock); + if (!cd->exclusive_dev && cd->exclusive_waits == 0) { + cd->exclusive_dev = ownptr; + goto exit; + } + + cd->exclusive_waits++; +wait: + mutex_unlock(&cd->system_lock); + if (with_timeout) { + t = wait_event_timeout(cd->wait_q, !cd->exclusive_dev, t); + if (IS_TMO(t)) { + pt_debug(cd->dev, DL_ERROR, + "%s: tmo waiting exclusive access\n", __func__); + return -ETIME; + } + } else { + wait_event(cd->wait_q, !cd->exclusive_dev); + } + mutex_lock(&cd->system_lock); + if (cd->exclusive_dev) + goto wait; + cd->exclusive_dev = ownptr; + cd->exclusive_waits--; +exit: + mutex_unlock(&cd->system_lock); + pt_debug(cd->dev, DL_DEBUG, "%s: request exclusive ok=%p\n", + __func__, ownptr); + + return 0; +} + +/******************************************************************************* + * FUNCTION: release_exclusive_ + * + * SUMMARY: Release exclusive access to the DUT + * + * RETURN: + * 0 = success + * + * PARAMETERS: + * *cd - pointer to core data + * *ownptr - pointer to device + ******************************************************************************/ +static int release_exclusive_(struct pt_core_data *cd, void *ownptr) +{ + pt_debug(cd->dev, DL_INFO, "%s: Attempt to Release EXCLUSIVE\n", + __func__); + if (cd->exclusive_dev != ownptr) + return -EINVAL; + + pt_debug(cd->dev, DL_DEBUG, "%s: exclusive_dev %p freed\n", + __func__, cd->exclusive_dev); + cd->exclusive_dev = NULL; + wake_up(&cd->wait_q); + return 0; +} + +/******************************************************************************* + * FUNCTION: release_exclusive + * + * SUMMARY: Protected wrapper to release_exclusive_() + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data + * *ownptr - pointer to device + ******************************************************************************/ +int release_exclusive(struct pt_core_data *cd, void *ownptr) +{ + int rc; + + mutex_lock(&cd->system_lock); + rc = release_exclusive_(cd, ownptr); + mutex_unlock(&cd->system_lock); + + return rc; +} + +/******************************************************************************* + * FUNCTION: pt_hid_exec_cmd_ + * + * SUMMARY: Send the HID command to the DUT + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data + * *hid_cmd - pointer to the HID command to send + ******************************************************************************/ +static int pt_hid_exec_cmd_(struct pt_core_data *cd, + struct pt_hid_cmd *hid_cmd) +{ + int rc = 0; + u8 *cmd; + u16 cmd_length; + u8 cmd_offset = 0; + + cmd_length = 2 /* command register */ + + 2 /* command */ + + (hid_cmd->report_id >= 0XF ? 1 : 0) /* Report ID */ + + (hid_cmd->has_data_register ? 2 : 0) /* Data register */ + + hid_cmd->write_length; /* Data length */ + + cmd = kzalloc(cmd_length, GFP_KERNEL); + if (!cmd) + return -ENOMEM; + + /* Set Command register */ + memcpy(&cmd[cmd_offset], &cd->hid_desc.command_register, + sizeof(cd->hid_desc.command_register)); + cmd_offset += sizeof(cd->hid_desc.command_register); + + /* Set Command */ + SET_CMD_REPORT_TYPE(cmd[cmd_offset], hid_cmd->report_type); + + if (hid_cmd->report_id >= 0XF) + SET_CMD_REPORT_ID(cmd[cmd_offset], 0xF); + else + SET_CMD_REPORT_ID(cmd[cmd_offset], hid_cmd->report_id); + cmd_offset++; + + SET_CMD_OPCODE(cmd[cmd_offset], hid_cmd->opcode); + cmd_offset++; + + if (hid_cmd->report_id >= 0XF) { + cmd[cmd_offset] = hid_cmd->report_id; + cmd_offset++; + } + + /* Set Data register */ + if (hid_cmd->has_data_register) { + memcpy(&cmd[cmd_offset], &cd->hid_desc.data_register, + sizeof(cd->hid_desc.data_register)); + cmd_offset += sizeof(cd->hid_desc.data_register); + } + + /* Set Data */ + if (hid_cmd->write_length && hid_cmd->write_buf) { + memcpy(&cmd[cmd_offset], hid_cmd->write_buf, + hid_cmd->write_length); + cmd_offset += hid_cmd->write_length; + } + + pt_debug(cd->dev, DL_INFO, + ">>> %s: Write Buffer Size[%d] Cmd[0x%02X]\n", + __func__, cmd_length, hid_cmd->report_id); + pt_pr_buf(cd->dev, DL_DEBUG, cmd, cmd_length, ">>> CMD"); + rc = pt_adap_write_read_specific(cd, cmd_length, cmd, + hid_cmd->read_buf); + if (rc) + pt_debug(cd->dev, DL_ERROR, + "%s: Fail pt_adap_transfer\n", __func__); + + kfree(cmd); + return rc; +} +#ifdef TTDL_DIAGNOSTICS +/******************************************************************************* + * FUNCTION: pt_toggle_err_gpio + * + * SUMMARY: Toggles the pre-defined error GPIO + * + * RETURN: n/a + * + * PARAMETERS: + * *cd - pointer to core data + * type - type of err that occured + ******************************************************************************/ +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 occured + ******************************************************************************/ +void _pt_request_toggle_err_gpio(struct device *dev, u8 type) +{ + struct pt_core_data *cd = dev_get_drvdata(dev); + + pt_toggle_err_gpio(cd, type); +} +#endif /* TTDL_DIAGNOSTICS */ + +/******************************************************************************* + * FUNCTION: pt_hid_exec_cmd_and_wait_ + * + * SUMMARY: Send the HID command to the DUT and wait for the response + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data + * *hid_cmd - pointer to the HID command to send + ******************************************************************************/ +static int pt_hid_exec_cmd_and_wait_(struct pt_core_data *cd, + struct pt_hid_cmd *hid_cmd) +{ + int rc = 0; + int t; + u16 timeout_ms; + int *cmd_state; + + if (hid_cmd->reset_cmd) + cmd_state = &cd->hid_reset_cmd_state; + else + cmd_state = &cd->hid_cmd_state; + + if (hid_cmd->wait_interrupt) { + mutex_lock(&cd->system_lock); + *cmd_state = 1; + mutex_unlock(&cd->system_lock); + } + + rc = pt_hid_exec_cmd_(cd, hid_cmd); + if (rc) { + if (hid_cmd->wait_interrupt) + goto error; + + goto exit; + } + + if (!hid_cmd->wait_interrupt) + goto exit; + + if (hid_cmd->timeout_ms) + timeout_ms = hid_cmd->timeout_ms; + else + timeout_ms = PT_HID_CMD_DEFAULT_TIMEOUT; + + t = wait_event_timeout(cd->wait_q, (*cmd_state == 0), + msecs_to_jiffies(timeout_ms)); + if (IS_TMO(t)) { +#ifdef TTDL_DIAGNOSTICS + cd->bus_transmit_error_count++; + pt_toggle_err_gpio(cd, PT_ERR_GPIO_I2C_TRANS); +#endif /* TTDL_DIAGNOSTICS */ + pt_debug(cd->dev, DL_ERROR, + "%s: HID output cmd execution timed out\n", + __func__); + rc = -ETIME; + goto error; + } + + goto exit; + +error: + mutex_lock(&cd->system_lock); + *cmd_state = 0; + mutex_unlock(&cd->system_lock); + +exit: + return rc; +} + +/******************************************************************************* + * FUNCTION: pt_hid_cmd_reset_ + * + * SUMMARY: Send the HID RESET command to the DUT + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data + ******************************************************************************/ +static int pt_hid_cmd_reset_(struct pt_core_data *cd) +{ + struct pt_hid_cmd hid_cmd = { + .opcode = HID_CMD_RESET, + .wait_interrupt = 1, + .reset_cmd = 1, + .timeout_ms = PT_HID_CMD_DEFAULT_TIMEOUT, + }; + + return pt_hid_exec_cmd_and_wait_(cd, &hid_cmd); +} + +/******************************************************************************* + * FUNCTION: pt_hid_cmd_reset + * + * SUMMARY: Wrapper function for pt_hid_cmd_reset_ that guarantees exclusive + * access. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data + ******************************************************************************/ +static int pt_hid_cmd_reset(struct pt_core_data *cd) +{ + int rc = 0; + + rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT); + if (rc < 0) { + pt_debug(cd->dev, DL_ERROR, + "%s: fail get exclusive ex=%p own=%p\n", + __func__, cd->exclusive_dev, cd->dev); + return rc; + } + + pt_debug(cd->dev, DL_INFO, "%s: Send HID Reset command\n", __func__); + rc = pt_hid_cmd_reset_(cd); + + if (release_exclusive(cd, cd->dev) < 0) + pt_debug(cd->dev, DL_ERROR, + "%s: fail to release exclusive\n", __func__); + + return rc; +} + +/******************************************************************************* + * FUNCTION: pt_hid_cmd_set_power_ + * + * SUMMARY: Send hid cmd to set power state for the DUT and wait for response + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data + * power_state - power state to set(HID_POWER_ON/HID_POWER_SLEEP) + ******************************************************************************/ +static int pt_hid_cmd_set_power_(struct pt_core_data *cd, + u8 power_state) +{ + int rc = 0; + struct pt_hid_cmd hid_cmd = { + .opcode = HID_CMD_SET_POWER, + .wait_interrupt = 1, + .timeout_ms = PT_HID_CMD_DEFAULT_TIMEOUT, + }; + hid_cmd.power_state = power_state; + + /* The chip won't give response if goes to Deep Standby */ + if (power_state == HID_POWER_STANDBY) { + rc = pt_hid_exec_cmd_(cd, &hid_cmd); + if (rc) + pt_debug(cd->dev, DL_ERROR, + "%s: Failed to set power to state:%d\n", + __func__, power_state); + else + cd->fw_sys_mode_in_standby_state = true; + return rc; + } + cd->fw_sys_mode_in_standby_state = false; + + rc = pt_hid_exec_cmd_and_wait_(cd, &hid_cmd); + if (rc) { + pt_debug(cd->dev, DL_ERROR, + "%s: Failed to set power to state:%d\n", + __func__, power_state); + return rc; + } + + /* validate */ + if ((cd->response_buf[2] != HID_RESPONSE_REPORT_ID) + || ((cd->response_buf[3] & 0x3) != power_state) + || ((cd->response_buf[4] & 0xF) != HID_CMD_SET_POWER)) + rc = -EINVAL; + + return rc; +} + +/******************************************************************************* + * FUNCTION: pt_hid_cmd_set_power + * + * SUMMARY: Wrapper function for pt_hid_cmd_set_power_ that guarantees + * exclusive access. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data + * power_state - power state to set(HID_POWER_ON/HID_POWER_SLEEP) + ******************************************************************************/ +static int pt_hid_cmd_set_power(struct pt_core_data *cd, + u8 power_state) +{ + int rc; + + rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT); + if (rc < 0) { + pt_debug(cd->dev, DL_ERROR, + "%s: fail get exclusive ex=%p own=%p\n", + __func__, cd->exclusive_dev, cd->dev); + return rc; + } + + rc = pt_hid_cmd_set_power_(cd, power_state); + + if (release_exclusive(cd, cd->dev) < 0) + pt_debug(cd->dev, DL_ERROR, + "%s: fail to release exclusive\n", __func__); + + return rc; +} + +static const u16 crc_table[16] = { + 0x0000, 0x1021, 0x2042, 0x3063, + 0x4084, 0x50a5, 0x60c6, 0x70e7, + 0x8108, 0x9129, 0xa14a, 0xb16b, + 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef, +}; + +/******************************************************************************* + * FUNCTION: _pt_compute_crc + * + * SUMMARY: Calculate CRC by CRC table. + * + * RETURN: + * CRC calculation result + * + * PARAMETERS: + * *buf - pointer to the data array to be calculated + * size - size of data array + ******************************************************************************/ +static u16 _pt_compute_crc(u8 *buf, u32 size) +{ + u16 remainder = 0xFFFF; + u16 xor_mask = 0x0000; + u32 index; + u32 byte_value; + u32 table_index; + u32 crc_bit_width = sizeof(u16) * 8; + + /* Divide the message by polynomial, via the table. */ + for (index = 0; index < size; index++) { + byte_value = buf[index]; + table_index = ((byte_value >> 4) & 0x0F) + ^ (remainder >> (crc_bit_width - 4)); + remainder = crc_table[table_index] ^ (remainder << 4); + table_index = (byte_value & 0x0F) + ^ (remainder >> (crc_bit_width - 4)); + remainder = crc_table[table_index] ^ (remainder << 4); + } + + /* Perform the final remainder CRC. */ + return remainder ^ xor_mask; +} + +u16 ccitt_Table[] = { + 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7, + 0x8108, 0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF, + 0x1231, 0x0210, 0x3273, 0x2252, 0x52B5, 0x4294, 0x72F7, 0x62D6, + 0x9339, 0x8318, 0xB37B, 0xA35A, 0xD3BD, 0xC39C, 0xF3FF, 0xE3DE, + 0x2462, 0x3443, 0x0420, 0x1401, 0x64E6, 0x74C7, 0x44A4, 0x5485, + 0xA56A, 0xB54B, 0x8528, 0x9509, 0xE5EE, 0xF5CF, 0xC5AC, 0xD58D, + 0x3653, 0x2672, 0x1611, 0x0630, 0x76D7, 0x66F6, 0x5695, 0x46B4, + 0xB75B, 0xA77A, 0x9719, 0x8738, 0xF7DF, 0xE7FE, 0xD79D, 0xC7BC, + 0x48C4, 0x58E5, 0x6886, 0x78A7, 0x0840, 0x1861, 0x2802, 0x3823, + 0xC9CC, 0xD9ED, 0xE98E, 0xF9AF, 0x8948, 0x9969, 0xA90A, 0xB92B, + 0x5AF5, 0x4AD4, 0x7AB7, 0x6A96, 0x1A71, 0x0A50, 0x3A33, 0x2A12, + 0xDBFD, 0xCBDC, 0xFBBF, 0xEB9E, 0x9B79, 0x8B58, 0xBB3B, 0xAB1A, + 0x6CA6, 0x7C87, 0x4CE4, 0x5CC5, 0x2C22, 0x3C03, 0x0C60, 0x1C41, + 0xEDAE, 0xFD8F, 0xCDEC, 0xDDCD, 0xAD2A, 0xBD0B, 0x8D68, 0x9D49, + 0x7E97, 0x6EB6, 0x5ED5, 0x4EF4, 0x3E13, 0x2E32, 0x1E51, 0x0E70, + 0xFF9F, 0xEFBE, 0xDFDD, 0xCFFC, 0xBF1B, 0xAF3A, 0x9F59, 0x8F78, + 0x9188, 0x81A9, 0xB1CA, 0xA1EB, 0xD10C, 0xC12D, 0xF14E, 0xE16F, + 0x1080, 0x00A1, 0x30C2, 0x20E3, 0x5004, 0x4025, 0x7046, 0x6067, + 0x83B9, 0x9398, 0xA3FB, 0xB3DA, 0xC33D, 0xD31C, 0xE37F, 0xF35E, + 0x02B1, 0x1290, 0x22F3, 0x32D2, 0x4235, 0x5214, 0x6277, 0x7256, + 0xB5EA, 0xA5CB, 0x95A8, 0x8589, 0xF56E, 0xE54F, 0xD52C, 0xC50D, + 0x34E2, 0x24C3, 0x14A0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, + 0xA7DB, 0xB7FA, 0x8799, 0x97B8, 0xE75F, 0xF77E, 0xC71D, 0xD73C, + 0x26D3, 0x36F2, 0x0691, 0x16B0, 0x6657, 0x7676, 0x4615, 0x5634, + 0xD94C, 0xC96D, 0xF90E, 0xE92F, 0x99C8, 0x89E9, 0xB98A, 0xA9AB, + 0x5844, 0x4865, 0x7806, 0x6827, 0x18C0, 0x08E1, 0x3882, 0x28A3, + 0xCB7D, 0xDB5C, 0xEB3F, 0xFB1E, 0x8BF9, 0x9BD8, 0xABBB, 0xBB9A, + 0x4A75, 0x5A54, 0x6A37, 0x7A16, 0x0AF1, 0x1AD0, 0x2AB3, 0x3A92, + 0xFD2E, 0xED0F, 0xDD6C, 0xCD4D, 0xBDAA, 0xAD8B, 0x9DE8, 0x8DC9, + 0x7C26, 0x6C07, 0x5C64, 0x4C45, 0x3CA2, 0x2C83, 0x1CE0, 0x0CC1, + 0xEF1F, 0xFF3E, 0xCF5D, 0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8, + 0x6E17, 0x7E36, 0x4E55, 0x5E74, 0x2E93, 0x3EB2, 0x0ED1, 0x1EF0, +}; + +/******************************************************************************* + * FUNCTION: crc_ccitt_calculate + * + * SUMMARY: Calculate CRC with ccitt standard by CRC table. + * + * RETURN: + * CRC calculation result + * + * PARAMETERS: + * *q - pointer to the data array to be calculated + * len - size of data array + ******************************************************************************/ +static unsigned short crc_ccitt_calculate(unsigned char *q, int len) +{ + unsigned short crc = 0xffff; + + while (len-- > 0) + crc = ccitt_Table[(crc >> 8 ^ *q++) & 0xff] ^ (crc << 8); + return crc; +} + +/******************************************************************************* + * FUNCTION: pt_pip2_cmd_calculate_crc + * + * SUMMARY: Calculate the CRC of a command packet + * + * RETURN: void + * + * PARAMETERS: + * *cmd - pointer to command data + * extra_bytes - Extra bytes included in command length + ******************************************************************************/ +static void pt_pip2_cmd_calculate_crc(struct pip2_cmd_structure *cmd, + u8 extra_bytes) +{ + u8 buf[PT_MAX_PIP2_MSG_SIZE + 1] = {0}; + unsigned short crc; + + buf[0] = cmd->len & 0xff; + buf[1] = (cmd->len & 0xff00) >> 8; + buf[2] = cmd->seq; + buf[3] = cmd->id; + memcpy(&buf[4], cmd->data, cmd->len - extra_bytes); + /* Calculate the CRC for the first 4 bytes above and the data payload */ + crc = crc_ccitt_calculate(buf, 4 + (cmd->len - extra_bytes)); + cmd->crc[0] = (crc & 0xff00) >> 8; + cmd->crc[1] = (crc & 0xff); +} + +/******************************************************************************* + * FUNCTION: pt_pip2_get_next_cmd_seq + * + * SUMMARY: Gets the next sequence number for a PIP2 command. The sequence + * number is a 3 bit value (bits [0-2]) but because TTDL will always have + * the TAG bit set (bit 3), the counter starts at 0x08 and goes to 0x0F. + * If the "force_pip2_seq" holds a valid seq value (0x08-0x0F) then do not + * increment, just use the forced value. + * + * RETURN: Next command sequence number [0x08-0x0F] + * + * PARAMETERS: + * *cd - pointer to core data + ******************************************************************************/ +static u8 pt_pip2_get_next_cmd_seq(struct pt_core_data *cd) +{ +#ifdef TTDL_DIAGNOSTICS + if (cd->force_pip2_seq <= 0x07) { + cd->pip2_cmd_tag_seq++; + if (cd->pip2_cmd_tag_seq > 0x0F) + cd->pip2_cmd_tag_seq = 0x08; + } else { + cd->pip2_cmd_tag_seq = cd->force_pip2_seq; + } +#else + cd->pip2_cmd_tag_seq++; + if (cd->pip2_cmd_tag_seq > 0x0F) + cd->pip2_cmd_tag_seq = 0x08; +#endif + return cd->pip2_cmd_tag_seq; +} + +/* + * Following macros are to define the response time (the interval between PIP2 + * command finishes sending and INT pin falls). The unit is in microsecond. + * It has different time settings between the solution GPIO polling and Bus + * polling due to the considration for system load. + */ +#ifdef PT_POLL_RESP_BY_BUS +#define POLL_RETRY_DEFAULT_INTERVAL 50 +#define PIP2_RESP_DEFAULT_TIME_MIN 50 +#define PIP2_RESP_DEFAULT_TIME_MAX (PT_PIP_CMD_DEFAULT_TIMEOUT * 1000) +#define PIP2_RESP_FILE_WRITE_TIME_MIN 220 +#define PIP2_RESP_FILE_IOCTL_TIME_MAX (PT_PIP2_CMD_FILE_ERASE_TIMEOUT * 1000) +#else +#define POLL_RETRY_DEFAULT_INTERVAL 20 +#define PIP2_RESP_DEFAULT_TIME_MIN 20 +#define PIP2_RESP_DEFAULT_TIME_MAX (PT_PIP_CMD_DEFAULT_TIMEOUT * 1000) +#define PIP2_RESP_FILE_WRITE_TIME_MIN 20 +#define PIP2_RESP_FILE_IOCTL_TIME_MAX (PT_PIP2_CMD_FILE_ERASE_TIMEOUT * 1000) +#endif +/* + * id: the command id defined in PIP2 + * response_len: the (maximum) length of response. + * response_time_min: minimum response time in microsecond + * response_time_max: maximum response time in microsecond + */ +static const struct pip2_cmd_response_structure pip2_cmd_response[] = { + {.id = PIP2_CMD_ID_PING, + .response_len = 255, + .response_time_min = PIP2_RESP_DEFAULT_TIME_MIN, + .response_time_max = PIP2_RESP_DEFAULT_TIME_MAX}, + {.id = PIP2_CMD_ID_STATUS, + .response_len = PIP2_EXTRA_BYTES_NUM + 5, + .response_time_min = PIP2_RESP_DEFAULT_TIME_MIN, + .response_time_max = PIP2_RESP_DEFAULT_TIME_MAX}, + {.id = PIP2_CMD_ID_CTRL, + .response_len = PIP2_EXTRA_BYTES_NUM + 1, + .response_time_min = PIP2_RESP_DEFAULT_TIME_MIN, + .response_time_max = PT_PIP2_CMD_FILE_ERASE_TIMEOUT}, + {.id = PIP2_CMD_ID_CONFIG, + .response_len = PIP2_EXTRA_BYTES_NUM + 1, + .response_time_min = PIP2_RESP_DEFAULT_TIME_MIN, + .response_time_max = PIP2_RESP_DEFAULT_TIME_MAX}, + {.id = PIP2_CMD_ID_CLEAR, + .response_len = PIP2_EXTRA_BYTES_NUM + 0, + .response_time_min = PIP2_RESP_DEFAULT_TIME_MIN, + .response_time_max = PIP2_RESP_DEFAULT_TIME_MAX}, + {.id = PIP2_CMD_ID_RESET, + .response_len = PIP2_EXTRA_BYTES_NUM + 1, + .response_time_min = PIP2_RESP_DEFAULT_TIME_MIN, + .response_time_max = PIP2_RESP_DEFAULT_TIME_MAX}, + {.id = PIP2_CMD_ID_VERSION, + .response_len = PIP2_EXTRA_BYTES_NUM + 23, + .response_time_min = PIP2_RESP_DEFAULT_TIME_MIN, + .response_time_max = PIP2_RESP_DEFAULT_TIME_MAX}, + {.id = PIP2_CMD_ID_FILE_OPEN, + .response_len = PIP2_EXTRA_BYTES_NUM + 2, + .response_time_min = PIP2_RESP_DEFAULT_TIME_MIN, + .response_time_max = PIP2_RESP_DEFAULT_TIME_MAX}, + {.id = PIP2_CMD_ID_FILE_CLOSE, + .response_len = PIP2_EXTRA_BYTES_NUM + 1, + .response_time_min = PIP2_RESP_DEFAULT_TIME_MIN, + .response_time_max = PIP2_RESP_DEFAULT_TIME_MAX}, + {.id = PIP2_CMD_ID_FILE_READ, + .response_len = 255, + .response_time_min = PIP2_RESP_DEFAULT_TIME_MIN, + .response_time_max = PIP2_RESP_DEFAULT_TIME_MAX}, + {.id = PIP2_CMD_ID_FILE_WRITE, + .response_len = PIP2_EXTRA_BYTES_NUM + 1, + .response_time_min = PIP2_RESP_FILE_WRITE_TIME_MIN, + .response_time_max = PIP2_RESP_DEFAULT_TIME_MAX}, + {.id = PIP2_CMD_ID_FILE_IOCTL, + .response_len = PIP2_EXTRA_BYTES_NUM + 10, + .response_time_min = PIP2_RESP_DEFAULT_TIME_MIN, + .response_time_max = PIP2_RESP_FILE_IOCTL_TIME_MAX}, + {.id = PIP2_CMD_ID_FLASH_INFO, + .response_len = PIP2_EXTRA_BYTES_NUM + 17, + .response_time_min = PIP2_RESP_DEFAULT_TIME_MIN, + .response_time_max = PIP2_RESP_DEFAULT_TIME_MAX}, + {.id = PIP2_CMD_ID_EXECUTE, + .response_len = PIP2_EXTRA_BYTES_NUM + 1, + .response_time_min = PIP2_RESP_DEFAULT_TIME_MIN, + .response_time_max = PIP2_RESP_DEFAULT_TIME_MAX}, + {.id = PIP2_CMD_ID_GET_LAST_ERRNO, + .response_len = PIP2_EXTRA_BYTES_NUM + 3, + .response_time_min = PIP2_RESP_DEFAULT_TIME_MIN, + .response_time_max = PIP2_RESP_DEFAULT_TIME_MAX}, + {.id = PIP2_CMD_ID_EXIT_HOST_MODE, + .response_len = PIP2_EXTRA_BYTES_NUM + 1, + .response_time_min = PIP2_RESP_DEFAULT_TIME_MIN, + .response_time_max = PIP2_RESP_DEFAULT_TIME_MAX}, + {.id = PIP2_CMD_ID_READ_GPIO, + .response_len = PIP2_EXTRA_BYTES_NUM + 5, + .response_time_min = PIP2_RESP_DEFAULT_TIME_MIN, + .response_time_max = PIP2_RESP_DEFAULT_TIME_MAX}, + {.id = PIP2_CMD_EXECUTE_SCAN, + .response_len = PIP2_EXTRA_BYTES_NUM + 1, + .response_time_min = PIP2_RESP_DEFAULT_TIME_MIN, + .response_time_max = PIP2_RESP_DEFAULT_TIME_MAX}, + {.id = PIP2_CMD_SET_PARAMETER, + .response_len = PIP2_EXTRA_BYTES_NUM + 1, + .response_time_min = PIP2_RESP_DEFAULT_TIME_MIN, + .response_time_max = PIP2_RESP_DEFAULT_TIME_MAX}, + {.id = PIP2_CMD_GET_PARAMETER, + .response_len = PIP2_EXTRA_BYTES_NUM + 7, + .response_time_min = PIP2_RESP_DEFAULT_TIME_MIN, + .response_time_max = PIP2_RESP_DEFAULT_TIME_MAX}, + {.id = PIP2_CMD_SET_DDI_REG, + .response_len = PIP2_EXTRA_BYTES_NUM + 1, + .response_time_min = PIP2_RESP_DEFAULT_TIME_MIN, + .response_time_max = PIP2_RESP_DEFAULT_TIME_MAX}, + {.id = PIP2_CMD_GET_DDI_REG, + .response_len = PIP2_EXTRA_BYTES_NUM + 249, + .response_time_min = PIP2_RESP_DEFAULT_TIME_MIN, + .response_time_max = PIP2_RESP_DEFAULT_TIME_MAX}, + {.id = PIP2_CMD_ID_END, + .response_len = 255, + .response_time_min = PIP2_RESP_DEFAULT_TIME_MIN, + .response_time_max = PIP2_RESP_DEFAULT_TIME_MAX} +}; + +/******************************************************************************* + * FUNCTION: pt_pip2_get_cmd_response_len + * + * SUMMARY: Gets the expected response length based on the command ID + * + * RETURN: Expected response length + * + * PARAMETERS: + * id - Command ID (-1 means input ID is not in list of PIP2 command) + ******************************************************************************/ +static int pt_pip2_get_cmd_response_len(u8 id) +{ + const struct pip2_cmd_response_structure *p = pip2_cmd_response; + + while ((p->id != id) && (p->id != PIP2_CMD_ID_END)) + p++; + + if (p->id != PIP2_CMD_ID_END) + return p->response_len; + else + return -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 lenght 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 = ¶m_buf[2]; + while (i < param_size) + param_value += *(param_buf++) << (8 * i++); + + /* Check command response for Set Parameter command */ + if (cd->response_buf[2] != PT_PIP_NON_HID_RESPONSE_ID + || (cd->response_buf[4] & + PIP1_RESP_COMMAND_ID_MASK) != + PIP1_CMD_ID_SET_PARAM + || cd->response_buf[5] != param_id + || cd->response_buf[6] != param_size) { + pt_debug(cd->dev, DL_ERROR, + "%s: Set Parameter command not successful\n", + __func__); + return; + } + + pt_add_parameter(cd, param_id, param_value, param_size); +} + +/******************************************************************************* + * FUNCTION: pt_check_command + * + * SUMMARY: Check the output command. The function pt_check_set_parameter() is + * called here to check output command and store parameter to the list. + * + * PARAMETERS: + * *cd - pointer to core data + * *hid_output - pointer to hid output data structure + * raw - flag to show if output cmd is user cmd(1:user cmd) + ******************************************************************************/ +static void pt_check_command(struct pt_core_data *cd, + struct pt_hid_output *hid_output, bool raw) +{ + pt_check_set_parameter(cd, hid_output, raw); +} + +/******************************************************************************* + * FUNCTION: pt_hid_output_validate_response + * + * SUMMARY: Validate the response of application or bootloader command. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data + * *hid_output - pointer to hid output data structure + ******************************************************************************/ +static int pt_hid_output_validate_response(struct pt_core_data *cd, + struct pt_hid_output *hid_output) +{ + if (hid_output->cmd_type == PIP1_CMD_TYPE_BL) + return pt_hid_output_validate_bl_response(cd, hid_output); + + return pt_hid_output_validate_app_response(cd, hid_output); + +} + +/******************************************************************************* + * FUNCTION: pt_hid_send_output_user_ + * + * SUMMARY: Blindly send user data to the DUT. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data + * *hid_output - pointer to the command to send + ******************************************************************************/ +static int pt_hid_send_output_user_(struct pt_core_data *cd, + struct pt_hid_output *hid_output) +{ + int rc = 0; + int cmd; + + if (!hid_output->length || !hid_output->write_buf) + return -EINVAL; + + if (cd->pip2_prot_active) { + cmd = hid_output->write_buf[PIP2_CMD_COMMAND_ID_OFFSET]; + cmd &= PIP2_CMD_COMMAND_ID_MASK; + } else + cmd = hid_output->write_buf[PIP1_CMD_COMMAND_ID_OFFSET]; + + pt_debug(cd->dev, DL_INFO, + ">>> %s: Write Buffer Size[%d] Cmd[0x%02X]\n", + __func__, hid_output->length, cmd); + pt_pr_buf(cd->dev, DL_DEBUG, hid_output->write_buf, + hid_output->length, ">>> User CMD"); + + rc = pt_adap_write_read_specific(cd, hid_output->length, + hid_output->write_buf, NULL); + if (rc) + pt_debug(cd->dev, DL_ERROR, + "%s: Fail pt_adap_transfer\n", __func__); + + return rc; +} + +/******************************************************************************* + * FUNCTION: pt_hid_send_output_user_and_wait_ + * + * SUMMARY: Blindly send user data to the DUT and wait for the response. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data + * *hid_output - pointer to the command to send + ******************************************************************************/ +static int pt_hid_send_output_user_and_wait_(struct pt_core_data *cd, + struct pt_hid_output *hid_output) +{ + int rc = 0; + int t; + + mutex_lock(&cd->system_lock); + cd->hid_cmd_state = PIP1_CMD_ID_USER_CMD + 1; + mutex_unlock(&cd->system_lock); + + rc = pt_hid_send_output_user_(cd, hid_output); + if (rc) + goto error; + + t = wait_event_timeout(cd->wait_q, (cd->hid_cmd_state == 0), + msecs_to_jiffies(cd->pip_cmd_timeout)); + if (IS_TMO(t)) { +#ifdef TTDL_DIAGNOSTICS + cd->bus_transmit_error_count++; + pt_toggle_err_gpio(cd, PT_ERR_GPIO_I2C_TRANS); +#endif /* TTDL_DIAGNOSTICS */ + pt_debug(cd->dev, DL_ERROR, + "%s: HID output cmd execution timed out\n", + __func__); + rc = -ETIME; + goto error; + } + + pt_check_command(cd, hid_output, true); + + goto exit; + +error: + mutex_lock(&cd->system_lock); + cd->hid_cmd_state = 0; + mutex_unlock(&cd->system_lock); + +exit: + return rc; +} + +/******************************************************************************* + * FUNCTION: pt_check_irq_asserted + * + * SUMMARY: Checks if the IRQ GPIO is asserted or not. There are times when + * the FW can hold the INT line low ~150us after the read is complete. + * NOTE: if irq_stat is not defined this function will return false + * + * RETURN: + * true = IRQ asserted + * false = IRQ not asserted + * + * PARAMETERS: + * *cd - pointer to core data + ******************************************************************************/ +static bool pt_check_irq_asserted(struct pt_core_data *cd) +{ +#ifdef ENABLE_WORKAROUND_FOR_GLITCH_AFTER_BL_LAUNCH_APP + /* + * Workaround for FW defect, CDT165308 + * bl_launch app creates a glitch in IRQ line + */ + if (cd->hid_cmd_state == PIP1_BL_CMD_ID_LAUNCH_APP + 1 + && cd->cpdata->irq_stat) { + /* + * in X1S panel and GC1546 panel, the width for the INT + * glitch is about 4us,the normal INT width of response + * will last more than 200us, so use 10us delay + * for distinguish the glitch the normal INT is enough. + */ + udelay(10); + } +#endif + if (cd->cpdata->irq_stat) { + if (cd->cpdata->irq_stat(cd->cpdata, cd->dev) + == PT_IRQ_ASSERTED_VALUE) { + /* Debounce to allow FW to release INT */ + usleep_range(100, 200); + } + if (cd->cpdata->irq_stat(cd->cpdata, cd->dev) + == PT_IRQ_ASSERTED_VALUE) + return true; + else + return false; + } + return true; +} + +/******************************************************************************* + * FUNCTION: pt_flush_bus + * + * SUMMARY: Force flushing the bus by reading len bytes or forced 255 bytes + * Used if IRQ is found to be stuck low + * + * RETURN: Length of bytes read from bus + * + * PARAMETERS: + * *cd - pointer to core data + * flush_type - type of flush + * - PT_FLUSH_BUS_BASED_ON_LEN (two reads) + * - PT_FLUSH_BUS_FULL_256_READ + * *read_buf - pointer to store read data + ******************************************************************************/ +static ssize_t pt_flush_bus(struct pt_core_data *cd, + u8 flush_type, u8 *read_buf) +{ + u8 buf[PT_MAX_PIP2_MSG_SIZE]; + u16 pip_len; + int bytes_read; + int rc = 0; + + if (flush_type == PT_FLUSH_BUS_BASED_ON_LEN) { + rc = pt_adap_read_default(cd, buf, 2); + if (rc) { + bytes_read = 0; + goto exit; + } + + pip_len = get_unaligned_le16(&buf[0]); + + if (pip_len == 2 || pip_len >= PT_PIP_1P7_EMPTY_BUF) { +#ifdef TTDL_DIAGNOSTICS + pt_toggle_err_gpio(cd, PT_ERR_GPIO_EMPTY_PACKET); +#endif + bytes_read = 2; + pt_debug(cd->dev, DL_INFO, + "%s: Empty buf detected - len=0x%04X\n", + __func__, pip_len); + } else if (pip_len == 0) { + bytes_read = 0; + pt_debug(cd->dev, DL_INFO, + "%s: Sentinel detected\n", __func__); + } else if (pip_len > PT_MAX_PIP2_MSG_SIZE) { + pt_debug(cd->dev, DL_ERROR, + "%s: Illegal len=0x%04x, force %d byte read\n", + __func__, pip_len, PT_MAX_PIP2_MSG_SIZE); + rc = pt_adap_read_default(cd, buf, + PT_MAX_PIP2_MSG_SIZE); + if (!rc) + bytes_read = PT_MAX_PIP2_MSG_SIZE; + else + bytes_read = 0; + } else { + pt_debug(cd->dev, DL_INFO, + "%s: Flush read of %d bytes...\n", + __func__, pip_len); + + rc = pt_adap_read_default(cd, buf, pip_len); + if (!rc) + bytes_read = pip_len; + else + bytes_read = 0; + } + } else { + pt_debug(cd->dev, DL_INFO, + "%s: Forced flush of max %d bytes...\n", + __func__, PT_MAX_PIP2_MSG_SIZE); + rc = pt_adap_read_default(cd, buf, PT_MAX_PIP2_MSG_SIZE); + if (!rc) + bytes_read = PT_MAX_PIP2_MSG_SIZE; + else + bytes_read = 0; + } + + if (read_buf && (bytes_read > 3)) + memcpy(read_buf, buf, bytes_read); + +exit: + return bytes_read; +} + +/******************************************************************************* + * FUNCTION: pt_flush_bus_if_irq_asserted + * + * SUMMARY: This function will flush the active bus if the INT is found to be + * asserted. + * + * RETURN: bytes cleared from bus + * + * PARAMETERS: + * *cd - pointer the core data structure + * flush_type - type of flush + * - PT_FLUSH_BUS_BASED_ON_LEN + * - PT_FLUSH_BUS_FULL_256_READ + ******************************************************************************/ +static int pt_flush_bus_if_irq_asserted(struct pt_core_data *cd, u8 flush_type) +{ + int count = 0; + int bytes_read = 0; + + while (pt_check_irq_asserted(cd) && count < 5) { + count++; + bytes_read = pt_flush_bus(cd, flush_type, NULL); + if (bytes_read) { + pt_debug(cd->dev, DL_WARN, + "%s: Cleared %d bytes off bus\n", + __func__, bytes_read); + } + } + if (pt_check_irq_asserted(cd)) { + pt_debug(cd->dev, DL_ERROR, + "%s: IRQ still asserted, %d bytes read\n", + __func__, bytes_read); + } else { + pt_debug(cd->dev, DL_INFO, + "%s: IRQ cleared, %d bytes read\n", + __func__, bytes_read); + } + return bytes_read; +} + +/******************************************************************************* + * FUNCTION: pt_hid_send_output_ + * + * SUMMARY: Send a touch application command to the DUT + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data + * *hid_output - pointer to the command to send + ******************************************************************************/ +static int pt_hid_send_output_(struct pt_core_data *cd, + struct pt_hid_output *hid_output) +{ + int rc = 0; + u8 *cmd; + u16 length; + u16 crc; + u8 report_id; + u8 cmd_offset = 0; + u8 cmd_allocated = 0; + + + switch (hid_output->cmd_type) { + case PIP1_CMD_TYPE_FW: + report_id = PT_PIP_NON_HID_COMMAND_ID; + length = 5; + break; + case PIP1_CMD_TYPE_BL: + report_id = PT_PIP_BL_COMMAND_REPORT_ID; + length = 11 /* 5 + SOP + LEN(2) + CRC(2) + EOP */; + break; + default: + return -EINVAL; + } + + length += hid_output->write_length; + + if (length + 2 > PT_PREALLOCATED_CMD_BUFFER) { + cmd = kzalloc(length + 2, GFP_KERNEL); + if (!cmd) + return -ENOMEM; + cmd_allocated = 1; + } else { + cmd = cd->cmd_buf; + } + + /* Set Output register */ + memcpy(&cmd[cmd_offset], &cd->hid_desc.output_register, + sizeof(cd->hid_desc.output_register)); + cmd_offset += sizeof(cd->hid_desc.output_register); + + cmd[cmd_offset++] = LOW_BYTE(length); + cmd[cmd_offset++] = HI_BYTE(length); + cmd[cmd_offset++] = report_id; + cmd[cmd_offset++] = 0x0; /* reserved */ + if (hid_output->cmd_type == PIP1_CMD_TYPE_BL) + cmd[cmd_offset++] = PIP1_BL_SOP; + cmd[cmd_offset++] = hid_output->command_code; + + /* Set Data Length for bootloader */ + if (hid_output->cmd_type == PIP1_CMD_TYPE_BL) { + cmd[cmd_offset++] = LOW_BYTE(hid_output->write_length); + cmd[cmd_offset++] = HI_BYTE(hid_output->write_length); + } + /* Set Data */ + if (hid_output->write_length && hid_output->write_buf) { + memcpy(&cmd[cmd_offset], hid_output->write_buf, + hid_output->write_length); + cmd_offset += hid_output->write_length; + } + if (hid_output->cmd_type == PIP1_CMD_TYPE_BL) { + crc = _pt_compute_crc(&cmd[6], + hid_output->write_length + 4); + cmd[cmd_offset++] = LOW_BYTE(crc); + cmd[cmd_offset++] = HI_BYTE(crc); + cmd[cmd_offset++] = PIP1_BL_EOP; + } + + pt_debug(cd->dev, DL_INFO, + ">>> %s: Write Buffer Size[%d] Cmd[0x%02X]\n", + __func__, length + 2, hid_output->command_code); + pt_pr_buf(cd->dev, DL_DEBUG, cmd, length + 2, ">>> CMD"); + rc = pt_adap_write_read_specific(cd, length + 2, cmd, NULL); + + if (rc) + pt_debug(cd->dev, DL_ERROR, + "%s: Fail pt_adap_transfer rc=%d\n", __func__, rc); + + if (cmd_allocated) + kfree(cmd); + return rc; +} + +/******************************************************************************* + * FUNCTION: pt_pip1_send_output_and_wait_ + * + * SUMMARY: Send valid PIP1 command to the DUT and wait for the response. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data + * *hid_output - pointer to the command to send + ******************************************************************************/ +static int pt_pip1_send_output_and_wait_(struct pt_core_data *cd, + struct pt_hid_output *hid_output) +{ + int rc = 0; + int t; + u16 timeout_ms; + + mutex_lock(&cd->system_lock); + cd->hid_cmd_state = hid_output->command_code + 1; + mutex_unlock(&cd->system_lock); + + if (hid_output->timeout_ms) + timeout_ms = hid_output->timeout_ms; + else + timeout_ms = PT_PIP1_CMD_DEFAULT_TIMEOUT; + + rc = pt_hid_send_output_(cd, hid_output); + if (rc) + goto error; + + t = wait_event_timeout(cd->wait_q, (cd->hid_cmd_state == 0), + msecs_to_jiffies(timeout_ms)); + if (IS_TMO(t)) { +#ifdef TTDL_DIAGNOSTICS + cd->bus_transmit_error_count++; + pt_toggle_err_gpio(cd, PT_ERR_GPIO_I2C_TRANS); +#endif /* TTDL_DIAGNOSTICS */ + pt_debug(cd->dev, DL_ERROR, + "%s: HID output cmd execution timed out (%dms)\n", + __func__, timeout_ms); + rc = -ETIME; + goto error; + } + + if (!hid_output->novalidate) + rc = pt_hid_output_validate_response(cd, hid_output); + + pt_check_command(cd, hid_output, false); + goto exit; + +error: + mutex_lock(&cd->system_lock); + cd->hid_cmd_state = 0; + mutex_unlock(&cd->system_lock); +exit: + return rc; +} + +/******************************************************************************* + * FUNCTION: pt_hid_output_user_cmd_ + * + * SUMMARY: Load the write buffer into a HID structure and send it as a HID cmd + * to the DUT waiting for the response and loading it into the read buffer + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data + * read_len - expected read length of the response + * *read_buf - pointer to where the response will be loaded + * write_len - length of the write buffer + * *write_buf - pointer to the write buffer + * *actual_read_len - pointer to the actual amount of data read back + ******************************************************************************/ +static int pt_hid_output_user_cmd_(struct pt_core_data *cd, + u16 read_len, u8 *read_buf, u16 write_len, u8 *write_buf, + u16 *actual_read_len) +{ + int rc = 0; + u16 size; + struct pt_hid_output hid_output = { + .length = write_len, + .write_buf = write_buf, + }; + +#ifdef TTHE_TUNER_SUPPORT + if (!cd->pip2_send_user_cmd) { + int command_code = 0; + int len; + + /* Print up to cmd ID */ + len = PIP1_CMD_COMMAND_ID_OFFSET + 1; + if (write_len < len) + len = write_len; + else + command_code = write_buf[PIP1_CMD_COMMAND_ID_OFFSET] + & PIP1_CMD_COMMAND_ID_MASK; + + /* Don't print EXEC_PANEL_SCAN & RETRIEVE_PANEL_SCAN commands */ + if (command_code != PIP1_CMD_ID_EXEC_PANEL_SCAN && + command_code != PIP1_CMD_ID_RETRIEVE_PANEL_SCAN) + tthe_print(cd, write_buf, len, "CMD="); + } +#endif + + rc = pt_hid_send_output_user_and_wait_(cd, &hid_output); + if (rc) + return rc; + + /* Get the response size from the first 2 bytes in the response */ + size = get_unaligned_le16(&cd->response_buf[0]); + + /* Ensure size is not greater than max buffer size */ + if (size > PT_MAX_PIP2_MSG_SIZE) + size = PT_MAX_PIP2_MSG_SIZE; + + /* Minimum size to read is the 2 byte len field */ + if (size == 0) + size = 2; + + if (size > read_len) { + pt_debug(cd->dev, DL_ERROR, + "%s: PIP2 len field=%d, requested read_len=%d\n", + __func__, size, read_len); + *actual_read_len = 0; + return -EIO; + } + + memcpy(read_buf, cd->response_buf, size); + *actual_read_len = size; + + return 0; +} + +/******************************************************************************* + * FUNCTION: pt_hid_output_user_cmd + * + * SUMMARY: Protected call to pt_hid_output_user_cmd_ by exclusive access to + * the DUT. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data + * read_len - length of data to read + * *read_buf - pointer to store read data + * write_len - length of data to write + * *write_buf - pointer to buffer to write + * *actual_read_len - pointer to store data length actually read + ******************************************************************************/ +static int pt_hid_output_user_cmd(struct pt_core_data *cd, + u16 read_len, u8 *read_buf, u16 write_len, u8 *write_buf, + u16 *actual_read_len) +{ + int rc; + + rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT); + if (rc < 0) { + pt_debug(cd->dev, DL_ERROR, + "%s: fail get exclusive ex=%p own=%p\n", + __func__, cd->exclusive_dev, cd->dev); + return rc; + } + + rc = pt_hid_output_user_cmd_(cd, read_len, read_buf, + write_len, write_buf, actual_read_len); + + if (release_exclusive(cd, cd->dev) < 0) + pt_debug(cd->dev, DL_ERROR, + "%s: fail to release exclusive\n", __func__); + + return rc; +} + +/******************************************************************************* + * FUNCTION: _pt_request_pip2_send_cmd + * + * SUMMARY: Writes a PIP2 command packet to DUT, then waits for the + * interrupt and reads response data to read_buf + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *dev - pointer to device structure + * protect - flag to run in protected mode + * id - ID of PIP command + * *data - pointer to PIP data payload + * report_body_len - report length + * *read_buf - pointer to response buffer + * *actual_read_len - pointer to response buffer length + ******************************************************************************/ +static int _pt_request_pip2_send_cmd(struct device *dev, + int protect, u8 id, u8 *data, u16 report_body_len, u8 *read_buf, + u16 *actual_read_len) +{ + struct pt_core_data *cd = dev_get_drvdata(dev); + struct pip2_cmd_structure pip2_cmd; + int rc = 0; + 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); + 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/sytem load heavily if the + * interval is longer than 10 us, so it is safe to poll GPIO with a + * fixed interval: 20 us. + */ + usleep_range(resp_time_min, resp_time_min+10); + max_retry = resp_time_max / POLL_RETRY_DEFAULT_INTERVAL; + while ((retry < max_retry) && (retry_total_time < resp_time_max)) { + if (!gpio_get_value(cd->cpdata->irq_gpio)) { + rc = pt_adap_read_default(cd, read_buf, read_len); + size = get_unaligned_le16(&read_buf[0]); + if (rc) + pt_debug(dev, DL_ERROR, + "%s: SPI read Error = %d\n", + __func__, rc); + else if (size > read_len) { + pt_debug(cd->dev, DL_ERROR, + "%s: PIP2 len field=%d, requested read_len=%d\n", + __func__, size, read_len); + rc = -EIO; + } + break; + } + /* + * Poll GPIO with fixed interval 20 us, and tollerance is + * limited to 10 us to speed up the process. + */ + retry_interval = POLL_RETRY_DEFAULT_INTERVAL; + usleep_range(retry_interval, retry_interval+10); + retry_total_time += retry_interval; + } +#endif + + *actual_read_len = size; + + if (rc || (retry >= max_retry) || (retry_total_time >= resp_time_max)) { + pt_debug(dev, DL_ERROR, + "%s cmd[0x%02X] timed out, send_seq=0x%02X, resp_seq=0x%02X\n", + __func__, pip2_cmd.id, pip2_cmd.seq, response_seq); + *actual_read_len = 0; + rc = -EINVAL; + } + + pt_pr_buf(cd->dev, DL_DEBUG, read_buf, *actual_read_len, + "<<< NO_INT Read"); + +exit: + kfree(write_buf); + if (protect == PT_CORE_CMD_PROTECTED) { + if (release_exclusive(cd, cd->dev) < 0) + pt_debug(cd->dev, DL_ERROR, + "%s: fail to release exclusive\n", __func__); + } + return rc; +} + +/******************************************************************************* + * FUNCTION: pt_pip_null_ + * + * SUMMARY: Send the PIP "ping"(0x00) command to the DUT and wait for response. + * This function is used by watchdog to check if the fw corrupts. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data + ******************************************************************************/ +static int pt_pip_null_(struct pt_core_data *cd) +{ + struct pt_hid_output hid_output = { + CREATE_PIP1_FW_CMD(PIP1_CMD_ID_NULL), + }; + + return pt_pip1_send_output_and_wait_(cd, &hid_output); +} + +/******************************************************************************* + * FUNCTION: pt_pip_null + * + * SUMMARY: Wrapper function for pt_pip_null_ that guarantees exclusive access. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data + ******************************************************************************/ +static int pt_pip_null(struct pt_core_data *cd) +{ + int rc; + + rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT); + if (rc < 0) { + pt_debug(cd->dev, DL_ERROR, + "%s: fail get exclusive ex=%p own=%p\n", + __func__, cd->exclusive_dev, cd->dev); + return rc; + } + + rc = pt_pip_null_(cd); + + if (release_exclusive(cd, cd->dev) < 0) + pt_debug(cd->dev, DL_ERROR, + "%s: fail to release exclusive\n", __func__); + + return rc; +} + +static void pt_stop_wd_timer(struct pt_core_data *cd); +/******************************************************************************* + * FUNCTION: pt_pip_start_bootloader_ + * + * SUMMARY: Sends the HID command start_bootloader [PIP cmd 0x01] to the DUT + * + * NOTE: The WD MUST be stopped/restarted by the calling Function. Having + * the WD active could cause this function to fail! + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data + ******************************************************************************/ +static int pt_pip_start_bootloader_(struct pt_core_data *cd) +{ + int rc = 0; + + struct pt_hid_output hid_output = { + CREATE_PIP1_FW_CMD(PIP1_CMD_ID_START_BOOTLOADER), + .timeout_ms = PT_PIP1_START_BOOTLOADER_TIMEOUT, + .reset_expected = 1, + }; + + if (cd->watchdog_enabled) { + pt_debug(cd->dev, DL_WARN, + "%s: watchdog isn't stopped before enter bl\n", + __func__); + goto exit; + } + + /* Reset startup status after entering BL, new DUT enum required */ + cd->startup_status = STARTUP_STATUS_START; + pt_debug(cd->dev, DL_DEBUG, "%s: Startup Status Reset\n", __func__); + rc = pt_pip1_send_output_and_wait_(cd, &hid_output); + if (rc) { + pt_debug(cd->dev, DL_ERROR, + "%s: Start BL PIP cmd failed. rc = %d\n", + __func__, rc); + } + +exit: + return rc; +} + +/******************************************************************************* + * FUNCTION: pt_pip_start_bootloader + * + * SUMMARY: Protected function to force DUT to enter the BL + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data structure + ******************************************************************************/ +static int pt_pip_start_bootloader(struct pt_core_data *cd) +{ + int rc; + + rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT); + if (rc < 0) { + pt_debug(cd->dev, DL_ERROR, + "%s: fail get exclusive ex=%p own=%p\n", + __func__, cd->exclusive_dev, cd->dev); + return rc; + } + + rc = pt_pip_start_bootloader_(cd); + + if (release_exclusive(cd, cd->dev) < 0) + pt_debug(cd->dev, DL_ERROR, + "%s: fail to release exclusive\n", __func__); + + return rc; +} + +/******************************************************************************* + * FUNCTION: _pt_request_pip_start_bl + * + * SUMMARY: Function pointer included in core_nonhid_cmds to allow other + * modules to request the DUT to enter the BL + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *dev - pointer to device structure + * protect - flag to run in protected mode + ******************************************************************************/ +static int _pt_request_pip_start_bl(struct device *dev, int protect) +{ + struct pt_core_data *cd = dev_get_drvdata(dev); + + if (protect) + return pt_pip_start_bootloader(cd); + + return pt_pip_start_bootloader_(cd); +} + +/******************************************************************************* + * FUNCTION: pt_pip2_ver_load_ttdata + * + * SUMMARY: Function to load the Version information from the PIP2 VERSION + * command into the core data struct. + * + * RETURN: n/a + * + * PARAMETERS: + * *cd - pointer to core data structure + * len - Length of data in response_buf + ******************************************************************************/ +static void pt_pip2_ver_load_ttdata(struct pt_core_data *cd, u16 len) +{ + struct pt_ttdata *ttdata = &cd->sysinfo.ttdata; + struct pt_pip2_version_full *full_ver; + struct pt_pip2_version *ver; + + /* + * The PIP2 VERSION command can return different lengths of data. + * The additional LOT fields are included when the packet + * size is >= 29 bytes. Older FW sends a reduced packet size. + * NOTE: + * - The FW would swap the BL and FW versions when reporting + * the small packet. + * - Sub Lot bytes 16 and 17 are reserved. + */ + if (len >= 0x1D) { + full_ver = (struct pt_pip2_version_full *) + &cd->response_buf[PIP2_RESP_STATUS_OFFSET]; + + ttdata->pip_ver_major = full_ver->pip2_version_msb; + ttdata->pip_ver_minor = full_ver->pip2_version_lsb; + ttdata->bl_ver_major = full_ver->bl_version_msb; + ttdata->bl_ver_minor = full_ver->bl_version_lsb; + ttdata->fw_ver_major = full_ver->fw_version_msb; + ttdata->fw_ver_minor = full_ver->fw_version_lsb; + /* + * BL PIP 2.02 and greater the version fields are + * swapped + */ + if (ttdata->pip_ver_major >= 2 && ttdata->pip_ver_minor >= 2) { + ttdata->chip_rev = + get_unaligned_le16(&full_ver->chip_rev); + ttdata->chip_id = + get_unaligned_le16(&full_ver->chip_id); + } else { + ttdata->chip_rev = + get_unaligned_le16(&full_ver->chip_id); + ttdata->chip_id = + get_unaligned_le16(&full_ver->chip_rev); + } + memcpy(ttdata->uid, full_ver->uid, PT_UID_SIZE); + + pt_pr_buf(cd->dev, DL_INFO, (u8 *)full_ver, + sizeof(struct pt_pip2_version_full), + "PIP2 VERSION FULL"); + } else { + ver = (struct pt_pip2_version *) + &cd->response_buf[PIP2_RESP_STATUS_OFFSET]; + + ttdata->pip_ver_major = ver->pip2_version_msb; + ttdata->pip_ver_minor = ver->pip2_version_lsb; + ttdata->bl_ver_major = ver->bl_version_msb; + ttdata->bl_ver_minor = ver->bl_version_lsb; + ttdata->fw_ver_major = ver->fw_version_msb; + ttdata->fw_ver_minor = ver->fw_version_lsb; + ttdata->chip_rev = get_unaligned_le16(&ver->chip_rev); + ttdata->chip_id = get_unaligned_le16(&ver->chip_id); + + pt_pr_buf(cd->dev, DL_INFO, (u8 *)ver, + sizeof(struct pt_pip2_version), "PIP2 VERSION"); + } +} + +/******************************************************************************* + * FUNCTION: pt_si_get_ttdata + * + * SUMMARY: Function to load the version information from the system information + * PIP command into the core data struct. + * + * RETURN: n/a + * + * PARAMETERS: + * *cd - pointer to core data structure + ******************************************************************************/ +static void pt_si_get_ttdata(struct pt_core_data *cd) +{ + struct pt_ttdata *ttdata = &cd->sysinfo.ttdata; + struct pt_ttdata_dev *ttdata_dev = + (struct pt_ttdata_dev *) + &cd->response_buf[PIP1_SYSINFO_TTDATA_OFFSET]; + + ttdata->pip_ver_major = ttdata_dev->pip_ver_major; + ttdata->pip_ver_minor = ttdata_dev->pip_ver_minor; + ttdata->bl_ver_major = ttdata_dev->bl_ver_major; + ttdata->bl_ver_minor = ttdata_dev->bl_ver_minor; + ttdata->fw_ver_major = ttdata_dev->fw_ver_major; + ttdata->fw_ver_minor = ttdata_dev->fw_ver_minor; + + ttdata->fw_pid = get_unaligned_le16(&ttdata_dev->fw_pid); + ttdata->fw_ver_conf = get_unaligned_le16(&ttdata_dev->fw_ver_conf); + ttdata->post_code = get_unaligned_le16(&ttdata_dev->post_code); + ttdata->revctrl = get_unaligned_le32(&ttdata_dev->revctrl); + ttdata->jtag_id_l = get_unaligned_le16(&ttdata_dev->jtag_si_id_l); + ttdata->jtag_id_h = get_unaligned_le16(&ttdata_dev->jtag_si_id_h); + + memcpy(ttdata->mfg_id, ttdata_dev->mfg_id, PT_NUM_MFGID); + + pt_pr_buf(cd->dev, DL_INFO, (u8 *)ttdata_dev, + sizeof(struct pt_ttdata_dev), "sysinfo_ttdata"); +} + +/******************************************************************************* + * FUNCTION: pt_si_get_sensing_conf_data + * + * SUMMARY: Function to load the sensing information from the system information + * PIP command into the core data struct. + * + * RETURN: n/a + * + * PARAMETERS: + * *cd - pointer to core data structure + ******************************************************************************/ +static void pt_si_get_sensing_conf_data(struct pt_core_data *cd) +{ + struct pt_sensing_conf_data *scd = &cd->sysinfo.sensing_conf_data; + struct pt_sensing_conf_data_dev *scd_dev = + (struct pt_sensing_conf_data_dev *) + &cd->response_buf[PIP1_SYSINFO_SENSING_OFFSET]; + + scd->electrodes_x = scd_dev->electrodes_x; + scd->electrodes_y = scd_dev->electrodes_y; + scd->origin_x = scd_dev->origin_x; + scd->origin_y = scd_dev->origin_y; + + /* PIP 1.4 (001-82649 *Q) add X_IS_TX bit in X_ORG */ + if (scd->origin_x & 0x02) { + scd->tx_num = scd->electrodes_x; + scd->rx_num = scd->electrodes_y; + } else { + scd->tx_num = scd->electrodes_y; + scd->rx_num = scd->electrodes_x; + } + + /* + * When the Panel ID is coming from an XY pin and not a dedicated + * GPIO, store the PID in pid_for_loader. This cannot be done for all + * other DUTs as the loader will use cd->pid_for_loader to generate + * the bin file name but will ignore it if pid_for_loader is still + * set to PANEL_ID_NOT_ENABLED + */ + if (cd->panel_id_support & + (PT_PANEL_ID_BY_BL | PT_PANEL_ID_BY_SYS_INFO)) { + mutex_lock(&cd->system_lock); + cd->pid_for_loader = scd_dev->panel_id; + mutex_unlock(&cd->system_lock); + } + + scd->panel_id = scd_dev->panel_id; + scd->btn = scd_dev->btn; + scd->scan_mode = scd_dev->scan_mode; + scd->max_tch = scd_dev->max_num_of_tch_per_refresh_cycle; + + scd->res_x = get_unaligned_le16(&scd_dev->res_x); + scd->res_y = get_unaligned_le16(&scd_dev->res_y); + scd->max_z = get_unaligned_le16(&scd_dev->max_z); + scd->len_x = get_unaligned_le16(&scd_dev->len_x); + scd->len_y = get_unaligned_le16(&scd_dev->len_y); + + pt_pr_buf(cd->dev, DL_INFO, (u8 *)scd_dev, + sizeof(struct pt_sensing_conf_data_dev), + "sensing_conf_data"); +} + +/******************************************************************************* + * FUNCTION: pt_si_setup + * + * SUMMARY: Setup the xy_data and xy_mode by allocating the needed memory + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data structure + ******************************************************************************/ +static int pt_si_setup(struct pt_core_data *cd) +{ + struct pt_sysinfo *si = &cd->sysinfo; + int max_tch = si->sensing_conf_data.max_tch; + + if (!si->xy_data) + si->xy_data = kzalloc(max_tch * si->desc.tch_record_size, + GFP_KERNEL); + if (!si->xy_data) + return -ENOMEM; + + if (!si->xy_mode) + si->xy_mode = kzalloc(si->desc.tch_header_size, GFP_KERNEL); + if (!si->xy_mode) { + kfree(si->xy_data); + return -ENOMEM; + } + + return 0; +} + +/******************************************************************************* + * FUNCTION: pt_si_get_btn_data + * + * SUMMARY: Setup the core data button information based on the response of the + * System Information PIP command. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data structure + ******************************************************************************/ +static int pt_si_get_btn_data(struct pt_core_data *cd) +{ + struct pt_sysinfo *si = &cd->sysinfo; + int num_btns = 0; + int num_defined_keys; + u16 *key_table; + int btn; + int i; + int rc = 0; + unsigned int btns = cd->response_buf[PIP1_SYSINFO_BTN_OFFSET] + & PIP1_SYSINFO_BTN_MASK; + size_t btn_keys_size; + + pt_debug(cd->dev, DL_INFO, "%s: get btn data\n", __func__); + + for (i = 0; i < PIP1_SYSINFO_MAX_BTN; i++) { + if (btns & (1 << i)) + num_btns++; + } + si->num_btns = num_btns; + + if (num_btns) { + btn_keys_size = num_btns * sizeof(struct pt_btn); + if (!si->btn) + si->btn = kzalloc(btn_keys_size, GFP_KERNEL); + if (!si->btn) + return -ENOMEM; + + if (cd->cpdata->sett[PT_IC_GRPNUM_BTN_KEYS] == NULL) + num_defined_keys = 0; + else if (cd->cpdata->sett[PT_IC_GRPNUM_BTN_KEYS]->data == NULL) + num_defined_keys = 0; + else + num_defined_keys = cd->cpdata->sett + [PT_IC_GRPNUM_BTN_KEYS]->size; + + for (btn = 0; btn < num_btns && btn < num_defined_keys; btn++) { + key_table = (u16 *)cd->cpdata->sett + [PT_IC_GRPNUM_BTN_KEYS]->data; + si->btn[btn].key_code = key_table[btn]; + si->btn[btn].enabled = true; + } + for (; btn < num_btns; btn++) { + si->btn[btn].key_code = KEY_RESERVED; + si->btn[btn].enabled = true; + } + + return rc; + } + + kfree(si->btn); + si->btn = NULL; + return rc; +} + +/******************************************************************************* + * FUNCTION: pt_si_put_log_data + * + * SUMMARY: Prints all sys info data to kmsg log + * + * RETURN: n/a + * + * PARAMETERS: + * *cd - pointer to core data structure + ******************************************************************************/ +static void pt_si_put_log_data(struct pt_core_data *cd) +{ + struct pt_sysinfo *si = &cd->sysinfo; + struct pt_ttdata *ttdata = &si->ttdata; + struct pt_sensing_conf_data *scd = &si->sensing_conf_data; + int i; + + pt_debug(cd->dev, DL_DEBUG, "%s: pip_ver_major = 0x%02X (%d)\n", + __func__, ttdata->pip_ver_major, ttdata->pip_ver_major); + pt_debug(cd->dev, DL_DEBUG, "%s: pip_ver_minor = 0x%02X (%d)\n", + __func__, ttdata->pip_ver_minor, ttdata->pip_ver_minor); + pt_debug(cd->dev, DL_DEBUG, "%s: fw_pid = 0x%04X (%d)\n", + __func__, ttdata->fw_pid, ttdata->fw_pid); + pt_debug(cd->dev, DL_DEBUG, "%s: fw_ver_major = 0x%02X (%d)\n", + __func__, ttdata->fw_ver_major, ttdata->fw_ver_major); + pt_debug(cd->dev, DL_DEBUG, "%s: fw_ver_minor = 0x%02X (%d)\n", + __func__, ttdata->fw_ver_minor, ttdata->fw_ver_minor); + pt_debug(cd->dev, DL_DEBUG, "%s: revctrl = 0x%08X (%d)\n", + __func__, ttdata->revctrl, ttdata->revctrl); + pt_debug(cd->dev, DL_DEBUG, "%s: fw_ver_conf = 0x%04X (%d)\n", + __func__, ttdata->fw_ver_conf, ttdata->fw_ver_conf); + pt_debug(cd->dev, DL_DEBUG, "%s: bl_ver_major = 0x%02X (%d)\n", + __func__, ttdata->bl_ver_major, ttdata->bl_ver_major); + pt_debug(cd->dev, DL_DEBUG, "%s: bl_ver_minor = 0x%02X (%d)\n", + __func__, ttdata->bl_ver_minor, ttdata->bl_ver_minor); + pt_debug(cd->dev, DL_DEBUG, "%s: jtag_id_h = 0x%04X (%d)\n", + __func__, ttdata->jtag_id_h, ttdata->jtag_id_h); + pt_debug(cd->dev, DL_DEBUG, "%s: jtag_id_l = 0x%04X (%d)\n", + __func__, ttdata->jtag_id_l, ttdata->jtag_id_l); + + for (i = 0; i < PT_NUM_MFGID; i++) + pt_debug(cd->dev, DL_DEBUG, + "%s: mfg_id[%d] = 0x%02X (%d)\n", + __func__, i, ttdata->mfg_id[i], + ttdata->mfg_id[i]); + + pt_debug(cd->dev, DL_DEBUG, "%s: post_code = 0x%04X (%d)\n", + __func__, ttdata->post_code, ttdata->post_code); + pt_debug(cd->dev, DL_DEBUG, "%s: electrodes_x = 0x%02X (%d)\n", + __func__, scd->electrodes_x, scd->electrodes_x); + pt_debug(cd->dev, DL_DEBUG, "%s: electrodes_y = 0x%02X (%d)\n", + __func__, scd->electrodes_y, scd->electrodes_y); + pt_debug(cd->dev, DL_DEBUG, "%s: len_x = 0x%04X (%d)\n", + __func__, scd->len_x, scd->len_x); + pt_debug(cd->dev, DL_DEBUG, "%s: len_y = 0x%04X (%d)\n", + __func__, scd->len_y, scd->len_y); + pt_debug(cd->dev, DL_DEBUG, "%s: res_x = 0x%04X (%d)\n", + __func__, scd->res_x, scd->res_x); + pt_debug(cd->dev, DL_DEBUG, "%s: res_y = 0x%04X (%d)\n", + __func__, scd->res_y, scd->res_y); + pt_debug(cd->dev, DL_DEBUG, "%s: max_z = 0x%04X (%d)\n", + __func__, scd->max_z, scd->max_z); + pt_debug(cd->dev, DL_DEBUG, "%s: origin_x = 0x%02X (%d)\n", + __func__, scd->origin_x, scd->origin_x); + pt_debug(cd->dev, DL_DEBUG, "%s: origin_y = 0x%02X (%d)\n", + __func__, scd->origin_y, scd->origin_y); + pt_debug(cd->dev, DL_DEBUG, "%s: panel_id = 0x%02X (%d)\n", + __func__, scd->panel_id, scd->panel_id); + pt_debug(cd->dev, DL_DEBUG, "%s: btn =0x%02X (%d)\n", + __func__, scd->btn, scd->btn); + pt_debug(cd->dev, DL_DEBUG, "%s: scan_mode = 0x%02X (%d)\n", + __func__, scd->scan_mode, scd->scan_mode); + pt_debug(cd->dev, DL_DEBUG, + "%s: max_num_of_tch_per_refresh_cycle = 0x%02X (%d)\n", + __func__, scd->max_tch, scd->max_tch); + pt_debug(cd->dev, DL_DEBUG, "%s: xy_mode = %p\n", + __func__, si->xy_mode); + pt_debug(cd->dev, DL_DEBUG, "%s: xy_data = %p\n", + __func__, si->xy_data); +} + +/******************************************************************************* + * FUNCTION: pt_get_sysinfo_regs + * + * SUMMARY: Setup all the core data System information based on the response + * of the System Information PIP command. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data structure + ******************************************************************************/ +static int pt_get_sysinfo_regs(struct pt_core_data *cd) +{ + struct pt_sysinfo *si = &cd->sysinfo; + int rc; + + rc = pt_si_get_btn_data(cd); + if (rc < 0) + return rc; + + pt_si_get_ttdata(cd); + + pt_si_get_sensing_conf_data(cd); + + pt_si_setup(cd); + + pt_si_put_log_data(cd); + + si->ready = true; + return rc; +} + +/******************************************************************************* + * FUNCTION: pt_free_si_ptrs + * + * SUMMARY: Frees all memory associated with the System Information within + * core data + * + * RETURN: n/a + * + * PARAMETERS: + * *cd - pointer to core data structure + ******************************************************************************/ +static void pt_free_si_ptrs(struct pt_core_data *cd) +{ + struct pt_sysinfo *si = &cd->sysinfo; + + kfree(si->btn); + kfree(si->xy_mode); + kfree(si->xy_data); +} + +/******************************************************************************* + * FUNCTION: pt_hid_output_get_sysinfo_ + * + * SUMMARY: Sends the PIP Get SYS INFO command to the DUT and waits for the + * response. + * + * RETURN:: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data structure + ******************************************************************************/ +static int pt_hid_output_get_sysinfo_(struct pt_core_data *cd) +{ + int rc = 0; + struct pt_hid_output hid_output = { + CREATE_PIP1_FW_CMD(PIP1_CMD_ID_GET_SYSINFO), + .timeout_ms = PT_PIP1_CMD_GET_SYSINFO_TIMEOUT, + }; + + rc = pt_pip1_send_output_and_wait_(cd, &hid_output); + if (rc) + return rc; + + /* Parse the sysinfo data */ + rc = pt_get_sysinfo_regs(cd); + if (rc) + pt_free_si_ptrs(cd); + + return rc; +} + +/******************************************************************************* + * FUNCTION: pt_hid_output_get_sysinfo + * + * SUMMARY: Protected call to pt_hid_output_get_sysinfo_ + * + * RETURN:: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data structure + ******************************************************************************/ +static int pt_hid_output_get_sysinfo(struct pt_core_data *cd) +{ + int rc; + + rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT); + if (rc < 0) { + pt_debug(cd->dev, DL_ERROR, + "%s: fail get exclusive ex=%p own=%p\n", + __func__, cd->exclusive_dev, cd->dev); + return rc; + } + + rc = pt_hid_output_get_sysinfo_(cd); + + if (release_exclusive(cd, cd->dev) < 0) + pt_debug(cd->dev, DL_ERROR, + "%s: fail to release exclusive\n", __func__); + + return rc; +} + +/******************************************************************************* + * FUNCTION: pt_pip_suspend_scanning_ + * + * SUMMARY: Sends the PIP Suspend Scanning command to the DUT + * + * RETURN:: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data structure + ******************************************************************************/ +static int pt_pip_suspend_scanning_(struct pt_core_data *cd) +{ + int rc = 0; + struct pt_hid_output hid_output = { + CREATE_PIP1_FW_CMD(PIP1_CMD_ID_SUSPEND_SCANNING), + }; + + rc = pt_pip1_send_output_and_wait_(cd, &hid_output); + if (rc) { + pt_debug(cd->dev, DL_ERROR, + "%s: Suspend Scan PIP cmd failed. rc = %d\n", + __func__, rc); + } + return rc; +} + +/******************************************************************************* + * FUNCTION: pt_pip_suspend_scanning + * + * SUMMARY: Protected wrapper for calling pt_hid_output_suspend_scanning_ + * + * RETURN:: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data structure + ******************************************************************************/ +static int pt_pip_suspend_scanning(struct pt_core_data *cd) +{ + int rc; + + rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT); + if (rc < 0) { + pt_debug(cd->dev, DL_ERROR, + "%s: fail get exclusive ex=%p own=%p\n", + __func__, cd->exclusive_dev, cd->dev); + return rc; + } + + rc = pt_pip_suspend_scanning_(cd); + + if (release_exclusive(cd, cd->dev) < 0) + pt_debug(cd->dev, DL_ERROR, + "%s: fail to release exclusive\n", __func__); + + return rc; +} + +/******************************************************************************* + * FUNCTION: _pt_request_pip_suspend_scanning + * + * SUMMARY: Function pointer included in core_nonhid_cmd struct for external + * calls to the protected or unprotected call to pt_pip_suspend_scanning + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *dev - pointer to device structure + * protect - 0 = call non-protected function + * 1 = call protected function + ******************************************************************************/ +static int _pt_request_pip_suspend_scanning(struct device *dev, + int protect) +{ + struct pt_core_data *cd = dev_get_drvdata(dev); + + if (protect) + return pt_pip_suspend_scanning(cd); + + return pt_pip_suspend_scanning_(cd); +} + +/******************************************************************************* + * FUNCTION: pt_pip_resume_scanning_ + * + * SUMMARY: Sends the PIP Resume Scanning command to the DUT + * + * RETURN:: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data structure + ******************************************************************************/ +static int pt_pip_resume_scanning_(struct pt_core_data *cd) +{ + int rc = 0; + struct pt_hid_output hid_output = { + CREATE_PIP1_FW_CMD(PIP1_CMD_ID_RESUME_SCANNING), + }; + + rc = pt_pip1_send_output_and_wait_(cd, &hid_output); + if (rc) { + pt_debug(cd->dev, DL_ERROR, + "%s: Resume Scan PIP cmd failed. rc = %d\n", + __func__, rc); + } + return rc; +} + +/******************************************************************************* + * FUNCTION: pt_pip_resume_scanning + * + * SUMMARY: Protected wrapper for calling pt_pip_resume_scanning_ + * + * RETURN:: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data structure + ******************************************************************************/ +static int pt_pip_resume_scanning(struct pt_core_data *cd) +{ + int rc; + + rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT); + if (rc < 0) { + pt_debug(cd->dev, DL_ERROR, + "%s: fail get exclusive ex=%p own=%p\n", + __func__, cd->exclusive_dev, cd->dev); + return rc; + } + + rc = pt_pip_resume_scanning_(cd); + + if (release_exclusive(cd, cd->dev) < 0) + pt_debug(cd->dev, DL_ERROR, + "%s: fail to release exclusive\n", __func__); + + return rc; +} + +/******************************************************************************* + * FUNCTION: _pt_request_pip_resume_scanning + * + * SUMMARY: Function pointer included in core_nonhid_cmd struct for external + * calls to the protected or unprotected call to pt_pip_resume_scanning + * + * RETURN:: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *dev - pointer to device structure + * protect - 0 = call non-protected function + * 1 = call protected function + ******************************************************************************/ +static int _pt_request_pip_resume_scanning(struct device *dev, + int protect) +{ + struct pt_core_data *cd = dev_get_drvdata(dev); + + if (protect) + return pt_pip_resume_scanning(cd); + + return pt_pip_resume_scanning_(cd); +} + +/******************************************************************************* + * FUNCTION: pt_pip_get_param_ + * + * SUMMARY: Sends a PIP command 0x05 Get Parameter to the DUT and returns + * the 32bit parameter value + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data + * param_id - parameter ID to retrieve + * *value - value of DUT parameter + ******************************************************************************/ +static int pt_pip_get_param_(struct pt_core_data *cd, + u8 param_id, u32 *value) +{ + int write_length = 1; + u8 param[1] = { param_id }; + u8 read_param_id; + int param_size; + u8 *ptr; + int rc = 0; + int i; + struct pt_hid_output hid_output = { + CREATE_PIP1_FW_CMD(PIP1_CMD_ID_GET_PARAM), + .write_length = write_length, + .write_buf = param, + }; + + rc = pt_pip1_send_output_and_wait_(cd, &hid_output); + if (rc) + return rc; + + read_param_id = cd->response_buf[5]; + if (read_param_id != param_id) + return -EPROTO; + + param_size = cd->response_buf[6]; + ptr = &cd->response_buf[7]; + *value = 0; + for (i = 0; i < param_size; i++) + *value += ptr[i] << (i * 8); + return 0; +} + +/******************************************************************************* + * FUNCTION: pt_pip_get_param + * + * SUMMARY: Protected call to pt_hid_output_get_param_ by a request exclusive + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data + * param_id - parameter ID to retrieve + * *value - value of DUT parameter + ******************************************************************************/ +static int pt_pip_get_param(struct pt_core_data *cd, + u8 param_id, u32 *value) +{ + int rc; + + rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT); + if (rc < 0) { + pt_debug(cd->dev, DL_ERROR, + "%s: fail get exclusive ex=%p own=%p\n", + __func__, cd->exclusive_dev, cd->dev); + return rc; + } + + rc = pt_pip_get_param_(cd, param_id, value); + + if (release_exclusive(cd, cd->dev) < 0) + pt_debug(cd->dev, DL_ERROR, + "%s: fail to release exclusive\n", __func__); + + return rc; +} + +/******************************************************************************* + * FUNCTION: _pt_request_pip_get_param + * + * SUMMARY: Function pointer included in core_nonhid_cmd struct for external + * calls to the protected or unprotected call to pt_pip_get_param + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *dev - pointer to device structure + * protect - flag to call protected or non protected function + * param_id - parameter ID to retrieve + * *value - value of DUT parameter + ******************************************************************************/ +int _pt_request_pip_get_param(struct device *dev, + int protect, u8 param_id, u32 *value) +{ + struct pt_core_data *cd = dev_get_drvdata(dev); + + if (protect) + return pt_pip_get_param(cd, param_id, value); + + return pt_pip_get_param_(cd, param_id, value); +} + +/******************************************************************************* + * FUNCTION: pt_pip_set_param_ + * + * SUMMARY: Sends a PIP command 0x06 Set Parameter to the DUT writing the + * passed in value to flash + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data + * param_id - parameter ID to set + * value - value to write + * size - size to write + ******************************************************************************/ +static int pt_pip_set_param_(struct pt_core_data *cd, + u8 param_id, u32 value, u8 size) +{ + u8 write_buf[6]; + u8 *ptr = &write_buf[2]; + int rc = 0; + int i; + struct pt_hid_output hid_output = { + CREATE_PIP1_FW_CMD(PIP1_CMD_ID_SET_PARAM), + .write_buf = write_buf, + }; + + write_buf[0] = param_id; + write_buf[1] = size; + for (i = 0; i < size; i++) { + ptr[i] = value & 0xFF; + value = value >> 8; + } + + hid_output.write_length = 2 + size; + + rc = pt_pip1_send_output_and_wait_(cd, &hid_output); + if (rc) + return rc; + + if (param_id != cd->response_buf[5] || size != cd->response_buf[6]) + return -EPROTO; + + return 0; +} + +/******************************************************************************* + * FUNCTION: pt_pip_set_param + * + * SUMMARY: Protected call to pt_hid_output_set_param_ by a request exclusive + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data + * param_id - parameter ID to set + * value - value to write + * size - size to write + ******************************************************************************/ +static int pt_pip_set_param(struct pt_core_data *cd, + u8 param_id, u32 value, u8 size) +{ + int rc; + + rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT); + if (rc < 0) { + pt_debug(cd->dev, DL_ERROR, + "%s: fail get exclusive ex=%p own=%p\n", + __func__, cd->exclusive_dev, cd->dev); + return rc; + } + + rc = pt_pip_set_param_(cd, param_id, value, size); + + if (release_exclusive(cd, cd->dev) < 0) + pt_debug(cd->dev, DL_ERROR, + "%s: fail to release exclusive\n", __func__); + + return rc; +} + +/******************************************************************************* + * FUNCTION: _pt_request_pip_set_param + * + * SUMMARY: Function pointer included in core_nonhid_cmd struct for external + * calls to the protected or unprotected call to pt_pip_set_param + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *dev - pointer to device structure + * protect - flag to call protected or non-protected + * param_id - parameter ID to set + * value - value to write + * size - size to write + ******************************************************************************/ +int _pt_request_pip_set_param(struct device *dev, int protect, + u8 param_id, u32 value, u8 size) +{ + struct pt_core_data *cd = dev_get_drvdata(dev); + + if (protect) + return pt_pip_set_param(cd, param_id, value, size); + + return pt_pip_set_param_(cd, param_id, value, size); +} + +/******************************************************************************* + * FUNCTION: _pt_pip_enter_easywake_state_ + * + * SUMMARY: Sends a PIP command 0x09 Enter EasyWake State to the DUT + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *dev - pointer to device structure + * data - easywake guesture (Only used for PIP1.6 and earlier) + * *return_data - return status if easywake was entered + ******************************************************************************/ +static int pt_hid_output_enter_easywake_state_( + struct pt_core_data *cd, u8 data, u8 *return_data) +{ + int write_length = 1; + u8 param[1] = { data }; + int rc = 0; + struct pt_hid_output hid_output = { + CREATE_PIP1_FW_CMD(PIP1_CMD_ID_ENTER_EASYWAKE_STATE), + .write_length = write_length, + .write_buf = param, + }; + + rc = pt_pip1_send_output_and_wait_(cd, &hid_output); + if (rc) + return rc; + + *return_data = cd->response_buf[5]; + return rc; +} + +/******************************************************************************* + * FUNCTION: pt_pip_verify_config_block_crc_ + * + * SUMMARY: Sends the PIP "Verify Data Block CRC" (0x20) command to the DUT + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer the core data structure + * ebid - enumerated block ID + * *status - PIP command status + * calculated_crc - calculated CRC + * stored_crc - stored CRC in config area + ******************************************************************************/ +static int pt_pip_verify_config_block_crc_( + struct pt_core_data *cd, u8 ebid, u8 *status, + u16 *calculated_crc, u16 *stored_crc) +{ + int write_length = 1; + u8 param[1] = { ebid }; + u8 *ptr; + int rc = 0; + struct pt_hid_output hid_output = { + CREATE_PIP1_FW_CMD(PIP1_CMD_ID_VERIFY_CONFIG_BLOCK_CRC), + .write_length = write_length, + .write_buf = param, + }; + + rc = pt_pip1_send_output_and_wait_(cd, &hid_output); + if (rc) + return rc; + + ptr = &cd->response_buf[5]; + *status = ptr[0]; + *calculated_crc = get_unaligned_le16(&ptr[1]); + *stored_crc = get_unaligned_le16(&ptr[3]); + return 0; +} + +/******************************************************************************* + * FUNCTION: pt_pip_verify_config_block_crc + * + * SUMMARY: Protected call to pt_hid_output_verify_config_block_crc_() within + * an exclusive access to the DUT. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer the core data structure + * ebid - enumerated block ID + * *status - PIP command status + * calculated_crc - calculated CRC + * stored_crc - stored CRC in config area + ******************************************************************************/ +static int pt_pip_verify_config_block_crc( + struct pt_core_data *cd, u8 ebid, u8 *status, + u16 *calculated_crc, u16 *stored_crc) +{ + int rc; + + rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT); + if (rc < 0) { + pt_debug(cd->dev, DL_ERROR, + "%s: fail get exclusive ex=%p own=%p\n", + __func__, cd->exclusive_dev, cd->dev); + return rc; + } + + rc = pt_pip_verify_config_block_crc_(cd, ebid, status, + calculated_crc, stored_crc); + + if (release_exclusive(cd, cd->dev) < 0) + pt_debug(cd->dev, DL_ERROR, + "%s: fail to release exclusive\n", __func__); + + return rc; +} + +/******************************************************************************* + * FUNCTION: _pt_request_pip_verify_config_block_crc + * + * SUMMARY: Function pointer included in core_nonhid_cmd struct for external + * calls to the protected or unprotected call to + * pt_pip_verify_config_block_crc_ + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *dev - pointer to device structure + * protect - flag to call protected or non-protected + * ebid - enumerated block ID + * *status - PIP command status + * calculated_crc - calculated CRC + * stored_crc - stored CRC in config area + ******************************************************************************/ +static int _pt_request_pip_verify_config_block_crc( + struct device *dev, int protect, u8 ebid, u8 *status, + u16 *calculated_crc, u16 *stored_crc) +{ + struct pt_core_data *cd = dev_get_drvdata(dev); + + if (protect) + return pt_pip_verify_config_block_crc(cd, ebid, + status, calculated_crc, stored_crc); + + return pt_pip_verify_config_block_crc_(cd, ebid, + status, calculated_crc, stored_crc); +} + +/******************************************************************************* + * FUNCTION: pt_pip_get_config_row_size_ + * + * SUMMARY: Sends the PIP "Get Data Row Size" (0x21) command to the DUT + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data + * protect - flag to call protected or non-protected + * *row_size - pointer to store the retrieved row size + ******************************************************************************/ +static int pt_pip_get_config_row_size_(struct pt_core_data *cd, + u16 *row_size) +{ + int rc = 0; + struct pt_hid_output hid_output = { + CREATE_PIP1_FW_CMD(PIP1_CMD_ID_GET_CONFIG_ROW_SIZE), + }; + + rc = pt_pip1_send_output_and_wait_(cd, &hid_output); + if (rc) + return rc; + + *row_size = get_unaligned_le16(&cd->response_buf[5]); + return 0; +} + +/******************************************************************************* + * FUNCTION: pt_pip_get_config_row_size + * + * SUMMARY: Protected call to pt_hid_output_get_config_row_size_ within + * an exclusive access to the DUT. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data + * protect - flag to call protected or non-protected + * *row_size - pointer to store the retrieved row size + ******************************************************************************/ +static int pt_pip_get_config_row_size(struct pt_core_data *cd, + u16 *row_size) +{ + int rc; + + rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT); + if (rc < 0) { + pt_debug(cd->dev, DL_ERROR, + "%s: fail get exclusive ex=%p own=%p\n", + __func__, cd->exclusive_dev, cd->dev); + return rc; + } + + rc = pt_pip_get_config_row_size_(cd, row_size); + + if (release_exclusive(cd, cd->dev) < 0) + pt_debug(cd->dev, DL_ERROR, + "%s: fail to release exclusive\n", __func__); + + return rc; +} + +/******************************************************************************* + * FUNCTION: _pt_request_pip_get_config_row_size + * + * SUMMARY: Function pointer included in core_nonhid_cmd struct for external + * calls to the protected or unprotected call to + * pt_pip_get_config_row_size_ + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *dev - pointer to device structure + * protect - flag to call protected or non-protected + * *row_size - pointer to store the retrieved row size + ******************************************************************************/ +static int _pt_request_pip_get_config_row_size(struct device *dev, + int protect, u16 *row_size) +{ + struct pt_core_data *cd = dev_get_drvdata(dev); + + if (protect) + return pt_pip_get_config_row_size(cd, row_size); + + return pt_pip_get_config_row_size_(cd, row_size); +} + +/******************************************************************************* + * FUNCTION: pt_pip1_read_data_block_ + * + * SUMMARY: Sends the PIP "Read Data Block" (0x22) command to the DUT and print + * output data to the "read_buf" and update "crc". + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data + * row_number - row number + * length - length of data to read + * ebid - block id + * *actual_read_len - Actual data length read + * *read_buf - pointer to the buffer to store read data + * read_buf_size - size of read_buf + * *crc - pointer to store CRC of row data + ******************************************************************************/ +static int pt_pip1_read_data_block_(struct pt_core_data *cd, + u16 row_number, u16 length, u8 ebid, u16 *actual_read_len, + u8 *read_buf, u16 read_buf_size, u16 *crc) +{ + int read_ebid; + int status; + int rc = 0; + int write_length = 5; + u8 write_buf[5]; + u8 cmd_offset = 0; + u16 calc_crc; + struct pt_hid_output hid_output = { + CREATE_PIP1_FW_CMD(PIP1_CMD_ID_READ_DATA_BLOCK), + .write_length = write_length, + .write_buf = write_buf, + }; + + write_buf[cmd_offset++] = LOW_BYTE(row_number); + write_buf[cmd_offset++] = HI_BYTE(row_number); + write_buf[cmd_offset++] = LOW_BYTE(length); + write_buf[cmd_offset++] = HI_BYTE(length); + write_buf[cmd_offset++] = ebid; + + rc = pt_pip1_send_output_and_wait_(cd, &hid_output); + if (rc) + return rc; + + status = cd->response_buf[5]; + if (status) + return status; + + read_ebid = cd->response_buf[6]; + if ((read_ebid != ebid) || (cd->response_buf[9] != 0)) + return -EPROTO; + + *actual_read_len = get_unaligned_le16(&cd->response_buf[7]); + if (length == 0 || *actual_read_len == 0) + return 0; + + if (read_buf_size >= *actual_read_len) + 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 Missmatch 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 transfered + * *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 occured, 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; + + sprintf(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); + + sprintf(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 paramters 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 paramters 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 paramters 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 paramters 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 "Get Bootloader Information" (0x48) command to the + * DUT to erases the entire TrueTouch application, Configuration Data block, + * and Design Data block in flash and enables the host to execute the Program + * and Verify Row command to bootload the application image and data. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data + * protect - flag to call protected or non-protected + * key_size - size of key + * *key_buf - pointer to key data to allow operation + * row_size - size of the meta data row + * *metadata_row_buf - pointer to meta data to write + ******************************************************************************/ +static int pt_hid_output_bl_initiate_bl_(struct pt_core_data *cd, + u16 key_size, u8 *key_buf, u16 row_size, u8 *metadata_row_buf) +{ + u16 write_length = key_size + row_size; + u8 *write_buf; + int rc = 0; + struct pt_hid_output hid_output = { + CREATE_PIP1_BL_CMD(PIP1_BL_CMD_ID_INITIATE_BL), + .write_length = write_length, + .timeout_ms = PT_PIP1_CMD_INITIATE_BL_TIMEOUT, + }; + + write_buf = kzalloc(write_length, GFP_KERNEL); + if (!write_buf) + return -ENOMEM; + + hid_output.write_buf = write_buf; + + if (key_size) + memcpy(write_buf, key_buf, key_size); + + if (row_size) + memcpy(&write_buf[key_size], metadata_row_buf, row_size); + + rc = pt_pip1_send_output_and_wait_(cd, &hid_output); + + kfree(write_buf); + return rc; +} + +/******************************************************************************* + * FUNCTION: pt_hid_output_bl_initiate_bl + * + * SUMMARY: Protected call to pt_hid_output_bl_initiate_bl_ by exclusive + * access to the DUT. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data + * key_size - size of key + * *key_buf - pointer to key data to allow operation + * row_size - size of the meta data row + * *metadata_row_buf - pointer to meta data to write + ******************************************************************************/ +static int pt_hid_output_bl_initiate_bl(struct pt_core_data *cd, + u16 key_size, u8 *key_buf, u16 row_size, u8 *metadata_row_buf) +{ + int rc; + + rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT); + if (rc < 0) { + pt_debug(cd->dev, DL_ERROR, + "%s: fail get exclusive ex=%p own=%p\n", + __func__, cd->exclusive_dev, cd->dev); + return rc; + } + + rc = pt_hid_output_bl_initiate_bl_(cd, key_size, key_buf, + row_size, metadata_row_buf); + + if (release_exclusive(cd, cd->dev) < 0) + pt_debug(cd->dev, DL_ERROR, + "%s: fail to release exclusive\n", __func__); + + return rc; +} + +/******************************************************************************* + * FUNCTION: _pt_request_pip_bl_initiate_bl + * + * SUMMARY: Function pointer included in core_nonhid_cmd struct for external + * calls to the protected or unprotected call to + * pt_hid_output_bl_initiate_bl + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *dev - pointer to device structure + * protect - flag to call protected or non-protected + * key_size - size of key + * *key_buf - pointer to key data to allow operation + * row_size - size of the meta data row + * *metadata_row_buf - pointer to meta data to write + ******************************************************************************/ +static int _pt_request_pip_bl_initiate_bl(struct device *dev, + int protect, u16 key_size, u8 *key_buf, u16 row_size, + u8 *metadata_row_buf) +{ + struct pt_core_data *cd = dev_get_drvdata(dev); + + if (protect) + return pt_hid_output_bl_initiate_bl(cd, key_size, key_buf, + row_size, metadata_row_buf); + + return pt_hid_output_bl_initiate_bl_(cd, key_size, key_buf, + row_size, metadata_row_buf); +} + +/******************************************************************************* + * FUNCTION: pt_hid_output_bl_program_and_verify_ + * + * SUMMARY: Sends the PIP "Get Bootloader Information" (0x39) command to upload + * and program a 128-byte row into the flash, and then verifies written data. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data + * data_len - length of data_buf + * *data_buf - firmware image to program + ******************************************************************************/ +static int pt_hid_output_bl_program_and_verify_( + struct pt_core_data *cd, u16 data_len, u8 *data_buf) +{ + struct pt_hid_output hid_output = { + CREATE_PIP1_BL_CMD(PIP1_BL_CMD_ID_PROGRAM_AND_VERIFY), + .write_length = data_len, + .write_buf = data_buf, + .timeout_ms = PT_PIP1_CMD_PROGRAM_AND_VERIFY_TIMEOUT, + }; + + return pt_pip1_send_output_and_wait_(cd, &hid_output); +} + +/******************************************************************************* + * FUNCTION: pt_hid_output_bl_program_and_verify + * + * SUMMARY: Protected call to pt_hid_output_bl_program_and_verify_ by exclusive + * access to the DUT. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data + * data_len - length of data_buf + * *data_buf - firmware image to program + ******************************************************************************/ +static int pt_hid_output_bl_program_and_verify( + struct pt_core_data *cd, u16 data_len, u8 *data_buf) +{ + int rc; + + rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT); + if (rc < 0) { + pt_debug(cd->dev, DL_ERROR, + "%s: fail get exclusive ex=%p own=%p\n", + __func__, cd->exclusive_dev, cd->dev); + return rc; + } + + rc = pt_hid_output_bl_program_and_verify_(cd, data_len, data_buf); + + if (release_exclusive(cd, cd->dev) < 0) + pt_debug(cd->dev, DL_ERROR, + "%s: fail to release exclusive\n", __func__); + + return rc; +} + +/******************************************************************************* + * FUNCTION: _pt_request_pip_bl_program_and_verify + * + * SUMMARY: Function pointer included in core_nonhid_cmds to allow other modules + * to request to have the BL program and verify a FW image + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *dev - pointer to device structure + * protect - boolean to determine to call the protected function + * data_len - length of data_buf + * *data_buf - firmware image to program + ******************************************************************************/ +static int _pt_request_pip_bl_program_and_verify( + struct device *dev, int protect, u16 data_len, u8 *data_buf) +{ + struct pt_core_data *cd = dev_get_drvdata(dev); + + if (protect) + return pt_hid_output_bl_program_and_verify(cd, data_len, + data_buf); + + return pt_hid_output_bl_program_and_verify_(cd, data_len, + data_buf); +} + +/******************************************************************************* + * FUNCTION: pt_hid_output_bl_verify_app_integrity_ + * + * SUMMARY: Sends the PIP "Get Bootloader Information" (0x31) command to + * perform a full verification of the application integrity by calculating the + * CRC of the image in flash and compare it to the expected CRC stored in the + * Metadata row. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data + * *result - pointer to store result + ******************************************************************************/ +static int pt_hid_output_bl_verify_app_integrity_( + struct pt_core_data *cd, u8 *result) +{ + int rc; + struct pt_hid_output hid_output = { + CREATE_PIP1_BL_CMD(PIP1_BL_CMD_ID_VERIFY_APP_INTEGRITY), + }; + + rc = pt_pip1_send_output_and_wait_(cd, &hid_output); + if (rc) { + *result = 0; + return rc; + } + + *result = cd->response_buf[8]; + return 0; +} + +/******************************************************************************* + * FUNCTION: pt_hid_output_bl_verify_app_integrity + * + * SUMMARY: Protected call to pt_hid_output_bl_verify_app_integrity_ by + * exclusive access to the DUT. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data + * *result - pointer to store result + ******************************************************************************/ +static int pt_hid_output_bl_verify_app_integrity( + struct pt_core_data *cd, u8 *result) +{ + int rc; + + rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT); + if (rc < 0) { + pt_debug(cd->dev, DL_ERROR, + "%s: fail get exclusive ex=%p own=%p\n", + __func__, cd->exclusive_dev, cd->dev); + return rc; + } + + rc = pt_hid_output_bl_verify_app_integrity_(cd, result); + + if (release_exclusive(cd, cd->dev) < 0) + pt_debug(cd->dev, DL_ERROR, + "%s: fail to release exclusive\n", __func__); + + return rc; +} + +/******************************************************************************* + * FUNCTION: _pt_request_pip_bl_verify_app_integrity + * + * SUMMARY: Function pointer included in core_nonhid_cmds to allow other modules + * to request to have the BL verify the application integrity (PIP1.x only) + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *dev - pointer to device structure + * protect - boolean to determine to call the protected function + * *result - pointer to store result + ******************************************************************************/ +static int _pt_request_pip_bl_verify_app_integrity( + struct device *dev, int protect, u8 *result) +{ + struct pt_core_data *cd = dev_get_drvdata(dev); + + if (protect) + return pt_hid_output_bl_verify_app_integrity(cd, result); + + return pt_hid_output_bl_verify_app_integrity_(cd, result); +} + +/******************************************************************************* + * FUNCTION: pt_hid_output_bl_launch_app_ + * + * SUMMARY: Sends the PIP "Launch Application" (0x3B) command to launch the + * application from bootloader (PIP1.x only). + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data + ******************************************************************************/ +static int pt_hid_output_bl_launch_app_(struct pt_core_data *cd) +{ + struct pt_hid_output hid_output = { + CREATE_PIP1_BL_CMD(PIP1_BL_CMD_ID_LAUNCH_APP), + .reset_expected = 1, + }; + + return pt_pip1_send_output_and_wait_(cd, &hid_output); +} + +/******************************************************************************* + * FUNCTION: pt_hid_output_bl_launch_app + * + * SUMMARY: Protected call to pt_hid_output_bl_launch_app_ by exclusive access + * to the DUT. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data + ******************************************************************************/ +static int pt_hid_output_bl_launch_app(struct pt_core_data *cd) +{ + int rc; + + rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT); + if (rc < 0) { + pt_debug(cd->dev, DL_ERROR, + "%s: fail get exclusive ex=%p own=%p\n", + __func__, cd->exclusive_dev, cd->dev); + return rc; + } + + rc = pt_hid_output_bl_launch_app_(cd); + + if (release_exclusive(cd, cd->dev) < 0) + pt_debug(cd->dev, DL_ERROR, + "%s: fail to release exclusive\n", __func__); + + return rc; +} + +/******************************************************************************* + * FUNCTION: _pt_request_pip_launch_app + * + * SUMMARY: Function pointer included in core_nonhid_cmds to allow other modules + * to request to have the BL launch the application. (PIP1.x only) + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *dev - pointer to device structure + * protect - boolean to determine to call the protected function + ******************************************************************************/ +static int _pt_request_pip_launch_app(struct device *dev, + int protect) +{ + struct pt_core_data *cd = dev_get_drvdata(dev); + + if (protect) + return pt_hid_output_bl_launch_app(cd); + + return pt_hid_output_bl_launch_app_(cd); +} + +/******************************************************************************* + * FUNCTION: pt_hid_output_bl_get_panel_id_ + * + * SUMMARY: Sends the PIP "Get Panel ID" (0x3E) command to return the Panel ID + * value store in the System Information. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data + * *panel_id - pointer to where the panel ID will be stored + ******************************************************************************/ +static int pt_hid_output_bl_get_panel_id_( + struct pt_core_data *cd, u8 *panel_id) +{ + int rc; + struct pt_hid_output hid_output = { + CREATE_PIP1_BL_CMD(PIP1_BL_CMD_ID_GET_PANEL_ID), + }; + + rc = pt_pip1_send_output_and_wait_(cd, &hid_output); + if (rc == -EPROTO && cd->response_buf[5] == ERROR_COMMAND) { + pt_debug(cd->dev, DL_ERROR, + "%s: Get Panel ID command not supported\n", + __func__); + *panel_id = PANEL_ID_NOT_ENABLED; + return 0; + } else if (rc < 0) { + pt_debug(cd->dev, DL_ERROR, + "%s: Error on Get Panel ID command\n", __func__); + return rc; + } + + *panel_id = cd->response_buf[8]; + return 0; +} + +/******************************************************************************* + * FUNCTION: pt_hid_output_bl_get_panel_id + * + * SUMMARY: Protected call to pt_hid_output_bl_get_panel_id_ by exclusive access + * to the DUT. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data + * *panel_id - pointer to where the panel ID will be stored + ******************************************************************************/ +static int pt_hid_output_bl_get_panel_id( + struct pt_core_data *cd, u8 *panel_id) +{ + int rc; + + rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT); + if (rc < 0) { + pt_debug(cd->dev, DL_ERROR, + "%s: fail get exclusive ex=%p own=%p\n", + __func__, cd->exclusive_dev, cd->dev); + return rc; + } + + rc = pt_hid_output_bl_get_panel_id_(cd, panel_id); + + if (release_exclusive(cd, cd->dev) < 0) + pt_debug(cd->dev, DL_ERROR, + "%s: fail to release exclusive\n", __func__); + + return rc; +} + +/******************************************************************************* + * FUNCTION: _pt_request_pip_bl_get_panel_id + * + * SUMMARY: Function pointer included in core_nonhid_cmds to allow other modules + * to have the BL retrieve the panel ID + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *dev - pointer to device structure + * protect - flag to run in protected mode + * *panel_id - pointer to where the panel ID will be stored + ******************************************************************************/ +static int _pt_request_pip_bl_get_panel_id( + struct device *dev, int protect, u8 *panel_id) +{ + struct pt_core_data *cd = dev_get_drvdata(dev); + + if (protect) + return pt_hid_output_bl_get_panel_id(cd, panel_id); + + return pt_hid_output_bl_get_panel_id_(cd, panel_id); +} + +/******************************************************************************* + * FUNCTION: pt_pip2_get_mode_sysmode_ + * + * SUMMARY: Determine the current mode and system mode of the DUT by use of the + * PIP2 STATUS command. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data structure + * *mode - pointer to store the retrieved mode + * *sys_mode - pointer to store the FW system mode + ******************************************************************************/ +static int pt_pip2_get_mode_sysmode_(struct pt_core_data *cd, + u8 *mode, u8 *sys_mode) +{ + u16 actual_read_len; + u8 read_buf[12]; + u8 status, boot; + int rc = 0; + + rc = _pt_request_pip2_send_cmd(cd->dev, PT_CORE_CMD_UNPROTECTED, + PIP2_CMD_ID_STATUS, NULL, 0, read_buf, &actual_read_len); + + pt_debug(cd->dev, DL_INFO, "%s: PIP2 STATUS command rc = %d\n", + __func__, rc); + + if (!rc) { + pt_pr_buf(cd->dev, DL_DEBUG, read_buf, actual_read_len, + "PIP2 STATUS"); + + status = read_buf[PIP2_RESP_STATUS_OFFSET]; + boot = read_buf[PIP2_RESP_BODY_OFFSET] & 0x01; + if (sys_mode) { + if (status == PIP2_RSP_ERR_NONE && + boot == PIP2_STATUS_APP_EXEC) + *sys_mode = read_buf[PIP2_RESP_BODY_OFFSET + 1]; + else + *sys_mode = FW_SYS_MODE_UNDEFINED; + } + if (mode) { + if (status == PIP2_RSP_ERR_NONE && + boot == PIP2_STATUS_BOOT_EXEC) + *mode = PT_MODE_BOOTLOADER; + else if (status == PIP2_RSP_ERR_NONE && + boot == PIP2_STATUS_APP_EXEC) + *mode = PT_MODE_OPERATIONAL; + else + *mode = PT_MODE_UNKNOWN; + } + } else { + if (mode) + *mode = PT_MODE_UNKNOWN; + if (sys_mode) + *sys_mode = FW_SYS_MODE_UNDEFINED; + pt_debug(cd->dev, DL_WARN, + "%s: Mode and sys_mode could not be determined\n", + __func__); + } + + return rc; +} + +/******************************************************************************* + * FUNCTION: pt_pip2_get_mode_sysmode + * + * SUMMARY: Protected call to pt_pip2_get_mode_sysmode_ by exclusive + * access to the DUT. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data structure + * *mode - pointer to store the retrieved mode + * *sys_mode - pointer to store the FW system mode + ******************************************************************************/ +static int pt_pip2_get_mode_sysmode(struct pt_core_data *cd, + u8 *mode, u8 *sys_mode) +{ + int rc; + + rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT); + if (rc < 0) { + pt_debug(cd->dev, DL_ERROR, + "%s: fail get exclusive ex=%p own=%p\n", + __func__, cd->exclusive_dev, cd->dev); + return rc; + } + + rc = pt_pip2_get_mode_sysmode_(cd, mode, sys_mode); + + if (release_exclusive(cd, cd->dev) < 0) + pt_debug(cd->dev, DL_ERROR, + "%s: fail to release exclusive\n", __func__); + + return rc; +} + +/******************************************************************************* + * FUNCTION: _pt_request_pip2_get_mode_sysmode + * + * SUMMARY: Function pointer included in core_commands struct for external + * calls to the protected or unprotected call to + * pt_pip2_get_mode_sysmode + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *dev - pointer to device structure + * *mode - pointer to store the retrieved mode + * *sys_mode - pointer to store the FW system mode + ******************************************************************************/ +static int _pt_request_pip2_get_mode_sysmode(struct device *dev, + int protect, u8 *mode, u8 *sys_mode) +{ + struct pt_core_data *cd = dev_get_drvdata(dev); + + if (protect) + return pt_pip2_get_mode_sysmode(cd, mode, sys_mode); + + return pt_pip2_get_mode_sysmode_(cd, mode, sys_mode); +} + +/******************************************************************************* + * FUNCTION: _pt_poll_for_fw_exit_boot_mode + * + * SUMMARY: Verify and or poll for the FW to exit BOOT mode. During the FW BOOT + * mode only the following PIP commands will be serviced, any other PIP + * command the FW will respond with an "Invalid PIP Command" response. + * - Get HID Descriptor (Register 0x0001, no command ID) + * - Reset (Register 0x0005, RESET HID request) + * - Ping (Register 0x0004, Command ID 0x00 + * - Get System Information (Register 0x0004, Command ID 0x02) + * - PIP2 Status (Register 0x0101, Command ID 0x01) + * - PIP2 Version (Register 0x0101, Command ID 0x07) + * This function will loop on the results of the STATUS command until + * the FW reports it is out of BOOT mode. + * + * NOTE: + * - This function will update cd->fw_system_mode + * - The STATUS cmd only supports this functionality for PIP 1.11+ + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data + * timeout - max time (ms) to wait for FW to exit BOOT mode + * actual_wait - pointer to actual time waited for FW to exit BOOT mode + ******************************************************************************/ +static int _pt_poll_for_fw_exit_boot_mode(struct pt_core_data *cd, int timeout, + int *actual_wait) +{ + int loop = 0; + u8 sys_mode = cd->fw_system_mode; + u8 pause = 10; /* in ms */ + int rc = 0; + int max_loop = (timeout / pause) + 1; /* Add 1 due to int math */ + + if (cd->sysinfo.ready && !IS_PIP_VER_GE(&cd->sysinfo, 1, 11)) { + /* + * For PIP <1.11, no support for polling wait so do a hard + * coded wait and assume the FW is out of BOOT. Added 1 to + * timeout to make it clear in kmsg if non polling was done. + */ + *actual_wait = PT_FW_EXIT_BOOT_MODE_TIMEOUT + 1; + pt_debug(cd->dev, DL_ERROR, + "%s: PIP %d.%d no support for ext STATUS, sleep %d\n", + __func__, + cd->sysinfo.ttdata.pip_ver_major, + cd->sysinfo.ttdata.pip_ver_minor, *actual_wait); + msleep(*actual_wait); + sys_mode = FW_SYS_MODE_SCANNING; + } + + if (sys_mode == FW_SYS_MODE_BOOT) { + while (!rc && loop <= max_loop && + (sys_mode == FW_SYS_MODE_BOOT)) { + loop++; + usleep_range(9000, pause * 1000); + rc = pt_pip2_get_mode_sysmode_(cd, NULL, &sys_mode); + pt_debug(cd->dev, DL_DEBUG, + "%s: FW in BOOT mode-sleep %dms, sys_mode=%d\n", + __func__, loop * pause, sys_mode); + } + *actual_wait = (int)(loop * pause); + pt_debug(cd->dev, DL_WARN, + "%s: FW exited BOOT mode in %dms, sys_mode=%d\n", + __func__, *actual_wait, sys_mode); + + if (rc) + sys_mode = FW_SYS_MODE_UNDEFINED; + else if (sys_mode == FW_SYS_MODE_BOOT || + sys_mode == FW_SYS_MODE_UNDEFINED) + rc = -EBUSY; + } + + mutex_lock(&cd->system_lock); + cd->fw_system_mode = sys_mode; + mutex_unlock(&cd->system_lock); + + return rc; +} + +/******************************************************************************* + * FUNCTION: pt_poll_for_fw_exit_boot_mode + * + * SUMMARY: Protected call to _pt_poll_for_fw_exit_boot_mode by exclusive + * access to the DUT. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data + * timeout - max time (ms) to wait for FW to exit BOOT mode + * actual_wait - pointer to actual time waited for FW to exit BOOT mode + ******************************************************************************/ +static int pt_poll_for_fw_exit_boot_mode(struct pt_core_data *cd, int timeout, + int *actual_wait) +{ + int rc; + + rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT); + if (rc < 0) { + pt_debug(cd->dev, DL_ERROR, + "%s: fail get exclusive ex=%p own=%p\n", + __func__, cd->exclusive_dev, cd->dev); + return rc; + } + + rc = _pt_poll_for_fw_exit_boot_mode(cd, timeout, actual_wait); + + if (release_exclusive(cd, cd->dev) < 0) + pt_debug(cd->dev, DL_ERROR, + "%s: fail to release exclusive\n", __func__); + + return rc; +} + +/******************************************************************************* + * FUNCTION: _pt_get_fw_sys_mode + * + * SUMMARY: Determine the FW system mode. For PIP 1.11+ the + * PIP2 STATUS command is used to directly query the FW system mode. For older + * PIP versions, there is no direct PIP commamnd that will directly provide this + * information but any PIP command above 0x1F requires scanning to be disabled + * before it will be operational. If scanning was not disabled before sending + * these PIP commands the FW will respond with a 6 byte error response. So to + * safely determine the scanning state, a PIP message that will not affect the + * operation of the FW was chosen: + * "Verify Data Block CRC (ID 0x20)" is sent and if a 6 byte error code is + * received scanning is active. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data + * *sys_mode - pointer to FW System mode + * *mode - pointer to mode (BL/FW) + ******************************************************************************/ +static int _pt_get_fw_sys_mode(struct pt_core_data *cd, u8 *sys_mode, u8 *mode) +{ + int write_length = 1; + int report_length; + int rc = 0; + u8 tmp_sys_mode = FW_SYS_MODE_UNDEFINED; + u8 tmp_mode = PT_MODE_UNKNOWN; + u8 param[1] = { PT_TCH_PARM_EBID }; + struct pt_hid_output hid_output = { + CREATE_PIP1_FW_CMD(PIP1_CMD_ID_VERIFY_CONFIG_BLOCK_CRC), + .write_length = write_length, + .write_buf = param, + .novalidate = true, + }; + + /* AFter PIP1.11 the preferred method is using STATUS cmd */ + if (IS_PIP_VER_GE(&cd->sysinfo, 1, 11)) { + rc = pt_pip2_get_mode_sysmode_(cd, &tmp_mode, &tmp_sys_mode); + pt_debug(cd->dev, DL_DEBUG, "%s: tmp_sys_mode=%d tmp_mode=%d\n", + __func__, tmp_sys_mode, tmp_mode); + if (!rc) { + if (tmp_mode != PT_MODE_OPERATIONAL) + tmp_sys_mode = FW_SYS_MODE_UNDEFINED; + } + goto exit; + } + + /* Older systems use PIP1 CONFIG_BLOCK_CRC to best determine sys_mode */ + if (cd->mode != PT_MODE_OPERATIONAL) { + tmp_mode = cd->mode; + goto exit; + } + + rc = pt_pip1_send_output_and_wait_(cd, &hid_output); + if (rc) + goto exit; + + report_length = (cd->response_buf[1] << 8) | (cd->response_buf[0]); + if ((report_length == 0x06) && + ((cd->response_buf[4] & PIP1_RESP_COMMAND_ID_MASK) == 0x00) && + (cd->response_buf[5] == PIP1_CMD_ID_VERIFY_CONFIG_BLOCK_CRC)) { + tmp_mode = PIP2_STATUS_APP_EXEC; + tmp_sys_mode = FW_SYS_MODE_SCANNING; + } else if ((report_length == 0x0A) && + ((cd->response_buf[4] & PIP1_RESP_COMMAND_ID_MASK) == + PIP1_CMD_ID_VERIFY_CONFIG_BLOCK_CRC)) { + tmp_mode = PIP2_STATUS_APP_EXEC; + tmp_sys_mode = FW_SYS_MODE_TEST; + } + +exit: + if (mode) + *mode = tmp_mode; + if (sys_mode) + *sys_mode = tmp_sys_mode; + pt_debug(cd->dev, DL_INFO, "%s: Return Mode=%d sys_mode=%d\n", + __func__, tmp_mode, tmp_sys_mode); + return rc; +} + +/******************************************************************************* + * FUNCTION: pt_get_fw_sys_mode + * + * SUMMARY: Protected call to _pt_get_fw_sys_mode() to determine if FW scanning + * is active or not. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data + * *sys_mode - pointer to fw system mode + * *mode - pointer to mode + ******************************************************************************/ +static int pt_get_fw_sys_mode(struct pt_core_data *cd, u8 *sys_mode, u8 *mode) +{ + int rc; + + rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT); + if (rc < 0) { + pt_debug(cd->dev, DL_ERROR, + "%s: fail get exclusive ex=%p own=%p\n", + __func__, cd->exclusive_dev, cd->dev); + return rc; + } + + rc = _pt_get_fw_sys_mode(cd, sys_mode, mode); + + if (release_exclusive(cd, cd->dev) < 0) + pt_debug(cd->dev, DL_ERROR, + "%s: fail to release exclusive\n", __func__); + + return rc; +} + +/******************************************************************************* + * FUNCTION: _pt_request_get_fw_sys_mode + * + * SUMMARY: Function pointer included in core_cmds to allow other modules + * to request to get scan state + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *dev - pointer to device structure + * protect - flag to call protected or non-protected + * *sys_mode - pointer to FW system mode + * *mode - pointer to mode + ******************************************************************************/ +static int _pt_request_get_fw_sys_mode(struct device *dev, int protect, + u8 *sys_mode, u8 *mode) +{ + struct pt_core_data *cd = dev_get_drvdata(dev); + + if (protect) + return pt_get_fw_sys_mode(cd, sys_mode, mode); + + return _pt_get_fw_sys_mode(cd, sys_mode, mode); +} + +/* Default hid descriptor to provide basic register map */ +const struct pt_hid_desc hid_desc_default = { + 230, /* hid_desc_len */ + HID_APP_REPORT_ID, /* packet_id */ + 0x00, /* reserved_byte */ + 0x0100, /* bcd_version */ + 0x00EC, /* report_desc_len */ + 0x0002, /* report_desc_register */ + 0x0003, /* input_register */ + 0x00FE, /* max_input_len */ + 0x0004, /* output_register */ + 0x00FE, /* max_output_len */ + 0x0005, /* command_register */ + 0x0006, /* data_register */ + 0x04B4, /* vendor_id */ + 0xC101, /* product_id */ + 0x0100, /* version_id */ + {0x00, 0x00, 0x00, 0x00} /* reserved[4] */ +}; + +/******************************************************************************* + * FUNCTION: pt_init_hid_descriptor + * + * SUMMARY: Setup default values for HID descriptor structure + * + * + * PARAMETERS: + * *desc - pointer to the HID descriptor data read back from DUT + ******************************************************************************/ +static inline void pt_init_hid_descriptor(struct pt_hid_desc *desc) +{ + memcpy(desc, &hid_desc_default, sizeof(hid_desc_default)); +} + +/******************************************************************************* + * FUNCTION: pt_get_hid_descriptor_ + * + * SUMMARY: Send the get HID descriptor command to the DUT and load the response + * into the HID descriptor structure + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data + * *desc - pointer to the HID descriptor data read back from DUT + ******************************************************************************/ +static int pt_get_hid_descriptor_(struct pt_core_data *cd, + struct pt_hid_desc *desc) +{ + struct device *dev = cd->dev; + int rc = 0; + int t; + u8 cmd[2]; + + /* + * During startup the HID descriptor is required for all future + * processing. If IRQ is already asserted due to an early touch report + * the report must be cleared before sending command. + */ + pt_flush_bus_if_irq_asserted(cd, PT_FLUSH_BUS_BASED_ON_LEN); + + /* Read HID descriptor length and version */ + mutex_lock(&cd->system_lock); + cd->hid_cmd_state = 1; + mutex_unlock(&cd->system_lock); + + /* Set HID descriptor register */ + memcpy(cmd, &cd->hid_core.hid_desc_register, + sizeof(cd->hid_core.hid_desc_register)); + + pt_debug(cd->dev, DL_INFO, ">>> %s: Write Buffer [%zu]", + __func__, sizeof(cmd)); + pt_pr_buf(cd->dev, DL_DEBUG, cmd, sizeof(cmd), ">>> Get HID Desc"); + rc = pt_adap_write_read_specific(cd, 2, cmd, NULL); + if (rc) { + pt_debug(dev, DL_ERROR, + "%s: failed to get HID descriptor, rc=%d\n", + __func__, rc); + goto error; + } + + t = wait_event_timeout(cd->wait_q, (cd->hid_cmd_state == 0), + msecs_to_jiffies(PT_GET_HID_DESCRIPTOR_TIMEOUT)); + + if (IS_TMO(t)) { +#ifdef TTDL_DIAGNOSTICS + cd->bus_transmit_error_count++; + pt_toggle_err_gpio(cd, PT_ERR_GPIO_I2C_TRANS); +#endif /* TTDL_DIAGNOSTICS */ + pt_debug(cd->dev, DL_ERROR, + "%s: HID get descriptor timed out\n", __func__); + rc = -ETIME; + goto error; + } else { + cd->hw_detected = true; + } + + /* Load the HID descriptor including all registers */ + memcpy((u8 *)desc, cd->response_buf, sizeof(struct pt_hid_desc)); + + /* Check HID descriptor length and version */ + pt_debug(dev, DL_INFO, "%s: HID len:%X HID ver:%X\n", __func__, + le16_to_cpu(desc->hid_desc_len), + le16_to_cpu(desc->bcd_version)); + + if (le16_to_cpu(desc->hid_desc_len) != sizeof(*desc) || + le16_to_cpu(desc->bcd_version) != HID_VERSION) { + pt_debug(dev, DL_ERROR, "%s: Unsupported HID version\n", + __func__); + return -ENODEV; + } + + goto exit; + +error: + mutex_lock(&cd->system_lock); + cd->hid_cmd_state = 0; + mutex_unlock(&cd->system_lock); +exit: + return rc; +} + +/******************************************************************************* + * FUNCTION: pt_get_hid_descriptor + * + * SUMMARY: Protected call to pt_get_hid_descriptor_() + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data + * *desc - pointer to the HID descriptor data read back from DUT + ******************************************************************************/ +static int pt_get_hid_descriptor(struct pt_core_data *cd, + struct pt_hid_desc *desc) +{ + int rc; + + rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT); + if (rc < 0) { + pt_debug(cd->dev, DL_ERROR, + "%s: fail get exclusive ex=%p own=%p\n", + __func__, cd->exclusive_dev, cd->dev); + return rc; + } + + rc = pt_get_hid_descriptor_(cd, desc); + + if (release_exclusive(cd, cd->dev) < 0) + pt_debug(cd->dev, DL_ERROR, + "%s: fail to release exclusive\n", __func__); + + return rc; +} + +/******************************************************************************* + * FUNCTION: pt_pip2_get_version_ + * + * SUMMARY: Sends a PIP2 VERSION command to the DUT and stores the data in + * cd-ttdata + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data + ******************************************************************************/ +static int pt_pip2_get_version_(struct pt_core_data *cd) +{ + int rc = 0; + int status; + u8 read_buf[64]; + u16 actual_read_len; + + rc = _pt_request_pip2_send_cmd(cd->dev, + PT_CORE_CMD_UNPROTECTED, PIP2_CMD_ID_VERSION, + NULL, 0, read_buf, &actual_read_len); + if (rc) { + pt_debug(cd->dev, DL_ERROR, + "%s: Error Sending PIP2 VERSION Cmd rc=%d\n", + __func__, rc); + return rc; + } + + status = read_buf[PIP2_RESP_STATUS_OFFSET]; + if (status == 0) { + /* Parse the PIP2 VERSION response into ttdata */ + pt_pip2_ver_load_ttdata(cd, actual_read_len); + } else { + pt_debug(cd->dev, DL_ERROR, + "%s: Error in PIP2 VERSION Cmd status=%d\n", + __func__, status); + return status; + } + + return rc; +} + +/******************************************************************************* + * FUNCTION: pt_pip2_get_version + * + * SUMMARY: Protected call to pt_pip2_get_version_ by exclusive + * access to the DUT. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data structure + ******************************************************************************/ +static int pt_pip2_get_version(struct pt_core_data *cd) +{ + int rc; + + rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT); + if (rc < 0) { + pt_debug(cd->dev, DL_ERROR, + "%s: fail get exclusive ex=%p own=%p\n", + __func__, cd->exclusive_dev, cd->dev); + return rc; + } + + rc = pt_pip2_get_version_(cd); + + if (release_exclusive(cd, cd->dev) < 0) + pt_debug(cd->dev, DL_ERROR, + "%s: fail to release exclusive\n", __func__); + + return rc; +} + + +/******************************************************************************* + * FUNCTION: _pt_request_active_pip_protocol + * + * SUMMARY: Get active PIP protocol version using the PIP2 version command. + * Function will return PIP version of BL or application based on + * when it's called. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *dev - pointer to device structure + * protect - flag to run in protected mode + * *pip_version_major - pointer to store PIP major version + * *pip_version_minor - pointer to store PIP minor version + ******************************************************************************/ +int _pt_request_active_pip_protocol(struct device *dev, int protect, + u8 *pip_version_major, u8 *pip_version_minor) +{ + struct pt_core_data *cd = dev_get_drvdata(dev); + struct pt_ttdata *ttdata = &cd->sysinfo.ttdata; + int rc = 0; + struct pt_hid_output sys_info = { + CREATE_PIP1_FW_CMD(PIP1_CMD_ID_GET_SYSINFO), + .timeout_ms = PT_PIP1_CMD_GET_SYSINFO_TIMEOUT, + }; + + pt_flush_bus_if_irq_asserted(cd, PT_FLUSH_BUS_BASED_ON_LEN); + + /* Skip PIP2 command if DUT generation is confirmed */ + if (cd->active_dut_generation == DUT_PIP1_ONLY) + goto skip_pip2_command; + + rc = pt_pip2_get_version_(cd); + if (!rc) { + *pip_version_major = ttdata->pip_ver_major; + *pip_version_minor = ttdata->pip_ver_minor; + pt_debug(dev, DL_INFO, + "%s: pip_version = %d.%d\n", __func__, + *pip_version_major, *pip_version_minor); + } else { + /* + * Legacy products do not support the pip2 protocol to get + * pip version. However, they do support the "get sysinfo" + * command to get pip version from FW, but the bootloader + * does not support it. This function will try "get sysinfo" + * command if the pip2 command failed but this cmd could also + * fail if DUT is stuck in bootloader mode. + */ + pt_debug(dev, DL_INFO, + "%s: PIP2 no response rc = %d, try legacy cmd\n", + __func__, rc); + +skip_pip2_command: + rc = pt_pip1_send_output_and_wait_(cd, &sys_info); + if (!rc) { + *pip_version_minor = + cd->response_buf[PIP1_SYSINFO_TTDATA_OFFSET + 1]; + *pip_version_major = + cd->response_buf[PIP1_SYSINFO_TTDATA_OFFSET]; + + pt_debug(dev, DL_INFO, + "%s: pip_version = %d.%d\n", __func__, + *pip_version_major, *pip_version_minor); + } else { + *pip_version_major = 0; + *pip_version_minor = 0; + pt_debug(dev, DL_ERROR, + "%s: pip_version Not Detected\n", __func__); + } + } + + return rc; +} +EXPORT_SYMBOL_GPL(_pt_request_active_pip_protocol); + +/******************************************************************************* + * FUNCTION: _pt_detect_dut_generation + * + * SUMMARY: Determine the generation of device that we are communicating with: + * DUT_PIP1_ONLY (Gen5 or Gen6) + * DUT_PIP2_CAPABLE (TC33xx or TT7xxx) + * The HID_DESC command is supported in Gen5/6 BL and FW as well as + * TT/TC FW. The packet ID in the descriptor, however, is unique when + * coming form the BL or the FW: + * Packet_ID in BL = HID_BL_REPORT_ID (0xFF) + * Packet_ID in FW = HID_APP_REPORT_ID (0xF7) + * This function will return a modified status if it detects the DUT + * is in the BL. In the case of a Gen5/6 BL, which also sends out a FW + * reset sentinel, the status is "corrected" from a FW to BL sentinel. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *dev - pointer to device structure + * *status - pointer to status bitmask + * *dut_gen - pointer to store the dut_generation + * *mode - pointer to store the PT_MODE + ******************************************************************************/ +static int _pt_detect_dut_generation(struct device *dev, + u32 *status, u8 *dut_gen, enum pt_mode *mode) +{ + int rc = 0; + u8 dut_gen_tmp = DUT_UNKNOWN; + u8 mode_tmp = PT_MODE_UNKNOWN; + u8 attempt = 1; + u32 status_tmp = STARTUP_STATUS_START; + struct pt_core_data *cd = dev_get_drvdata(dev); + struct pt_hid_desc hid_desc; + + memset(&hid_desc, 0, sizeof(hid_desc)); + rc = pt_get_hid_descriptor_(cd, &hid_desc); + while (rc && attempt < 3) { + attempt++; + usleep_range(2000, 5000); + rc = pt_get_hid_descriptor_(cd, &hid_desc); + } + + if (!rc && hid_desc.packet_id == HID_BL_REPORT_ID) { + dut_gen_tmp = DUT_PIP1_ONLY; /* Gen5/6 BL */ + mode_tmp = PT_MODE_BOOTLOADER; + status_tmp = STARTUP_STATUS_BL_RESET_SENTINEL; + } else if (!rc && hid_desc.packet_id == HID_APP_REPORT_ID) { + rc = pt_pip2_get_version_(cd); + if (!rc) + dut_gen_tmp = DUT_PIP2_CAPABLE; /* TT/TC FW */ + else + dut_gen_tmp = DUT_PIP1_ONLY; /* Gen5/6 FW */ + mode_tmp = PT_MODE_OPERATIONAL; + status_tmp = STARTUP_STATUS_FW_RESET_SENTINEL; + rc = 0; /* To return success instead of error code */ + } else if (rc) { + rc = pt_pip2_get_version_(cd); + if (!rc) { + dut_gen_tmp = DUT_PIP2_CAPABLE; /* TT/TC BL */ + mode_tmp = PT_MODE_BOOTLOADER; + status_tmp = STARTUP_STATUS_BL_RESET_SENTINEL; + } + } + + mutex_lock(&cd->system_lock); + if (dut_gen) + *dut_gen = dut_gen_tmp; + if (mode) + *mode = mode_tmp; + if (status) + *status = status_tmp; + mutex_unlock(&cd->system_lock); + +#ifdef TTDL_DIAGNOSTICS + pt_debug(cd->dev, DL_INFO, "%s: Generation=%d Mode=%d\n", + __func__, dut_gen_tmp, mode_tmp); +#endif /* TTDL_DIAGNOSTICS */ + + return rc; +} + +/******************************************************************************* + * FUNCTION: _pt_request_dut_generation + * + * SUMMARY: Function pointer included in core_cmds to allow other modules + * to get current dut generation. + * + * NOTE: This function WILL NOT try to determine dut generation. + * + * RETURN: + * The current dut generation. + * + * PARAMETERS: + * *dev - pointer to device structure + ******************************************************************************/ +static int _pt_request_dut_generation(struct device *dev) +{ + struct pt_core_data *cd = dev_get_drvdata(dev); + + return cd->active_dut_generation; +} + +#define HW_VERSION_LEN_MAX 13 +/******************************************************************************* + * FUNCTION: _legacy_generate_hw_version + * + * SUMMARY: Format chip infomation from struct ttdata (maintained by PIP1 + * SYSINFO command) or struct bl_info (maintained by PIP1 BL INFOMATION + * 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) { + snprintf(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) { + snprintf(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 infomation 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 unkown!\n", + __func__); + } + +exit_error: + snprintf(hw_version, HW_VERSION_LEN_MAX, "FFFF.FFFF.FF"); + return rc; +} + +/******************************************************************************* + * FUNCTION: pt_start_wd_timer + * + * SUMMARY: Starts the TTDL watchdog timer if the timer interval is > 0 + * + * RETURN: void + * + * PARAMETERS: + * *cd - pointer to core data + ******************************************************************************/ +static void pt_start_wd_timer(struct pt_core_data *cd) +{ + if (cd->watchdog_interval < 100) { + pt_debug(cd->dev, DL_ERROR, + "%s: WARNING: Invalid watchdog interval: %d\n", + __func__, cd->watchdog_interval); + return; + } + + if (cd->watchdog_force_stop) { + pt_debug(cd->dev, DL_INFO, + "%s: TTDL WD Forced stop\n", __func__); + return; + } + + mod_timer(&cd->watchdog_timer, jiffies + + msecs_to_jiffies(cd->watchdog_interval)); + cd->watchdog_enabled = 1; + pt_debug(cd->dev, DL_INFO, "%s: TTDL WD Started\n", __func__); +} + +/******************************************************************************* + * FUNCTION: pt_stop_wd_timer + * + * SUMMARY: Stops the TTDL watchdog timer if the timer interval is > 0 + * + * RETURN: void + * + * PARAMETERS: + * *cd - pointer to core data + ******************************************************************************/ +static void pt_stop_wd_timer(struct pt_core_data *cd) +{ + if (!cd->watchdog_interval) + return; + + /* + * Ensure we wait until the watchdog timer + * running on a different CPU finishes + */ + del_timer_sync(&cd->watchdog_timer); + cancel_work_sync(&cd->watchdog_work); + del_timer_sync(&cd->watchdog_timer); + cd->watchdog_enabled = 0; + pt_debug(cd->dev, DL_INFO, "%s: TTDL WD Stopped\n", __func__); +} + +/******************************************************************************* + * FUNCTION: pt_hw_soft_reset + * + * SUMMARY: Sends a PIP reset command to the DUT. Disable/re-enable the + * TTDL watchdog around the reset to ensure the WD doesn't happen to + * schedule an enum if it fires when the DUT is being reset. + * This can cause a double reset. + * + * NOTE: The WD MUST be stopped/restarted by the calling Function. Having + * the WD active could cause this function to fail! + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data struct + * protect - flag to call protected or non-protected + ******************************************************************************/ +static int pt_hw_soft_reset(struct pt_core_data *cd, int protect) +{ + int rc = 0; + + mutex_lock(&cd->system_lock); + cd->startup_status = STARTUP_STATUS_START; + pt_debug(cd->dev, DL_DEBUG, "%s: Startup Status Reset\n", __func__); + mutex_unlock(&cd->system_lock); + if (protect) + rc = pt_hid_cmd_reset(cd); + else + rc = pt_hid_cmd_reset_(cd); + if (rc < 0) { + pt_debug(cd->dev, DL_ERROR, + "%s: FAILED to execute SOFT reset\n", __func__); + return rc; + } + pt_debug(cd->dev, DL_INFO, "%s: SOFT reset successful\n", + __func__); + + return 0; +} + +/******************************************************************************* + * FUNCTION: pt_hw_hard_reset + * + * SUMMARY: Calls the platform xres function if it exists to perform a hard + * reset on the DUT by toggling the XRES gpio. Disable/re-enable the + * TTDL watchdog around the reset to ensure the WD doesn't happen to + * schedule an enum if it fires when the DUT is being reset. + * This can cause a double reset. + * + * NOTE: The WD MUST be stopped/restarted by the calling Function. Having + * the WD active could cause this function to fail! + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data struct + ******************************************************************************/ +static int pt_hw_hard_reset(struct pt_core_data *cd) +{ + if (cd->cpdata->xres) { + cd->startup_status = STARTUP_STATUS_START; + pt_debug(cd->dev, DL_DEBUG, "%s: Startup Status Reset\n", + __func__); + cd->cpdata->xres(cd->cpdata, cd->dev); + pt_debug(cd->dev, DL_WARN, "%s: executed HARD reset\n", + __func__); + return 0; + } + pt_debug(cd->dev, DL_ERROR, + "%s: FAILED to execute HARD reset\n", __func__); + + return -ENODEV; +} + +/******************************************************************************* + * FUNCTION: pt_dut_reset + * + * SUMMARY: Attempts to reset the DUT by a hard reset and if that fails a + * soft reset. + * + * NOTE: The WD MUST be stopped/restarted by the calling Function. Having + * the WD active could cause this function to fail! + * NOTE: "protect" flag is only used for soft reset. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data structure + * protect - flag to call protected or non-protected + ******************************************************************************/ +static int pt_dut_reset(struct pt_core_data *cd, int protect) +{ + int rc = 0; + + pt_debug(cd->dev, DL_INFO, "%s: reset hw...\n", __func__); + mutex_lock(&cd->system_lock); + cd->hid_reset_cmd_state = 1; + rc = pt_hw_hard_reset(cd); + mutex_unlock(&cd->system_lock); + + if (rc == -ENODEV) { + mutex_lock(&cd->system_lock); + cd->hid_reset_cmd_state = 0; + mutex_unlock(&cd->system_lock); + pt_debug(cd->dev, DL_ERROR, + "%s: Hard reset failed, try soft reset\n", __func__); + rc = pt_hw_soft_reset(cd, protect); + } + + if (rc) + pt_debug(cd->dev, DL_ERROR, "%s: %s dev='%s' r=%d\n", + __func__, "Fail hw reset", dev_name(cd->dev), rc); + return rc; +} + +/******************************************************************************* + * FUNCTION: pt_dut_reset_and_wait + * + * SUMMARY: Wrapper function for pt_dut_reset that waits for the reset to + * complete + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data structure + ******************************************************************************/ +static int pt_dut_reset_and_wait(struct pt_core_data *cd) +{ + int rc = 0; + int t; + + rc = pt_dut_reset(cd, PT_CORE_CMD_UNPROTECTED); + if (rc < 0) + goto exit; + + t = wait_event_timeout(cd->wait_q, + (cd->hid_reset_cmd_state == 0), + msecs_to_jiffies(PT_HID_CMD_DEFAULT_TIMEOUT)); + if (IS_TMO(t)) { +#ifdef TTDL_DIAGNOSTICS + cd->bus_transmit_error_count++; + pt_toggle_err_gpio(cd, PT_ERR_GPIO_I2C_TRANS); +#endif /* TTDL_DIAGNOSTICS */ + pt_debug(cd->dev, DL_ERROR, "%s: reset timed out\n", + __func__); + rc = -ETIME; + goto exit; + } + +exit: + return rc; +} + +/* + * touch default parameters (from report descriptor) to resolve protocol for + * touch report + */ +const struct pt_tch_abs_params tch_hdr_default[PT_TCH_NUM_HDR] = { + /* byte offset, size, min, max, bit offset, report */ + {0x00, 0x02, 0x00, 0x10000, 0x00, 0x01}, /* SCAN TIME */ + {0x02, 0x01, 0x00, 0x20, 0x00, 0x01}, /* NUMBER OF RECORDS */ + {0x02, 0x01, 0x00, 0x02, 0x05, 0x01}, /* LARGE OBJECT */ + {0x03, 0x01, 0x00, 0x08, 0x00, 0x01}, /* NOISE EFFECT */ + {0x03, 0x01, 0x00, 0x04, 0x06, 0x01}, /* REPORT_COUNTER */ +}; + +/* + * button default parameters (from report descriptor) to resolve protocol for + * button report + */ +const struct pt_tch_abs_params tch_abs_default[PT_TCH_NUM_ABS] = { + /* byte offset, size, min, max, bit offset, report */ + {0x02, 0x02, 0x00, 0x10000, 0x00, 0x01}, /* X */ + {0x04, 0x02, 0x00, 0x10000, 0x00, 0x01}, /* Y */ + {0x06, 0x01, 0x00, 0x100, 0x00, 0x01}, /* P (Z) */ + {0x01, 0x01, 0x00, 0x20, 0x00, 0x01}, /* TOUCH ID */ + {0x01, 0x01, 0x00, 0x04, 0x05, 0x01}, /* EVENT ID */ + {0x00, 0x01, 0x00, 0x08, 0x00, 0x01}, /* OBJECT ID */ + {0x01, 0x01, 0x00, 0x02, 0x07, 0x01}, /* LIFTOFF */ + {0x07, 0x01, 0x00, 0x100, 0x00, 0x01}, /* TOUCH_MAJOR */ + {0x08, 0x01, 0x00, 0x100, 0x00, 0x01}, /* TOUCH_MINOR */ + {0x09, 0x01, 0x00, 0x100, 0x00, 0x01}, /* ORIENTATION */ +}; + +/******************************************************************************* + * FUNCTION: pt_init_pip_report_fields + * + * SUMMARY: Setup default values for touch/button report parsing. + * + * PARAMETERS: + * *cd - pointer to core data structure + ******************************************************************************/ +static void pt_init_pip_report_fields(struct pt_core_data *cd) +{ + struct pt_sysinfo *si = &cd->sysinfo; + + memcpy(si->tch_hdr, tch_hdr_default, sizeof(tch_hdr_default)); + memcpy(si->tch_abs, tch_abs_default, sizeof(tch_abs_default)); + + si->desc.tch_report_id = PT_PIP_TOUCH_REPORT_ID; + si->desc.tch_record_size = TOUCH_REPORT_SIZE; + si->desc.tch_header_size = TOUCH_INPUT_HEADER_SIZE; + si->desc.btn_report_id = PT_PIP_CAPSENSE_BTN_REPORT_ID; + + cd->features.easywake = 1; + cd->features.noise_metric = 1; + cd->features.tracking_heatmap = 1; + cd->features.sensor_data = 1; +} + +/******************************************************************************* + * FUNCTION: pt_get_mode + * + * SUMMARY: Determine the current mode from the contents of a HID descriptor + * message + * + * RETURN: Enum of the current mode + * + * PARAMETERS: + * *cd - pointer to the Core Data structure + * protect - run command in protected mode + * *mode - pointer to store the retrieved mode + ******************************************************************************/ +static int pt_get_mode(struct pt_core_data *cd, struct pt_hid_desc *desc) +{ + if (desc->packet_id == HID_APP_REPORT_ID) + return PT_MODE_OPERATIONAL; + else if (desc->packet_id == HID_BL_REPORT_ID) + return PT_MODE_BOOTLOADER; + + return PT_MODE_UNKNOWN; +} + +/******************************************************************************* + * FUNCTION: _pt_request_get_mode + * + * SUMMARY: Function pointer included in core_cmds to allow other modules + * to determine the current mode of the DUT by use of the Get HID + * Descriptor command. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *dev - pointer to device structure + * protect - run command in protected mode + * *mode - pointer to store the retrieved mode + ******************************************************************************/ +static int _pt_request_get_mode(struct device *dev, int protect, u8 *mode) +{ + struct pt_core_data *cd = dev_get_drvdata(dev); + struct pt_hid_desc hid_desc; + int rc = 0; + + memset(&hid_desc, 0, sizeof(hid_desc)); + if (protect) + rc = pt_get_hid_descriptor(cd, &hid_desc); + else + rc = pt_get_hid_descriptor_(cd, &hid_desc); + + if (rc) + *mode = PT_MODE_UNKNOWN; + else + *mode = pt_get_mode(cd, &hid_desc); + + return rc; +} + +/******************************************************************************* + * FUNCTION: pt_queue_enum_ + * + * SUMMARY: Queues a TTDL enum by scheduling work with the pt_enum_with_dut() + * function. It won't try to add/delete sysfs node or modules. + * + * RETURN: void + * + * PARAMETERS: + * *cd - pointer to core data + ******************************************************************************/ +static void pt_queue_enum_(struct pt_core_data *cd) +{ + if (cd->startup_state == STARTUP_NONE) { + cd->startup_state = STARTUP_QUEUED; + +#ifdef TTDL_DIAGNOSTICS + if (!cd->bridge_mode) + schedule_work(&cd->enum_work); + else + cd->startup_state = STARTUP_NONE; +#else + schedule_work(&cd->enum_work); +#endif + pt_debug(cd->dev, DL_INFO, + "%s: enum_work queued\n", __func__); + } else { + pt_debug(cd->dev, DL_WARN, + "%s: Enum not queued - startup_state = %d\n", + __func__, cd->startup_state); + } +} + +/******************************************************************************* + * FUNCTION: pt_queue_enum + * + * SUMMARY: Queues a TTDL enum within a mutex lock + * + * RETURN: void + * + * PARAMETERS: + * *cd - pointer to core data + ******************************************************************************/ +static void pt_queue_enum(struct pt_core_data *cd) +{ + mutex_lock(&cd->system_lock); + pt_queue_enum_(cd); + mutex_unlock(&cd->system_lock); +} + +static void remove_sysfs_and_modules(struct device *dev); +/******************************************************************************* + * FUNCTION: pt_queue_restart + * + * SUMMARY: Queues a TTDL restart within a mutex lock + * + * RETURN: void + * + * PARAMETERS: + * *cd - pointer to core data + * remove_sysfs_module - True: remove all DUT relative sysfs nodes and modules + * False: will not perform remove action + ******************************************************************************/ +static void pt_queue_restart(struct pt_core_data *cd) +{ + mutex_lock(&cd->system_lock); + if (cd->startup_state == STARTUP_NONE) { + cd->startup_state = STARTUP_QUEUED; + + schedule_work(&cd->ttdl_restart_work); + pt_debug(cd->dev, DL_INFO, + "%s: pt_ttdl_restart queued\n", __func__); + } else { + pt_debug(cd->dev, DL_INFO, "%s: startup_state = %d\n", + __func__, cd->startup_state); + } + mutex_unlock(&cd->system_lock); +} + +/******************************************************************************* + * FUNCTION: call_atten_cb + * + * SUMMARY: Iterate over attention list call the function that registered. + * + * RETURN: void + * + * PARAMETERS: + * *cd - pointer to core data + * type - type of attention list + * mode - condition for execution + ******************************************************************************/ +static void call_atten_cb(struct pt_core_data *cd, + enum pt_atten_type type, int mode) +{ + struct atten_node *atten, *atten_n; + + pt_debug(cd->dev, DL_DEBUG, "%s: check list type=%d mode=%d\n", + __func__, type, mode); + spin_lock(&cd->spinlock); + list_for_each_entry_safe(atten, atten_n, + &cd->atten_list[type], node) { + if (!mode || atten->mode & mode) { + spin_unlock(&cd->spinlock); + pt_debug(cd->dev, DL_DEBUG, + "%s: attention for '%s'", + __func__, dev_name(atten->dev)); + atten->func(atten->dev); + spin_lock(&cd->spinlock); + } + } + spin_unlock(&cd->spinlock); +} + +/******************************************************************************* + * FUNCTION: start_fw_upgrade + * + * SUMMARY: Calling "PT_ATTEN_LOADER" attention list that loader registered to + * start firmware upgrade. + * + * RETURN: + * 0 = success + * + * PARAMETERS: + * *data - pointer to core data + ******************************************************************************/ +static int start_fw_upgrade(void *data) +{ + struct pt_core_data *cd = (struct pt_core_data *)data; + + call_atten_cb(cd, PT_ATTEN_LOADER, 0); + + return 0; +} + +/******************************************************************************* + * FUNCTION: pt_put_device_into_easy_wakeup_ + * + * SUMMARY: Call the enter_easywake_state function and set the device into easy + * wake up state. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data + ******************************************************************************/ +static int pt_put_device_into_easy_wakeup_(struct pt_core_data *cd) +{ + int rc = 0; + u8 status = 0; + + 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; + + mutex_lock(&cd->system_lock); + if (cd->sleep_state == SS_SLEEP_OFF) { + cd->sleep_state = SS_SLEEPING; + } else { + mutex_unlock(&cd->system_lock); + return 1; + } + 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); + cd->sleep_state = SS_SLEEP_ON; + mutex_unlock(&cd->system_lock); + + return rc; +} + +/******************************************************************************* + * FUNCTION: pt_core_sleep + * + * SUMMARY: Protected call to pt_core_sleep_ by exclusive access to the DUT. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data + ******************************************************************************/ +static int pt_core_sleep(struct pt_core_data *cd) +{ + int rc = 0; + + rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT); + if (rc < 0) { + pt_debug(cd->dev, DL_ERROR, + "%s: fail get exclusive ex=%p own=%p\n", + __func__, cd->exclusive_dev, cd->dev); + return rc; + } + + rc = pt_core_sleep_(cd); + + if (release_exclusive(cd, cd->dev) < 0) + pt_debug(cd->dev, DL_ERROR, + "%s: fail to release exclusive\n", __func__); + else + pt_debug(cd->dev, DL_DEBUG, "%s: pass release exclusive\n", + __func__); + + return rc; +} + +/******************************************************************************* + * FUNCTION: pt_wakeup_host + * + * SUMMARY: Check wake up report and call the PT_ATTEN_WAKE attention list. + * + * NOTE: TSG5 EasyWake and TSG6 EasyWake use different protocol. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data + ******************************************************************************/ +static int pt_wakeup_host(struct pt_core_data *cd) +{ +#ifndef EASYWAKE_TSG6 + /* TSG5 EasyWake */ + int rc = 0; + int event_id; + int size = get_unaligned_le16(&cd->input_buf[0]); + + /* Validate report */ + if (size != 4 || cd->input_buf[2] != 4) + rc = -EINVAL; + + event_id = cd->input_buf[3]; + + pt_debug(cd->dev, DL_INFO, "%s: e=%d, rc=%d\n", + __func__, event_id, rc); + + if (rc) { + pt_core_sleep_(cd); + goto exit; + } + + /* attention WAKE */ + call_atten_cb(cd, PT_ATTEN_WAKE, 0); +exit: + return rc; +#else + /* TSG6 FW1.3 EasyWake */ + int rc = 0; + int i = 0; + int report_length; + + /* Validate report */ + if (cd->input_buf[2] != PT_PIP_WAKEUP_REPORT_ID) { + rc = -EINVAL; + pt_core_sleep_(cd); + goto exit; + } + + /* Get gesture id and gesture data length */ + cd->gesture_id = cd->input_buf[3]; + report_length = (cd->input_buf[1] << 8) | (cd->input_buf[0]); + cd->gesture_data_length = report_length - 4; + + pt_debug(cd->dev, DL_INFO, + "%s: gesture_id = %d, gesture_data_length = %d\n", + __func__, cd->gesture_id, cd->gesture_data_length); + + for (i = 0; i < cd->gesture_data_length; i++) + cd->gesture_data[i] = cd->input_buf[4 + i]; + + /* attention WAKE */ + call_atten_cb(cd, PT_ATTEN_WAKE, 0); +exit: + return rc; +#endif +} + +/******************************************************************************* + * FUNCTION: pt_get_touch_axis + * + * SUMMARY: Function to calculate touch axis + * + * PARAMETERS: + * *cd - pointer to core data structure + * *axis - pointer to axis calculation result + * size - size in bytes + * max - max value of result + * *xy_data - pointer to input data to be parsed + * bofs - bit offset + ******************************************************************************/ +static void pt_get_touch_axis(struct pt_core_data *cd, + int *axis, int size, int max, u8 *data, int bofs) +{ + int nbyte; + int next; + + for (nbyte = 0, *axis = 0, next = 0; nbyte < size; nbyte++) { + *axis = *axis + ((data[next] >> bofs) << (nbyte * 8)); + next++; + } + + *axis &= max - 1; +} + +/******************************************************************************* + * FUNCTION: move_tracking_heatmap_data + * + * SUMMARY: Move the valid tracking heatmap data from the input buffer into the + * system information structure, xy_mode and xy_data. + * - If TTHE_TUNER_SUPPORT is defined print the raw sensor data into + * the tthe_tuner sysfs node under the label "THM" + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data + * *si - pointer to the system information structure + ******************************************************************************/ +static int move_tracking_heatmap_data(struct pt_core_data *cd, + struct pt_sysinfo *si) +{ +#ifdef TTHE_TUNER_SUPPORT + int size = get_unaligned_le16(&cd->input_buf[0]); + + if (size) + tthe_print(cd, cd->input_buf, size, "THM="); +#endif + memcpy(si->xy_mode, cd->input_buf, SENSOR_HEADER_SIZE); + return 0; +} + +/******************************************************************************* + * FUNCTION: move_sensor_data + * + * SUMMARY: Move the valid sensor data from the input buffer into the system + * 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 + * system information structure, xy_mode and xy_data. + * - If TTHE_TUNER_SUPPORT is defined print the raw button data into + * the tthe_tuner sysfs node under the label "OpModeData" + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data + * *si - pointer to the system information structure + ******************************************************************************/ +static int move_button_data(struct pt_core_data *cd, + struct pt_sysinfo *si) +{ +#ifdef TTHE_TUNER_SUPPORT + int size = get_unaligned_le16(&cd->input_buf[0]); + + if (size) + tthe_print(cd, cd->input_buf, size, "OpModeData="); +#endif + memcpy(si->xy_mode, cd->input_buf, BTN_INPUT_HEADER_SIZE); + pt_pr_buf(cd->dev, DL_INFO, (u8 *)si->xy_mode, BTN_INPUT_HEADER_SIZE, + "xy_mode"); + + memcpy(si->xy_data, &cd->input_buf[BTN_INPUT_HEADER_SIZE], + BTN_REPORT_SIZE); + pt_pr_buf(cd->dev, DL_INFO, (u8 *)si->xy_data, BTN_REPORT_SIZE, + "xy_data"); + return 0; +} + +/******************************************************************************* + * FUNCTION: move_touch_data + * + * SUMMARY: Move the valid touch data from the input buffer into the system + * system information structure, xy_mode and xy_data. + * - If TTHE_TUNER_SUPPORT is defined print the raw touch data into + * the tthe_tuner sysfs node under the label "OpModeData" + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data + * *si - pointer to the system information structure + ******************************************************************************/ +static int move_touch_data(struct pt_core_data *cd, struct pt_sysinfo *si) +{ + int max_tch = si->sensing_conf_data.max_tch; + int num_cur_tch; + int length; + struct pt_tch_abs_params *tch = &si->tch_hdr[PT_TCH_NUM]; +#ifdef TTHE_TUNER_SUPPORT + int size = get_unaligned_le16(&cd->input_buf[0]); + + if (size) + tthe_print(cd, cd->input_buf, size, "OpModeData="); +#endif + + memcpy(si->xy_mode, cd->input_buf, si->desc.tch_header_size); + pt_pr_buf(cd->dev, DL_INFO, (u8 *)si->xy_mode, + si->desc.tch_header_size, "xy_mode"); + + pt_get_touch_axis(cd, &num_cur_tch, tch->size, + tch->max, si->xy_mode + 3 + tch->ofs, tch->bofs); + if (unlikely(num_cur_tch > max_tch)) + num_cur_tch = max_tch; + + length = num_cur_tch * si->desc.tch_record_size; + + memcpy(si->xy_data, &cd->input_buf[si->desc.tch_header_size], length); + pt_pr_buf(cd->dev, DL_INFO, (u8 *)si->xy_data, length, "xy_data"); + return 0; +} + +/******************************************************************************* + * FUNCTION: move_hid_pen_data + * + * SUMMARY: TODO Move the valid pen data from the input buffer into the system + * system information structure, xy_mode and xy_data. + * - If TTHE_TUNER_SUPPORT is defined print the raw pen data into + * the tthe_tuner sysfs node under the label "HID" starting with the + * report ID. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data + * *si - pointer to the system information structure + ******************************************************************************/ +static int move_hid_pen_data(struct pt_core_data *cd, struct pt_sysinfo *si) +{ +#ifdef TTHE_TUNER_SUPPORT + int size = get_unaligned_le16(&cd->input_buf[0]); + + if (size) { + /* + * HID over USB does not require the two byte length field, so + * this should print from input_buf[2] but to keep both finger + * and pen reports the same the length is included + */ + if (cd->tthe_hid_usb_format == PT_FEATURE_ENABLE) + tthe_print(cd, &(cd->input_buf[2]), size - 2, + "HID-USB="); + else + tthe_print(cd, &(cd->input_buf[0]), size, + "HID-I2C="); + } +#endif + pt_pr_buf(cd->dev, DL_INFO, (u8 *)&(cd->input_buf[0]), size, "HID Pen"); + return 0; +} + +/******************************************************************************* + * FUNCTION: parse_touch_input + * + * SUMMARY: Parse the touch report and take action based on the touch + * report_id. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data + * size - size of touch record + ******************************************************************************/ +static int parse_touch_input(struct pt_core_data *cd, int size) +{ + struct pt_sysinfo *si = &cd->sysinfo; + int report_id = cd->input_buf[2]; + int rc = -EINVAL; + + pt_debug(cd->dev, DL_DEBUG, "%s: Received touch report\n", + __func__); + if (!si->ready) { + pt_debug(cd->dev, DL_ERROR, + "%s: Need system information to parse touches\n", + __func__); + return 0; + } + + if (!si->xy_mode || !si->xy_data) + return rc; + + if (report_id == PT_PIP_TOUCH_REPORT_ID) + rc = move_touch_data(cd, si); + else if (report_id == PT_HID_PEN_REPORT_ID) + rc = move_hid_pen_data(cd, si); + else if (report_id == PT_PIP_CAPSENSE_BTN_REPORT_ID) + rc = move_button_data(cd, si); + else if (report_id == PT_PIP_SENSOR_DATA_REPORT_ID) + rc = move_sensor_data(cd, si); + else if (report_id == PT_PIP_TRACKING_HEATMAP_REPORT_ID) + rc = move_tracking_heatmap_data(cd, si); + + if (rc) + return rc; + + /* attention IRQ */ + call_atten_cb(cd, PT_ATTEN_IRQ, cd->mode); + + return 0; +} + +/******************************************************************************* + * FUNCTION: parse_command_input + * + * SUMMARY: Move the response data from the input buffer to the response buffer + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data + * size - size of response data + ******************************************************************************/ +static int parse_command_input(struct pt_core_data *cd, int size) +{ + pt_debug(cd->dev, DL_DEBUG, "%s: Received cmd interrupt\n", + __func__); + + memcpy(cd->response_buf, cd->input_buf, size); +#if defined(TTHE_TUNER_SUPPORT) && defined(TTDL_DIAGNOSTICS) + if (size && cd->show_tt_data) { + if (cd->pip2_prot_active) + tthe_print(cd, cd->input_buf, size, "TT_DATA_PIP2="); + else + tthe_print(cd, cd->input_buf, size, "TT_DATA="); + } +#endif + + mutex_lock(&cd->system_lock); + cd->hid_cmd_state = 0; + mutex_unlock(&cd->system_lock); + wake_up(&cd->wait_q); + + return 0; +} + +/******************************************************************************* + * FUNCTION: pt_allow_enumeration + * + * SUMMARY: Determine if an enumeration or fully re-probe should perform when + * FW sentinel is seen. + * + * RETURN: + * true = allow enumeration or fully re-probe + * false = skip enumeration and fully re-probe + * + * PARAMETERS: + * *cd - pointer to core data + ******************************************************************************/ +static inline bool pt_allow_enumeration(struct pt_core_data *cd) +{ + if ((cd->panel_id_support & PT_PANEL_ID_BY_SYS_INFO) && + (!cd->hid_reset_cmd_state) && + (cd->core_probe_complete) && + (cd->hid_cmd_state != PIP1_CMD_ID_START_BOOTLOADER + 1) && + (cd->hid_cmd_state != PIP1_BL_CMD_ID_LAUNCH_APP + 1) && + (cd->mode == PT_MODE_OPERATIONAL)) { + return true; + } + + if ((!cd->hid_reset_cmd_state) && + (cd->core_probe_complete) && + (cd->hid_cmd_state != PIP1_CMD_ID_START_BOOTLOADER + 1) && + (cd->hid_cmd_state != PIP1_BL_CMD_ID_LAUNCH_APP + 1) && + (cd->active_dut_generation != DUT_PIP1_ONLY)) { + return true; + } + + pt_debug(cd->dev, DL_INFO, + "%s: Dissallow - %s=%d %s=%d %s=0x%02X %s=%d\n", + __func__, + "hid_reset_cmd_state(0)", cd->hid_reset_cmd_state, + "core_probe_complete(1)", cd->core_probe_complete, + "hid_cmd_state(Not 0x02 or 0x39)", cd->hid_cmd_state, + "active_dut_gen(0,2)", cd->active_dut_generation); + return false; +} + +/******************************************************************************* + * FUNCTION: pt_is_touch_report + * + * SUMMARY: Determine if a report ID should be treated as a touch report + * + * RETURN: + * true = report ID is a touch report + * false = report ID is not a touch report + * + * PARAMETERS: + * id - Report ID + ******************************************************************************/ +static bool pt_is_touch_report(int id) +{ + if (id == PT_PIP_TOUCH_REPORT_ID || + id == PT_HID_PEN_REPORT_ID || + id == PT_PIP_CAPSENSE_BTN_REPORT_ID || + id == PT_PIP_SENSOR_DATA_REPORT_ID || + id == PT_PIP_TRACKING_HEATMAP_REPORT_ID) + return true; + else + return false; +} + +/******************************************************************************* + * FUNCTION: pt_parse_input + * + * SUMMARY: Parse the input data read from DUT due to IRQ. Handle data based + * on if its a response to a command or asynchronous touch data / reset + * sentinel. PIP2.x messages have additional error checking that is + * parsed (SEQ match from cmd to rsp, CRC valid). + * Look for special packets based on unique lengths: + * 0 bytes - APP(FW) reset sentinel or Gen5/6 BL sentinel + * 2 bytes - Empty buffer (PIP 1.7 and earlier) + * 11 bytes - possible PIP2.x reset sentinel (TAG and SEQ must = 0) + * 0xFFXX - Empty buffer (PIP 1.7+) + * Queue a startup after any asynchronous FW reset sentinel is seen, unless + * the initial probe has not yet been done. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data + ******************************************************************************/ +static int pt_parse_input(struct pt_core_data *cd) +{ + int report_id; + int cmd_id; + int is_command = 0; + int size; + int print_size; + bool touch_report = true; + unsigned short calc_crc; + unsigned short resp_crc; + + cd->fw_sys_mode_in_standby_state = false; + size = get_unaligned_le16(&cd->input_buf[0]); + print_size = size; + pt_debug(cd->dev, DL_DEBUG, "<<< %s: IRQ Triggered, read len [%d]\n", + __func__, print_size); + if (print_size <= PT_MAX_INPUT) + pt_pr_buf(cd->dev, DL_DEBUG, cd->input_buf, print_size, + "<<< Read buf"); + + if (size == 0 || + (size == 11 && + (cd->input_buf[PIP2_RESP_SEQUENCE_OFFSET] & + PIP2_RESP_SEQUENCE_MASK) == 0 && + (cd->input_buf[PIP2_RESP_REPORT_ID_OFFSET] & + PIP2_CMD_COMMAND_ID_MASK) == + PIP2_CMD_ID_STATUS)) { + touch_report = false; + cd->hw_detected = true; + cd->bl_pip_ver_ready = false; + cd->app_pip_ver_ready = false; + if (size == 0) { + mutex_lock(&cd->system_lock); + cd->pip2_prot_active = false; + if (cd->active_dut_generation == DUT_PIP1_ONLY) { + /* + * For Gen5/6 this sentinel could be from + * the BL or FW. Attempt to set the correct + * mode based on the previous PIP command. + */ + if (cd->hid_cmd_state == + PIP1_BL_CMD_ID_LAUNCH_APP + 1) { + cd->mode = PT_MODE_OPERATIONAL; + cd->startup_status = + STARTUP_STATUS_FW_RESET_SENTINEL; + } else if (cd->hid_cmd_state == + PIP1_CMD_ID_START_BOOTLOADER + 1 || + cd->hid_reset_cmd_state) { + cd->mode = PT_MODE_BOOTLOADER; + cd->startup_status = + STARTUP_STATUS_BL_RESET_SENTINEL; + } else { + cd->mode = PT_MODE_UNKNOWN; + cd->startup_status = + STARTUP_STATUS_START; + } + cd->fw_system_mode = FW_SYS_MODE_UNDEFINED; + + pt_debug(cd->dev, DL_INFO, + "%s: ATM Gen5/6 %s sentinel received\n", + __func__, + (cd->mode == PT_MODE_OPERATIONAL ? + "FW" : + (cd->mode == PT_MODE_BOOTLOADER ? + "BL" : "Unknown"))); + } else { + cd->mode = PT_MODE_OPERATIONAL; + cd->fw_system_mode = FW_SYS_MODE_BOOT; + cd->startup_status = + STARTUP_STATUS_FW_RESET_SENTINEL; + pt_debug(cd->dev, DL_INFO, + "%s: ATM PT/TT FW sentinel received\n", + __func__); + } + mutex_unlock(&cd->system_lock); + + if (pt_allow_enumeration(cd)) { + if (cd->active_dut_generation == DUT_UNKNOWN) { + pt_debug(cd->dev, DL_INFO, + "%s: Queue Restart\n", __func__); + pt_queue_restart(cd); + } else { + pt_debug(cd->dev, DL_INFO, + "%s: Queue Enum\n", __func__); + pt_queue_enum(cd); + } + } else { + pt_debug(cd->dev, DL_INFO, + "%s: Sentinel - No Queued Action\n", + __func__); + } + + } else { + /* Sentinel must be from TT/TC BL */ + mutex_lock(&cd->system_lock); + cd->pip2_prot_active = true; + cd->startup_status = STARTUP_STATUS_BL_RESET_SENTINEL; + cd->mode = PT_MODE_BOOTLOADER; + cd->sysinfo.ready = false; + mutex_unlock(&cd->system_lock); + pt_debug(cd->dev, DL_INFO, + "%s: BL Reset sentinel received\n", __func__); + if (cd->flashless_dut && + cd->flashless_auto_bl == PT_ALLOW_AUTO_BL) { + pt_debug(cd->dev, DL_INFO, + "%s: BL to RAM for flashless DUT\n", + __func__); + kthread_run(start_fw_upgrade, cd, "pt_loader"); + } + } + mutex_lock(&cd->system_lock); + memcpy(cd->response_buf, cd->input_buf, 2); + if (!cd->hid_reset_cmd_state && !cd->hid_cmd_state) { + mutex_unlock(&cd->system_lock); + pt_debug(cd->dev, DL_WARN, + "%s: Device Initiated Reset\n", __func__); + wake_up(&cd->wait_q); + return 0; + } + + cd->hid_reset_cmd_state = 0; + if (cd->hid_cmd_state == PIP1_CMD_ID_START_BOOTLOADER + 1 || + cd->hid_cmd_state == PIP1_BL_CMD_ID_LAUNCH_APP + 1 || + cd->hid_cmd_state == PIP1_CMD_ID_USER_CMD + 1) + cd->hid_cmd_state = 0; + wake_up(&cd->wait_q); + mutex_unlock(&cd->system_lock); + return 0; + } else if (size == 2 || size >= PT_PIP_1P7_EMPTY_BUF) { + /* + * This debug message below is used by PBATS to calculate the + * time from the last lift off IRQ to when FW exits LFT mode. + */ + touch_report = false; + pt_debug(cd->dev, DL_WARN, + "%s: DUT - Empty buffer detected\n", __func__); + return 0; + } else if (size > PT_MAX_INPUT) { + pt_debug(cd->dev, DL_ERROR, + "%s: DUT - Unexpected len field in active bus data!\n", + __func__); + return -EINVAL; + } + + if (cd->pip2_prot_active) { + pt_debug(cd->dev, DL_DEBUG, + "%s: Decode PIP2.x Response\n", __func__); + + /* PIP2 does not have a report id, hard code it */ + report_id = 0x00; + cmd_id = cd->input_buf[PIP2_RESP_COMMAND_ID_OFFSET]; + + calc_crc = crc_ccitt_calculate(cd->input_buf, size - 2); + resp_crc = cd->input_buf[size - 2] << 8; + resp_crc |= cd->input_buf[size - 1]; + + if ((cd->pip2_cmd_tag_seq != + (cd->input_buf[PIP2_RESP_SEQUENCE_OFFSET] & 0x0F)) && + (resp_crc != calc_crc) && + ((cd->input_buf[PIP1_RESP_REPORT_ID_OFFSET] + == PT_PIP_TOUCH_REPORT_ID) || + (cd->input_buf[PIP1_RESP_REPORT_ID_OFFSET] + == PT_PIP_CAPSENSE_BTN_REPORT_ID))) { + pt_debug(cd->dev, DL_WARN, + "%s: %s %d %s\n", + __func__, + "Received PIP1 report id =", + cd->input_buf[PIP1_RESP_REPORT_ID_OFFSET], + "when expecting a PIP2 report - IGNORE report"); + return 0; + } + + is_command = 1; + touch_report = false; + } else { + report_id = cd->input_buf[PIP1_RESP_REPORT_ID_OFFSET]; + cmd_id = cd->input_buf[PIP1_RESP_COMMAND_ID_OFFSET]; + } + +#ifdef TTDL_DIAGNOSTICS + pt_debug(cd->dev, DL_DEBUG, + "%s: pip2 = %d report_id: 0x%02X, cmd_code: 0x%02X\n", + __func__, cd->pip2_prot_active, report_id, + (cmd_id & PIP2_CMD_COMMAND_ID_MASK)); +#endif /* TTDL_DIAGNOSTICS */ + + if (report_id == PT_PIP_WAKEUP_REPORT_ID) { + pt_wakeup_host(cd); +#ifdef TTHE_TUNER_SUPPORT + tthe_print(cd, cd->input_buf, size, "TT_WAKEUP="); +#endif + return 0; + } + + mod_timer_pending(&cd->watchdog_timer, jiffies + + msecs_to_jiffies(cd->watchdog_interval)); + + /* + * If it is PIP2 response, the report_id has been set to 0, + * so it will not be parsed as a touch packet. + */ + if (!pt_is_touch_report(report_id)) { + is_command = 1; + touch_report = false; + } + + if (unlikely(is_command)) { + parse_command_input(cd, size); + return 0; + } + + if (touch_report) + parse_touch_input(cd, size); + + return 0; +} + +/******************************************************************************* + * FUNCTION: pt_read_input + * + * SUMMARY: Reads incoming data off of the active bus + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data + ******************************************************************************/ +static int pt_read_input(struct pt_core_data *cd) +{ + struct device *dev = cd->dev; + int rc = 0; + int t; + int retry = PT_BUS_READ_INPUT_RETRY_COUNT; + + /* + * Workaround for easywake failure + * Interrupt for easywake, wait for bus controller to wake + */ + mutex_lock(&cd->system_lock); + if (IS_EASY_WAKE_CONFIGURED(cd->easy_wakeup_gesture)) { + if (cd->sleep_state == SS_SLEEP_ON) { + mutex_unlock(&cd->system_lock); + if (!dev->power.is_suspended) + goto read; + t = wait_event_timeout(cd->wait_q, + (cd->wait_until_wake == 1), + msecs_to_jiffies(2000)); +#ifdef TTDL_DIAGNOSTICS + if (IS_TMO(t)) { + cd->bus_transmit_error_count++; + pt_toggle_err_gpio(cd, PT_ERR_GPIO_I2C_TRANS); + pt_debug(dev, DL_ERROR, + "%s: !!!I2C Transmission Error %d\n", + __func__, + cd->bus_transmit_error_count); + } +#else + if (IS_TMO(t)) + pt_queue_enum(cd); +#endif /* TTDL_DIAGNOSTICS */ + goto read; + } + } + mutex_unlock(&cd->system_lock); + +read: + /* Try reading up to 'retry' times */ + while (retry-- != 0) { + rc = pt_adap_read_default_nosize(cd, cd->input_buf, + PT_MAX_INPUT); + if (!rc) { + pt_debug(dev, DL_DEBUG, + "%s: Read input successfully\n", __func__); + goto read_exit; + } + usleep_range(1000, 2000); + } + pt_debug(dev, DL_ERROR, + "%s: Error getting report, rc=%d\n", __func__, rc); + +read_exit: + return rc; +} + +/******************************************************************************* + * FUNCTION: pt_irq + * + * SUMMARY: Process all detected interrupts + * + * RETURN: + * IRQ_HANDLED - Finished processing the interrupt + * + * PARAMETERS: + * irq - IRQ number + * *handle - pointer to core data struct + ******************************************************************************/ +irqreturn_t pt_irq(int irq, void *handle) +{ + struct pt_core_data *cd = handle; + int rc = 0; + + if (!pt_check_irq_asserted(cd)) + return IRQ_HANDLED; + + rc = pt_read_input(cd); +#ifdef TTDL_DIAGNOSTICS + cd->irq_count++; + + /* Used to calculate T-Refresh */ + if (cd->t_refresh_active) { + if (cd->t_refresh_count == 0) { + cd->t_refresh_time = jiffies; + cd->t_refresh_count++; + } else if (cd->t_refresh_count < cd->t_refresh_total) { + cd->t_refresh_count++; + } else { + cd->t_refresh_active = 0; + cd->t_refresh_time = + jiffies_to_msecs(jiffies - cd->t_refresh_time); + } + } +#endif /* TTDL_DIAGNOSTICS */ + + if (!rc) + pt_parse_input(cd); + + return IRQ_HANDLED; +} + + +/******************************************************************************* + * FUNCTION: _pt_subscribe_attention + * + * SUMMARY: Function pointer included in core_cmds to allow other modules + * to subscribe themselves into the TTDL attention list + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *dev - pointer to device structure + * type - attention type enum + * *id - ID of the module calling this function + * *func - callback function pointer to be called when notified + * mode - attention mode + ******************************************************************************/ +int _pt_subscribe_attention(struct device *dev, + enum pt_atten_type type, char *id, int (*func)(struct device *), + int mode) +{ + struct pt_core_data *cd = dev_get_drvdata(dev); + struct atten_node *atten, *atten_new; + + atten_new = kzalloc(sizeof(*atten_new), GFP_KERNEL); + if (!atten_new) + return -ENOMEM; + + pt_debug(cd->dev, DL_INFO, "%s from '%s'\n", __func__, + dev_name(cd->dev)); + + spin_lock(&cd->spinlock); + list_for_each_entry(atten, &cd->atten_list[type], node) { + if (atten->id == id && atten->mode == mode) { + spin_unlock(&cd->spinlock); + kfree(atten_new); + pt_debug(cd->dev, DL_INFO, "%s: %s=%p %s=%d\n", + __func__, + "already subscribed attention", + dev, "mode", mode); + + return 0; + } + } + + atten_new->id = id; + atten_new->dev = dev; + atten_new->mode = mode; + atten_new->func = func; + + list_add(&atten_new->node, &cd->atten_list[type]); + spin_unlock(&cd->spinlock); + + return 0; +} + +/******************************************************************************* + * FUNCTION: _pt_unsubscribe_attention + * + * SUMMARY: Function pointer included in core_cmds to allow other modules + * to unsubscribe themselves from the TTDL attention list + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *dev - pointer to device structure + * type - attention type enum + * *id - ID of the module calling this function + * *func - function pointer + * mode - attention mode + ******************************************************************************/ +int _pt_unsubscribe_attention(struct device *dev, + enum pt_atten_type type, char *id, int (*func)(struct device *), + int mode) +{ + struct pt_core_data *cd = dev_get_drvdata(dev); + struct atten_node *atten, *atten_n; + + spin_lock(&cd->spinlock); + list_for_each_entry_safe(atten, atten_n, &cd->atten_list[type], node) { + if (atten->id == id && atten->mode == mode) { + list_del(&atten->node); + spin_unlock(&cd->spinlock); + 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) { + strcpy(status_str, + "No FW sentinel after BL"); + 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)) { + strcpy(status_str, + "FW Stuck in Boot mode"); + goto exit; + } + } + + /* + * The comming 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) + strcpy(status_str, + "Failed - Invalid image in FLASH"); + else if (!rc) + strcpy(status_str, + "Entered APP from BL mode"); + else + strcpy(status_str, + "Failed to enter APP from BL mode"); + } + } 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) + strcpy(status_str, + "Already in APP mode"); + else + strcpy(status_str, + "Already in APP mode - FW stuck in Boot mode"); + } + } else if (rc || mode == PT_MODE_UNKNOWN) { + mutex_lock(&cd->system_lock); + cd->mode = mode; + mutex_unlock(&cd->system_lock); + if (load_status_str) + strcpy(status_str, "Failed to determine active mode"); + } + +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 succesfully\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; + + mutex_lock(&cd->system_lock); + if (cd->sleep_state == SS_SLEEP_ON) { + cd->sleep_state = SS_WAKING; + } else { + mutex_unlock(&cd->system_lock); + return 1; + } + 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); + cd->sleep_state = SS_SLEEP_OFF; + mutex_unlock(&cd->system_lock); + + pt_start_wd_timer(cd); + return rc; +} + +/******************************************************************************* + * FUNCTION: pt_core_wake_ + * + * SUMMARY: Protected call to pt_core_wake_ by exclusive access to the DUT. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data + ******************************************************************************/ +static int pt_core_wake(struct pt_core_data *cd) +{ + int rc; + + rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT); + if (rc < 0) { + pt_debug(cd->dev, DL_ERROR, + "%s: fail get exclusive ex=%p own=%p\n", + __func__, cd->exclusive_dev, cd->dev); + return rc; + } + + rc = pt_core_wake_(cd); + + if (release_exclusive(cd, cd->dev) < 0) + pt_debug(cd->dev, DL_ERROR, "%s: fail to release exclusive\n", + __func__); + else + pt_debug(cd->dev, DL_DEBUG, "%s: pass release exclusive\n", + __func__); + + return rc; +} + +/******************************************************************************* + * FUNCTION: pt_get_ic_crc_ + * + * SUMMARY: This function retrieves the config block CRC + * + * NOTE: The post condition of calling this function will be that the DUT will + * be in SCANNINING mode if no failures occur + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer the core data structure + * ebid - enumerated block ID + ******************************************************************************/ +static int pt_get_ic_crc_(struct pt_core_data *cd, u8 ebid) +{ + struct pt_sysinfo *si = &cd->sysinfo; + int rc = 0; + u8 status; + u16 calculated_crc = 0; + u16 stored_crc = 0; + + rc = pt_pip_suspend_scanning_(cd); + if (rc) + goto error; + + rc = pt_pip_verify_config_block_crc_(cd, ebid, &status, + &calculated_crc, &stored_crc); + if (rc) + goto exit; + + if (status) { + rc = -EINVAL; + goto exit; + } + + si->ttconfig.crc = stored_crc; + +exit: + pt_pip_resume_scanning_(cd); +error: + pt_debug(cd->dev, DL_INFO, + "%s: CRC: ebid:%d, calc:0x%04X, stored:0x%04X, rc=%d\n", + __func__, ebid, calculated_crc, stored_crc, rc); + return rc; +} + +/******************************************************************************* + * FUNCTION: pt_pip2_read_gpio + * + * SUMMARY: Sends a PIP2 READ_GPIO command to the DUT and stores the 32 gpio + * bits into the passed in variable + * + * NOTE: PIP2 READ_GPIO command is only supported in bootloader + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *dev - pointer to device structure + * *status - pointer to where the command response status is stored + * *gpio - pointer to device attributes structure + ******************************************************************************/ +static int pt_pip2_read_gpio(struct device *dev, u8 *status, u32 *gpio) +{ + int rc = 0; + u16 actual_read_len; + u8 read_buf[12]; + u8 tmp_status = 0; + u8 index = PIP2_RESP_STATUS_OFFSET; + + memset(read_buf, 0, ARRAY_SIZE(read_buf)); + rc = _pt_request_pip2_send_cmd(dev, + PT_CORE_CMD_UNPROTECTED, PIP2_CMD_ID_READ_GPIO, + NULL, 0, read_buf, &actual_read_len); + + if (rc) { + pt_debug(dev, DL_ERROR, + "%s: Failed to send PIP2 READ_GPIO cmd\n", __func__); + rc = -ECOMM; + } else { + tmp_status = read_buf[index]; + } + + if (status) + *status = tmp_status; + + if (!rc && gpio && (tmp_status == 0)) { + *gpio = ((read_buf[index + 4] << 24) | + (read_buf[index + 3] << 16) | + (read_buf[index + 2] << 8) | + (read_buf[index + 1])); + } + + return rc; +} + +/******************************************************************************* + * FUNCTION: _pt_pip2_get_panel_id_by_gpio + * + * SUMMARY: Wrapper function to call pt_pip2_read_gpio() to get panel ID + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer the core data structure + * *pid - pointer to store panel ID + ******************************************************************************/ +static int _pt_pip2_get_panel_id_by_gpio(struct pt_core_data *cd, u8 *pid) +{ + u32 gpio_value = 0; + u8 status = 0; + u8 panel_id = PANEL_ID_NOT_ENABLED; + int rc = 0; + + if (!pid) + return -ENOMEM; + + rc = pt_pip2_read_gpio(cd->dev, &status, &gpio_value); + if (!rc) { + if (status == 0) { + panel_id = (gpio_value & PT_PANEL_ID_BITMASK) >> + PT_PANEL_ID_SHIFT; + pt_debug(cd->dev, DL_INFO, "%s: %s=0x%02X %s=0x%08X\n", + __func__, + "BL mode PID", panel_id, "gpio", gpio_value); + *pid = panel_id; + } else { + pt_debug(cd->dev, DL_ERROR, "%s: %s=%d\n", + __func__, + "BL read gpio failed status", status); + } + } else { + pt_debug(cd->dev, DL_ERROR, "%s: %s=%d\n", + __func__, + "BL read gpio failed status", status); + } + + return rc; +} + +/******************************************************************************* + * FUNCTION: pt_enum_with_dut_ + * + * SUMMARY: This function does the full enumeration of the DUT with TTDL. + * The core data (cd) startup_status will store, as a bitmask, each + * state of the enumeration process. The startup will be attempted + * PT_CORE_STARTUP_RETRY_COUNT times before giving up. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer the core data structure + * reset - Flag to reset the DUT before attempting to enumerate + * *status - poionter to store the enum status bitmask flags + ******************************************************************************/ +static int pt_enum_with_dut_(struct pt_core_data *cd, bool reset, + u32 *enum_status) +{ + int try = 1; + int rc = 0; + int wait_time = 0; + bool detected = false; + u8 return_data[8]; + u8 mode = PT_MODE_UNKNOWN; + u8 pid = PANEL_ID_NOT_ENABLED; + u8 sys_mode = FW_SYS_MODE_UNDEFINED; + struct pt_hid_desc hid_desc; + + memset(&hid_desc, 0, sizeof(hid_desc)); +#ifdef TTHE_TUNER_SUPPORT + tthe_print(cd, NULL, 0, "enter startup"); +#endif + pt_debug(cd->dev, DL_INFO, "%s: Start enum... 0x%04X, reset=%d\n", + __func__, cd->startup_status, reset); + pt_stop_wd_timer(cd); + +reset: + if (try > 1) + pt_debug(cd->dev, DL_WARN, "%s: DUT Enum Attempt %d\n", + __func__, try); + pt_flush_bus_if_irq_asserted(cd, PT_FLUSH_BUS_BASED_ON_LEN); + + if (cd->active_dut_generation == DUT_PIP1_ONLY) { + pt_debug(cd->dev, DL_INFO, + "%s: PIP1 Enumeration start\n", __func__); + + /* Only reset the DUT after the first try */ + if (reset || (try > 1)) { + /* + * Reset hardware only for Legacy parts. Skip for TT/TC + * parts because if the FW image was loaded directly + * to SRAM issueing a reset ill wipe out what was just + * loaded. + */ + rc = pt_dut_reset_and_wait(cd); + if (rc < 0) { + pt_debug(cd->dev, DL_ERROR, + "%s: Error on h/w reset r=%d\n", + __func__, rc); + if (try++ < PT_CORE_STARTUP_RETRY_COUNT) + goto reset; + goto exit; + } + /* sleep to allow FW to be launched if available */ + msleep(120); + } + + rc = pt_get_hid_descriptor_(cd, &hid_desc); + if (rc < 0) { + pt_debug(cd->dev, DL_ERROR, + "%s: Error getting HID Descriptor r=%d\n", + __func__, rc); + if (try++ < PT_CORE_STARTUP_RETRY_COUNT) + goto reset; + rc = -EIO; + goto exit; + } + detected = true; + cd->mode = pt_get_mode(cd, &hid_desc); + + /* + * Most systems do not use an XY pin as the panel_id and so + * the BL is used to retrieve the panel_id, however for + * systems that do use an XY pin, the panel_id MUST be + * retrieved from the system information when running FW + * (done below) and so this section of code is skipped. + * Entering the BL here is only needed on XY_PIN systems. + */ + if (cd->panel_id_support & PT_PANEL_ID_BY_BL) { + if (cd->mode == PT_MODE_OPERATIONAL) { + rc = pt_pip_start_bootloader_(cd); + if (rc < 0) { + pt_debug(cd->dev, DL_ERROR, + "%s: Error on start bootloader r=%d\n", + __func__, rc); + if (try++ < PT_CORE_STARTUP_RETRY_COUNT) + goto reset; + goto exit; + } + cd->mode = PT_MODE_BOOTLOADER; + pt_debug(cd->dev, DL_INFO, "%s: Bootloader mode\n", + __func__); + } + + rc = pt_hid_output_bl_get_information_(cd, return_data); + if (!rc) { + cd->bl_info.ready = true; + cd->bl_info.chip_id = + get_unaligned_le16(&return_data[2]); + pt_debug(cd->dev, DL_INFO, "%s: chip ID %04X\n", + __func__, cd->bl_info.chip_id); + } else { + pt_debug(cd->dev, DL_ERROR, + "%s: failed to get chip ID, r=%d\n", + __func__, rc); + } + + rc = pt_hid_output_bl_get_panel_id_(cd, &pid); + mutex_lock(&cd->system_lock); + if (!rc) { + cd->pid_for_loader = pid; + pt_debug(cd->dev, DL_INFO, "%s: Panel ID: 0x%02X\n", + __func__, cd->pid_for_loader); + } else { + cd->pid_for_loader = PANEL_ID_NOT_ENABLED; + pt_debug(cd->dev, DL_WARN, + "%s: Read Failed, disable Panel ID: 0x%02X\n", + __func__, cd->pid_for_loader); + } + mutex_unlock(&cd->system_lock); + } + + + /* Exit BL due to XY_PIN case or any other cause to be in BL */ + if (cd->mode == PT_MODE_BOOTLOADER) { + rc = pt_hid_output_bl_launch_app_(cd); + if (rc < 0) { + pt_debug(cd->dev, DL_ERROR, + "%s: Error on launch app r=%d\n", + __func__, rc); + if (try++ < PT_CORE_STARTUP_RETRY_COUNT) + goto reset; + rc = -ENODEV; + goto exit; + } + + /* sleep to allow FW to be launched if available */ + msleep(120); + + /* Ensure the DUT is now in Application mode */ + rc = pt_get_hid_descriptor_(cd, &hid_desc); + if (rc < 0) { + pt_debug(cd->dev, DL_ERROR, + "%s: Error getting HID Desc r=%d\n", + __func__, rc); + if (try++ < PT_CORE_STARTUP_RETRY_COUNT) + goto reset; + rc = -EIO; + goto exit; + } + cd->mode = pt_get_mode(cd, &hid_desc); + if (cd->mode == PT_MODE_BOOTLOADER) { + pt_debug(cd->dev, DL_WARN, + "%s: Error confiming exit BL\n", + __func__); + if (try++ < PT_CORE_STARTUP_RETRY_COUNT) + goto reset; + rc = -ENODEV; + goto exit; + } + } + pt_debug(cd->dev, DL_INFO, "%s: Operational mode\n", __func__); + cd->mode = PT_MODE_OPERATIONAL; + *enum_status |= STARTUP_STATUS_GET_DESC; + *enum_status |= STARTUP_STATUS_FW_OUT_OF_BOOT; + } else { + /* Generation is PIP2 Capable */ + pt_debug(cd->dev, DL_INFO, + "%s: PIP2 Enumeration start\n", __func__); + rc = pt_pip2_get_mode_sysmode_(cd, &mode, NULL); + if (rc) { + pt_debug(cd->dev, DL_ERROR, + "%s: Get mode failed, mode unknown\n", + __func__); + mode = PT_MODE_UNKNOWN; + } else + detected = true; + + cd->mode = mode; + + switch (cd->mode) { + case PT_MODE_OPERATIONAL: + pt_debug(cd->dev, DL_INFO, + "%s: Operational mode\n", __func__); + if (cd->app_pip_ver_ready == false) { + rc = pt_pip2_get_version_(cd); + if (!rc) + cd->app_pip_ver_ready = true; + else { + if (try++ < PT_CORE_STARTUP_RETRY_COUNT) + goto reset; + goto exit; + } + } + break; + case PT_MODE_BOOTLOADER: + pt_debug(cd->dev, DL_INFO, + "%s: Bootloader mode\n", __func__); + if (cd->panel_id_support & PT_PANEL_ID_BY_BL) { + rc = _pt_pip2_get_panel_id_by_gpio(cd, &pid); + mutex_lock(&cd->system_lock); + if (!rc) { + cd->pid_for_loader = pid; + pt_debug(cd->dev, DL_INFO, + "%s: Panel ID: 0x%02X\n", + __func__, cd->pid_for_loader); + } else { + cd->pid_for_loader = + PANEL_ID_NOT_ENABLED; + pt_debug(cd->dev, DL_WARN, + "%s: Read Failed, disable Panel ID: 0x%02X\n", + __func__, cd->pid_for_loader); + } + mutex_unlock(&cd->system_lock); + } + + if (cd->bl_pip_ver_ready == false) { + rc = pt_pip2_get_version_(cd); + if (!rc) + cd->bl_pip_ver_ready = true; + else { + if (try++ < PT_CORE_STARTUP_RETRY_COUNT) + goto reset; + goto exit; + } + } + + /* + * Launch app command will fail in flashless mode. + * Skip launch app command here to save time for + * enumeration flow. + */ + if (cd->flashless_dut) + goto exit; + + /* + * pt_pip2_launch_app() is needed here instead of + * pt_pip2_exit_bl() because exit_bl will cause several + * hw_resets to occur and the auto BL on a flashless + * DUT will fail. + */ + rc = pt_pip2_launch_app(cd->dev, + PT_CORE_CMD_UNPROTECTED); + if (rc) { + pt_debug(cd->dev, DL_ERROR, + "%s: Error on launch app r=%d\n", + __func__, rc); + msleep(50); + if (try++ < PT_CORE_STARTUP_RETRY_COUNT) + goto reset; + goto exit; + } + + /* + * IRQ thread can be delayed if the serial log print is + * enabled. It causes next command to get wrong response + * Here the delay is to ensure pt_parse_input() to be + * finished. + */ + msleep(60); + /* Check and update the mode */ + rc = pt_pip2_get_mode_sysmode_(cd, &mode, NULL); + if (rc) { + pt_debug(cd->dev, DL_ERROR, + "%s: Get mode failed, mode unknown\n", + __func__); + mode = PT_MODE_UNKNOWN; + } + cd->mode = mode; + if (cd->mode == PT_MODE_OPERATIONAL) { + pt_debug(cd->dev, DL_INFO, + "%s: Launched to Operational mode\n", + __func__); + } else if (cd->mode == PT_MODE_BOOTLOADER) { + pt_debug(cd->dev, DL_ERROR, + "%s: Launch failed, Bootloader mode\n", + __func__); + goto exit; + } else if (cd->mode == PT_MODE_UNKNOWN) { + pt_debug(cd->dev, DL_ERROR, + "%s: Launch failed, Unknown mode\n", + __func__); + if (try++ < PT_CORE_STARTUP_RETRY_COUNT) + goto reset; + goto exit; + } + + if (cd->app_pip_ver_ready == false) { + rc = pt_pip2_get_version_(cd); + if (!rc) + cd->app_pip_ver_ready = true; + else { + if (try++ < PT_CORE_STARTUP_RETRY_COUNT) + goto reset; + goto exit; + } + } + break; + default: + pt_debug(cd->dev, DL_ERROR, + "%s: Unknown mode\n", __func__); + if (try++ < PT_CORE_STARTUP_RETRY_COUNT) + goto reset; + goto exit; + } + *enum_status |= STARTUP_STATUS_GET_DESC; + } + + pt_init_pip_report_fields(cd); + *enum_status |= STARTUP_STATUS_GET_RPT_DESC; + if (!cd->features.easywake) + cd->easy_wakeup_gesture = PT_CORE_EWG_NONE; + + pt_debug(cd->dev, DL_INFO, "%s: Reading sysinfo\n", __func__); + rc = pt_hid_output_get_sysinfo_(cd); + if (rc) { + pt_debug(cd->dev, DL_ERROR, + "%s: Error on getting sysinfo r=%d\n", __func__, rc); + if (try++ < PT_CORE_STARTUP_RETRY_COUNT) + goto reset; + goto exit; + } + *enum_status |= STARTUP_STATUS_GET_SYS_INFO; + + /* FW cannot handle most PIP cmds when it is still in BOOT mode */ + rc = _pt_poll_for_fw_exit_boot_mode(cd, 10000, &wait_time); + if (!rc) { + *enum_status |= STARTUP_STATUS_FW_OUT_OF_BOOT; + pt_debug(cd->dev, DL_WARN, + "%s: Exit FW BOOT Mode after %dms\n", + __func__, wait_time); + } else { + pt_debug(cd->dev, DL_WARN, + "%s: FW stuck in BOOT Mode after %dms\n", + __func__, wait_time); + goto exit; + } + + pt_debug(cd->dev, DL_INFO, "%s pt Prot Version: %d.%d\n", + __func__, + cd->sysinfo.ttdata.pip_ver_major, + cd->sysinfo.ttdata.pip_ver_minor); + + rc = pt_get_ic_crc_(cd, PT_TCH_PARM_EBID); + if (rc) { + pt_debug(cd->dev, DL_ERROR, + "%s: DUT Config block CRC failure rc=%d\n", + __func__, rc); + if (try++ < PT_CORE_STARTUP_RETRY_COUNT) + goto reset; + } else { + _pt_get_fw_sys_mode(cd, &sys_mode, NULL); + if (sys_mode != FW_SYS_MODE_SCANNING) { + pt_debug(cd->dev, DL_ERROR, + "%s: scan state: %d, retry: %d\n", + __func__, sys_mode, try); + if (try++ < PT_CORE_STARTUP_RETRY_COUNT) + goto reset; + } else + *enum_status |= STARTUP_STATUS_GET_CFG_CRC; + } + + rc = pt_restore_parameters_(cd); + if (rc) { + pt_debug(cd->dev, DL_ERROR, + "%s: Failed to restore parameters rc=%d\n", + __func__, rc); + } else + *enum_status |= STARTUP_STATUS_RESTORE_PARM; + + call_atten_cb(cd, PT_ATTEN_STARTUP, 0); + cd->watchdog_interval = PT_WATCHDOG_TIMEOUT; + cd->startup_retry_count = 0; + +exit: + /* Generate the HW Version of the connected DUT and store in cd */ + pt_generate_hw_version(cd, cd->hw_version); + pt_debug(cd->dev, DL_WARN, "%s: HW Version: %s\n", __func__, + cd->hw_version); + + pt_start_wd_timer(cd); + + if (!detected) + rc = -ENODEV; + +#ifdef TTHE_TUNER_SUPPORT + tthe_print(cd, NULL, 0, "exit startup"); +#endif + + pt_debug(cd->dev, DL_WARN, + "%s: Completed Enumeration rc=%d On Attempt %d\n", + __func__, rc, try); + return rc; +} + +/******************************************************************************* + * FUNCTION: pt_enum_with_dut + * + * SUMMARY: This is the safe function wrapper for pt_enum_with_dut_() by + * requesting exclusive access around the call. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer the core data structure + * reset - Flag to reset the DUT before attempting to enumerate + * *status - pointer to store the enum status bitmask flags + ******************************************************************************/ +static int pt_enum_with_dut(struct pt_core_data *cd, bool reset, u32 *status) +{ + int rc = 0; + + mutex_lock(&cd->system_lock); + cd->startup_state = STARTUP_RUNNING; + mutex_unlock(&cd->system_lock); + + rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT); + if (rc) { + pt_debug(cd->dev, DL_ERROR, + "%s: fail get exclusive ex=%p own=%p\n", + __func__, cd->exclusive_dev, cd->dev); + goto exit; + } + + rc = pt_enum_with_dut_(cd, reset, status); + + if (release_exclusive(cd, cd->dev) < 0) + /* Don't return fail code, mode is already changed. */ + pt_debug(cd->dev, DL_ERROR, "%s: fail to release exclusive\n", + __func__); + else + pt_debug(cd->dev, DL_DEBUG, "%s: pass release exclusive\n", + __func__); + +exit: + mutex_lock(&cd->system_lock); + /* Clear startup state for any tasks waiting for startup completion */ + cd->startup_state = STARTUP_NONE; + mutex_unlock(&cd->system_lock); + + /* Set STATUS_COMPLETE bit to indicate the status is ready to be read */ + *status |= STARTUP_STATUS_COMPLETE; + + /* Wake the waiters for end of startup */ + wake_up(&cd->wait_q); + + return rc; +} + +static int add_sysfs_interfaces(struct device *dev); +static void remove_sysfs_interfaces(struct device *dev); +static void remove_sysfs_and_modules(struct device *dev); +static void pt_release_modules(struct pt_core_data *cd); +static void pt_probe_modules(struct pt_core_data *cd); +/******************************************************************************* + * FUNCTION: _pt_ttdl_restart + * + * SUMMARY: Restarts TTDL enumeration with the DUT and re-probes all modules + * + * NOTE: The function DOSE NOT remove sysfs and modulues. 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 - Supress auto BL on next BL sentinel */ + pt_debug(dev, DL_INFO, "%s: Flashless Auto_BL - SUPPRESS\n", __func__); + cd->flashless_auto_bl = PT_SUPPRESS_AUTO_BL; + + switch (mode) { + case PT_MODE_UNKNOWN: + /* + * When the mode could not be determined the DUT could be + * in App mode running corrupted FW or FW that is not + * responding to the mode request, assume no communication + * and do a hard reset + */ + mutex_lock(&cd->system_lock); + cd->startup_status = STARTUP_STATUS_START; + pt_debug(dev, DL_DEBUG, "%s: Startup Status Reset\n", __func__); + mutex_unlock(&cd->system_lock); + + rc = pt_dut_reset(cd, PT_CORE_CMD_UNPROTECTED); + if (rc) { + tmp_result = PT_ENTER_BL_RESET_FAIL; + goto exit; + } + break; + + case PT_MODE_OPERATIONAL: + if (sys_mode == FW_SYS_MODE_SCANNING) { + pt_debug(dev, DL_INFO, "%s: Suspend Scanning\n", + __func__); + rc = pt_pip_suspend_scanning_(cd); + if (rc) { + /* + * Print to log but don't exit, the FW could be + * running but be hung or fail to respond to + * this request + */ + pt_debug(dev, DL_ERROR, + "%s Suspend Scan Failed\n", __func__); + } + /* sleep to allow the suspend scan to be processed */ + usleep_range(1000, 2000); + } + + mutex_lock(&cd->system_lock); + cd->startup_status = STARTUP_STATUS_START; + pt_debug(dev, DL_DEBUG, "%s: Startup Status Reset\n", __func__); + mutex_unlock(&cd->system_lock); + + /* Reset device to enter the BL */ + rc = pt_dut_reset(cd, PT_CORE_CMD_UNPROTECTED); + if (rc) { + tmp_result = PT_ENTER_BL_RESET_FAIL; + goto exit; + } + break; + + case PT_MODE_BOOTLOADER: + /* Do nothing as we are already in the BL */ + tmp_result = PT_ENTER_BL_PASS; + goto exit; + + default: + /* Should NEVER get here */ + tmp_result = PT_ENTER_BL_ERROR; + pt_debug(dev, DL_ERROR, "%s: Unknown mode code\n", __func__); + goto exit; + } + + if (!cd->flashless_dut && + (mode == PT_MODE_UNKNOWN || mode == PT_MODE_OPERATIONAL)) { + /* + * Sending the special "Host Mode" command will instruct the + * BL to not execute the FW it has loaded into RAM. + * The command must be sent within a 40ms window from releasing + * the XRES pin. If the messages is sent too early it will NAK, + * so keep sending it every 2ms until it is accepted by the BL. + * A no-flash DUT does not require this command as there is no + * FW for the BL to load and execute. + */ + usleep_range(4000, 6000); + pt_debug(cd->dev, DL_INFO, + ">>> %s: Write Buffer Size[%d] Stay in BL\n", + __func__, (int)sizeof(host_mode_cmd)); + pt_pr_buf(cd->dev, DL_DEBUG, host_mode_cmd, + (int)sizeof(host_mode_cmd), ">>> User CMD"); + rc = 1; + while (rc && time < 34) { + rc = pt_adap_write_read_specific(cd, 4, + host_mode_cmd, NULL); + usleep_range(1800, 2000); + time += 2; + } + + /* Sleep to allow the BL to come up */ + usleep_range(1000, 2000); + } + + /* + * To avoid the case that next PIP command can be confused by BL/FW + * sentinel's "wakeup" event, chekcing hid_reset_cmd_state which is + * followed by "wakeup event" function can lower the failure rate. + */ + t = wait_event_timeout(cd->wait_q, + ((cd->startup_status != STARTUP_STATUS_START) + && (cd->hid_reset_cmd_state == 0)), + msecs_to_jiffies(300)); + if (IS_TMO(t)) { + pt_debug(cd->dev, DL_ERROR, + "%s: TMO waiting for BL sentinel\n", __func__); + } + + /* Check if device is now in BL mode */ + rc = pt_pip2_get_mode_sysmode_(cd, &mode, NULL); + pt_debug(dev, DL_INFO, "%s: Mode=%d, Status=0x%04X\n", __func__, mode, + cd->startup_status); + if (!rc && mode == PT_MODE_BOOTLOADER) { + pt_debug(dev, DL_INFO, "%s In bootloader mode now\n", __func__); + mutex_lock(&cd->system_lock); + cd->pip2_prot_active = true; + cd->mode = PT_MODE_BOOTLOADER; + mutex_unlock(&cd->system_lock); + tmp_result = PT_ENTER_BL_PASS; + } else { + /* + * If the device doesn't enter BL mode as expected and rc is + * tested pass by above function pt_pip2_get_mode_sysmode_(), + * the function should return an error code to indicate this + * failure PT_ENTER_BL_CONFIRM_FAIL. + */ + if (!rc) + rc = -EINVAL; + tmp_result = PT_ENTER_BL_CONFIRM_FAIL; + mutex_lock(&cd->system_lock); + cd->mode = mode; + mutex_unlock(&cd->system_lock); + pt_debug(dev, DL_ERROR, + "%s ERROR: Not in BL as expected", __func__); + } + +exit: + if (!cd->flashless_dut && (tmp_result == PT_ENTER_BL_PASS)) { + /* Check to get (buffered) flash information */ + rc = _pt_request_pip2_send_cmd(dev, PT_CORE_CMD_UNPROTECTED, + PIP2_CMD_ID_FLASH_INFO, NULL, 0, + read_buf, &actual_read_len); + if (!rc) { + if (read_buf[PIP2_RESP_BODY_OFFSET] == 0) { + pt_debug( + dev, DL_WARN, + "%s Unavailable Manufacturer ID: 0x%02x\n", + __func__, + read_buf[PIP2_RESP_BODY_OFFSET]); + /* + * If the BL was unable to cache the correct + * values when entering the first time due to + * the Flash part not having been powered up + * long enough, re-enter the BL to trigger the + * BL to re-attempt to cache the values. + */ + if (flash_info_retry-- > 0) { + mode = PT_MODE_UNKNOWN; + pt_debug(dev, DL_WARN, + "%s Assume mode to UNKNOWN to enter BL again, retry=%d\n", + __func__, flash_info_retry); + goto _retry; + } else { + pt_debug(dev, DL_WARN, + "%s Manufacturer ID Unknown\n", + __func__); + tmp_result = PT_ENTER_BL_PASS; + } + } + } else { + tmp_result = PT_ENTER_BL_GET_FLASH_INFO_FAIL; + pt_debug( + dev, DL_ERROR, + "%s: Failed to send PIP2 READ_FLASH_INFO cmd\n", + __func__); + } + } + + pt_debug(dev, DL_INFO, "%s Flashless Auto_BL - %s\n", __func__, + saved_flashless_auto_bl_mode == PT_ALLOW_AUTO_BL ? "ALLOW" : + "SUPPRESS"); + cd->flashless_auto_bl = saved_flashless_auto_bl_mode; + if (result) + *result = tmp_result; + return rc; +} + +/******************************************************************************* + * FUNCTION: _pt_pip2_file_open + * + * SUMMARY: Using the BL PIP2 commands open a file and return the file handle + * + * NOTE: The DUT must be in BL mode for this command to work + * + * RETURNS: + * <0 = Error + * >0 = file handle opened + * + * PARAMETERS: + * *dev - pointer to device structure + * file_no - PIP2 file number to open + ******************************************************************************/ +int _pt_pip2_file_open(struct device *dev, u8 file_no) +{ + int ret = 0; + u16 status; + u16 actual_read_len; + u8 file_handle; + u8 data[2]; + u8 read_buf[10]; + + pt_debug(dev, DL_DEBUG, "%s: OPEN file %d\n", __func__, file_no); + data[0] = file_no; + ret = _pt_request_pip2_send_cmd(dev, + PT_CORE_CMD_UNPROTECTED, PIP2_CMD_ID_FILE_OPEN, + data, 1, read_buf, &actual_read_len); + if (ret) { + pt_debug(dev, DL_ERROR, + "%s ROM BL FILE_OPEN timeout for file = %d\n", + __func__, file_no); + return -PIP2_RSP_ERR_NOT_OPEN; + } + + status = read_buf[PIP2_RESP_STATUS_OFFSET]; + file_handle = read_buf[PIP2_RESP_BODY_OFFSET]; + if (ret || ((status != 0x00) && (status != 0x03)) || + (file_handle != file_no)) { + pt_debug(dev, DL_ERROR, + "%s ROM BL FILE_OPEN failure: 0x%02X for file = %d\n", + __func__, status, file_handle); + return -status; + } + return file_handle; +} + +/******************************************************************************* + * FUNCTION: _pt_pip2_file_close + * + * SUMMARY: Using the BL PIP2 commands close a file + * + * NOTE: The DUT must be in BL mode for this command to work + * + * RETURNS: + * <0 = Error + * >0 = file handle closed + * + * PARAMETERS: + * *dev - pointer to device structure + * file_handle - handle to the file to be closed + ******************************************************************************/ +int _pt_pip2_file_close(struct device *dev, u8 file_handle) +{ + int ret = 0; + u16 status; + u16 actual_read_len; + u8 data[2]; + u8 read_buf[32]; + + pt_debug(dev, DL_DEBUG, "%s: CLOSE file %d\n", __func__, file_handle); + data[0] = file_handle; + ret = _pt_request_pip2_send_cmd(dev, + PT_CORE_CMD_UNPROTECTED, PIP2_CMD_ID_FILE_CLOSE, + data, 1, read_buf, &actual_read_len); + if (ret) { + pt_debug(dev, DL_ERROR, + "%s ROM BL FILE_CLOSE timeout for file = %d\n", + __func__, file_handle); + return -ETIME; + } + + status = read_buf[PIP2_RESP_STATUS_OFFSET]; + if (status != 0x00) { + pt_debug(dev, DL_ERROR, + "%s ROM BL FILE_CLOSE failure: 0x%02X for file = %d\n", + __func__, status, file_handle); + return -status; + } + return file_handle; +} + +/******************************************************************************* + * FUNCTION: _pt_pip2_file_erase + * + * SUMMARY: Using the BL PIP2 commands erase a file + * + * NOTE: The DUT must be in BL mode for this command to work + * NOTE: Some FLASH parts can time out while erasing one or more sectors, + * one retry is attempted for each sector in a file. + * + * RETURNS: + * <0 = Error + * >0 = file handle closed + * + * PARAMETERS: + * *dev - pointer to device structure + * file_handle - handle to the file to be erased + * *status - PIP2 erase status code + ******************************************************************************/ +int _pt_pip2_file_erase(struct device *dev, u8 file_handle, int *status) +{ + int ret = 0; + int max_retry = PT_PIP2_MAX_FILE_SIZE/PT_PIP2_FILE_SECTOR_SIZE; + int retry = 1; + u16 actual_read_len; + u8 data[2]; + u8 read_buf[10]; + struct pt_core_data *cd = dev_get_drvdata(dev); + + pt_debug(dev, DL_DEBUG, "%s: ERASE file %d\n", __func__, file_handle); + data[0] = file_handle; + data[1] = PIP2_FILE_IOCTL_CODE_ERASE_FILE; + *status = PIP2_RSP_ERR_TIMEOUT; + + /* Increase waiting time for large file erase */ + mutex_lock(&cd->system_lock); + cd->pip_cmd_timeout = PT_PIP2_CMD_FILE_ERASE_TIMEOUT; + mutex_unlock(&cd->system_lock); + while (*status == PIP2_RSP_ERR_TIMEOUT && retry < max_retry) { + ret = _pt_request_pip2_send_cmd(dev, + PT_CORE_CMD_UNPROTECTED, PIP2_CMD_ID_FILE_IOCTL, + data, 2, read_buf, &actual_read_len); + if (ret) + break; + *status = read_buf[PIP2_RESP_STATUS_OFFSET]; + if (*status == PIP2_RSP_ERR_TIMEOUT) { +#ifdef TTDL_DIAGNOSTICS + cd->file_erase_timeout_count++; +#endif + pt_debug(dev, DL_WARN, + "%s: ERASE timeout %d for file = %d\n", + __func__, retry, file_handle); + } + retry++; + } + mutex_lock(&cd->system_lock); + cd->pip_cmd_timeout = cd->pip_cmd_timeout_default; + mutex_unlock(&cd->system_lock); + if (ret) { + pt_debug(dev, DL_ERROR, + "%s ROM FILE_ERASE cmd failure: %d for file = %d\n", + __func__, ret, file_handle); + return -EIO; + } + + if (*status != 0x00) { + pt_debug(dev, DL_ERROR, + "%s ROM FILE_ERASE failure: 0x%02X for file = %d\n", + __func__, *status, file_handle); + return -EIO; + } + return file_handle; +} + +/******************************************************************************* + * FUNCTION: _pt_pip2_file_read + * + * SUMMARY: Using the BL PIP2 commands read n bytes from a already opened file + * + * NOTE: The DUT must be in BL mode for this command to work + * + * RETURNS: + * <0 = Error + * >0 = number of bytes read + * + * PARAMETERS: + * *dev - pointer to device structure + * file_handle - File handle to read from + * num_bytes - number of bytes to read + ******************************************************************************/ +int _pt_pip2_file_read(struct device *dev, u8 file_handle, u16 num_bytes, + u8 *read_buf) +{ + int ret = 0; + u16 status; + u16 actual_read_len; + u8 data[3]; + + data[0] = file_handle; + data[1] = (num_bytes & 0x00FF); + data[2] = (num_bytes & 0xFF00) >> 8; + ret = _pt_request_pip2_send_cmd(dev, + PT_CORE_CMD_UNPROTECTED, PIP2_CMD_ID_FILE_READ, + data, 3, read_buf, &actual_read_len); + status = read_buf[PIP2_RESP_STATUS_OFFSET]; + if (ret || ((status != 0x00) && (status != 0x03))) { + pt_debug(dev, DL_ERROR, + "%s File open failure with error code = %d\n", + __func__, status); + return -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 (vfs_read(filp, buf, read_len, &(filp->f_pos)) != read_len) { + 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; +} + +/******************************************************************************* + * 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 simliar work while the type of + * buffer to store data is "u32". This function is still needed by the + * "command" sysfs node because the buffer type to store data is "u8". + * + * RETURN: Length of parsed data + * + * PARAMETERS: + * *dev - pointer to device structure + * *buf - pointer to buffer that holds the input array to parse + * buf_size - size of buf + * *ic_buf - pointer to array to store parsed data + * ic_buf_size - max size of ic_buf + ******************************************************************************/ +static int _pt_ic_parse_input_hex(struct device *dev, const char *buf, + size_t buf_size, u8 *ic_buf, size_t ic_buf_size) +{ + const char *pbuf = buf; + unsigned long value; + char scan_buf[PT_MAX_PIP2_MSG_SIZE + 1]; + u32 i = 0; + u32 j; + int last = 0; + int ret; + + pt_debug(dev, DL_DEBUG, + "%s: pbuf=%p buf=%p size=%zu %s=%d buf=%s\n", + __func__, pbuf, buf, buf_size, "scan buf size", + PT_MAX_PIP2_MSG_SIZE, buf); + + while (pbuf <= (buf + buf_size)) { + if (i >= PT_MAX_PIP2_MSG_SIZE) { + pt_debug(dev, DL_ERROR, "%s: %s size=%d max=%d\n", + __func__, "Max cmd size exceeded", i, + PT_MAX_PIP2_MSG_SIZE); + return -EINVAL; + } + if (i >= ic_buf_size) { + pt_debug(dev, DL_ERROR, "%s: %s size=%d buf_size=%zu\n", + __func__, "Buffer size exceeded", i, + ic_buf_size); + return -EINVAL; + } + while (((*pbuf == ' ') || (*pbuf == ',')) + && (pbuf < (buf + buf_size))) { + last = *pbuf; + pbuf++; + } + + if (pbuf >= (buf + buf_size)) + break; + + memset(scan_buf, 0, PT_MAX_PIP2_MSG_SIZE); + if ((last == ',') && (*pbuf == ',')) { + pt_debug(dev, DL_ERROR, "%s: %s \",,\" not allowed.\n", + __func__, "Invalid data format."); + return -EINVAL; + } + for (j = 0; j < (PT_MAX_PIP2_MSG_SIZE - 1) + && (pbuf < (buf + buf_size)) + && (*pbuf != ' ') + && (*pbuf != ','); j++) { + last = *pbuf; + scan_buf[j] = *pbuf++; + } + ret = kstrtoul(scan_buf, 16, &value); + if (ret < 0) { + pt_debug(dev, DL_ERROR, + "%s: %s '%s' %s%s i=%d r=%d\n", __func__, + "Invalid data format. ", scan_buf, + "Use \"0xHH,...,0xHH\"", " instead.", + i, ret); + return ret; + } + + ic_buf[i] = value; + pt_debug(dev, DL_DEBUG, "%s: item = %d, value = 0x%02lx", + __func__, i, value); + i++; + } + + return i; +} + +/******************************************************************************* + * FUNCTION: _pt_ic_parse_input + * + * SUMMARY: Parse user sysfs input data as a space or comma delimited list of + * hex values or dec values into an int array with the following rules: + * 1) Hex values must have a "0x" prefix for each element or the first element + * only + * 2) Dec values do not have any prefix + * 3) It is not allowed to have a mix of dec and hex values in the user input + * string + * + * RETURN: Number of values parsed + * + * PARAMETERS: + * *dev - pointer to device structure + * *buf - pointer to buffer that holds the input array to parse + * buf_size - size of buf + * *out_buf - pointer to array to store parsed data + * out_buf_size - max size of buffer to be stored + ******************************************************************************/ +static int _pt_ic_parse_input(struct device *dev, + const char *buf, size_t buf_size, + u32 *out_buf, size_t out_buf_size) +{ + const char *pbuf = buf; + unsigned long value; + char scan_buf[PT_MAX_PIP2_MSG_SIZE + 1]; + u32 i = 0; + u32 j; + int last = 0; + int ret = 0; + u8 str_base = 0; + + pt_debug(dev, DL_DEBUG, + "%s: in_buf_size=%zu out_buf_size=%zu %s=%d buf=%s\n", + __func__, buf_size, out_buf_size, "scan buf size", + PT_MAX_PIP2_MSG_SIZE, buf); + + while (pbuf <= (buf + buf_size)) { + if (i >= PT_MAX_PIP2_MSG_SIZE) { + pt_debug(dev, DL_ERROR, "%s: %s size=%d max=%d\n", + __func__, "Max cmd size exceeded", i, + PT_MAX_PIP2_MSG_SIZE); + ret = -EINVAL; + goto error; + } + if (i >= out_buf_size) { + pt_debug(dev, DL_ERROR, "%s: %s size=%d buf_size=%zu\n", + __func__, "Buffer size exceeded", i, + out_buf_size); + ret = -EINVAL; + goto error; + } + while (((*pbuf == ' ') || (*pbuf == ',')) + && (pbuf < (buf + buf_size))) { + last = *pbuf; + pbuf++; + } + + if (pbuf >= (buf + buf_size)) + break; + + memset(scan_buf, 0, PT_MAX_PIP2_MSG_SIZE); + if ((last == ',') && (*pbuf == ',')) { + pt_debug(dev, DL_ERROR, "%s: %s \",,\" not allowed.\n", + __func__, "Invalid data format."); + ret = -EINVAL; + goto error; + } + for (j = 0; j < (PT_MAX_PIP2_MSG_SIZE - 1) + && (pbuf < (buf + buf_size)) + && (*pbuf != ' ') + && (*pbuf != ','); j++) { + last = *pbuf; + scan_buf[j] = *pbuf++; + } + + if (i == 0) { + if ((strncmp(scan_buf, "0x", 2) == 0) || + (strncmp(scan_buf, "0X", 2) == 0)) + str_base = 16; + else + str_base = 10; + } else { + if (((strncmp(scan_buf, "0x", 2) == 0) || + (strncmp(scan_buf, "0X", 2) == 0)) && + (str_base == 10)) { + pt_debug(dev, DL_ERROR, + "%s: Decimal and Heximal data mixed\n", + __func__); + ret = -EINVAL; + goto error; + } + } + ret = kstrtoul(scan_buf, str_base, &value); + if (ret < 0) { + pt_debug(dev, DL_ERROR, + "%s: %s '%s' %s%s i=%d r=%d\n", __func__, + "Invalid data format. ", scan_buf, + "Use \"0xHH,...,0xHH\" or \"DD DD DD ... DD\"", + " instead.", i, ret); + goto error; + } + + out_buf[i] = value; + pt_debug(dev, DL_DEBUG, "%s: item = %d, value = 0x%02lx(%lu)", + __func__, i, value, value); + i++; + } + ret = i; +error: + return ret; +} + +#ifdef TTHE_TUNER_SUPPORT +/******************************************************************************* + * FUNCTION: tthe_debugfs_open + * + * SUMMARY: Open method for tthe_tuner debugfs node. On some hosts the size of + * PT_MAX_PRBUF_SIZE (equal to PAGE_SIZE) is not large enough to handle + * printing a large number of fingers and sensor data without overflowing + * the buffer. tthe_tuner needs ~4K and so the buffer is sized to some + * even multiple of PAGE_SIZE + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *inode - file inode number + * *filp - file pointer to debugfs file + ******************************************************************************/ +static int tthe_debugfs_open(struct inode *inode, struct file *filp) +{ + struct pt_core_data *cd = inode->i_private; + u32 buf_size = PT_MAX_PRBUF_SIZE; + + filp->private_data = inode->i_private; + + if (cd->tthe_buf) + return -EBUSY; + + while (buf_size < 4096) + buf_size = buf_size << 1; + + pt_debug(cd->dev, DL_INFO, "%s:PT_MAX_BRBUF_SIZE=%d buf_size=%d\n", + __func__, (int)PT_MAX_PRBUF_SIZE, (int)buf_size); + + cd->tthe_buf_size = buf_size; + cd->tthe_buf = kzalloc(cd->tthe_buf_size, GFP_KERNEL); + if (!cd->tthe_buf) + return -ENOMEM; + + return 0; +} + +/******************************************************************************* + * FUNCTION: tthe_debugfs_close + * + * SUMMARY: Close method for tthe_tuner debugfs node. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *inode - file inode number + * *filp - file pointer to debugfs file + ******************************************************************************/ +static int tthe_debugfs_close(struct inode *inode, struct file *filp) +{ + struct pt_core_data *cd = filp->private_data; + + filp->private_data = NULL; + + kfree(cd->tthe_buf); + cd->tthe_buf = NULL; + + return 0; +} + +/******************************************************************************* + * FUNCTION: tthe_debugfs_read + * + * SUMMARY: Read method for tthe_tuner debugfs node. This function prints + * tthe_buf to user buffer. + * + * RETURN: Size of debugfs data print + * + * PARAMETERS: + * *filp - file pointer to debugfs file + * *buf - the user space buffer to read to + * count - the maximum number of bytes to read + * *ppos - the current position in the buffer + ******************************************************************************/ +static ssize_t tthe_debugfs_read(struct file *filp, char __user *buf, + size_t count, loff_t *ppos) +{ + struct pt_core_data *cd = filp->private_data; + int size; + int ret; + static int partial_read; + + wait_event_interruptible(cd->wait_q, + cd->tthe_buf_len != 0 || cd->tthe_exit); + mutex_lock(&cd->tthe_lock); + if (cd->tthe_exit) { + mutex_unlock(&cd->tthe_lock); + return 0; + } + if (count > cd->tthe_buf_len) + size = cd->tthe_buf_len; + else + size = count; + if (!size) { + mutex_unlock(&cd->tthe_lock); + return 0; + } + + if (partial_read) { + ret = copy_to_user(buf, cd->tthe_buf + partial_read, size); + partial_read = 0; + } else { + ret = copy_to_user(buf, cd->tthe_buf, size); + } + if (size == count) + partial_read = count; + + if (ret == size) + return -EFAULT; + size -= ret; + cd->tthe_buf_len -= size; + mutex_unlock(&cd->tthe_lock); + *ppos += size; + return size; +} + +static const struct file_operations tthe_debugfs_fops = { + .open = tthe_debugfs_open, + .release = tthe_debugfs_close, + .read = tthe_debugfs_read, +}; +#endif + +static struct pt_core_nonhid_cmd _pt_core_nonhid_cmd = { + .start_bl = _pt_request_pip_start_bl, + .suspend_scanning = _pt_request_pip_suspend_scanning, + .resume_scanning = _pt_request_pip_resume_scanning, + .get_param = _pt_request_pip_get_param, + .set_param = _pt_request_pip_set_param, + .verify_cfg_block_crc = _pt_request_pip_verify_config_block_crc, + .get_config_row_size = _pt_request_pip_get_config_row_size, + .get_data_structure = _pt_request_pip_get_data_structure, + .run_selftest = _pt_request_pip_run_selftest, + .get_selftest_result = _pt_request_pip_get_selftest_result, + .load_self_test_param = _pt_request_pip_load_self_test_param, + .calibrate_idacs = _pt_request_pip_calibrate_idacs, + .calibrate_ext = _pt_request_pip_calibrate_ext, + .initialize_baselines = _pt_request_pip_initialize_baselines, + .exec_panel_scan = _pt_request_pip_exec_panel_scan, + .retrieve_panel_scan = _pt_request_pip_retrieve_panel_scan, + .read_data_block = _pt_request_pip_read_data_block, + .write_data_block = _pt_request_pip_write_data_block, + .user_cmd = _pt_request_pip_user_cmd, + .get_bl_info = _pt_request_pip_bl_get_information, + .initiate_bl = _pt_request_pip_bl_initiate_bl, + .launch_app = _pt_request_pip_launch_app, + .prog_and_verify = _pt_request_pip_bl_program_and_verify, + .verify_app_integrity = _pt_request_pip_bl_verify_app_integrity, + .get_panel_id = _pt_request_pip_bl_get_panel_id, + .pip2_send_cmd = _pt_request_pip2_send_cmd, + .pip2_send_cmd_no_int = _pt_pip2_send_cmd_no_int, + .pip2_file_open = _pt_pip2_file_open, + .pip2_file_close = _pt_pip2_file_close, + .pip2_file_erase = _pt_pip2_file_erase, + .read_us_file = _pt_read_us_file, + .manage_cal_data = _pt_manage_local_cal_data, + .calc_crc = crc_ccitt_calculate, +#ifdef TTDL_DIAGNOSTICS + .pip2_file_read = _pt_pip2_file_read, + .pip2_file_seek_offset = _pt_pip2_file_seek_offset, + .pip2_file_get_stats = _pt_pip2_file_get_stats, + .pip2_file_crc = _pt_pip2_file_crc, +#endif +}; + +static struct pt_core_commands _pt_core_commands = { + .subscribe_attention = _pt_subscribe_attention, + .unsubscribe_attention = _pt_unsubscribe_attention, + .request_exclusive = _pt_request_exclusive, + .release_exclusive = _pt_release_exclusive, + .request_reset = _pt_request_reset, + .request_pip2_launch_app = _pt_request_pip2_launch_app, + .request_enum = _pt_request_enum, + .request_sysinfo = _pt_request_sysinfo, + .request_loader_pdata = _pt_request_loader_pdata, + .request_stop_wd = _pt_request_stop_wd, + .request_start_wd = _pt_request_start_wd, + .request_get_mode = _pt_request_get_mode, + .request_active_pip_prot = _pt_request_active_pip_protocol, + .request_pip2_get_mode_sysmode = _pt_request_pip2_get_mode_sysmode, + .request_pip2_enter_bl = _pt_request_pip2_enter_bl, + .request_pip2_bin_hdr = _pt_request_pip2_bin_hdr, + .request_dut_generation = _pt_request_dut_generation, + .request_hw_version = _pt_request_hw_version, + .parse_sysfs_input = _pt_ic_parse_input, +#ifdef TTHE_TUNER_SUPPORT + .request_tthe_print = _pt_request_tthe_print, +#endif +#ifdef TTDL_DIAGNOSTICS + .request_toggle_err_gpio = _pt_request_toggle_err_gpio, +#endif + .nonhid_cmd = &_pt_core_nonhid_cmd, + .request_get_fw_mode = _pt_request_get_fw_sys_mode, +}; + +struct pt_core_commands *pt_get_commands(void) +{ + return &_pt_core_commands; +} +EXPORT_SYMBOL_GPL(pt_get_commands); + +static DEFINE_MUTEX(core_list_lock); +static LIST_HEAD(core_list); +static DEFINE_MUTEX(module_list_lock); +static LIST_HEAD(module_list); +static int core_number; + +/******************************************************************************* + * FUNCTION: pt_probe_module + * + * SUMMARY: Add the module pointer to module_node and call the probe pointer. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data + * *module - pointer to module structure + ******************************************************************************/ +static int pt_probe_module(struct pt_core_data *cd, + struct pt_module *module) +{ + struct module_node *module_node; + int rc = 0; + + module_node = kzalloc(sizeof(*module_node), GFP_KERNEL); + if (!module_node) + return -ENOMEM; + + module_node->module = module; + + mutex_lock(&cd->module_list_lock); + list_add(&module_node->node, &cd->module_list); + mutex_unlock(&cd->module_list_lock); + + rc = module->probe(cd->dev, &module_node->data); + if (rc) { + /* + * Remove from the list when probe fails + * in order not to call release + */ + mutex_lock(&cd->module_list_lock); + list_del(&module_node->node); + mutex_unlock(&cd->module_list_lock); + kfree(module_node); + goto exit; + } + +exit: + return rc; +} + +/******************************************************************************* + * FUNCTION: pt_release_module + * + * SUMMARY: Call the release pointer and remove the module pointer from + * module_list. + * + * PARAMETERS: + * *cd - pointer to core data + * *module - pointer to module structure + ******************************************************************************/ +static void pt_release_module(struct pt_core_data *cd, + struct pt_module *module) +{ + struct module_node *m, *m_n; + + mutex_lock(&cd->module_list_lock); + list_for_each_entry_safe(m, m_n, &cd->module_list, node) + if (m->module == module) { + module->release(cd->dev, m->data); + list_del(&m->node); + kfree(m); + break; + } + mutex_unlock(&cd->module_list_lock); +} + +/******************************************************************************* + * FUNCTION: pt_probe_modules + * + * SUMMARY: Iterate module_list and probe each module. + * + * PARAMETERS: + * *cd - pointer to core data + ******************************************************************************/ +static void pt_probe_modules(struct pt_core_data *cd) +{ + struct pt_module *m; + int rc = 0; + + mutex_lock(&module_list_lock); + list_for_each_entry(m, &module_list, node) { + pt_debug(cd->dev, DL_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 funtion 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 + + + + +/******************************************************************************* + * 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 sprintf(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 sprintf(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 infromation 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 sprintf(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 sprintf(buf, + "Status: %d\n", + rc); + } +} +static DEVICE_ATTR(sysinfo, 0444, pt_sysinfo_show, NULL); + +/******************************************************************************* + * FUNCTION: pt_hw_reset_show + * + * SUMMARY: The show method for the hw_reset sysfs node that does a hw reset + * by toggling the XRES line and then calls the startup function to + * allow TTDL to re-enumerate the DUT. + * The printed value reflects the status of the full reset/enum. + * + * PARAMETERS: + * *dev - pointer to Device structure + * *attr - pointer to the device attribute structure + * *buf - pointer to buffer to print + ******************************************************************************/ +static ssize_t pt_hw_reset_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct pt_core_data *cd = dev_get_drvdata(dev); + int rc = 0; + int time = 0; + u8 reset_status = 0; + int t; + struct pt_hid_desc hid_desc; + + memset(&hid_desc, 0, sizeof(hid_desc)); + + /* Only allow DUT reset if no active BL in progress */ + mutex_lock(&cd->firmware_class_lock); + + mutex_lock(&cd->system_lock); + cd->startup_state = STARTUP_NONE; + mutex_unlock(&(cd->system_lock)); + + pt_stop_wd_timer(cd); + + /* ensure no left over exclusive access is still locked */ + release_exclusive(cd, cd->dev); + + rc = pt_dut_reset(cd, PT_CORE_CMD_PROTECTED); + if (rc) { + mutex_unlock(&cd->firmware_class_lock); + pt_debug(cd->dev, DL_ERROR, + "%s: HW reset failed rc = %d\n", __func__, rc); + goto exit_hw_reset; + } + reset_status |= 0x01 << 0; + + if (cd->flashless_dut) { + mutex_unlock(&cd->firmware_class_lock); + + t = wait_event_timeout(cd->wait_q, (cd->fw_updating == true), + msecs_to_jiffies(200)); + if (IS_TMO(t)) { + pt_debug(dev, DL_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, 150, + STARTUP_STATUS_BL_RESET_SENTINEL | + STARTUP_STATUS_FW_RESET_SENTINEL); + if (rc) { + mutex_unlock(&cd->firmware_class_lock); + pt_debug(cd->dev, DL_ERROR, + "%s: No Sentinel detected rc = %d\n", + __func__, rc); + goto exit_hw_reset; + } + + /* sleep needed to ensure no cmd is sent while DUT will NAK */ + msleep(30); + + if (cd->active_dut_generation == DUT_PIP1_ONLY) { + rc = pt_get_hid_descriptor_(cd, &hid_desc); + if (rc < 0) { + pt_debug(cd->dev, DL_ERROR, + "%s: Error on getting HID descriptor r=%d\n", + __func__, rc); + goto exit_hw_reset; + } + + cd->mode = pt_get_mode(cd, &hid_desc); + if (cd->mode == PT_MODE_BOOTLOADER) + rc = pt_hid_output_bl_launch_app_(cd); + } else { + if (cd->mode == PT_MODE_BOOTLOADER) + rc = pt_pip2_launch_app(dev, + PT_CORE_CMD_UNPROTECTED); + } + + if (rc) { + mutex_unlock(&cd->firmware_class_lock); + pt_debug(cd->dev, DL_ERROR, + "%s: PIP launch app failed rc = %d\n", + __func__, rc); + goto exit_hw_reset; + } + mutex_unlock(&cd->firmware_class_lock); + + reset_status |= 0x01 << 1; + msleep(20); + if ((cd->active_dut_generation == DUT_UNKNOWN) || + (cd->mode != PT_MODE_OPERATIONAL)) + pt_queue_restart(cd); + else + pt_queue_enum(cd); + } + + while (!(cd->startup_status & STARTUP_STATUS_COMPLETE) && time < 2000) { + msleep(50); + pt_debug(cd->dev, DL_INFO, + "%s: wait %dms for 0x%04X, current enum=0x%04X\n", + __func__, time, STARTUP_STATUS_COMPLETE, + cd->startup_status); + time += 50; + } + if (!(cd->startup_status & STARTUP_STATUS_COMPLETE)) { + rc = -ETIME; + goto exit_hw_reset; + } + + pt_debug(cd->dev, DL_INFO, "%s: HW Reset complete. enum=0x%04X\n", + __func__, cd->startup_status); + reset_status |= 0x01 << 2; + pt_start_wd_timer(cd); + +exit_hw_reset: + return sprintf(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: Insufficent data provided for PIP2 cmd\n", + __func__); + } + +exit: + if (rc) + return rc; + return size; +} +/******************************************************************************* + * FUNCTION: pt_pip2_cmd_rsp_show + * + * SUMMARY: The show method for the raw pip2_cmd_rsp sysfs node. Any PIP2 + * response generated after using the store method of the pip2_cmd_rsp + * sysfs node, are available to be read here. + * + * PARAMETERS: + * *dev - pointer to Device structure + * *attr - pointer to the device attribute structure + * *buf - pointer to buffer to print + ******************************************************************************/ +static ssize_t pt_pip2_cmd_rsp_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct pt_core_data *cd = dev_get_drvdata(dev); + int i; + ssize_t data_len; + int index; + + mutex_lock(&cd->sysfs_lock); + index = scnprintf(buf, PT_MAX_PRBUF_SIZE, + "Status: %d\n", cd->raw_cmd_status); + if (cd->raw_cmd_status) + goto error; + + /* Remove the CRC from the length of the response */ + data_len = cd->cmd_rsp_buf_len - 2; + + /* Start printing from the data payload */ + for (i = PIP1_RESP_COMMAND_ID_OFFSET; i < data_len; i++) + index += scnprintf(buf + index, PT_MAX_PRBUF_SIZE - index, + "%02X ", cd->cmd_rsp_buf[i]); + + if (data_len >= PIP1_RESP_COMMAND_ID_OFFSET) { + index += scnprintf(buf + index, PT_MAX_PRBUF_SIZE - index, + "\n(%zd bytes)\n", + data_len - PIP1_RESP_COMMAND_ID_OFFSET); + } else { + index += scnprintf(buf + index, PT_MAX_PRBUF_SIZE - index, + "\n(%zd bytes)\n", 0); + } + +error: + mutex_unlock(&cd->sysfs_lock); + return index; +} +static DEVICE_ATTR(pip2_cmd_rsp, 0644, pt_pip2_cmd_rsp_show, + pt_pip2_cmd_rsp_store); + +/******************************************************************************* + * FUNCTION: pt_command_store + * + * SUMMARY: This is the store method for the raw PIP command sysfs node. Any + * raw PIP command echo'd to this node will be sent directly to the DUT. + * TTDL will not parse the command. + * + * RETURN: Size of passed in buffer + * + * PARAMETERS: + * *dev - pointer to device structure + * *attr - pointer to device attributes + * *buf - pointer to buffer that hold the command parameters + * size - size of buf + ******************************************************************************/ +static ssize_t pt_command_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct pt_core_data *cd = dev_get_drvdata(dev); + unsigned short crc; + u16 actual_read_len; + u8 input_data[PT_MAX_PIP2_MSG_SIZE + 1]; + int length; + int len_field; + int rc = 0; + + mutex_lock(&cd->sysfs_lock); + cd->cmd_rsp_buf_len = 0; + memset(cd->cmd_rsp_buf, 0, sizeof(cd->cmd_rsp_buf)); + mutex_unlock(&cd->sysfs_lock); + + length = _pt_ic_parse_input_hex(dev, buf, size, + input_data, PT_MAX_PIP2_MSG_SIZE); + if (length <= 0 || length > PT_MAX_PIP2_MSG_SIZE) { + pt_debug(dev, DL_WARN, "%s: Invalid number of arguments\n", + __func__); + rc = -EINVAL; + goto pt_command_store_exit; + } + + /* PIP2 messages begin with 01 01 */ + if (length >= 2 && input_data[0] == 0x01 && input_data[1] == 0x01) { + cd->pip2_prot_active = 1; + /* Override next seq tag with what was sent */ + cd->pip2_cmd_tag_seq = input_data[4] & 0x0F; + /* For PIP2 cmd if length does not include crc, add it */ + len_field = (input_data[3] << 8) | input_data[2]; + if (len_field == length && length <= 254) { + crc = crc_ccitt_calculate(&input_data[2], + length - 2); + pt_debug(dev, DL_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); + + rc = pt_hid_output_user_cmd(cd, PT_MAX_INPUT, cd->cmd_rsp_buf, + length, input_data, &actual_read_len); + pm_runtime_put(dev); + + mutex_lock(&cd->sysfs_lock); + cd->raw_cmd_status = rc; + if (rc) { + cd->cmd_rsp_buf_len = 0; + pt_debug(dev, DL_ERROR, "%s: Failed to send command: %s\n", + __func__, buf); + } else { + cd->cmd_rsp_buf_len = actual_read_len; + } + cd->pip2_prot_active = 0; + mutex_unlock(&cd->sysfs_lock); + +pt_command_store_exit: + if (rc) + return rc; + return size; +} +static DEVICE_ATTR(command, 0220, NULL, pt_command_store); + +/******************************************************************************* + * FUNCTION: pt_response_show + * + * SUMMARY: The show method for the raw PIP response sysfs node. Any PIP + * response generated after using the pt_command_store sysfs node, are + * available to be read here. + * + * PARAMETERS: + * *dev - pointer to Device structure + * *attr - pointer to the device attribute structure + * *buf - pointer to buffer to print + ******************************************************************************/ +static ssize_t pt_response_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct pt_core_data *cd = dev_get_drvdata(dev); + int i; + ssize_t num_read; + int index; + + mutex_lock(&cd->sysfs_lock); + index = scnprintf(buf, PT_MAX_PRBUF_SIZE, + "Status: %d\n", cd->raw_cmd_status); + if (cd->raw_cmd_status) + goto error; + + num_read = cd->cmd_rsp_buf_len; + for (i = 0; i < num_read; i++) + index += scnprintf(buf + index, PT_MAX_PRBUF_SIZE - index, + "0x%02X\n", cd->cmd_rsp_buf[i]); + + index += scnprintf(buf + index, PT_MAX_PRBUF_SIZE - index, + "(%zd bytes)\n", num_read); + +error: + mutex_unlock(&cd->sysfs_lock); + return index; +} +static DEVICE_ATTR(response, 0444, pt_response_show, NULL); + +/******************************************************************************* + * FUNCTION: pt_dut_debug_show + * + * SUMMARY: Show method for the dut_debug sysfs node. Shows what parameters + * are available for the store method. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *dev - pointer to device structure + * *attr - pointer to device attributes + * *buf - pointer to output buffer + ******************************************************************************/ +static ssize_t pt_dut_debug_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t ret; + + ret = sprintf(buf, + "Status: 0\n" + "dut_debug sends the following commands to the DUT:\n" + "%d %s \t- %s\n" + "%d %s \t- %s\n" + "%d %s \t- %s\n" + "%d %s \t- %s\n" + "%d %s \t- %s\n" + "%d %s \t- %s\n" + "%d %s \t- %s\n" + "%d %s \t- %s\n" + "%d %s \t- %s\n" + "%d %s \t- %s\n" + "%d %s \t- %s\n" + "%d %s \t- %s\n" + "%d %s \t- %s\n" + "%d %s \t- %s\n" + "%d %s \t- %s\n" + "%d %s \t- %s\n" + "%d %s \t- %s\n" + , + PIP1_BL_CMD_ID_VERIFY_APP_INTEGRITY, "", "BL Verify APP", + PT_DUT_DBG_HID_RESET, "", "HID Reset", + PT_DUT_DBG_HID_SET_POWER_ON, "", "HID SET_POWER ON", + PT_DUT_DBG_HID_SET_POWER_SLEEP, "", "HID SET_POWER SLEEP", + PT_DUT_DBG_HID_SET_POWER_STANDBY, "", "HID SET_POWER STANDBY", + PIP1_BL_CMD_ID_GET_INFO, "", "BL Get Info", + PIP1_BL_CMD_ID_PROGRAM_AND_VERIFY, "", "BL Program & Verify", + PIP1_BL_CMD_ID_LAUNCH_APP, "", "BL Launch APP", + PIP1_BL_CMD_ID_INITIATE_BL, "", "BL Initiate BL", + PT_DUT_DBG_PIP_SOFT_RESET, "", "PIP Soft Reset", + PT_DUT_DBG_RESET, "", "Toggle the TP_XRES GPIO", + PT_DUT_DBG_PIP_NULL, "", "PIP NULL (PING)", + PT_DUT_DBG_PIP_ENTER_BL, "", "PIP enter BL", + PT_DUT_DBG_HID_SYSINFO, "", "HID system info", + PT_DUT_DBG_PIP_SUSPEND_SCAN, "", "Suspend Scan", + PT_DUT_DBG_PIP_RESUME_SCAN, "", "Resume Scan", + PT_DUT_DBG_HID_DESC, "", "Get HID Desc" + ); + + return ret; +} +/******************************************************************************* + * FUNCTION: pt_drv_debug_show + * + * SUMMARY: Show method for the drv_debug sysfs node. Shows what parameters + * are available for the store method. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *dev - pointer to device structure + * *attr - pointer to device attributes + * *buf - pointer to output buffer + ******************************************************************************/ +static ssize_t pt_drv_debug_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t ret; + + ret = sprintf(buf, + "Status: 0\n" + "drv_debug supports the following values:\n" + "%d %s \t- %s\n" + "%d %s \t- %s\n" + "%d %s \t- %s\n" + "%d %s \t- %s\n" + "%d %s \t- %s\n" + "%d %s \t- %s\n" + "%d %s \t- %s\n" + "%d %s \t- %s\n" + "%d %s \t- %s\n" + "%d %s - %s\n" + "%d %s \t- %s\n" + "%d %s \t- %s\n" +#ifdef TTDL_DIAGNOSTICS + "%d %s \t- %s\n" + "%d %s \t- %s\n" + "%d %s \t- %s\n" +#endif /* TTDL_DIAGNOSTICS */ + "%d %s \t- %s\n" +#ifdef TTDL_DIAGNOSTICS + "%d %s \t- %s\n" + "%d %s \t- %s\n" + "%d %s \t- %s\n" + "%d %s \t- %s\n" + "%d %s \t- %s\n" + "%d %s \t- %s\n" + "%d %s \t- %s\n" + "%d %s \t- %s\n" + "%d %s \t- %s\n" +#endif /* TTDL_DIAGNOSTICS */ + , + PT_DRV_DBG_SUSPEND, " ", "Suspend TTDL responding to INT", + PT_DRV_DBG_RESUME, " ", "Resume TTDL responding to INT", + PT_DRV_DBG_STOP_WD, " ", "Stop TTDL WD", + PT_DRV_DBG_START_WD, " ", "Start TTDL WD", + PT_DRV_DBG_TTHE_TUNER_EXIT, " ", "Exit TTHE Tuner Logging", + PT_DRV_DBG_TTHE_BUF_CLEAN, " ", "Clear TTHE Tuner buffer", + PT_DRV_DBG_CLEAR_PARM_LIST, " ", "Clear RAM Param list", + PT_DRV_DBG_FORCE_BUS_READ, " ", "Force bus read", + PT_DRV_DBG_CLEAR_CAL_DATA, " ", "Clear CAL Cache", + PT_DRV_DBG_REPORT_LEVEL, "[0|1|2|3|4]", "Set TTDL Debug Level", + PT_DRV_DBG_WATCHDOG_INTERVAL, "[n] ", "TTDL WD Interval in ms", + PT_DRV_DBG_SHOW_TIMESTAMP, "[0|1]", "Show Timestamps" +#ifdef TTDL_DIAGNOSTICS + , PT_DRV_DBG_SETUP_PWR, "[0|1]", "Power DUT up/down", + PT_DRV_DBG_GET_PUT_SYNC, "[0|1]", "Get/Put Linux Sleep", + PT_DRV_DBG_SET_TT_DATA, "[0|1]", "Display TT_DATA" +#endif /* TTDL_DIAGNOSTICS */ + , PT_DRV_DBG_SET_GENERATION, "[0|1|2]", "Set DUT generation" +#ifdef TTDL_DIAGNOSTICS + , PT_DRV_DBG_SET_BRIDGE_MODE, "[0|1]", "On/Off Bridge Mode", + PT_DRV_DBG_SET_I2C_ADDRESS, "[0-127]", "I2C DUT Address", + PT_DRV_DBG_SET_FLASHLESS_DUT, "[0|1]", "Flashless DUT yes/no", + PT_DRV_DBG_SET_FORCE_SEQ, "[8-15]", "Force PIP2 Sequence #", + PT_DRV_DBG_BL_WITH_NO_INT, "[0|1]", "BL with no INT", + PT_DRV_DBG_CAL_CACHE_IN_HOST, "[0|1]", "CAL Cache in host", + PT_DRV_DBG_MULTI_CHIP, "[0|1]", "Multi Chip Support", + PT_DRV_DBG_PIP_TIMEOUT, "[100-7000]", "PIP Resp Timeout (ms)", + PT_DRV_DBG_TTHE_HID_USB_FORMAT, "[0|1]", + "TTHE_TUNER HID USB Format" +#endif /* TTDL_DIAGNOSTICS */ + ); + + return ret; +} + +/******************************************************************************* + * FUNCTION: pt_drv_debug_store + * + * SUMMARY: Currently the store method for both sysfs nodes: drv_debug and + * dut_debug. Drv_debug will contain all functionality that can be run + * without a DUT preset and is available anytime TTDL is running. + * Dut_debug requires a DUT to be available and will only be created after + * a DUT has been detected. + * This function will eventually be split into two but until the overlap + * has been depricated this function contains all commands that can be + * used for TTDL/DUT debugging status and control. + * All commands require at least one value to be passed in *buf with some + * requiring two. + * + * RETURN: Size of passed in buffer + * + * PARAMETERS: + * *dev - pointer to device structure + * *attr - pointer to device attributes + * *buf - pointer to buffer that hold the command parameters + * size - size of buf + ******************************************************************************/ +static ssize_t pt_drv_debug_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct pt_core_data *cd = dev_get_drvdata(dev); + unsigned long value; + int rc = 0; + u8 return_data[8]; + static u8 wd_disabled; + u32 input_data[3]; + int length; +#ifdef TTDL_DIAGNOSTICS + struct i2c_client *client = to_i2c_client(dev); + unsigned short crc = 0; + u16 cal_size; +#endif + + input_data[0] = 0; + input_data[1] = 0; + + /* Maximmum input is two elements */ + length = _pt_ic_parse_input(dev, buf, size, + input_data, ARRAY_SIZE(input_data)); + if (length < 1 || length > 2) { + pt_debug(dev, DL_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 Recieved (id=%d)\n", + __func__, input_data[0]); + } else if (length == 2) { + pt_debug(dev, DL_DEBUG, + "%s: Debug Cmd Recieved (id=%d, data=%d)\n", + __func__, input_data[0], input_data[1]); + } else { + pt_debug(dev, DL_DEBUG, + "%s: Invalid arguments recieved\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_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_MULTI_CHIP: /* 217 */ + mutex_lock(&cd->system_lock); + if (input_data[1] == 0) { + cd->multi_chip = PT_FEATURE_DISABLE; + cd->ttdl_bist_select = 0x07; + pt_debug(dev, DL_INFO, + "%s: Disable Multi-chip support\n", __func__); + } else if (input_data[1] == 1) { + cd->multi_chip = PT_FEATURE_ENABLE; + cd->ttdl_bist_select = 0x3F; + pt_debug(dev, DL_INFO, + "%s: Enable Multi-chip support\n", __func__); + } else { + rc = -EINVAL; + pt_debug(dev, DL_ERROR, "%s: Invalid parameter: %d\n", + __func__, input_data[1]); + } + mutex_unlock(&(cd->system_lock)); + break; + case PT_DRV_DBG_SET_PANEL_ID_TYPE: /* 218 */ + mutex_lock(&cd->system_lock); + if (input_data[1] <= 0x07) { + cd->panel_id_support = input_data[1]; + pt_debug(dev, DL_INFO, + "%s: Set panel_id_support to %d\n", + __func__, cd->panel_id_support); + } else { + rc = -EINVAL; + pt_debug(dev, DL_ERROR, "%s: Invalid parameter: %d\n", + __func__, input_data[1]); + } + mutex_unlock(&(cd->system_lock)); + break; + case PT_DRV_DBG_PIP_TIMEOUT: /* 219 */ + mutex_lock(&cd->system_lock); + if (input_data[1] >= 100 && input_data[1] <= 7000) { + /* + * The timeout is changed for some cases so the + * pip_cmd_timeout_default is used to retore back to + * what the user requested as the new timeout. + */ + cd->pip_cmd_timeout_default = input_data[1]; + cd->pip_cmd_timeout = input_data[1]; + pt_debug(dev, DL_INFO, + "%s: PIP Timeout = %d\n", __func__, + cd->pip_cmd_timeout_default); + } else { + rc = -EINVAL; + pt_debug(dev, DL_ERROR, "%s: Invalid parameter: %d\n", + __func__, input_data[1]); + } + mutex_unlock(&(cd->system_lock)); + break; + case PT_DRV_DBG_TTHE_HID_USB_FORMAT: /* 220 */ + mutex_lock(&cd->system_lock); + if (input_data[1] == 0) { + cd->tthe_hid_usb_format = PT_FEATURE_DISABLE; + pt_debug(dev, DL_INFO, + "%s: Disable tthe_tuner HID-USB format\n", + __func__); + } else if (input_data[1] == 1) { + cd->tthe_hid_usb_format = PT_FEATURE_ENABLE; + pt_debug(dev, DL_INFO, + "%s: Enable tthe_tuner HID-USB format\n", + __func__); + } else { + rc = -EINVAL; + pt_debug(dev, DL_ERROR, "%s: Invalid parameter: %d\n", + __func__, input_data[1]); + } + mutex_unlock(&(cd->system_lock)); + break; +#endif /* TTDL_DIAGNOSTICS */ + default: + rc = -EINVAL; + pt_debug(dev, DL_ERROR, "%s: Invalid value\n", __func__); + } + + /* Enable watchdog timer */ + if (!wd_disabled) + pt_start_wd_timer(cd); + +pt_drv_debug_store_exit: + if (rc) + return rc; + return size; +} +static DEVICE_ATTR(drv_debug, 0644, pt_drv_debug_show, + pt_drv_debug_store); + +/******************************************************************************* + * FUNCTION: pt_sleep_status_show + * + * SUMMARY: Show method for the sleep_status sysfs node that will show the + * sleep status as either ON or OFF + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *dev - pointer to device structure + * *attr - pointer to device attributes + * *buf - pointer to output buffer + ******************************************************************************/ +static ssize_t pt_sleep_status_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct pt_core_data *cd = dev_get_drvdata(dev); + ssize_t ret; + + mutex_lock(&cd->system_lock); + if (cd->sleep_state == SS_SLEEP_ON) + ret = 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 = sprintf(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 = sprintf(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 sprintf(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 = sprintf(buf, + "%s: 0x%04X\n" + "%s: %d\n" + "%s: %s\n" + "%s: %s %s\n" + "%s: %s\n" + "%s: 0x%02X\n" + "%s: %s\n" + "%s: %s\n" + "%s: %s\n" + "%s: %s\n" + "%s: %d\n" + "%s: %d\n" + "%s: %s\n" + "%s: %s\n" + "%s: %d\n" +#ifdef TTDL_DIAGNOSTICS + "%s: %d\n" + "%s: %d\n" + "%s: %d\n" + "%s: %d\n" + "%s: %d\n" + "%s: %d\n" + "%s: %d\n" + "%s: %d\n" + "%s: %d\n" + "%s: %d\n" + "%s: %s\n" + "%s: %s\n" + "%s: %s\n" + "%s: %d\n" + "%s: 0x%04X\n" + "%s: %s\n" +#endif /* TTDL_DIAGNOSTICS */ + , + "Startup Status ", cd->startup_status, + "TTDL Debug Level ", cd->debug_level, + "Mode ", + cd->mode ? (cd->mode == PT_MODE_OPERATIONAL ? + "Operational" : "BL") : "Unknown", + "DUT Generation ", + cd->active_dut_generation ? + (cd->active_dut_generation == DUT_PIP2_CAPABLE ? + "PT TC/TT" : "Gen5/6") : "Unknown", + cd->active_dut_generation ? + (cd->set_dut_generation == true ? + "(Set)" : "(Detected)") : "", + "HW Detected ", + cd->hw_detected ? "True" : "False", + "I2C Address ", + cd->bus_ops->bustype == BUS_I2C ? client->addr : 0, + "Active Bus Module ", + cd->bus_ops->bustype == BUS_I2C ? "I2C" : "SPI", + "Flashless Mode ", + cd->flashless_dut == 1 ? "Yes" : "No", + "GPIO state - IRQ ", + cd->cpdata->irq_stat ? + (cd->cpdata->irq_stat(cd->cpdata, dev) ? + "High" : "Low") : "not defined", + "GPIO state - TP_XRES ", + pdata->core_pdata->rst_gpio ? + (gpio_get_value(pdata->core_pdata->rst_gpio) ? + "High" : "Low") : "not defined", + "RAM Parm restore list ", pt_count_parameter_list(cd), + "Startup Retry Count ", cd->startup_retry_count, + "WD - Manual Force Stop ", + cd->watchdog_force_stop ? "True" : "False", + "WD - Enabled ", + cd->watchdog_enabled ? "True" : "False", + "WD - Interval (ms) ", cd->watchdog_interval +#ifdef TTDL_DIAGNOSTICS + , "WD - Triggered Count ", cd->watchdog_count, + "WD - IRQ Stuck low count ", cd->watchdog_irq_stuck_count, + "WD - Device Access Errors ", cd->watchdog_failed_access_count, + "WD - XRES Count ", cd->wd_xres_count, + "IRQ Triggered Count ", cd->irq_count, + "BL Packet Retry Count ", cd->bl_retry_packet_count, + "PIP2 CRC Error Count ", cd->pip2_crc_error_count, + "Bus Transmit Error Count ", cd->bus_transmit_error_count, + "File Erase Timeout Count ", cd->file_erase_timeout_count, + "Error GPIO trigger type ", cd->err_gpio_type, + "Exclusive Access Lock ", cd->exclusive_dev ? "Set":"Free", + "Suppress No-Flash Auto BL ", + cd->flashless_auto_bl == PT_SUPPRESS_AUTO_BL ? + "Yes" : "No", + "Calibration Cache on host ", + cd->cal_cache_in_host == PT_FEATURE_ENABLE ? + "Yes" : "No", + "Calibration Cache size ", cal_size, + "Calibration Cache chip ID ", crc, + "Multi-Chip Support ", + cd->multi_chip == PT_FEATURE_ENABLE ? "Yes" : "No" +#endif /* TTDL_DIAGNOSTICS */ + ); + +#ifdef TTDL_DIAGNOSTICS + /* Reset all diagnostic counters */ + cd->watchdog_count = 0; + cd->watchdog_irq_stuck_count = 0; + cd->watchdog_failed_access_count = 0; + cd->wd_xres_count = 0; + cd->irq_count = 0; + cd->bl_retry_packet_count = 0; + cd->pip2_crc_error_count = 0; + cd->bus_transmit_error_count = 0; +#endif + + return ret; +} +static DEVICE_ATTR(ttdl_status, 0444, pt_ttdl_status_show, NULL); + +/******************************************************************************* + * FUNCTION: pt_pip2_enter_bl_show + * + * SUMMARY: Show method for the pip2_enter_bl sysfs node that will force + * the DUT into the BL and show the success or failure of entering the BL + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *dev - pointer to device structure + * *attr - pointer to device attributes + * *buf - pointer to output buffer + ******************************************************************************/ +static ssize_t pt_pip2_enter_bl_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t ret = 0; + int rc = 0; + int result = 0; + u8 mode = PT_MODE_UNKNOWN; + struct pt_core_data *cd = dev_get_drvdata(dev); + + /* Turn off the TTDL WD befor 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; + }; + + /* Allow enumeration work to be queued again */ + cd->bridge_mode = false; + + return ret; +} +static DEVICE_ATTR(pip2_enter_bl, 0444, pt_pip2_enter_bl_show, NULL); + +/******************************************************************************* + * FUNCTION: pt_pip2_exit_bl_show + * + * SUMMARY: Show method for the pip2_exit_bl sysfs node that will attempt to + * launch the APP and put the DUT Application mode + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *dev - pointer to device structure + * *attr - pointer to device attributes + * *buf - pointer to output buffer + ******************************************************************************/ +static ssize_t pt_pip2_exit_bl_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct pt_core_data *cd = dev_get_drvdata(dev); + ssize_t ret = 0; + int rc = 0; + int status_str_buf_len = 50; + u8 status_str[status_str_buf_len]; + + rc = pt_pip2_exit_bl_(cd, status_str, status_str_buf_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)) { + strcpy(status_str, + "Already in APP mode - FW stuck in Boot mode"); + } + } + ret = snprintf(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 sprintf(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 = sprintf(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 = sprintf(buf, + "%s: %d\n" + "%s: %d\n" + "%s: %d\n" + "%s: %d\n" + "%s: %d\n" + "%s: %d\n" + "%s: %d\n" + "%s: %d\n" + "%s: %d\n" + "%s: %d\n" + "%s: %d\n" + "%s: %d\n" + "%s: %d\n" + "%s: %d\n" + "%s: %d\n" + "%s: %d\n" + "%s: %d\n" + "%s: %d\n" + "%s: %d\n" + "%s: %d\n" + "%s: %d\n" + "%s: %d\n" + "%s: %d\n" + "%s: %d\n" + "%s: %d\n" + "%s: %d\n" + "%s: %d\n" + "%s: %s\n" + "%s: %s\n" + "%s: %d\n", + "Status", 0, + "Interrupt GPIO ", pdata->core_pdata->irq_gpio, + "Interrupt GPIO Value ", + pdata->core_pdata->irq_gpio ? + gpio_get_value(pdata->core_pdata->irq_gpio) : 0, + "Reset GPIO ", pdata->core_pdata->rst_gpio, + "Reset GPIO Value ", + pdata->core_pdata->rst_gpio ? + gpio_get_value(pdata->core_pdata->rst_gpio) : 0, + "DDI Reset GPIO ", pdata->core_pdata->ddi_rst_gpio, + "DDI Reset GPIO Value ", + pdata->core_pdata->ddi_rst_gpio ? + gpio_get_value(pdata->core_pdata->ddi_rst_gpio) : 0, + "VDDI GPIO ", pdata->core_pdata->vddi_gpio, + "VDDI GPIO Value ", + pdata->core_pdata->vddi_gpio ? + gpio_get_value(pdata->core_pdata->vddi_gpio) : 0, + "VCC GPIO ", pdata->core_pdata->vcc_gpio, + "VCC GPIO Value ", + pdata->core_pdata->vcc_gpio ? + gpio_get_value(pdata->core_pdata->vcc_gpio) : 0, + "AVDD GPIO ", pdata->core_pdata->avdd_gpio, + "AVDD GPIO Value ", + pdata->core_pdata->avdd_gpio ? + gpio_get_value(pdata->core_pdata->avdd_gpio) : 0, + "AVEE GPIO ", pdata->core_pdata->avee_gpio, + "AVEE GPIO Value ", + pdata->core_pdata->avee_gpio ? + gpio_get_value(pdata->core_pdata->avee_gpio) : 0, + "Vendor ID ", pdata->core_pdata->vendor_id, + "Product ID ", pdata->core_pdata->product_id, + "Vkeys x ", pdata->mt_pdata->vkeys_x, + "Vkeys y ", pdata->mt_pdata->vkeys_y, + "Core data flags ", pdata->core_pdata->flags, + "MT data flags ", pdata->mt_pdata->flags, + "Loader data flags ", pdata->loader_pdata->flags, + "Level trigger delay (us) ", + pdata->core_pdata->level_irq_udelay, + "HID Descriptor Register ", + pdata->core_pdata->hid_desc_register, + "HID Output Register ", + cd->hid_desc.output_register, + "HID Command Register ", + cd->hid_desc.command_register, + "Easy wakeup gesture ", + pdata->core_pdata->easy_wakeup_gesture, + "Config DUT generation ", + pdata->core_pdata->config_dut_generation ? + (pdata->core_pdata->config_dut_generation == + CONFIG_DUT_PIP2_CAPABLE ? + "PT TC/TT" : "Gen5/6") : "Auto", + "Watchdog Force Stop ", + pdata->core_pdata->watchdog_force_stop ? + "True" : "False", + "Panel ID Support ", + pdata->core_pdata->panel_id_support); + return ret; +} + +#define PT_ERR_STR_SIZE 64 +/******************************************************************************* + * FUNCTION: pt_bist_bus_test + * + * SUMMARY: Tests the connectivity of the active bus pins: + * I2C - SDA and SCL + * SPI - MOSI, MISO, CLK + * + * Disable TTDL interrupts, send a PIP cmd and then manually read the + * bus. If any data is read we know the I2C/SPI pins are connected + * + * RETURN: + * 0 = Success + * !0 = Failure + * + * PARAMETERS: + * *dev - pointer to device structure + * *net_toggled - pointer to where to store if bus toggled + * *err_str - pointer to error string buffer + ******************************************************************************/ +static int pt_bist_bus_test(struct device *dev, u8 *net_toggled, u8 *err_str) +{ + struct pt_core_data *cd = dev_get_drvdata(dev); + u8 ver_cmd[8] = {0x01, 0x01, 0x06, 0x00, 0x0E, 0x07, 0xF0, 0xB1}; + u8 *read_buf = NULL; + int bytes_read = 0; + int rc = 0; + + read_buf = kzalloc(PT_MAX_PIP1_MSG_SIZE, GFP_KERNEL); + if (read_buf == NULL) { + rc = -ENOMEM; + goto exit; + } + + bytes_read = pt_flush_bus(cd, PT_FLUSH_BUS_BASED_ON_LEN, NULL); + + pt_debug(dev, DL_INFO, "%s: TTDL Core Suspend\n", __func__); + disable_irq(cd->irq); + mutex_lock(&cd->system_lock); + cd->irq_disabled = true; + mutex_unlock(&cd->system_lock); + + /* + * Sleep >4ms to allow any pending TTDL IRQ to finish. Without this + * the disable_irq_nosync() could cause the IRQ to get stuck asserted + */ + usleep_range(5000, 6000); + + pt_debug(cd->dev, DL_INFO, ">>> %s: Write Buffer Size[%d] VERSION\n", + __func__, (int)sizeof(ver_cmd)); + pt_pr_buf(cd->dev, DL_DEBUG, ver_cmd, (int)sizeof(ver_cmd), + ">>> User CMD"); + rc = pt_adap_write_read_specific(cd, sizeof(ver_cmd), ver_cmd, NULL); + if (rc) { + pt_debug(dev, DL_ERROR, + "%s: BUS Test - Failed to send VER cmd\n", __func__); + *net_toggled = 0; + strcpy(err_str, + "- Write failed, bus open or shorted or DUT in reset"); + 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__); + strcpy(err_str, + "- Bus open, shorted or DUT in reset"); + 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); + strcpy(err_str, + "- Bus open, shorted or DUT in reset"); + } + } + } + +exit_enable_irq: + enable_irq(cd->irq); + usleep_range(5000, 6000); + mutex_lock(&cd->system_lock); + cd->irq_disabled = false; + mutex_unlock(&cd->system_lock); + pt_debug(dev, DL_INFO, "%s: TTDL Core Resumed\n", __func__); + +exit: + kfree(read_buf); + return rc; +} + +/******************************************************************************* + * FUNCTION: pt_bist_irq_test + * + * SUMMARY: Tests the connectivity of the IRQ net + * + * This test will ensure there is a good connection between the host + * and the DUT on the irq pin. First determine if the IRQ is stuck + * asserted and if so keep reading messages off of the bus until + * it de-asserts. Possible outcomes: + * - IRQ was already de-asserted: Send a PIP command and if an + * interrupt is generated the test passes. + * - IRQ was asserted: Reading off the bus de-assertes the IRQ, + * test passes. + * - IRQ stays asserted: After reading the bus multiple times + * the IRQ stays asserted. Likely open or shorted to GND + * + * RETURN: + * 0 = Success + * !0 = Failure + * + * PARAMETERS: + * *dev - pointer to device structure + * *bus_toggled - pointer to where to store if bus toggled + * *irq_toggled - pointer to where to store if IRQ toggled + * *xres_toggled - pointer to where to store if XRES toggled + * *err_str - pointer to error string buffer + ******************************************************************************/ +static int pt_bist_irq_test(struct device *dev, + u8 *bus_toggled, u8 *irq_toggled, u8 *xres_toggled, u8 *err_str) +{ + struct pt_core_data *cd = dev_get_drvdata(dev); + u8 *read_buf = NULL; + u8 mode = PT_MODE_UNKNOWN; + u16 actual_read_len; + int bytes_read = 0; + int count = 0; + int rc = 0; + + read_buf = kzalloc(PT_MAX_PIP1_MSG_SIZE, GFP_KERNEL); + if (read_buf == NULL) { + rc = -ENOMEM; + goto exit; + } + + /* Clear IRQ triggered count, and re-evaluate at the end of test */ + cd->irq_count = 0; + + /* + * Check if IRQ is stuck asserted, if so try a non forced flush of + * the bus based on the 2 byte initial length read. Try up to 5x. + */ + while (pt_check_irq_asserted(cd) && count < 5) { + count++; + bytes_read += pt_flush_bus(cd, PT_FLUSH_BUS_BASED_ON_LEN, NULL); + } + + if (count > 1 && count < 5 && bytes_read > 0) { + /* + * IRQ was stuck but data was successfully read from the + * bus releasing the IRQ line. + */ + pt_debug(dev, DL_INFO, "%s: count=%d bytes_read=%d\n", + __func__, count, bytes_read); + *bus_toggled = 1; + *irq_toggled = 1; + goto exit; + } + + if (count == 5 && bytes_read == 0) { + /* + * Looped 5x and read nothing off the bus yet the IRQ is still + * asserted. Possible conditions: + * - IRQ open circuit + * - IRQ shorted to GND + * - I2C/SPI bus is disconnected + * - FW holding the pin low + * Try entering the BL to see if communication works there. + */ + mode = PT_MODE_IGNORE; + rc = _pt_request_pip2_enter_bl(dev, &mode, NULL); + if (rc) { + pt_debug(dev, DL_ERROR, + "%s Failed to enter BL\n", __func__); + strcpy(err_str, + "- likely shorted to GND or FW holding it."); + *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. + */ + strcpy(err_str, "- likely shorted to GND."); + 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)) { + strcpy(err_str, "- likely shorted to GND"); + 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; + strcpy(err_str, "- likely open or shorted to VDDI."); + 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. + */ + strcpy(err_str, "- likely open or shorted to VDDI."); + 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 additonal information on the failure: + * - soft reset fails to send: XRES and IRQ likely open + * - soft reset passes: XRES likely open or stuck de-asserted + * - soft reset fails: XRES likely stuck asserted + * + * RETURN: + * 0 = Success + * !0 = Failure + * + * PARAMETERS: + * *dev - pointer to device structure + * *bus_toggled - pointer to where to store if bus toggled + * *irq_toggled - pointer to where to store if IRQ toggled + * *xres_toggled - pointer to where to store if XRES toggled + * *err_str - pointer to error string buffer + ******************************************************************************/ +static int pt_bist_xres_test(struct device *dev, + u8 *bus_toggled, u8 *irq_toggled, u8 *xres_toggled, u8 *err_str) +{ + struct pt_core_data *cd = dev_get_drvdata(dev); + struct pt_platform_data *pdata = dev_get_platdata(dev); + u8 *read_buf = NULL; + u8 mode = PT_MODE_UNKNOWN; + int rc = 0; + int t = 0; + int timeout = 300; + + read_buf = kzalloc(PT_MAX_PIP1_MSG_SIZE, GFP_KERNEL); + if (read_buf == NULL) { + rc = -ENOMEM; + goto exit; + } + + /* Clear the startup bit mask, reset and enum will re-populate it */ + cd->startup_status = STARTUP_STATUS_START; + pt_debug(dev, DL_DEBUG, "%s: Startup Status Reset\n", __func__); + + if ((!pdata->core_pdata->rst_gpio) || (!pdata->core_pdata->xres)) { + strcpy(err_str, "- Net not configured or available"); + rc = -ENODEV; + goto exit; + } + + + /* Ensure we have nothing pending on active bus */ + pt_flush_bus_if_irq_asserted(cd, PT_FLUSH_BUS_BASED_ON_LEN); + + /* Perform a hard XRES toggle and wait for reset sentinel */ + mutex_lock(&cd->system_lock); + cd->hid_reset_cmd_state = 1; + mutex_unlock(&cd->system_lock); + pt_debug(dev, DL_INFO, "%s: Perform a hard reset\n", __func__); + rc = pt_hw_hard_reset(cd); + + /* Set timeout to 1s for the flashless case where a BL could be done */ + if (cd->flashless_dut) + timeout = 1000; + + /* + * To avoid the case that next PIP command can be confused by BL/FW + * sentinel's "wakeup" event, chekcing hid_reset_cmd_state which is + * followed by "wakeup event" function can lower the failure rate. + */ + t = wait_event_timeout(cd->wait_q, + ((cd->startup_status != STARTUP_STATUS_START) + && (cd->hid_reset_cmd_state == 0)), + msecs_to_jiffies(timeout)); + if (IS_TMO(t)) { + pt_debug(cd->dev, DL_ERROR, + "%s: TMO waiting for sentinel\n", __func__); + *xres_toggled = 0; + strcpy(err_str, "- likely open. (No Reset Sentinel)"); + + /* + * 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; + strcpy(err_str, "- likely open or shorted to VDDI."); + 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; + strcpy(err_str, + "- likely open or shorted to VDDI."); + 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) { + strcpy(err_str, "- likely open."); + 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) { + strcpy(err_str, + "- likely open or stuck high, soft reset OK"); + pt_debug(dev, DL_ERROR, + "%s: Hard reset failed, soft reset passed-%s\n", + __func__, err_str); + } else if (cd->startup_status == 0) { + strcpy(err_str, "- likely stuck high."); + pt_debug(dev, DL_ERROR, + "%s: Hard reset failed, soft reset failed-%s\n", + __func__, err_str); + } else { + strcpy(err_str, "- open or stuck."); + pt_debug(dev, DL_ERROR, + "%s: Hard and Soft reset failed - %s\n", + __func__, err_str); + } + } + +exit: + kfree(read_buf); + return rc; +} + +/******************************************************************************* + * FUNCTION: pt_bist_slave_irq_test + * + * SUMMARY: Tests the connectivity of the Master/Slave IRQ net + * + * This test will ensure there is a good IRQ connection between the master + * DUT and the slave DUT(s). After power up the STATUS command is sent + * and the 'Slave Detect' bits are verified to ensure the master DUT + * saw each slave trigger the IRQ with it's reset sentinel. + * + * RETURN: + * 0 = Success + * !0 = Failure + * + * PARAMETERS: + * *dev - pointer to device structure + * *slave_irq_toggled - pointer to where to store if slave IRQ toggled + * *slave_bus_toggled - pointer to where to store if slave Bus toggled + * *err_str - pointer to error string buffer + * *slave_detect - pointer to slave detect buffer + * *boot_err - pointer to boot_err buffer + ******************************************************************************/ +static int pt_bist_slave_irq_test(struct device *dev, + u8 *slave_irq_toggled, u8 *slave_bus_toggled, u8 *err_str, + u8 *slave_detect, u8 *boot_err) +{ + struct pt_core_data *cd = dev_get_drvdata(dev); + u8 mode = PT_MODE_UNKNOWN; + u8 status; + u8 boot; + u8 read_buf[12]; + u8 detected = 0; + u8 last_err = -1; + u16 actual_read_len; + int result = 0; + int rc = 0; + + /* + * Ensure DUT is in the BL where the STATUS cmd will report the slave + * detect bits. If the DUT was in FW, entering the BL will cause an + * XRES signal which will inadvertently test the XRES net as well + */ + rc = _pt_request_pip2_enter_bl(dev, &mode, &result); + if (rc) { + pt_debug(cd->dev, DL_WARN, "%s: Error entering BL rc=%d\n", + __func__, rc); + strcpy(err_str, "- State could not be determined."); + goto exit; + } + + /* Use the STATUS command to retrieve the slave detect bit(s) */ + rc = _pt_request_pip2_send_cmd(cd->dev, PT_CORE_CMD_UNPROTECTED, + PIP2_CMD_ID_STATUS, NULL, 0, read_buf, + &actual_read_len); + if (!rc) { + pt_pr_buf(cd->dev, DL_INFO, read_buf, actual_read_len, + "PIP2 STATUS"); + status = read_buf[PIP2_RESP_STATUS_OFFSET]; + boot = read_buf[PIP2_RESP_BODY_OFFSET] & 0x01; + + /* Slave detect is only valid if status ok and in boot exec */ + if (status == PIP2_RSP_ERR_NONE && + boot == PIP2_STATUS_BOOT_EXEC) { + detected = read_buf[PIP2_RESP_BODY_OFFSET + 2] & + SLAVE_DETECT_MASK; + } else { + strcpy(err_str, "- State could not be determined"); + rc = -EPERM; + } + } else { + pt_debug(cd->dev, DL_WARN, "%s: STATUS cmd failure\n", + __func__); + strcpy(err_str, "- State could not be determined."); + 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__); + strcpy(err_str, "- stuck, likely shorted to GND."); + } + +exit: + pt_debug(cd->dev, DL_INFO, + "%s: rc=%d detected=0x%02X boot_err=0x%02X\n", + __func__, rc, detected, last_err); + if (err_str && last_err) { + if (detected) + sprintf(err_str, "%s 0x%02X", + "- Likely stuck low. Boot Error:", + last_err); + else + sprintf(err_str, "%s 0x%02X", + "- Likely stuck high. Boot Error:", + last_err); + } + + /* Ignore an invalid image error as BIST doesn't need valid FW */ + if (last_err == PIP2_RSP_ERR_INVALID_IMAGE) + last_err = PIP2_RSP_ERR_NONE; + + if (slave_irq_toggled) + *slave_irq_toggled = (detected && !last_err) ? true : false; + if (slave_bus_toggled) { + /* Leave as UNTEST if slave not detected */ + if (detected) + *slave_bus_toggled = !last_err ? true : false; + } + if (slave_detect) + *slave_detect = detected; + if (boot_err) + *boot_err = last_err; + + pt_debug(cd->dev, DL_INFO, "%s: %s=0x%02X, %s=0x%02X, %s=0x%02X\n", + __func__, + "Detected", detected, + "slave_irq_toggled", *slave_irq_toggled, + "slave_bus_toggled", *slave_bus_toggled); + return rc; +} + +/******************************************************************************* + * FUNCTION: pt_bist_slave_xres_test + * + * SUMMARY: Tests the connectivity of the Master/Slave TP_XRES net + * + * This test will ensure there is a good TP_XRES connection between the + * master DUT and the slave DUT(s). After toggling the XRES pin to the + * master, the STATUS command is sent and the 'Slave Detect' bits are + * verified to ensure the master DUT saw each slave trigger the IRQ with + * it's reset sentinel. + * + * RETURN: + * 0 = Success + * !0 = Failure + * + * PARAMETERS: + * *dev - pointer to device structure + * *slave_irq_toggled - pointer to where to store if slave IRQ toggled + * *slave_bus_toggled - pointer to where to store if slave Bus toggled + * *slave_xres_toggled - pointer to where to store if slave XRES toggled + * *err_str - pointer to error string buffer + ******************************************************************************/ +static int pt_bist_slave_xres_test(struct device *dev, + u8 *slave_irq_toggled, u8 *slave_bus_toggled, u8 *slave_xres_toggled, + u8 *err_str) +{ + struct pt_core_data *cd = dev_get_drvdata(dev); + u8 slave_detect = 0; + u8 boot_err = 0; + int rc = 0; + + /* Force a reset to force the 'slave detect' bits to be re-aquired */ + mutex_lock(&cd->system_lock); + cd->hid_reset_cmd_state = 1; + mutex_unlock(&cd->system_lock); + pt_debug(dev, DL_INFO, "%s: Perform a hard reset\n", __func__); + pt_hw_hard_reset(cd); + msleep(100); + + rc = pt_bist_slave_irq_test(dev, slave_irq_toggled, + slave_bus_toggled, err_str, &slave_detect, &boot_err); + pt_debug(dev, DL_INFO, "%s: IRQ test rc = %d\n", __func__, rc); + + if (!rc && *slave_irq_toggled == false) { + /* + * If the slave IRQ did not toggle, either the slave_detect + * bit was not set or we had a boot error. If the slave + * detect was not set the slave did not reset causing a boot + * error. + */ + if (!slave_detect) + strcpy(err_str, "- likely open."); + else + sprintf(err_str, "%s 0x%02X", + "- likely open or an IRQ issue. Boot Error:", + boot_err); + } + if (slave_xres_toggled) { + if (!rc) + *slave_xres_toggled = *slave_irq_toggled ? true : false; + else + *slave_xres_toggled = false; + + } + + return rc; +} + +/******************************************************************************* + * FUNCTION: pt_bist_slave_bus_test + * + * SUMMARY: Tests the connectivity of the Master/Slave SPI bus net + * + * This test will ensure a good SPI bus connection between the + * master DUT and the slave DUT(s). This bus connection is ensured by + * opening file 0 (SRAM loader). If there is a slave and the open fails + * then there is a master/slave communication issue. Opening file 0 on + * the master will open it on the slave as well if the slave was detected. + * + * RETURN: + * 0 = Success + * !0 = Failure + * + * PARAMETERS: + * *dev - pointer to device structure + * *slave_irq_toggled - pointer to where to store if slave IRQ toggled + * *slave_bus_toggled - pointer to where to store if slave Bus toggled + * *err_str - pointer to error string buffer + ******************************************************************************/ +static int pt_bist_slave_bus_test(struct device *dev, + u8 *slave_irq_toggled, u8 *slave_bus_toggled, u8 *err_str) +{ + struct pt_core_data *cd = dev_get_drvdata(dev); + u8 mode = PT_MODE_UNKNOWN; + u8 bus_toggled = false; + u8 file_handle; + int result = 0; + int rc = 0; + + rc = _pt_request_pip2_enter_bl(dev, &mode, &result); + if (rc) { + pt_debug(cd->dev, DL_WARN, "%s: Error entering BL rc=%d\n", + __func__, rc); + strcpy(err_str, "- State could not be determined."); + 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__); + strcpy(err_str, "- Bus open, shorted or DUT in reset"); + 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 */ + if (slave_irq_toggled) + *slave_irq_toggled = bus_toggled; + if (slave_bus_toggled) + *slave_bus_toggled = bus_toggled; + + return rc; +} + +/******************************************************************************* + * FUNCTION: pt_ttdl_bist_show + * + * SUMMARY: Show method for the ttdl_bist sysfs node. This built in self test + * will test that I2C/SPI, IRQ and TP_XRES pins are operational. + * + * NOTE: This function will reset the DUT and the startup_status bit + * mask. A pt_enum will be queued after completion. + * + * NOTE: The order of the net tests is done to optimize the time it takes + * to run. The first test is capable of verifying all nets, each subsequent + * test is only run if the previous was not able to see all nets toggle. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *dev - pointer to device structure + * *attr - pointer to device attributes + * *buf - pointer to output buffer + ******************************************************************************/ +static ssize_t pt_ttdl_bist_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct pt_core_data *cd = dev_get_drvdata(dev); + ssize_t ret; + char *bus_err_str = NULL; + char *irq_err_str = NULL; + char *xres_err_str = NULL; + char *slave_bus_err_str = NULL; + char *slave_irq_err_str = NULL; + char *slave_xres_err_str = NULL; + u8 tests; + int rc = 0; + int num_tests = 0; + int status = 1; /* 0 = Pass, !0 = fail */ + u8 bus_toggled = 0x0F; /* default to untested */ + u8 i2c_toggled = 0x0F; /* default to untested */ + u8 spi_toggled = 0x0F; /* default to untested */ + u8 irq_toggled = 0x0F; /* default to untested */ + u8 xres_toggled = 0x0F; /* default to untested */ + u8 slave_bus_toggled = 0x0F; /* default to untested */ + u8 slave_irq_toggled = 0x0F; /* default to untested */ + u8 slave_xres_toggled = 0x0F; /* default to untested */ + + bus_err_str = kzalloc(PT_ERR_STR_SIZE, GFP_KERNEL); + irq_err_str = kzalloc(PT_ERR_STR_SIZE, GFP_KERNEL); + xres_err_str = kzalloc(PT_ERR_STR_SIZE, GFP_KERNEL); + if (!bus_err_str || !irq_err_str || !xres_err_str) + goto print_results; + + memset(xres_err_str, 0, PT_ERR_STR_SIZE); + memset(irq_err_str, 0, PT_ERR_STR_SIZE); + memset(bus_err_str, 0, PT_ERR_STR_SIZE); + + if (cd->multi_chip) { + slave_bus_err_str = kzalloc(PT_ERR_STR_SIZE, GFP_KERNEL); + slave_irq_err_str = kzalloc(PT_ERR_STR_SIZE, GFP_KERNEL); + slave_xres_err_str = kzalloc(PT_ERR_STR_SIZE, GFP_KERNEL); + if (!slave_bus_err_str || + !slave_irq_err_str || + !slave_xres_err_str) + goto print_results; + memset(slave_bus_err_str, 0, PT_ERR_STR_SIZE); + memset(slave_irq_err_str, 0, PT_ERR_STR_SIZE); + memset(slave_xres_err_str, 0, PT_ERR_STR_SIZE); + } + + /* Turn off the TTDL WD during the test */ + pt_stop_wd_timer(cd); + + /* Shorten default PIP cmd timeout while running BIST */ + cd->pip_cmd_timeout = 200; + + /* Count the number of tests to run */ + tests = cd->ttdl_bist_select; + while (tests) { + num_tests += tests & 1; + tests >>= 1; + } + pt_debug(dev, DL_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_XRES BIST TEST --------------- */ + if ((cd->ttdl_bist_select & PT_TTDL_BIST_TP_XRES_TEST) != 0) { + pt_debug(dev, DL_INFO, + "%s: ----- Start TP_XRES BIST -----", __func__); + rc = pt_bist_xres_test(dev, &bus_toggled, &irq_toggled, + &xres_toggled, xres_err_str); + /* Done if the rest of all nets toggled */ + if (bus_toggled == 1 && irq_toggled == 1 && xres_toggled == 1) + goto host_nets_complete; + } + + /* Flush bus in case a PIP response is waiting from previous test */ + pt_flush_bus(cd, PT_FLUSH_BUS_BASED_ON_LEN, NULL); + + /* --------------- IRQ BIST TEST --------------- */ + if ((cd->ttdl_bist_select & PT_TTDL_BIST_IRQ_TEST) != 0) { + pt_debug(dev, DL_INFO, + "%s: ----- Start IRQ BIST -----", __func__); + bus_toggled = 0xFF; + irq_toggled = 0xFF; + rc = pt_bist_irq_test(dev, &bus_toggled, &irq_toggled, + &xres_toggled, irq_err_str); + /* If this net failed clear results from previous net */ + if (irq_toggled != 1) { + xres_toggled = 0x0F; + memset(xres_err_str, 0, PT_ERR_STR_SIZE); + } + if (bus_toggled == 1 && irq_toggled == 1) + goto host_nets_complete; + } + + /* Flush bus in case a PIP response is waiting from previous test */ + pt_flush_bus(cd, PT_FLUSH_BUS_BASED_ON_LEN, NULL); + + /* --------------- BUS BIST TEST --------------- */ + if ((cd->ttdl_bist_select & PT_TTDL_BIST_BUS_TEST) != 0) { + pt_debug(dev, DL_INFO, + "%s: ----- Start BUS BIST -----", __func__); + bus_toggled = 0xFF; + rc = pt_bist_bus_test(dev, &bus_toggled, bus_err_str); + /* If this net failed clear results from previous net */ + if (bus_toggled == 0) { + irq_toggled = 0x0F; + memset(irq_err_str, 0, PT_ERR_STR_SIZE); + } + } + +host_nets_complete: + /* --------------- SLAVE XRES BIST TEST --------------- */ + if (cd->multi_chip && + (cd->ttdl_bist_select & PT_TTDL_BIST_SLAVE_XRES_TEST) != 0) { + pt_debug(dev, DL_INFO, + "%s: ----- Start Slave XRES BIST -----", __func__); + slave_xres_toggled = 0xFF; + rc = pt_bist_slave_xres_test(dev, &slave_irq_toggled, + &slave_bus_toggled, &slave_xres_toggled, + slave_xres_err_str); + if ((slave_bus_toggled == 1 && slave_irq_toggled == 1 && + slave_xres_toggled == 1) || slave_xres_toggled == 0) + goto print_results; + } + + /* --------------- SLAVE IRQ BIST TEST --------------- */ + if (cd->multi_chip && + (cd->ttdl_bist_select & PT_TTDL_BIST_SLAVE_IRQ_TEST) != 0) { + pt_debug(dev, DL_INFO, + "%s: ----- Start Slave IRQ BIST -----", __func__); + slave_irq_toggled = 0xFF; + rc = pt_bist_slave_irq_test(dev, &slave_irq_toggled, + &slave_bus_toggled, slave_irq_err_str, NULL, NULL); + pt_debug(dev, DL_INFO, "%s: slave_irq_toggled = 0x%02X\n", + __func__, slave_irq_toggled); + if (slave_irq_toggled == 1) { + slave_bus_toggled = 1; + goto print_results; + } + } + + /* --------------- SLAVE BUS BIST TEST --------------- */ + if (cd->multi_chip && + (cd->ttdl_bist_select & PT_TTDL_BIST_SLAVE_BUS_TEST) != 0) { + pt_debug(dev, DL_INFO, + "%s: ----- Start Slave BUS BIST -----", __func__); + slave_bus_toggled = 0xFF; + rc = pt_bist_slave_bus_test(dev, &slave_irq_toggled, + &slave_bus_toggled, slave_bus_err_str); + } + +print_results: + /* Restore PIP command timeout */ + cd->pip_cmd_timeout = cd->pip_cmd_timeout_default; + + /* + * We're done! - Perform a hard XRES toggle, allowing BL + * to load FW if there is any in Flash + */ + mutex_lock(&cd->system_lock); + cd->hid_reset_cmd_state = 0; + mutex_unlock(&cd->system_lock); + + pt_debug(dev, DL_INFO, + "%s: TTDL BIST Complete - Final reset\n", __func__); + + if (cd->flashless_dut) { + /* + * For Flashless solution, FW update is triggered after BL is + * seen that several miliseconds delay is needed. + */ + pt_debug(dev, DL_INFO, "%s Flashless Auto_BL - ALLOW\n", + __func__); + mutex_lock(&cd->system_lock); + cd->flashless_auto_bl = PT_ALLOW_AUTO_BL; + mutex_unlock(&cd->system_lock); + + /* Reset DUT and wait 100ms to see if loader started */ + pt_hw_hard_reset(cd); + msleep(100); + if (cd->fw_updating) { + pt_debug(dev, DL_INFO, + "%s: ----- BIST Wait FW Loading ----", + __func__); + rc = _pt_request_wait_for_enum_state( + dev, 4000, STARTUP_STATUS_COMPLETE); + } + } else { + if (cd->mode == PT_MODE_BOOTLOADER) { + rc = pt_pip2_launch_app(dev, PT_CORE_CMD_PROTECTED); + if (rc) { + pt_debug(dev, DL_ERROR, + "%s Failed to launch app\n", __func__); + rc = pt_hw_hard_reset(cd); + } + } + + /* + * If FW exists the BL may have just started or will start soon, + * so the FW sentinel may be on it's way but with no FW it will + * not arrive, wait for it before deciding if we need to queue + * an enum. + */ + rc = _pt_request_wait_for_enum_state( + dev, 400, STARTUP_STATUS_FW_RESET_SENTINEL); + if ((cd->startup_status & STARTUP_STATUS_FW_RESET_SENTINEL) && + (cd->startup_status & STARTUP_STATUS_COMPLETE) == 0) { + pt_debug(dev, DL_INFO, "%s: ----- BIST Enum ----", + __func__); + pt_queue_enum(cd); + rc = _pt_request_wait_for_enum_state( + dev, 2000, STARTUP_STATUS_COMPLETE); + } + } + msleep(20); + + /* --------------- PRINT OUT BIST RESULTS ---------------*/ + pt_debug(dev, DL_INFO, "%s: ----- BIST Print Results ----", __func__); + pt_start_wd_timer(cd); + + /* Canned print if any memory allocation issues */ + if (!bus_err_str || !irq_err_str || !xres_err_str) { + ret = sprintf(buf, + "Status: %d\n" + "I2C (SDA,SCL): [UNTEST]\n" + "SPI (MISO,MOSI,CS,CLK): [UNTEST]\n" + "IRQ connection: [UNTEST]\n" + "TP_XRES connection: [UNTEST]\n", -ENOMEM); + if (cd->multi_chip) { + ret += sprintf(buf + ret, + "I/P SPI (MISO,MOSI,CS,CLK): [UNTEST]\n" + "I/P IRQ connection: [UNTEST]\n" + "I/P TP_XRES connection: [UNTEST]\n"); + } + } else { + status = 0; + if (bus_toggled == 1) + memset(bus_err_str, 0, PT_ERR_STR_SIZE); + if (irq_toggled == 1) + memset(irq_err_str, 0, PT_ERR_STR_SIZE); + if (xres_toggled == 1) + memset(xres_err_str, 0, PT_ERR_STR_SIZE); + + if (cd->ttdl_bist_select & PT_TTDL_BIST_BUS_TEST) + status += bus_toggled; + if (cd->ttdl_bist_select & PT_TTDL_BIST_IRQ_TEST) + status += irq_toggled; + if (cd->ttdl_bist_select & PT_TTDL_BIST_TP_XRES_TEST) + status += xres_toggled; + pt_debug(dev, DL_WARN, "%s: status = %d (%d,%d,%d)\n", + __func__, status, bus_toggled, irq_toggled, + xres_toggled); + + if (cd->multi_chip) { + if (slave_irq_toggled == 1) + memset(slave_irq_err_str, 0, PT_ERR_STR_SIZE); + if (slave_xres_toggled == 1) + memset(slave_xres_err_str, 0, PT_ERR_STR_SIZE); + if (slave_bus_toggled == 1) + memset(slave_bus_err_str, 0, PT_ERR_STR_SIZE); + + if (cd->ttdl_bist_select & PT_TTDL_BIST_SLAVE_BUS_TEST) + status += slave_bus_toggled; + if (cd->ttdl_bist_select & PT_TTDL_BIST_SLAVE_IRQ_TEST) + status += slave_irq_toggled; + if (cd->ttdl_bist_select & PT_TTDL_BIST_SLAVE_XRES_TEST) + status += slave_xres_toggled; + pt_debug(dev, DL_WARN, + "%s: status = %d (%d,%d,%d,%d,%d,%d)\n", + __func__, status, bus_toggled, irq_toggled, + xres_toggled, slave_bus_toggled, + slave_irq_toggled, slave_xres_toggled); + } + + if (cd->bus_ops->bustype == BUS_I2C) + i2c_toggled = bus_toggled; + else + spi_toggled = bus_toggled; + + ret = sprintf(buf, + "Status: %d\n" + "I2C (SDA,SCL): %s %s\n" + "SPI (MISO,MOSI,CS,CLK): %s %s\n" + "IRQ connection: %s %s\n" + "TP_XRES connection: %s %s\n", + status == num_tests ? 0 : 1, + i2c_toggled == 0x0F ? "[UNTEST]" : + i2c_toggled == 1 ? "[ OK ]" : "[FAILED]", + i2c_toggled == 0x0F ? "" : bus_err_str, + spi_toggled == 0x0F ? "[UNTEST]" : + spi_toggled == 1 ? "[ OK ]" : "[FAILED]", + spi_toggled == 0x0F ? "" : bus_err_str, + irq_toggled == 0x0F ? "[UNTEST]" : + irq_toggled == 1 ? "[ OK ]" : "[FAILED]", + irq_err_str, + xres_toggled == 0x0F ? "[UNTEST]" : + xres_toggled == 1 ? "[ OK ]" : "[FAILED]", + xres_err_str); + + if (cd->multi_chip) { + ret += sprintf(buf + ret, + "I/P SPI (MISO,MOSI,CS,CLK): %s %s\n" + "I/P IRQ connection: %s %s\n" + "I/P TP_XRES connection: %s %s\n", + slave_bus_toggled == 0x0F ? "[UNTEST]" : + slave_bus_toggled == 1 ? "[ OK ]" : + "[FAILED]", slave_bus_err_str, + slave_irq_toggled == 0x0F ? "[UNTEST]" : + slave_irq_toggled == 1 ? "[ OK ]" : + "[FAILED]", slave_irq_err_str, + slave_xres_toggled == 0x0F ? "[UNTEST]" : + slave_xres_toggled == 1 ? "[ OK ]" : + "[FAILED]", slave_xres_err_str); + } + } + + /* Put TTDL back into a known state, issue a ttdl enum if needed */ + pt_debug(dev, DL_INFO, "%s: Startup_status = 0x%04X\n", + __func__, cd->startup_status); + kfree(bus_err_str); + kfree(irq_err_str); + kfree(xres_err_str); + return ret; +} + +/******************************************************************************* + * FUNCTION: pt_ttdl_bist_store + * + * SUMMARY: Store method for the ttdl_bist sysfs node. + * + * RETURN: Size of passed in buffer if successful + * + * PARAMETERS: + * *dev - pointer to device structure + * *attr - pointer to device attributes + * *buf - pointer to command buffer + * size - size of buf + ******************************************************************************/ +static ssize_t pt_ttdl_bist_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct pt_core_data *cd = dev_get_drvdata(dev); + u32 input_data[2] = {0}; + int length; + int rc = 0; + + /* Maximum input of one value */ + length = _pt_ic_parse_input(dev, buf, size, input_data, + ARRAY_SIZE(input_data)); + if (length != 1) { + pt_debug(dev, DL_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 = sprintf(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 = sprintf(buf, "Status: %d\n", rc); + return ret; + } + ret = sprintf(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 = sprintf(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 = sprintf(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, + &dev_attr_pip2_cmd_rsp.attr, + &dev_attr_command.attr, + &dev_attr_drv_debug.attr, + &dev_attr_hw_reset.attr, + &dev_attr_response.attr, + &dev_attr_ttdl_restart.attr, +#ifdef TTDL_DIAGNOSTICS + &dev_attr_ttdl_status.attr, + &dev_attr_pip2_enter_bl.attr, + &dev_attr_pip2_exit_bl.attr, + &dev_attr_err_gpio.attr, + &dev_attr_flush_bus.attr, + &dev_attr_ttdl_bist.attr, +#endif /* TTDL_DIAGNOSTICS */ + NULL, +}; + +static struct attribute_group early_attr_group = { + .attrs = early_attrs, +}; + +static struct device_attribute pip2_attributes[] = { + __ATTR(pip2_version, 0444, pt_pip2_version_show, NULL), + __ATTR(pip2_gpio_read, 0444, pt_pip2_gpio_read_show, NULL), +#ifdef TTDL_DIAGNOSTICS + __ATTR(pip2_bin_hdr, 0444, pt_pip2_bin_hdr_show, NULL), + __ATTR(pip2_ping_test, 0644, pt_pip2_ping_test_show, + pt_pip2_ping_test_store), +#endif +}; + +static struct device_attribute attributes[] = { + __ATTR(dut_debug, 0644, + pt_dut_debug_show, pt_drv_debug_store), + __ATTR(sleep_status, 0444, pt_sleep_status_show, NULL), + __ATTR(panel_id, 0444, pt_panel_id_show, NULL), + __ATTR(get_param, 0644, + pt_get_param_show, pt_get_param_store), +#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); +} + + +/******************************************************************************* + ******************************************************************************* + * 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; + u8 pip_ver_major; + u8 pip_ver_minor; + u32 status = STARTUP_STATUS_START; + + if (!pdata || !pdata->core_pdata || !pdata->mt_pdata) { + pt_debug(dev, DL_ERROR, "%s: Missing platform data\n", + __func__); + rc = -ENODEV; + goto error_no_pdata; + } + + if (pdata->core_pdata->flags & PT_CORE_FLAG_POWEROFF_ON_SLEEP) { + if (!pdata->core_pdata->power) { + pt_debug(dev, DL_ERROR, + "%s: Missing platform data function\n", + __func__); + rc = -ENODEV; + goto error_no_pdata; + } + } + + /* get context and debug print buffers */ + cd = kzalloc(sizeof(*cd), GFP_KERNEL); + if (!cd) { + rc = -ENOMEM; + goto error_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->multi_chip = PT_FEATURE_DISABLE; + cd->tthe_hid_usb_format = PT_FEATURE_DISABLE; + + if (cd->cpdata->config_dut_generation == CONFIG_DUT_PIP2_CAPABLE) { + cd->set_dut_generation = true; + cd->active_dut_generation = DUT_PIP2_CAPABLE; + } else if (cd->cpdata->config_dut_generation == CONFIG_DUT_PIP1_ONLY) { + cd->set_dut_generation = true; + cd->active_dut_generation = DUT_PIP1_ONLY; + } else { + cd->set_dut_generation = false; + cd->active_dut_generation = DUT_UNKNOWN; + } + + /* Initialize with platform data */ + cd->watchdog_force_stop = cd->cpdata->watchdog_force_stop; + cd->watchdog_interval = PT_WATCHDOG_TIMEOUT; + cd->hid_cmd_state = 1; + cd->fw_updating = false; + cd->multi_chip = 0; + +#ifdef TTDL_DIAGNOSTICS + cd->t_refresh_active = 0; + cd->t_refresh_count = 0; + cd->pip2_crc_error_count = 0; + cd->wd_xres_count = 0; + cd->bl_retry_packet_count = 0; + cd->file_erase_timeout_count = 0; + cd->show_tt_data = false; + cd->flush_bus_type = PT_FLUSH_BUS_BASED_ON_LEN; + cd->err_gpio = 0; + cd->err_gpio_type = PT_ERR_GPIO_NONE; + cd->ttdl_bist_select = 0x07; + cd->force_pip2_seq = 0; +#endif /* TTDL_DIAGNOSTICS */ + + + memset(cd->pip2_us_file_path, 0, PT_MAX_PATH_SIZE); + memcpy(cd->pip2_us_file_path, PT_PIP2_BIN_FILE_PATH, + sizeof(PT_PIP2_BIN_FILE_PATH)); + + pt_init_hid_descriptor(&cd->hid_desc); + /* Read and store the descriptor lengths */ + cd->hid_core.hid_report_desc_len = + le16_to_cpu(cd->hid_desc.report_desc_len); + cd->hid_core.hid_max_input_len = + le16_to_cpu(cd->hid_desc.max_input_len); + cd->hid_core.hid_max_output_len = + le16_to_cpu(cd->hid_desc.max_output_len); + + /* Initialize mutexes and spinlocks */ + mutex_init(&cd->module_list_lock); + mutex_init(&cd->system_lock); + mutex_init(&cd->sysfs_lock); + mutex_init(&cd->ttdl_restart_lock); + mutex_init(&cd->firmware_class_lock); + spin_lock_init(&cd->spinlock); + + /* Initialize module list */ + INIT_LIST_HEAD(&cd->module_list); + + /* Initialize attention lists */ + for (type = 0; type < PT_ATTEN_NUM_ATTEN; type++) + INIT_LIST_HEAD(&cd->atten_list[type]); + + /* Initialize parameter list */ + INIT_LIST_HEAD(&cd->param_list); + + /* Initialize wait queue */ + init_waitqueue_head(&cd->wait_q); + + /* 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); + /* PtSBC builds will call this function in pt_probe_complete() */ + pt_add_core(dev); + + rc = sysfs_create_group(&dev->kobj, &early_attr_group); + if (rc) + pt_debug(cd->dev, DL_WARN, "%s:create early attrs failed\n", + __func__); + + /* + * 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); + } + + + /* 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 */ + + 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__); + } + + 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); + /* For PtSBC don't error out, allow TTDL to stay up */ + goto error_after_startup; + } + + /* Suspend scanning until probe is complete to avoid asyc touches */ + pt_pip_suspend_scanning_(cd); + + if (cd->hw_detected) { + pt_debug(dev, DL_INFO, "%s: Add sysfs interfaces\n", + __func__); + rc = add_sysfs_interfaces(dev); + if (rc < 0) { + pt_debug(dev, DL_ERROR, + "%s: Error, fail sysfs init\n", __func__); + goto error_after_startup; + } + } else { + pt_debug(dev, DL_WARN, + "%s: No HW detected, sysfs interfaces not added\n", + __func__); + } + +skip_enum: + pm_runtime_put_sync(dev); + + pt_debug(dev, DL_INFO, "%s: Probe: MT, BTN\n", __func__); + rc = pt_mt_probe(dev); + if (rc < 0) { + pt_debug(dev, DL_ERROR, "%s: Error, fail mt probe\n", + __func__); + goto error_after_sysfs_create; + } + + rc = pt_btn_probe(dev); + if (rc < 0) { + pt_debug(dev, DL_ERROR, "%s: Error, fail btn probe\n", + __func__); + goto error_after_startup_mt; + } + + pt_probe_modules(cd); + +#ifdef CONFIG_HAS_EARLYSUSPEND + pt_setup_early_suspend(cd); +#elif defined(CONFIG_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); + remove_sysfs_interfaces(dev); + +error_after_startup: + pr_err("%s PARADE error_after_startup\n", __func__); + del_timer(&cd->watchdog_timer); + if (cd->cpdata->setup_irq) + cd->cpdata->setup_irq(cd->cpdata, PT_MT_IRQ_FREE, dev); +error_setup_irq: +error_detect: + if (cd->cpdata->init) + cd->cpdata->init(cd->cpdata, PT_MT_POWER_OFF, dev); + if (cd->cpdata->setup_power) + cd->cpdata->setup_power(cd->cpdata, PT_MT_POWER_OFF, dev); + sysfs_remove_group(&dev->kobj, &early_attr_group); + pt_del_core(dev); + dev_set_drvdata(dev, NULL); + kfree(cd); +error_alloc_data: +error_no_pdata: + 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); + 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); + + 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); + + remove_sysfs_interfaces(dev); + + disable_irq_nosync(cd->irq); + if (cd->cpdata->setup_irq) + cd->cpdata->setup_irq(cd->cpdata, PT_MT_IRQ_FREE, dev); + if (cd->cpdata->init) + cd->cpdata->init(cd->cpdata, PT_MT_POWER_OFF, dev); + if (cd->cpdata->setup_power) + cd->cpdata->setup_power(cd->cpdata, PT_MT_POWER_OFF, dev); + dev_set_drvdata(dev, NULL); + pt_del_core(dev); + pt_free_si_ptrs(cd); + kfree(cd); + return 0; +} +EXPORT_SYMBOL_GPL(pt_release); + + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Parade TrueTouch(R) Standard Product Core Driver"); +MODULE_AUTHOR("Parade Technologies "); diff --git a/drivers/input/touchscreen/pt_debug.c b/drivers/input/touchscreen/pt_debug.c new file mode 100755 index 000000000000..b6c90254a49f --- /dev/null +++ b/drivers/input/touchscreen/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 + */ + +#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 funtion 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 "); diff --git a/drivers/input/touchscreen/pt_device_access.c b/drivers/input/touchscreen/pt_device_access.c new file mode 100755 index 000000000000..baa0d264823c --- /dev/null +++ b/drivers/input/touchscreen/pt_device_access.c @@ -0,0 +1,6733 @@ +/* + * 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 + */ + +#include "pt_regs.h" +#include + +#include +#include +#include + +#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 (22) + +/* 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 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 { + 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 order in test_case_field_array */ +enum case_order { + 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[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, bool 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 result *result) +{ + struct rtc_time tm; + char time_buf[100] = {0}; + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 0, 0)) + struct timespec ts; + + getnstimeofday(&ts); + rtc_time_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); + 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, 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_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_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] = { + {"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 ouput 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, bool 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; + + /* + * 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) { + 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 ouput + * 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_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(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 ouput + * 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 ouput 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: Funtion 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 "); diff --git a/drivers/input/touchscreen/pt_devtree.c b/drivers/input/touchscreen/pt_devtree.c new file mode 100755 index 000000000000..873d67b90abe --- /dev/null +++ b/drivers/input/touchscreen/pt_devtree.c @@ -0,0 +1,1069 @@ +/* + * 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 + */ + +#include +#include +#include +#include +#include "pt_regs.h" +#include + +#define MAX_NAME_LENGTH 64 + +static bool is_create_and_get_pdata; + +enum pt_device_type { + DEVICE_MT, + DEVICE_BTN, + 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_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_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_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_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 "); diff --git a/drivers/input/touchscreen/pt_i2c.c b/drivers/input/touchscreen/pt_i2c.c new file mode 100755 index 000000000000..1a7b02a1a68c --- /dev/null +++ b/drivers/input/touchscreen/pt_i2c.c @@ -0,0 +1,306 @@ +/* + * 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 + */ + +#include "pt_regs.h" + +#include +#include + +#define PT_I2C_DATA_SIZE (2 * 256) + + +/******************************************************************************* + * 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) +{ + struct i2c_client *client = to_i2c_client(dev); + int rc; + int read_size = size; + + if (!buf || !size || size > PT_I2C_DATA_SIZE) + return -EINVAL; + + rc = i2c_master_recv(client, buf, read_size); + + 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; + + 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; + + rc = i2c_master_recv(client, buf, size); + 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 + ******************************************************************************/ +static int pt_i2c_write_read_specific(struct device *dev, u16 write_len, + u8 *write_buf, u8 *read_buf) +{ + struct i2c_client *client = to_i2c_client(dev); + 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; + rc = i2c_transfer(client->adapter, msgs, msg_count); + + if (rc < 0 || rc != msg_count) + return (rc < 0) ? rc : -EIO; + + rc = 0; + + if (read_buf) { + 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 + + + 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 + + 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 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 "); diff --git a/drivers/input/touchscreen/pt_loader.c b/drivers/input/touchscreen/pt_loader.c new file mode 100755 index 000000000000..d139f7ce63e5 --- /dev/null +++ b/drivers/input/touchscreen/pt_loader.c @@ -0,0 +1,5433 @@ +/* + * 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 + */ + +#include "pt_regs.h" +#include + +#define PT_LOADER_NAME "pt_loader" + + +#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 update_fw_status; +static int pip2_erase_status; +static int pip2_erase_rc; + +/* PIP2 update fw status codes. 1-99% are "active" */ +enum UPDATE_FW_STATUS { + UPDATE_FW_IDLE = 0, + 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_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_UNDEFINED_ERROR = 255, +}; + +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) +{ + u16 length = row_image->row_size + 3; + u8 data[3 + row_image->row_size]; + u8 offset = 0; + + 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); + return cmd->nonhid_cmd->prog_and_verify(dev, 0, length, data); +} + +/******************************************************************************* + * 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); +} + +#define PT_NO_INC 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, if 0 then ignore. + * inc - Value to increment, if 0 then ignore. + ******************************************************************************/ +static u8 _pt_pip2_update_bl_status(struct device *dev, u8 value, u8 inc) +{ + pt_debug(dev, DL_DEBUG, + "%s: fw_status = %d, request val=%d, request inc=%d\n", + __func__, update_fw_status, value, inc); + + /* Reset status if both value and inc are 0 */ + if (value == UPDATE_FW_IDLE && inc == 0) { + update_fw_status = value; + pt_debug(dev, DL_WARN, "%s: ATM - Reset BL Status to %d\n", + __func__, value); + return update_fw_status; + } + + /* Set to value if valid */ + if (value > UPDATE_FW_IDLE && value < UPDATE_FW_UNDEFINED_ERROR) { + if (value <= UPDATE_FW_COMPLETE && value > update_fw_status) { + update_fw_status = value; + } else if (value >= UPDATE_FW_GENERAL_ERROR) { + update_fw_status = value; + pt_debug(dev, DL_WARN, + "%s: BL Status set to error code %d\n", + __func__, value); + } + return update_fw_status; + } + + if (inc > 0 && update_fw_status + inc <= UPDATE_FW_COMPLETE) + update_fw_status += inc; + else + pt_debug(dev, DL_ERROR, + "%s: Inc Request out of bounds: status=%d inc=%d\n", + __func__, update_fw_status, inc); + return update_fw_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_pip2_update_bl_status(dev, UPDATE_FW_IDLE, PT_NO_INC); + + 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_pip2_update_bl_status(dev, UPDATE_FW_NO_PLATFORM_DATA, + PT_NO_INC); + 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_pip2_update_bl_status(dev, UPDATE_FW_NO_FW_PROVIDED, + PT_NO_INC); + pt_debug(dev, DL_ERROR, + "%s: No platform firmware\n", __func__); + return rc; + } + + if (!fw->ver || !fw->vsize) { + _pt_pip2_update_bl_status(dev, UPDATE_FW_INVALID_FW_IMAGE, + PT_NO_INC); + 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, &update_fw_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_pip2_update_bl_status(dev, UPDATE_FW_IDLE, PT_NO_INC); + + fw_img = kzalloc(PT_PIP2_MAX_FILE_SIZE, GFP_KERNEL); + if (!fw_img) { + _pt_pip2_update_bl_status(dev, UPDATE_FW_SYSTEM_NOMEM, + PT_NO_INC); + 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_pip2_update_bl_status(dev, UPDATE_FW_NO_FW_PROVIDED, + PT_NO_INC); + goto exit; + } + + if (!fw_img || !fw_size) { + pt_debug(dev, DL_ERROR, + "%s: No firmware received\n", __func__); + _pt_pip2_update_bl_status(dev, UPDATE_FW_INVALID_FW_IMAGE, + PT_NO_INC); + 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_pip2_update_bl_status(dev, UPDATE_FW_INVALID_FW_IMAGE, + PT_NO_INC); + goto exit; + } + + pt_upgrade_firmware(dev, &(fw_img[header_size + 1]), + fw_size - (header_size + 1), &update_fw_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_pip2_update_bl_status(dev, UPDATE_FW_IDLE, PT_NO_INC); + if (!fw) { + pt_debug(dev, DL_ERROR, "%s: No firmware\n", __func__); + _pt_pip2_update_bl_status(dev, UPDATE_FW_NO_FW_PROVIDED, + PT_NO_INC); + goto pt_firmware_cont_exit; + } + + if (!fw->data || !fw->size) { + pt_debug(dev, DL_ERROR, + "%s: No firmware received\n", __func__); + _pt_pip2_update_bl_status(dev, UPDATE_FW_INVALID_FW_IMAGE, + PT_NO_INC); + 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_pip2_update_bl_status(dev, UPDATE_FW_INVALID_FW_IMAGE, + PT_NO_INC); + goto pt_firmware_cont_release_exit; + } + + pt_upgrade_firmware(dev, &(fw->data[header_size + 1]), + fw->size - (header_size + 1), &update_fw_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 */ + +#if PT_TTCONFIG_UPGRADE +/******************************************************************************* + * FUNCTION: pt_write_config_row_ + * + * SUMMARY: Alow 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 followings: + * 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 */ + +#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_pip2_update_bl_status(dev, UPDATE_FW_IDLE, PT_NO_INC); + 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; +} + +static DEVICE_ATTR(manual_upgrade, 0200, NULL, pt_manual_upgrade_store); +#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 druing 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_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_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: _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) +{ + u8 read_buf[PT_MAX_PIP2_MSG_SIZE]; + u8 buf[PT_MAX_PIP2_MSG_SIZE]; + u8 *fw_img = NULL; + u16 write_len; + u8 mode = PT_MODE_UNKNOWN; + u8 retry_packet = 0; + u8 us_fw_used = 0; + u16 actual_read_len; + u16 status = 0; + u16 packet_size; + int fw_size = 0; + int remain_bytes; + int ret = 0; + int percent_cmplt; + int t; + int erase_status; + u32 max_file_size; + bool wait_for_calibration_complete = false; + PIP2_SEND_CMD pip2_send_cmd; + struct device *dev = context; + struct pt_loader_data *ld = pt_get_loader_data(dev); + struct pip2_loader_data *pip2_data = ld->pip2_data; + struct pt_core_data *cd = dev_get_drvdata(dev); + + pt_debug(dev, DL_WARN, "%s: ATM - Begin BL\n", __func__); + _pt_pip2_update_bl_status(dev, UPDATE_FW_IDLE, PT_NO_INC); + + 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; + + 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__); + update_fw_status = UPDATE_FW_EXCLUSIVE_ACCESS_ERROR; + goto exit; + } + _pt_pip2_update_bl_status(dev, 0, 1); + + if (!fw) { + if (ld->pip2_load_builtin) { + pt_debug(dev, DL_ERROR, + "%s: No builtin firmware\n", __func__); + ld->builtin_bin_fw_status = -EINVAL; + pt_debug(dev, DL_ERROR, "%s: Exit BL\n", __func__); + _pt_pip2_update_bl_status(dev, + UPDATE_FW_NO_FW_PROVIDED, PT_NO_INC); + goto exit; + } else { + fw_img = kzalloc(PT_PIP2_MAX_FILE_SIZE, GFP_KERNEL); + us_fw_used = 1; + 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_pip2_update_bl_status(dev, + UPDATE_FW_NO_FW_PROVIDED, PT_NO_INC); + goto exit; + } + } + } else { + fw_img = (u8 *)&(fw->data[0]); + fw_size = fw->size; + } + + if (!fw_img || !fw_size) { + pt_debug(dev, DL_ERROR, + "%s: Invalid fw or file size=%d\n", __func__, + (int)fw_size); + _pt_pip2_update_bl_status(dev, UPDATE_FW_INVALID_FW_IMAGE, + PT_NO_INC); + 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_pip2_update_bl_status(dev, + UPDATE_FW_INVALID_FW_IMAGE, PT_NO_INC); + goto exit; + } + } + + cd->fw_updating = true; + wake_up(&cd->wait_q); + + _pt_pip2_update_bl_status(dev, 0, 1); + pt_debug(dev, DL_INFO, + "%s: Found file of size: %d bytes\n", __func__, (int)fw_size); + pm_runtime_get_sync(dev); + _pt_pip2_update_bl_status(dev, 0, 1); + + /* Wait for completion of FW upgrade thread before continuing */ + if (!ld->pip2_load_builtin) + init_completion(&pip2_data->pip2_fw_upgrade_complete); + + _pt_pip2_update_bl_status(dev, 0, 1); + + cmd->request_stop_wd(dev); + + if (cd->flashless_dut) { + 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_pip2_update_bl_status(dev, UPDATE_FW_ENTER_BL_ERROR, + PT_NO_INC); + goto exit; + } + _pt_pip2_update_bl_status(dev, 0, 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) { + _pt_pip2_update_bl_status(dev, 0, 1); + } else { + _pt_pip2_update_bl_status(dev, + UPDATE_FW_VERSION_ERROR, PT_NO_INC); + goto exit; + } + } + + pt_debug(dev, DL_INFO, "%s OPEN File %d for write\n", + __func__, ld->pip2_load_file_no); + ret = cmd->nonhid_cmd->pip2_file_open(dev, ld->pip2_load_file_no); + if (ret < 0) { + pt_debug(dev, DL_ERROR, "%s Open file %d failed\n", + __func__, ld->pip2_load_file_no); + _pt_pip2_update_bl_status(dev, UPDATE_FW_FILE_OPEN_ERROR, + PT_NO_INC); + goto exit; + } + pip2_data->pip2_file_handle = ret; + _pt_pip2_update_bl_status(dev, 0, 1); + + /* Regarding to TC3315, the size of RAM_FILE is less than fw image */ + if (ld->pip2_load_file_no != PIP2_RAM_FILE) { + ret = cmd->nonhid_cmd->pip2_file_get_stats(dev, + ld->pip2_load_file_no, 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_pip2_update_bl_status(dev, + UPDATE_FW_INVALID_FW_IMAGE, PT_NO_INC); + goto exit_close_file; + } +#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, &erase_status); + if (ret < 0) { + pt_debug(dev, DL_ERROR, + "%s: File erase failed rc=%d status=%d\n", + __func__, ret, erase_status); + _pt_pip2_update_bl_status(dev, + UPDATE_FW_ERASE_ERROR, PT_NO_INC); + goto exit_close_file; + } + _pt_pip2_update_bl_status(dev, 0, 5); + } + + remain_bytes = fw_size; + buf[0] = pip2_data->pip2_file_handle; + 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) { + pip2_send_cmd = cmd->nonhid_cmd->pip2_send_cmd_no_int; + disable_irq_nosync(cd->irq); + } else + pip2_send_cmd = cmd->nonhid_cmd->pip2_send_cmd; + + /* Continue writing while data remains */ + while (remain_bytes > packet_size) { + write_len = packet_size; + + /* Don't update BL status on every pass */ + if (remain_bytes % 2000 < packet_size) { + /* Calculate % complete for update_fw_status sysfs */ + percent_cmplt = (fw_size - remain_bytes) * + 100 / fw_size; + if (percent_cmplt > 0 && + percent_cmplt > UPDATE_FW_ACTIVE_90) + percent_cmplt = UPDATE_FW_ACTIVE_90; + _pt_pip2_update_bl_status(dev, percent_cmplt, + PT_NO_INC); + +#ifdef TTDL_DIAGNOSTICS + pt_debug(dev, DL_INFO, + "Wrote %d bytes with %d bytes remaining\n", + fw_size - remain_bytes - write_len, + remain_bytes); +#endif + } + if (retry_packet > 0) { +#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); + } + + memcpy(&buf[1], fw_img, 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); + status = read_buf[PIP2_RESP_STATUS_OFFSET]; + + /* Write cmd successful with a fail status */ + if (!ret && 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 ((ld->pip2_load_file_no == PIP2_RAM_FILE) && + (status == PIP2_RSP_ERR_END_OF_FILE) && + (remain_bytes == write_len)) { + pt_debug(dev, DL_WARN, + "%s Last write, ret = 0x%02x\n", + __func__, status); + /* Drop out of the while loop */ + break; + } + _pt_pip2_update_bl_status(dev, + UPDATE_FW_WRITE_ERROR, PT_NO_INC); + pt_debug(dev, DL_ERROR, + "%s file write failure, status = 0x%02x\n", + __func__, status); + if (retry_packet >= 3) { + /* Tripple retry error - break */ + remain_bytes = 0; + pt_debug(dev, DL_ERROR, + "%s %d - Packet status error - Break\n", + __func__, status); + } else { + retry_packet++; + } + } else if (ret) { + /* Packet write failed - retry 3x */ + if (retry_packet >= 3) { + remain_bytes = 0; + pt_debug(dev, DL_ERROR, + "%s %d - Packet cmd error - Break\n", + __func__, ret); + } + retry_packet++; + } else { + /* Cmd success and status success */ + retry_packet = 0; + } + + if (retry_packet == 0) { + fw_img += write_len; + remain_bytes -= write_len; + } + } + /* Write the remaining bytes if any remain */ + if (remain_bytes > 0 && retry_packet == 0) { + pt_debug(dev, DL_INFO, + "Write last %d bytes to File = 0x%02x\n", + remain_bytes, pip2_data->pip2_file_handle); + memcpy(&buf[1], fw_img, remain_bytes); + while (remain_bytes > 0 && retry_packet <= 3) { + ret = pip2_send_cmd(dev, PT_CORE_CMD_UNPROTECTED, + PIP2_CMD_ID_FILE_WRITE, buf, remain_bytes + 1, + read_buf, &actual_read_len); + status = read_buf[PIP2_RESP_STATUS_OFFSET]; + if (ret || status) { + /* + * Any non zero status when writing to FLASH is + * an error. Only when writing to RAM, the last + * packet must respond with an EOF status + */ + if ((ld->pip2_load_file_no >= PIP2_FW_FILE) || + (ld->pip2_load_file_no == PIP2_RAM_FILE && + status != PIP2_RSP_ERR_END_OF_FILE)) { + _pt_pip2_update_bl_status(dev, + UPDATE_FW_WRITE_ERROR, + PT_NO_INC); + pt_debug(dev, DL_ERROR, + "%s Write Fail-status=0x%02x\n", + __func__, status); + retry_packet++; + } else if (ld->pip2_load_file_no == + PIP2_RAM_FILE && + status == PIP2_RSP_ERR_END_OF_FILE) { + /* Special case EOF writing to SRAM */ + remain_bytes = 0; + status = 0; + } + } else { + remain_bytes = 0; + } + } + } + + if (cd->bl_with_no_int) + enable_irq(cd->irq); + + if (remain_bytes == 0 && retry_packet == 0) + _pt_pip2_update_bl_status(dev, UPDATE_FW_ACTIVE_99, PT_NO_INC); + + if (retry_packet >= 3) { + /* A packet write failure occurred 3x */ + pt_debug(dev, DL_ERROR, + "%s: BL terminated due to consecutive write errors\n", + __func__); + _pt_pip2_update_bl_status(dev, UPDATE_FW_WRITE_ERROR, + PT_NO_INC); + } else if (status) { + ret = status; + pt_debug(dev, DL_ERROR, + "%s: File write failed with status=%d\n", + __func__, status); + _pt_pip2_update_bl_status(dev, UPDATE_FW_WRITE_ERROR, + PT_NO_INC); + } else + pt_debug(dev, DL_INFO, + "%s: BIN file write finished successfully\n", __func__); + + ret = cmd->nonhid_cmd->pip2_file_close(dev, ld->pip2_load_file_no); + if (ret != ld->pip2_load_file_no) { + pt_debug(dev, DL_ERROR, + "%s file close failure\n", __func__); + _pt_pip2_update_bl_status(dev, UPDATE_FW_FILE_CLOSE_ERROR, + PT_NO_INC); + goto exit; + } + + /* When updating non FW files, stay in BL */ + if (ld->pip2_load_file_no >= PIP2_CONFIG_FILE) + goto exit; + + if ((ld->pip2_load_file_no == PIP2_RAM_FILE) && + (update_fw_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 = cmd->nonhid_cmd->pip2_send_cmd(dev, + PT_CORE_CMD_UNPROTECTED, PIP2_CMD_ID_EXECUTE, + NULL, 0, read_buf, &actual_read_len); + status = read_buf[PIP2_RESP_STATUS_OFFSET]; + if (ret || status) { + pt_debug(dev, DL_ERROR, + "%s Execute command failure\n", __func__); + _pt_pip2_update_bl_status(dev, + UPDATE_FW_EXECUTE_ERROR, PT_NO_INC); + goto exit; + } + } else if (ld->pip2_load_file_no == PIP2_FW_FILE && + update_fw_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 occured simply close the file and exit */ + if (update_fw_status > UPDATE_FW_COMPLETE) + goto exit_close_file; + + /* 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_pip2_update_bl_status(dev, UPDATE_FW_MODE_ERROR, + PT_NO_INC); + 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_pip2_update_bl_status(dev, UPDATE_FW_SENTINEL_NOT_SEEN, + PT_NO_INC); + 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__); + goto exit; + +exit_close_file: + ret = cmd->nonhid_cmd->pip2_file_close(dev, ld->pip2_load_file_no); + if (ret < 0) { + pt_debug(dev, DL_ERROR, + "%s file close failure\n", __func__); + _pt_pip2_update_bl_status(dev, UPDATE_FW_FILE_CLOSE_ERROR, + PT_NO_INC); + } + +exit: + cd->fw_updating = false; + if (us_fw_used) + kfree(fw_img); + if (ld->pip2_load_file_no > PIP2_FW_FILE) + goto exit_staying_in_bl; + + cmd->release_exclusive(dev); + + pm_runtime_put_sync(dev); + if (fw) + release_firmware(fw); + + /* + * For built-in FW update, it should not warn builtin_bin_fw_status + * for each bootup since update_fw_status would be + * UPDATE_FW_VERSION_ERROR in most situations because firmware + * should have been up to date. + */ + if (ld->pip2_load_builtin) { + if ((update_fw_status == UPDATE_FW_ACTIVE_99) || + (update_fw_status == UPDATE_FW_VERSION_ERROR)) + ld->builtin_bin_fw_status = 0; + else + ld->builtin_bin_fw_status = -EINVAL; + } + + if ((update_fw_status == UPDATE_FW_ACTIVE_99) || + (update_fw_status == UPDATE_FW_VERSION_ERROR)) { + pt_debug(dev, DL_WARN, "%s: Queue ENUM\n", __func__); + cmd->request_enum(dev, true); + } + if (update_fw_status < UPDATE_FW_COMPLETE) + _pt_pip2_update_bl_status(dev, UPDATE_FW_COMPLETE, PT_NO_INC); + + if (wait_for_calibration_complete) + wait_for_completion(&ld->calibration_complete); + + pt_debug(dev, DL_INFO, "%s: Starting watchdog\n", __func__); + ret = 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; + + return; + +exit_staying_in_bl: + if (us_fw_used) + kfree(fw_img); + /* When updating a non FW file, a restart is not wanted. Stay in BL */ + _pt_pip2_update_bl_status(dev, UPDATE_FW_COMPLETE, PT_NO_INC); + cmd->release_exclusive(dev); + pm_runtime_put_sync(dev); + if (fw) + release_firmware(fw); +} + +#define PIP2_MAX_FILE_NAMES 3 +/******************************************************************************* + * 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_loader_data *ld = pt_get_loader_data(dev); + struct pt_core_data *cd = dev_get_drvdata(dev); + const struct firmware *fw_entry = NULL; + int retval; + int rc = 0; + int read_size = 16; + u8 image[16]; + int index = 0; + int file_count = 0; + char *filename[PIP2_MAX_FILE_NAMES]; + + /* + * 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; + } + + 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; + if (cd->pip2_us_file_path[0] == '\0') { + ld->pip2_load_builtin = true; + pt_debug(dev, DL_WARN, + "%s: US Path not defined, BL from built-in\n", + __func__); + } else { + /* 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) { + ld->pip2_load_builtin = false; + pt_debug(dev, DL_WARN, + "%s: %s Found, BL from US\n", + __func__, cd->pip2_us_file_path); + goto ready; + } else { + ld->pip2_load_builtin = true; + pt_debug(dev, DL_WARN, + "%s: ATM - %s NOT Found, BL from built-in\n", + __func__, cd->pip2_us_file_path); + } + } + } else { + ld->pip2_load_file_no = PIP2_FW_FILE; + ld->pip2_load_builtin = true; + } + + /* Look for any FW file name match and request the FW */ + 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 (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: ATM - Fail request FW %s load\n", + __func__, filename[index]); + } else { + pt_debug(dev, DL_INFO, "%s: FW %s class file loading\n", + __func__, filename[index]); + break; + } + index++; + } + + /* No matching file names found */ + if (index == file_count) { + pt_debug(dev, DL_WARN, "%s: No FW is found\n", __func__); + goto exit; + } + +ready: + _pt_pip2_firmware_cont(fw_entry, dev); + retval = ld->builtin_bin_fw_status; +exit: + mutex_unlock(&cd->firmware_class_lock); + index = 0; + while (index < file_count) + kfree(filename[index++]); + + return retval; +} + +/******************************************************************************* + * FUNCTION: pt_pip2_create_fw_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_create_fw_class(struct pip2_loader_data *pip2_data) +{ + + int ret = 0; + struct device *dev = pip2_data->dev; + 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_firmware_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; + const struct firmware *fw_entry = NULL; + + _pt_pip2_firmware_cont(fw_entry, 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; + const struct firmware *fw_entry = NULL; + u8 dut_gen = cmd->request_dut_generation(dev); + + if (dut_gen == DUT_PIP2_CAPABLE) + _pt_pip2_firmware_cont(fw_entry, 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]; + + 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 { + 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]; + + 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) { + 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 { + 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_RAM_FILE) || (file_no > PIP2_FILE_MAX))) { + 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; + } + + _pt_pip2_update_bl_status(dev, UPDATE_FW_IDLE, PT_NO_INC); + if (ld->is_manual_upgrade_enabled) { + pt_debug(dev, DL_ERROR, + "%s: ERROR - Manual upgrade busy\n", __func__); + rc = -EBUSY; + goto exit; + } + 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_create_fw_class(ld->pip2_data); + 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; +} +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; + } + + _pt_pip2_update_bl_status(dev, UPDATE_FW_IDLE, PT_NO_INC); + if (ld->is_manual_upgrade_enabled) { + pt_debug(dev, DL_ERROR, + "%s: ERROR - Manual upgrade busy\n", __func__); + rc = -EBUSY; + goto exit; + } + + 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_create_fw_class(ld->pip2_data); + 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_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; + } + + 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_create_fw_class(ld->pip2_data); + 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); + +/******************************************************************************* + * 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 */ + +#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_7) { + 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; + 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; + } + + file_handle = cmd->nonhid_cmd->pip2_file_erase(dev, file, + &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_pip2_bl_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_pip2_bl_status_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t ret; + u8 status = update_fw_status; + + if (update_fw_status <= UPDATE_FW_COMPLETE) { + pt_debug(dev, DL_DEBUG, + "%s BL_STATUS = %d\n", __func__, update_fw_status); + return sprintf(buf, "%d\n", update_fw_status); + } + + switch (status) { + case UPDATE_FW_GENERAL_ERROR: + ret = sprintf(buf, + "ERROR: %d - General programming failure\n", status); + break; + case UPDATE_FW_PIP_VERSION_ERROR: + ret = sprintf(buf, + "ERROR: %d - Wrong PIP version detected\n", status); + break; + case UPDATE_FW_VERSION_ERROR: + ret = sprintf(buf, + "ERROR: %d - FW vervion newer than bin file\n", status); + break; + case UPDATE_FW_ERASE_ERROR: + ret = sprintf(buf, + "ERROR: %d - ROM BL failed to erase FW file in FLASH\n", + status); + break; + case UPDATE_FW_FILE_CLOSE_ERROR: + ret = sprintf(buf, + "ERROR: %d - ROM BL failed to close FW file in FLASH\n", + status); + break; + case UPDATE_FW_WRITE_ERROR: + ret = sprintf(buf, + "ERROR: %d - ROM BL file write failure\n", status); + break; + case UPDATE_FW_EXECUTE_ERROR: + ret = sprintf(buf, + "ERROR: %d - ROM BL failed to execute RAM image\n", + status); + break; + case UPDATE_FW_RESET_ERROR: + ret = sprintf(buf, + "ERROR: %d - Reset DUT failure\n", + status); + break; + case UPDATE_FW_MODE_ERROR: + ret = sprintf(buf, + "ERROR: %d - Program complete, Incorrect BL/APP mode detected after reset sentinel\n", + status); + break; + case UPDATE_FW_ENTER_BL_ERROR: + ret = sprintf(buf, + "ERROR: %d - Could not enter the BL\n", status); + break; + case UPDATE_FW_FILE_OPEN_ERROR: + ret = sprintf(buf, + "ERROR: %d - ROM BL failed to open FW file in FLASH\n", + status); + break; + case UPDATE_FW_SENTINEL_NOT_SEEN: + ret = sprintf(buf, + "ERROR: %d - FW Reset Sentinel not seen after XRES\n", + status); + break; + case UPDATE_FW_EXCLUSIVE_ACCESS_ERROR: + ret = sprintf(buf, + "ERROR: %d - Failed to get DUT exclusive access\n", + status); + break; + case UPDATE_FW_NO_FW_PROVIDED: + ret = sprintf(buf, + "ERROR: %d - No FW provided to load\n", status); + break; + case UPDATE_FW_INVALID_FW_IMAGE: + ret = sprintf(buf, "ERROR: %d - Invalid FW image\n", status); + break; + case UPDATE_FW_MISALIGN_FW_IMAGE: + ret = sprintf(buf, + "ERROR: %d - FW image is misaligned\n", status); + break; + case UPDATE_FW_SYSTEM_NOMEM: + ret = sprintf(buf, + "ERROR: %d - Failed to alloc memory\n", status); + break; + case UPDATE_FW_INIT_BL_ERROR: + ret = sprintf(buf, + "ERROR: %d - Failed to init bootloader\n", status); + break; + case UPDATE_FW_PARSE_ROW_ERROR: + ret = sprintf(buf, + "ERROR: %d - Failed to parse row of FW image\n", + status); + break; + case UPDATE_FW_PROGRAM_ROW_ERROR: + ret = sprintf(buf, + "ERROR: %d - Failed to progrem FW image\n", status); + break; + case UPDATE_FW_EXIT_BL_ERROR: + ret = sprintf(buf, + "ERROR: %d - Failed to exit bootloader\n", status); + break; + case UPDATE_FW_CHECK_SUM_ERROR: + ret = sprintf(buf, + "ERROR: %d - Failed to verify app checksum\n", status); + break; + case UPDATE_FW_NO_PLATFORM_DATA: + ret = sprintf(buf, + "ERROR: %d - No platform data\n", status); + break; + case UPDATE_FW_UNDEFINED_ERROR: + default: + ret = sprintf(buf, "ERROR: %d - Unknown error\n", status); + break; + } + return ret; +} +static DEVICE_ATTR(pip2_bl_status, 0444, pt_pip2_bl_status_show, NULL); +#if PT_FW_UPGRADE +static DEVICE_ATTR(update_fw_status, 0444, pt_pip2_bl_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); + +#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 (update_fw_status != UPDATE_FW_COMPLETE) + _pt_pip2_update_bl_status(dev, UPDATE_FW_IDLE, PT_NO_INC); +#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 */ + +#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 + } 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 */ +#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 */ + } + + 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 + + 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); + + pt_debug(dev, DL_INFO, "%s: Successful probe %s\n", + __func__, dev_name(dev)); + return 0; + + +remove_files: +#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 */ + + 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 */ +#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 */ + 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 */ +#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 */ + } + 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 "); diff --git a/drivers/input/touchscreen/pt_mt_common.c b/drivers/input/touchscreen/pt_mt_common.c new file mode 100755 index 000000000000..aee85ce41c89 --- /dev/null +++ b/drivers/input/touchscreen/pt_mt_common.c @@ -0,0 +1,998 @@ +/* + * 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 + */ + +#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_axis + * + * SUMMARY: Calculates touch axis + * + * PARAMETERS: + * *md - pointer to touch data structure + * *axis - pointer to axis calculation result + * size - size in byte + * max - max value of result + * *xy_data - pointer to input data to be parsed + * bofs - bit offset + ******************************************************************************/ +static void pt_get_touch_axis(struct pt_mt_data *md, + int *axis, int size, int max, u8 *xy_data, int bofs) +{ + int nbyte; + int next; + + for (nbyte = 0, *axis = 0, next = 0; nbyte < size; nbyte++) { + pt_debug(md->dev, DL_DEBUG, + "%s: *axis=%02X(%d) size=%d max=%08X xy_data=%p xy_data[%d]=%02X(%d) bofs=%d\n", + __func__, *axis, *axis, size, max, xy_data, next, + xy_data[next], xy_data[next], bofs); + *axis = *axis + ((xy_data[next] >> bofs) << (nbyte * 8)); + next++; + } + + *axis &= max - 1; + + pt_debug(md->dev, DL_DEBUG, + "%s: *axis=%02X(%d) size=%d max=%08X xy_data=%p xy_data[%d]=%02X(%d)\n", + __func__, *axis, *axis, size, max, xy_data, next, + xy_data[next], xy_data[next]); +} + +/******************************************************************************* + * 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_axis(md, &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_axis(md, &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, si->tch_abs[PT_TCH_T].max); + int mt_sync_count = 0; + u8 *tch_addr; + + bitmap_zero(ids, si->tch_abs[PT_TCH_T].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; +} diff --git a/drivers/input/touchscreen/pt_mta.c b/drivers/input/touchscreen/pt_mta.c new file mode 100755 index 000000000000..5564816fa5cf --- /dev/null +++ b/drivers/input/touchscreen/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 + */ + +#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; +} diff --git a/drivers/input/touchscreen/pt_mtb.c b/drivers/input/touchscreen/pt_mtb.c new file mode 100755 index 000000000000..d40f81776691 --- /dev/null +++ b/drivers/input/touchscreen/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 + */ + +#include "pt_regs.h" +#include +#include + +/******************************************************************************* + * 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; +} + diff --git a/drivers/input/touchscreen/pt_platform.c b/drivers/input/touchscreen/pt_platform.c new file mode 100755 index 000000000000..2473d2235c69 --- /dev/null +++ b/drivers/input/touchscreen/pt_platform.c @@ -0,0 +1,1049 @@ +/* + * 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 + */ + +#include "pt_regs.h" +#include + +#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 */ + +/******************************************************************************* + * 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; + +#ifdef PT_PINCTRL_EN + if (on) { + 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 + 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_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_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) +{ + /* Example for TT7XXX */ + int rc = 0; + +#ifdef PT_PINCTRL_EN + pt_pinctrl_select_normal(pdata, dev); +#endif + +#ifdef TT7XXX_EXAMPLE + pt_debug(dev, DL_INFO, + "%s: Enable defined pwr: VDDI, VCC\n", __func__); + /* + * Force part into RESET by holding XRES#(TP_XRES) + * while powering it up + */ + if (pdata->rst_gpio) + gpio_set_value(pdata->rst_gpio, 0); + + /* Turn on VDDI [Digital Interface] (+1.8v) */ + if (pdata->vddi_gpio) { + rc = gpio_request(pdata->vddi_gpio, NULL); + if (rc < 0) { + gpio_free(pdata->vddi_gpio); + rc = gpio_request(pdata->vddi_gpio, NULL); + } + if (rc < 0) { + pr_err("%s: Failed requesting VDDI GPIO %d\n", + __func__, pdata->vddi_gpio); + } + rc = gpio_direction_output(pdata->vddi_gpio, 1); + if (rc) + pr_err("%s: setcfg for VDDI GPIO %d failed\n", + __func__, pdata->vddi_gpio); + gpio_free(pdata->vddi_gpio); + usleep_range(3000, 4000); + } + + /* Turn on VCC */ + if (pdata->vcc_gpio) { + rc = gpio_request(pdata->vcc_gpio, NULL); + if (rc < 0) { + gpio_free(pdata->vcc_gpio); + rc = gpio_request(pdata->vcc_gpio, NULL); + } + if (rc < 0) { + pr_err("%s: Failed requesting VCC GPIO %d\n", + __func__, pdata->vcc_gpio); + } + rc = gpio_direction_output(pdata->vcc_gpio, 1); + if (rc) + pr_err("%s: setcfg for VCC GPIO %d failed\n", + __func__, pdata->vcc_gpio); + gpio_free(pdata->vcc_gpio); + usleep_range(3000, 4000); + } + + usleep_range(12000, 15000); + /* Force part out of RESET by releasing XRES#(TP_XRES) */ + if (pdata->rst_gpio) + gpio_set_value(pdata->rst_gpio, 1); +#else + pt_debug(dev, DL_INFO, "%s: Enable defined pwr\n", __func__); +#endif + 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) +{ + /* Example for TT7XXX */ + int rc = 0; + +#ifdef TT7XXX_EXAMPLE + pt_debug(dev, DL_INFO, + "%s: Turn off defined pwr: VCC, VDDI\n", __func__); + /* + * Force part into RESET by holding XRES#(TP_XRES) + * while powering it up + */ + if (pdata->rst_gpio) + gpio_set_value(pdata->rst_gpio, 0); + + /* Turn off VCC */ + if (pdata->vcc_gpio) { + rc = gpio_request(pdata->vcc_gpio, NULL); + if (rc < 0) { + gpio_free(pdata->vcc_gpio); + rc = gpio_request(pdata->vcc_gpio, NULL); + } + if (rc < 0) { + pr_err("%s: Failed requesting VCC GPIO %d\n", + __func__, pdata->vcc_gpio); + } + rc = gpio_direction_output(pdata->vcc_gpio, 0); + if (rc) + pr_err("%s: setcfg for VCC GPIO %d failed\n", + __func__, pdata->vcc_gpio); + gpio_free(pdata->vcc_gpio); + } + + /* Turn off VDDI [Digital Interface] (+1.8v) */ + if (pdata->vddi_gpio) { + rc = gpio_request(pdata->vddi_gpio, NULL); + if (rc < 0) { + gpio_free(pdata->vddi_gpio); + rc = gpio_request(pdata->vddi_gpio, NULL); + } + if (rc < 0) { + pr_err("%s: Failed requesting VDDI GPIO %d\n", + __func__, pdata->vddi_gpio); + } + rc = gpio_direction_output(pdata->vddi_gpio, 0); + if (rc) + pr_err("%s: setcfg for VDDI GPIO %d failed\n", + __func__, pdata->vddi_gpio); + gpio_free(pdata->vddi_gpio); + usleep_range(10000, 12000); + } +#else + pt_debug(dev, DL_INFO, "%s: Turn off defined pwr\n", __func__); +#endif +#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); +} + +/******************************************************************************* + * 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 + +/******************************************************************************* + * 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 en_vcc = pdata->vcc_gpio; + int en_vddi = pdata->vddi_gpio; + int en_avdd = pdata->avdd_gpio; + int en_avee = pdata->avee_gpio; + 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); + + 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); + } + } + + /* 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_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) + /* use level triggered interrupts */ + irq_flags = IRQF_TRIGGER_LOW | IRQF_ONESHOT; + else + /* use edge triggered interrupts */ + irq_flags = IRQF_TRIGGER_FALLING | IRQF_ONESHOT; + + 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__); + } else { + disable_irq_nosync(cd->irq); + free_irq(cd->irq, cd); + } + return rc; +} diff --git a/drivers/input/touchscreen/pt_proximity.c b/drivers/input/touchscreen/pt_proximity.c new file mode 100755 index 000000000000..1ad0006c0843 --- /dev/null +++ b/drivers/input/touchscreen/pt_proximity.c @@ -0,0 +1,815 @@ +/* + * 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 + * + */ + +#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_axis + * + * SUMMARY: Calculates touch axis + * + * PARAMETERS: + * *pd - pointer to proximity data structure + * *axis - pointer to axis calculation result + * size - size in bytes + * max - max value of result + * *xy_data - pointer to input data to be parsed + * bofs - bit offset + ******************************************************************************/ +static void pt_get_touch_axis(struct pt_proximity_data *pd, + int *axis, int size, int max, u8 *xy_data, int bofs) +{ + int nbyte; + int next; + + for (nbyte = 0, *axis = 0, next = 0; nbyte < size; nbyte++) { + pt_debug(pd->dev, DL_INFO, + "%s: *axis=%02X(%d) size=%d max=%08X xy_data=%p xy_data[%d]=%02X(%d) bofs=%d\n", + __func__, *axis, *axis, size, max, xy_data, next, + xy_data[next], xy_data[next], bofs); + *axis = *axis + ((xy_data[next] >> bofs) << (nbyte * 8)); + next++; + } + + *axis &= max - 1; + + pt_debug(pd->dev, DL_INFO, + "%s: *axis=%02X(%d) size=%d max=%08X xy_data=%p xy_data[%d]=%02X(%d)\n", + __func__, *axis, *axis, size, max, xy_data, next, + xy_data[next], xy_data[next]); +} + +/******************************************************************************* + * 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_axis(pd, &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_axis(pd, &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; +} diff --git a/drivers/input/touchscreen/pt_regs.h b/drivers/input/touchscreen/pt_regs.h new file mode 100755 index 000000000000..8f3e40d10a89 --- /dev/null +++ b/drivers/input/touchscreen/pt_regs.h @@ -0,0 +1,1706 @@ +/* + * 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 + * + */ + +#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 +#ifdef CONFIG_HAS_EARLYSUSPEND +#include +#elif defined(CONFIG_FB) +#include +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#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" +/* 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 + +#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 + * 256 byte payload + * 2 byte CRC + */ +#define PT_MAX_PIP2_MSG_SIZE 264 +#define PT_MAX_PIP1_MSG_SIZE 255 + +/* + * 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 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 +}; + + +/* HID IDs and commands */ +#define HID_VENDOR_ID 0x04B4 +#define HID_APP_PRODUCT_ID 0xC101 +#define HID_VERSION 0x0100 +#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_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 + +/* 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 + +/* 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 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 + +/* + * 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_MULTI_CHIP 217 +#define PT_DRV_DBG_SET_PANEL_ID_TYPE 218 +#define PT_DRV_DBG_PIP_TIMEOUT 219 +#define PT_DRV_DBG_TTHE_HID_USB_FORMAT 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 */ + +/* TTDL Built In Self Test selection bit masks */ +#define PT_TTDL_BIST_BUS_TEST 0x01 +#define PT_TTDL_BIST_IRQ_TEST 0x02 +#define PT_TTDL_BIST_TP_XRES_TEST 0x04 +#define PT_TTDL_BIST_SLAVE_BUS_TEST 0x08 +#define PT_TTDL_BIST_SLAVE_IRQ_TEST 0x10 +#define PT_TTDL_BIST_SLAVE_XRES_TEST 0x20 + +#define SLAVE_DETECT_MASK 0x01 + +#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 0x000D0022 +#define HID_PT_BTN_COL_USAGE_PG 0xFF010020 + +#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_MAX = PIP2_FILE_7, +}; + +/* 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 + +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, +}; + +/* + * 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_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 +#define PT_ST_GET_RESULTS true +#define PT_ST_DONT_GET_RESULTS false + +/* + * 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_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; +}; + +struct pt_touch { + int hdr[PT_TCH_NUM_HDR]; + int abs[PT_TCH_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; +}; + +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]; + 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 8 +#define PT_HID_MAX_FIELDS 128 +#define PT_HID_MAX_COLLECTIONS 3 +#define PT_HID_MAX_NESTED_COLLECTIONS PT_HID_MAX_COLLECTIONS + +/* Max input is for ASCII representation of hex characters */ +#define PT_MAX_INPUT (PT_MAX_PIP2_MSG_SIZE * 2) +#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_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, 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); + int (*parse_sysfs_input)(struct device *dev, + const char *buf, size_t buf_size, + u32 *out_buf, size_t out_buf_size); +#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); +}; + +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; + enum pt_mode mode; + spinlock_t spinlock; + struct pt_mt_data md; + 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; + 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; + 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_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]; + 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 multi_chip; + 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) +{ + return cd->bus_ops->write_read_specific(cd->dev, write_len, write_buf, + read_buf); +} + +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); + + +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 timespec ts; + + getnstimeofday(&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); +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 */ diff --git a/drivers/input/touchscreen/pt_spi.c b/drivers/input/touchscreen/pt_spi.c new file mode 100755 index 000000000000..d8db40528211 --- /dev/null +++ b/drivers/input/touchscreen/pt_spi.c @@ -0,0 +1,429 @@ +/* + * 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 + */ + +#include "pt_regs.h" + +#include +#include + +/* 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); + + +/******************************************************************************* + * 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) +{ + int rc = 0; + + if (!buf || !size || size > PT_MAX_PIP2_MSG_SIZE) + return -EINVAL; + + mutex_lock(&pt_spi_bus_lock); + rc = pt_spi_xfer(dev, PT_SPI_RD_OP, buf, size); + 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; + + 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 + ******************************************************************************/ +static int pt_spi_write_read_specific(struct device *dev, u16 write_len, + u8 *write_buf, u8 *read_buf) +{ + 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); + rc = pt_spi_xfer(dev, PT_SPI_WR_OP, write_buf, write_len); + if (rc < 0) + goto error; + 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; + + + 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 + + + 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); + + + 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 "); diff --git a/include/linux/pt_core.h b/include/linux/pt_core.h new file mode 100755 index 000000000000..1972f40e5d11 --- /dev/null +++ b/include/linux/pt_core.h @@ -0,0 +1,219 @@ +/* + * pt_core.h + * 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.parade.com + */ + +#ifndef _LINUX_PT_CORE_H +#define _LINUX_PT_CORE_H + +#include + +#define PT_I2C_NAME "pt_i2c_adapter" +#define PT_SPI_NAME "pt_spi_adapter" + +#define PT_CORE_NAME "pt_core" +#define PT_MT_NAME "pt_mt" +#define PT_BTN_NAME "pt_btn" +#define PT_PROXIMITY_NAME "pt_proximity" + +#define PT_DRIVER_NAME TTDL +#define PT_DRIVER_MAJOR 04 +#define PT_DRIVER_MINOR 11 + +#define PT_DRIVER_REVCTRL 977092 + +#define PT_DRIVER_VERSION \ +__stringify(PT_DRIVER_NAME) \ +"." __stringify(PT_DRIVER_MAJOR) \ +"." __stringify(PT_DRIVER_MINOR) \ +"." __stringify(PT_DRIVER_REVCTRL) + +#define PT_DRIVER_DATE "20201210" + +/* abs settings */ +#define PT_IGNORE_VALUE -1 + +enum pt_core_platform_flags { + PT_CORE_FLAG_NONE, + PT_CORE_FLAG_POWEROFF_ON_SLEEP = 0x02, + PT_CORE_FLAG_RESTORE_PARAMETERS = 0x04, + PT_CORE_FLAG_DEEP_STANDBY = 0x08, + PT_CORE_FLAG_SKIP_SYS_SLEEP = 0x10, + PT_CORE_FLAG_SKIP_RUNTIME = 0x20, + PT_CORE_FLAG_SKIP_RESUME = 0x40, +}; + +enum pt_core_platform_easy_wakeup_gesture { + PT_CORE_EWG_NONE, + PT_CORE_EWG_TAP_TAP, + PT_CORE_EWG_TWO_FINGER_SLIDE, + PT_CORE_EWG_RESERVED, + PT_CORE_EWG_WAKE_ON_INT_FROM_HOST = 0xFF, +}; + +enum pt_loader_platform_flags { + PT_LOADER_FLAG_NONE, + PT_LOADER_FLAG_CALIBRATE_AFTER_FW_UPGRADE, + /* Use CONFIG_VER field in TT_CFG to decide TT_CFG update */ + PT_LOADER_FLAG_CHECK_TTCONFIG_VERSION, + PT_LOADER_FLAG_CALIBRATE_AFTER_TTCONFIG_UPGRADE, +}; + +enum CONFIG_DUT_GENERATION { + CONFIG_DUT_AUTO_DETECT = 0x00, + CONFIG_DUT_PIP1_ONLY = 0x01, + CONFIG_DUT_PIP2_CAPABLE = 0x02, +}; + +enum pt_core_platform_panel_id_flags { + PT_PANEL_ID_DISABLE = 0x00, + PT_PANEL_ID_BY_BL = 0x01, + PT_PANEL_ID_BY_SYS_INFO = 0x02, + PT_PANEL_ID_BY_MFG_DATA = 0x04, +}; + +struct touch_settings { + const uint8_t *data; + uint32_t size; + uint8_t tag; +}; + +struct pt_touch_firmware { + const uint8_t *img; + uint32_t size; + const uint8_t *ver; + uint8_t vsize; + uint8_t panel_id; +}; + +struct pt_touch_config { + struct touch_settings *param_regs; + struct touch_settings *param_size; + const uint8_t *fw_ver; + uint8_t fw_vsize; + uint8_t panel_id; +}; + +struct pt_loader_platform_data { + struct pt_touch_firmware *fw; + struct pt_touch_config *ttconfig; + struct pt_touch_firmware **fws; + struct pt_touch_config **ttconfigs; + u32 flags; +}; + +typedef int (*pt_platform_read) (struct device *dev, void *buf, int size); + +#define PT_TOUCH_SETTINGS_MAX 32 + +struct pt_core_platform_data { + int irq_gpio; + int rst_gpio; + int ddi_rst_gpio; + int vddi_gpio; + int vcc_gpio; + int avdd_gpio; + int avee_gpio; + int level_irq_udelay; + u16 hid_desc_register; + u16 vendor_id; + u16 product_id; + + int (*xres)(struct pt_core_platform_data *pdata, + struct device *dev); + int (*init)(struct pt_core_platform_data *pdata, + int on, struct device *dev); + int (*power)(struct pt_core_platform_data *pdata, + int on, struct device *dev, atomic_t *ignore_irq); + int (*detect)(struct pt_core_platform_data *pdata, + struct device *dev, pt_platform_read read); + int (*irq_stat)(struct pt_core_platform_data *pdata, + struct device *dev); + int (*setup_power)(struct pt_core_platform_data *pdata, + int on, struct device *dev); + int (*setup_irq)(struct pt_core_platform_data *pdata, + int on, struct device *dev); + struct touch_settings *sett[PT_TOUCH_SETTINGS_MAX]; + u32 flags; + u8 easy_wakeup_gesture; + u8 config_dut_generation; + u8 watchdog_force_stop; + u8 panel_id_support; +}; + +struct touch_framework { + const int16_t *abs; + uint8_t size; + uint8_t enable_vkeys; +} __packed; + +enum pt_mt_platform_power_state { + PT_MT_POWER_OFF = 0x00, + PT_MT_POWER_ON = 0x01 +}; + +enum pt_mt_platform_irq_state { + PT_MT_IRQ_FREE = 0x00, + PT_MT_IRQ_REG = 0x01 +}; + +enum pt_mt_platform_flags { + PT_MT_FLAG_NONE, + PT_MT_FLAG_HOVER = 0x04, + PT_MT_FLAG_FLIP = 0x08, + PT_MT_FLAG_INV_X = 0x10, + PT_MT_FLAG_INV_Y = 0x20, + PT_MT_FLAG_VKEYS = 0x40, + PT_MT_FLAG_NO_TOUCH_ON_LO = 0x80, +}; + +struct pt_mt_platform_data { + struct touch_framework *frmwrk; + unsigned short flags; + char const *inp_dev_name; + int vkeys_x; + int vkeys_y; +}; + +struct pt_btn_platform_data { + char const *inp_dev_name; +}; + +struct pt_proximity_platform_data { + struct touch_framework *frmwrk; + char const *inp_dev_name; +}; + +struct pt_platform_data { + struct pt_core_platform_data *core_pdata; + struct pt_mt_platform_data *mt_pdata; + struct pt_btn_platform_data *btn_pdata; + struct pt_proximity_platform_data *prox_pdata; + struct pt_loader_platform_data *loader_pdata; +}; + +#endif /* _LINUX_PT_CORE_H */ diff --git a/include/linux/pt_platform.h b/include/linux/pt_platform.h new file mode 100755 index 000000000000..07c951e3b0ba --- /dev/null +++ b/include/linux/pt_platform.h @@ -0,0 +1,68 @@ +/* + * pt_platform.h + * 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.parade.com + */ + +#ifndef _LINUX_PT_PLATFORM_H +#define _LINUX_PT_PLATFORM_H + +#include + +#if defined(CONFIG_TOUCHSCREEN_PARADE) \ + || defined(CONFIG_TOUCHSCREEN_PARADE_MODULE) +extern struct pt_loader_platform_data _pt_loader_platform_data; +extern irqreturn_t pt_irq(int irq, void *handle); + +int pt_xres(struct pt_core_platform_data *pdata, struct device *dev); +int pt_init(struct pt_core_platform_data *pdata, int on, + struct device *dev); +int pt_power(struct pt_core_platform_data *pdata, int on, + struct device *dev, atomic_t *ignore_irq); +#ifdef PT_DETECT_HW +int pt_detect(struct pt_core_platform_data *pdata, + struct device *dev, pt_platform_read read); +#else +#define pt_detect NULL +#endif +int pt_irq_stat(struct pt_core_platform_data *pdata, + struct device *dev); +int pt_setup_power(struct pt_core_platform_data *pdata, int on, + struct device *dev); +int pt_setup_irq(struct pt_core_platform_data *pdata, int on, + struct device *dev); +#else /* !CONFIG_TOUCHSCREEN_PARADE */ +static struct pt_loader_platform_data _pt_loader_platform_data; +#define pt_xres NULL +#define pt_init NULL +#define pt_power NULL +#define pt_irq_stat NULL +#define pt_detect NULL +#define pt_setup_power NULL +#define pt_setup_irq NULL +#endif /* CONFIG_TOUCHSCREEN_PARADE */ + +#endif /* _LINUX_PT_PLATFORM_H */