aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/input/touchscreen/cyttsp4_core.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/input/touchscreen/cyttsp4_core.c')
-rw-r--r--drivers/input/touchscreen/cyttsp4_core.c6685
1 files changed, 6685 insertions, 0 deletions
diff --git a/drivers/input/touchscreen/cyttsp4_core.c b/drivers/input/touchscreen/cyttsp4_core.c
new file mode 100644
index 0000000..d1ab003
--- /dev/null
+++ b/drivers/input/touchscreen/cyttsp4_core.c
@@ -0,0 +1,6685 @@
+/*
+ * Core Source for:
+ * Cypress TrueTouch(TM) Standard Product (TTSP) touchscreen drivers.
+ * For use with Cypress Gen4 and Solo parts.
+ * Supported parts include:
+ * CY8CTMA884/616
+ * CY8CTMA4XX
+ *
+ * Copyright (C) 2009-2012 Cypress Semiconductor, Inc.
+ * Copyright (C) 2011 Motorola Mobility, Inc.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Contact Cypress Semiconductor at www.cypress.com <kev@cypress.com>
+ *
+ */
+#define TOUCH_BOOST 0
+#include "cyttsp4_core.h"
+
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/platform_data/cypress_cyttsp4.h>
+#include <linux/firmware.h> /* This enables firmware class loader code */
+
+#if TOUCH_BOOST
+#include <linux/workqueue.h>
+#include <linux/timer.h>
+#include <mach/cpufreq_limits.h>
+#ifdef CONFIG_DVFS_LIMIT
+#include <linux/cpufreq.h>
+#endif
+bool boost;
+#endif
+
+/* platform address lookup offsets */
+#define CY_TCH_ADDR_OFS 0
+#define CY_LDR_ADDR_OFS 1
+
+/* helpers */
+#define GET_NUM_TOUCHES(x) ((x) & 0x1F)
+#define IS_LARGE_AREA(x) ((x) & 0x20)
+#define IS_BAD_PKT(x) ((x) & 0x20)
+#define GET_HSTMODE(reg) ((reg & 0x70) >> 4)
+#define IS_BOOTLOADERMODE(reg) (reg & 0x01)
+
+/* maximum number of concurrent tracks */
+#define CY_NUM_TCH_ID 10
+/* maximum number of track IDs */
+#define CY_NUM_TRK_ID 16
+/* maximum number of command data bytes */
+#define CY_NUM_DAT 6
+/* maximum number of config block read data */
+#define CY_NUM_CONFIG_BYTES 128
+
+#define CY_REG_BASE 0x00
+#define CY_DELAY_DFLT 20 /* ms */
+#define CY_DELAY_MAX (500/CY_DELAY_DFLT) /* half second */
+#define CY_HALF_SEC_TMO_MS 500 /* half second in msecs */
+#define CY_TEN_SEC_TMO_MS 10000 /* ten seconds in msecs */
+#define CY_HANDSHAKE_BIT 0x80
+#define CY_WAKE_DFLT 99 /* causes wake strobe on INT line
+ * in sample board configuration
+ * platform data->hw_recov() function
+ */
+/* power mode select bits */
+#define CY_SOFT_RESET_MODE 0x01
+#define CY_DEEP_SLEEP_MODE 0x02
+#define CY_LOW_POWER_MODE 0x04
+/* device mode bits */
+#define CY_MODE_CHANGE 0x08 /* rd/wr hst_mode */
+#define CY_OPERATE_MODE 0x00 /* rd/wr hst_mode */
+#define CY_SYSINFO_MODE 0x10 /* rd/wr hst_mode */
+#define CY_CONFIG_MODE 0x20 /* rd/wr hst_mode */
+#define CY_BL_MODE 0x01 /* wr hst mode == soft reset
+ * was 0x10 to rep_stat for LTS
+ */
+#define CY_CMD_RDY_BIT 0x40
+
+#define CY_REG_OP_START 0
+#define CY_REG_SI_START 0
+#define CY_REG_OP_END 0x20
+#define CY_REG_SI_END 0x20
+
+#ifdef CY_USE_TMA400
+#define CY_TCH_CRC_LOC_TMA400 5884 /* location of CRC in touch EBID */
+#endif /* --CY_USE_TMA400 */
+
+/* register field lengths */
+#define CY_NUM_REVCTRL 8
+#define CY_NUM_MFGID 8
+#define CY_NUM_TCHREC 10
+#define CY_NUM_DDATA 32
+#define CY_NUM_MDATA 64
+#define CY_TMA884_MAX_BYTES 255 /*
+ * max reg access for TMA884
+ * in config mode
+ */
+#define CY_TMA400_MAX_BYTES 512 /*
+ * max reg access for TMA400
+ * in config mode
+ */
+
+/* touch event id codes */
+#define CY_GET_EVENTID(reg) ((reg & 0x60) >> 5)
+#define CY_GET_TRACKID(reg) (reg & 0x1F)
+#define CY_NOMOVE 0
+#define CY_TOUCHDOWN 1
+#define CY_MOVE 2
+#define CY_LIFTOFF 3
+
+#define CY_CFG_BLK_SIZE 126
+#define CY_EBID_ROW_SIZE_DFLT 128
+
+#define CY_BL_VERS_SIZE 12
+#define CY_NUM_TMA400_TT_CFG_BLK 51 /* Rev84 mapping */
+
+#if defined(CY_USE_FORCE_LOAD) || defined(CONFIG_TOUCHSCREEN_DEBUG)
+#define CY_BL_FW_NAME_SIZE NAME_MAX
+#endif
+
+
+#ifdef CY_USE_REG_ACCESS
+#define CY_RW_REGID_MAX 0xFFFF
+#define CY_RW_REG_DATA_MAX 0xFF
+#endif
+
+/* abs settings */
+#define CY_IGNORE_VALUE 0xFFFF
+/* abs signal capabilities offsets in the frameworks array */
+enum cyttsp4_sig_caps {
+ CY_SIGNAL_OST = 0,
+ CY_MIN_OST = 1,
+ CY_MAX_OST = 2,
+ CY_FUZZ_OST = 3,
+ CY_FLAT_OST = 4,
+ CY_NUM_ABS_SET /* number of signal capability fields */
+};
+
+/* abs axis signal offsets in the framworks array */
+enum cyttsp4_sig_ost {
+ CY_ABS_X_OST = 0,
+ CY_ABS_Y_OST = 1,
+ CY_ABS_P_OST = 2,
+ CY_ABS_W_OST = 3,
+ CY_ABS_ID_OST = 4,
+ CY_ABS_MAJ_OST = 5,
+ CY_ABS_MIN_OST = 6,
+ CY_ABS_OR_OST = 7,
+ CY_NUM_ABS_OST /* number of abs signals */
+};
+
+/* touch record system information offset masks and shifts */
+#define CY_SIZE_FIELD_MASK 0x1F
+#define CY_BOFS_MASK 0xE0
+#define CY_BOFS_SHIFT 5
+
+enum cyttsp4_driver_state {
+ CY_IDLE_STATE, /* IC cannot be reached */
+ CY_READY_STATE, /* pre-operational; ready to go to ACTIVE */
+ CY_ACTIVE_STATE, /* app is running, IC is scanning */
+ CY_SLEEP_STATE, /* app is running, IC is idle */
+ CY_BL_STATE, /* bootloader is running */
+ CY_SYSINFO_STATE, /* switching to sysinfo mode */
+ CY_CMD_STATE, /* command initiation mode */
+ CY_EXIT_BL_STATE, /* sync bl heartbeat to app ready int */
+ CY_TRANSFER_STATE, /* changing states */
+ CY_INVALID_STATE /* always last in the list */
+};
+
+static const char * const cyttsp4_driver_state_string[] = {
+ /* Order must match enum cyttsp4_driver_state above */
+ "IDLE",
+ "READY",
+ "ACTIVE",
+ "SLEEP",
+ "BOOTLOADER",
+ "SYSINFO",
+ "CMD",
+ "EXIT_BL",
+ "TRANSFER",
+ "INVALID"
+};
+
+enum cyttsp4_controller_mode {
+ CY_MODE_BOOTLOADER,
+ CY_MODE_SYSINFO,
+ CY_MODE_OPERATIONAL,
+ CY_MODE_CONFIG,
+ CY_MODE_NUM
+};
+
+enum cyttsp4_ic_grpnum {
+ CY_IC_GRPNUM_RESERVED = 0,
+ CY_IC_GRPNUM_CMD_REGS,
+ CY_IC_GRPNUM_TCH_REP,
+ CY_IC_GRPNUM_DATA_REC,
+ CY_IC_GRPNUM_TEST_REC,
+ CY_IC_GRPNUM_PCFG_REC,
+ CY_IC_GRPNUM_TCH_PARM_VAL,
+ CY_IC_GRPNUM_TCH_PARM_SIZ,
+ CY_IC_GRPNUM_RESERVED1,
+ CY_IC_GRPNUM_RESERVED2,
+ CY_IC_GRPNUM_OPCFG_REC,
+ CY_IC_GRPNUM_DDATA_REC,
+ CY_IC_GRPNUM_MDATA_REC,
+ CY_IC_GRPNUM_TEST_REGS,
+ CY_IC_GRPNUM_BTN_KEYS,
+ CY_IC_GRPNUM_NUM
+};
+
+enum cyttsp4_ic_op_mode_commands {
+ CY_GET_PARAM_CMD = 0x02,
+ CY_SET_PARAM_CMD = 0x03,
+ CY_GET_CFG_BLK_CRC = 0x05,
+};
+
+enum cyttsp4_ic_config_mode_commands {
+ CY_GET_EBID_ROW_SIZE = 0x02,
+ CY_READ_EBID_DATA = 0x03,
+ CY_WRITE_EBID_DATA = 0x04,
+ CY_VERIFY_EBID_CRC = 0x11,
+};
+
+#ifdef CY_USE_TMA400
+enum cyttsp4_ic_ebid {
+ CY_TCH_PARM_EBID = 0x00,
+ CY_MDATA_EBID = 0x01,
+ CY_DDATA_EBID = 0x02,
+};
+#endif /* --CY_USE_TMA400 */
+
+#ifdef CY_USE_TMA884
+enum cyttsp4_ic_ebid {
+ CY_TCH_PARM_EBID = 0x00,
+ CY_DDATA_EBID = 0x05,
+ CY_MDATA_EBID = 0x06,
+};
+#endif /* --CY_USE_TMA884 */
+
+enum cyttsp4_flags {
+ CY_FLAG_NONE = 0x00,
+ CY_FLAG_HOVER = 0x04,
+#ifdef CY_USE_DEBUG_TOOLS
+ CY_FLAG_FLIP = 0x08,
+ CY_FLAG_INV_X = 0x10,
+ CY_FLAG_INV_Y = 0x20,
+#endif /* --CY_USE_DEBUG_TOOLS */
+};
+
+enum cyttsp4_event_id {
+ CY_EV_NO_EVENT = 0,
+ CY_EV_TOUCHDOWN = 1,
+ CY_EV_MOVE = 2, /* significant displacement (> act dist) */
+ CY_EV_LIFTOFF = 3, /* record reports last position */
+};
+
+enum cyttsp4_object_id {
+ CY_OBJ_STANDARD_FINGER = 0,
+ CY_OBJ_LARGE_OBJECT = 1,
+ CY_OBJ_STYLUS = 2,
+ CY_OBJ_HOVER = 3,
+};
+
+enum cyttsp4_test_cmd {
+ CY_TEST_CMD_NULL = 0,
+};
+
+/* test mode NULL command driver codes; D */
+enum cyttsp4_null_test_cmd_code {
+ CY_NULL_CMD_NULL = 0,
+ CY_NULL_CMD_MODE,
+ CY_NULL_CMD_STATUS_SIZE,
+ CY_NULL_CMD_HANDSHAKE,
+};
+
+enum cyttsp_test_mode {
+ CY_TEST_MODE_NORMAL_OP = 0, /* Send touch data to OS; normal op */
+ CY_TEST_MODE_CAT, /* Configuration and Test */
+ CY_TEST_MODE_CLOSED_UNIT, /* Send scan data to sysfs */
+};
+
+struct cyttsp4_test_mode {
+ int cur_mode;
+ int cur_cmd;
+ size_t cur_status_size;
+};
+
+/* GEN4/SOLO Operational interface definitions */
+enum cyttsp4_tch_abs { /* for ordering within the extracted touch data array */
+ CY_TCH_X = 0, /* X */
+ CY_TCH_Y, /* Y */
+ CY_TCH_P, /* P (Z) */
+ CY_TCH_T, /* TOUCH ID */
+ CY_TCH_E, /* EVENT ID */
+ CY_TCH_O, /* OBJECT ID */
+ CY_TCH_W, /* SIZE (SOLO - Corresponds to TOUCH_MAJOR) */
+#ifdef CY_USE_TMA400_SP2
+#ifdef CY_USE_TMA400
+ CY_TCH_MAJ, /* TOUCH_MAJOR */
+ CY_TCH_MIN, /* TOUCH_MINOR */
+ CY_TCH_OR, /* ORIENTATION */
+#endif /* --CY_USE_TMA400 */
+#endif /* --CY_USE_TMA400_SP2 */
+ CY_TCH_NUM_ABS
+};
+static const char * const cyttsp4_tch_abs_string[] = {
+ /* Order must match enum cyttsp4_tch_descriptor above */
+ "X",
+ "Y",
+ "P",
+ "T",
+ "E",
+ "O",
+ "W",
+#ifdef CY_USE_TMA400_SP2
+#ifdef CY_USE_TMA400
+ "MAJ",
+ "MIN",
+ "OR",
+#endif /* --CY_USE_TMA400 */
+#endif /* --CY_USE_TMA400_SP2 */
+ "INVALID"
+};
+
+#ifdef CY_USE_TMA400
+#ifdef CY_USE_TMA400_SP2
+#define CY_NUM_NEW_TCH_FIELDS 3
+#else
+#define CY_NUM_NEW_TCH_FIELDS 0
+#endif /* --CY_USE_TMA400_SP2 */
+#endif /* --CY_USE_TMA400 */
+
+#ifdef CY_USE_TMA884
+#define CY_NUM_NEW_TCH_FIELDS 0
+#endif /* --CY_USE_TMA884 */
+
+#define CY_NUM_OLD_TCH_FIELDS (CY_TCH_NUM_ABS - CY_NUM_NEW_TCH_FIELDS)
+
+struct cyttsp4_touch {
+ int abs[CY_TCH_NUM_ABS];
+};
+
+/* TTSP System Information interface definitions */
+struct cyttsp4_cydata {
+ u8 ttpidh;
+ u8 ttpidl;
+ u8 fw_ver_major;
+ u8 fw_ver_minor;
+ u8 revctrl[CY_NUM_REVCTRL];
+ u8 blver_major;
+ u8 blver_minor;
+ u8 jtag_si_id3;
+ u8 jtag_si_id2;
+ u8 jtag_si_id1;
+ u8 jtag_si_id0;
+ u8 mfgid_sz;
+ u8 mfg_id[CY_NUM_MFGID];
+ u8 cyito_idh;
+ u8 cyito_idl;
+ u8 cyito_verh;
+ u8 cyito_verl;
+ u8 ttsp_ver_major;
+ u8 ttsp_ver_minor;
+ u8 device_info;
+} __packed;
+
+struct cyttsp4_test {
+ u8 post_codeh;
+ u8 post_codel;
+} __packed;
+
+struct cyttsp4_pcfg {
+ u8 electrodes_x;
+ u8 electrodes_y;
+ u8 len_xh;
+ u8 len_xl;
+ u8 len_yh;
+ u8 len_yl;
+ u8 axis_xh;
+ u8 axis_xl;
+ u8 axis_yh;
+ u8 axis_yl;
+ u8 max_zh;
+ u8 max_zl;
+} __packed;
+
+struct cyttsp4_tch_rec_params {
+ u8 loc;
+ u8 size;
+} __packed;
+
+struct cyttsp4_opcfg {
+ u8 cmd_ofs;
+ u8 rep_ofs;
+ u8 rep_szh;
+ u8 rep_szl;
+ u8 num_btns;
+ u8 tt_stat_ofs;
+ u8 obj_cfg0;
+ u8 max_tchs;
+ u8 tch_rec_siz;
+ struct cyttsp4_tch_rec_params tch_rec_old[CY_NUM_OLD_TCH_FIELDS];
+ u8 btn_rec_siz; /* btn record size (in bytes) */
+ u8 btn_diff_ofs;/* btn data loc ,diff counts, (Op-Mode byte ofs) */
+ u8 btn_diff_siz;/* btn size of diff counts (in bits) */
+#ifdef CY_USE_TMA400
+ struct cyttsp4_tch_rec_params tch_rec_new[CY_NUM_NEW_TCH_FIELDS];
+#endif /* --CY_USE_TMA400 */
+} __packed;
+
+struct cyttsp4_sysinfo_data {
+ u8 hst_mode;
+ u8 reserved;
+ u8 map_szh;
+ u8 map_szl;
+ u8 cydata_ofsh;
+ u8 cydata_ofsl;
+ u8 test_ofsh;
+ u8 test_ofsl;
+ u8 pcfg_ofsh;
+ u8 pcfg_ofsl;
+ u8 opcfg_ofsh;
+ u8 opcfg_ofsl;
+ u8 ddata_ofsh;
+ u8 ddata_ofsl;
+ u8 mdata_ofsh;
+ u8 mdata_ofsl;
+} __packed;
+
+struct cyttsp4_sysinfo_ptr {
+ struct cyttsp4_cydata *cydata;
+ struct cyttsp4_test *test;
+ struct cyttsp4_pcfg *pcfg;
+ struct cyttsp4_opcfg *opcfg;
+ struct cyttsp4_ddata *ddata;
+ struct cyttsp4_mdata *mdata;
+} __packed;
+
+struct cyttsp4_tch_abs_params {
+ size_t ofs; /* abs byte offset */
+ size_t size; /* size in bits */
+ size_t max; /* max value */
+ size_t bofs; /* bit offset */
+};
+
+struct cyttsp4_sysinfo_ofs {
+ size_t cmd_ofs;
+ size_t rep_ofs;
+ size_t rep_sz;
+ size_t num_btns;
+ size_t num_btn_regs; /* ceil(num_btns/4) */
+ size_t tt_stat_ofs;
+ size_t tch_rec_siz;
+ size_t obj_cfg0;
+ size_t max_tchs;
+ size_t mode_size;
+ size_t data_size;
+ size_t map_sz;
+ size_t cydata_ofs;
+ size_t test_ofs;
+ size_t pcfg_ofs;
+ size_t opcfg_ofs;
+ size_t ddata_ofs;
+ size_t mdata_ofs;
+ size_t cydata_size;
+ size_t test_size;
+ size_t pcfg_size;
+ size_t opcfg_size;
+ size_t ddata_size;
+ size_t mdata_size;
+ size_t btn_keys_size;
+ struct cyttsp4_tch_abs_params tch_abs[CY_TCH_NUM_ABS];
+ size_t btn_rec_siz; /* btn record size (in bytes) */
+ size_t btn_diff_ofs;/* btn data loc ,diff counts, (Op-Mode byte ofs) */
+ size_t btn_diff_siz;/* btn size of diff counts (in bits) */
+};
+
+/* button to keycode support */
+#define CY_NUM_BTN_PER_REG 4
+#define CY_NUM_BTN_EVENT_ID 4
+#define CY_BITS_PER_BTN 2
+
+enum cyttsp4_btn_state {
+ CY_BTN_RELEASED = 0,
+ CY_BTN_PRESSED = 1,
+ CY_BTN_NUM_STATE
+};
+
+struct cyttsp4_btn {
+ bool enabled;
+ int state; /* CY_BTN_PRESSED, CY_BTN_RELEASED */
+ int key_code;
+};
+
+#define FW_VERSION 0x0100
+
+#define FACTORY_TESTING
+
+#ifdef FACTORY_TESTING
+extern struct class *sec_class;
+#define TSP_VENDOR "CYPRESS"
+#define TSP_IC "GEN4"
+
+#define TSP_CMD_STR_LEN 32
+#define TSP_CMD_RESULT_STR_LEN 512
+#define TSP_CMD_PARAM_NUM 8
+
+struct factory_data {
+ struct list_head cmd_list_head;
+ u8 cmd_state;
+ char cmd[TSP_CMD_STR_LEN];
+ int cmd_param[TSP_CMD_PARAM_NUM];
+ char cmd_result[TSP_CMD_RESULT_STR_LEN];
+ char cmd_buff[TSP_CMD_RESULT_STR_LEN];
+ struct mutex cmd_lock;
+ bool cmd_is_running;
+};
+
+struct node_data {
+ s16 *cm_delta_data;
+ s16 *cm_abs_data;
+ s16 *intensity_data;
+ s16 *reference_data;
+};
+
+#define TSP_CMD(name, func) .cmd_name = name, .cmd_func = func
+#define TOSTRING(x) #x
+
+enum { /* this is using by cmd_state valiable. */
+ WAITING = 0,
+ RUNNING,
+ OK,
+ FAIL,
+ NOT_APPLICABLE,
+};
+
+struct tsp_cmd {
+ struct list_head list;
+ const char *cmd_name;
+ void (*cmd_func)(void *device_data);
+};
+
+#endif
+
+
+/* driver context structure definitions */
+
+struct cyttsp4 {
+ struct device *dev;
+ int irq;
+ struct input_dev *input;
+ struct mutex data_lock; /* prevent concurrent accesses */
+ struct workqueue_struct *cyttsp4_wq;
+ struct work_struct cyttsp4_resume_startup_work;
+ char phys[32];
+ const struct bus_type *bus_type;
+ const struct touch_platform_data *platform_data;
+ u8 *xy_mode; /* operational mode and status regs */
+ u8 *xy_data; /* operational touch regs */
+ u8 *xy_data_touch1; /* includes 1-byte for tt_stat */
+ u8 *btn_rec_data; /* button diff count data */
+ struct cyttsp4_bus_ops *bus_ops;
+ struct cyttsp4_sysinfo_data sysinfo_data;
+ struct cyttsp4_sysinfo_ptr sysinfo_ptr;
+ struct cyttsp4_sysinfo_ofs si_ofs;
+ struct cyttsp4_btn *btn;
+ struct cyttsp4_test_mode test;
+ struct completion int_running;
+ struct completion si_int_running;
+ struct completion ready_int_running;
+ enum cyttsp4_driver_state driver_state;
+ enum cyttsp4_controller_mode current_mode;
+ bool irq_enabled;
+ bool powered; /* protect against multiple open */
+ bool was_suspended;
+ bool switch_flag;
+ bool soft_reset_asserted;
+ u16 flags;
+ size_t max_config_bytes;
+ size_t ebid_row_size;
+ int num_prv_tch;
+#ifdef CY_USE_TMA400
+ bool starting_up;
+#endif /* --CY_USE_TMA400 */
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ struct early_suspend early_suspend;
+#endif
+#ifdef CY_USE_WATCHDOG
+ struct work_struct work;
+ struct timer_list timer;
+#endif
+#if defined(CY_USE_FORCE_LOAD) || defined(CONFIG_TOUCHSCREEN_DEBUG)
+ bool waiting_for_fw;
+ char *fwname;
+#endif
+#ifdef CY_USE_REG_ACCESS
+ size_t rw_regid;
+#endif
+#ifdef FACTORY_TESTING
+ struct factory_data *factory_data;
+ struct node_data *node_data;
+#endif
+#if TOUCH_BOOST
+ struct timer_list dvfs_timer;
+#endif
+};
+
+#if defined(CY_AUTO_LOAD_FW) || \
+ defined(CY_USE_FORCE_LOAD) || \
+ defined(CONFIG_TOUCHSCREEN_DEBUG)
+static int _cyttsp4_load_app(struct cyttsp4 *ts, const u8 *fw, int fw_size);
+#endif /* CY_AUTO_LOAD_FW || CY_USE_FORCE_LOAD || CONFIG_TOUCHSCREEN_DEBUG */
+static int _cyttsp4_ldr_exit(struct cyttsp4 *ts);
+static int _cyttsp4_startup(struct cyttsp4 *ts);
+static int _cyttsp4_get_ic_crc(struct cyttsp4 *ts,
+ enum cyttsp4_ic_ebid ebid, u8 *crc_h, u8 *crc_l);
+static irqreturn_t cyttsp4_irq(int irq, void *handle);
+static int _cyttsp4_set_mode(struct cyttsp4 *ts, u8 new_mode);
+#ifdef CY_USE_TMA884
+static int _cyttsp4_calc_data_crc(struct cyttsp4 *ts,
+ size_t ndata, u8 *pdata, u8 *crc_h, u8 *crc_l, const char *name);
+#endif /* --CY_USE_TMA884 */
+
+static void _cyttsp4_pr_state(struct cyttsp4 *ts)
+{
+ dev_info(ts->dev,
+ "%s: %s\n", __func__,
+ ts->driver_state < CY_INVALID_STATE ?
+ cyttsp4_driver_state_string[ts->driver_state] :
+ "INVALID");
+}
+
+static void _cyttsp4_pr_buf(struct cyttsp4 *ts, u8 *dptr, int size,
+ const char *data_name)
+{
+ return;
+}
+
+static int _cyttsp4_read_block_data(struct cyttsp4 *ts, u16 command,
+ size_t length, void *buf, int i2c_addr, bool use_subaddr)
+{
+ int retval = 0;
+ int tries = 0;
+
+ if ((buf == NULL) || (length == 0)) {
+ dev_err(ts->dev,
+ "%s: pointer or length error"
+ " buf=%p length=%d\n", __func__, buf, length);
+ retval = -EINVAL;
+ } else {
+ for (tries = 0, retval = -1;
+ tries < CY_NUM_RETRY && (retval < 0);
+ tries++) {
+ retval = ts->bus_ops->read(ts->bus_ops, command,
+ length, buf, i2c_addr, use_subaddr);
+ if (retval < 0) {
+ msleep(CY_DELAY_DFLT);
+ /*
+ * TODO: remove the extra sleep delay when
+ * the loader exit sequence is streamlined
+ */
+ msleep(150);
+ }
+ }
+
+ if (retval < 0) {
+ dev_err(ts->dev,
+ "%s: bus read block data fail (ret=%d)\n",
+ __func__, retval);
+ }
+ }
+
+ return retval;
+}
+
+static int _cyttsp4_write_block_data(struct cyttsp4 *ts, u16 command,
+ size_t length, const void *buf, int i2c_addr, bool use_subaddr)
+{
+ int retval = 0;
+ int tries = 0;
+
+ if ((buf == NULL) || (length == 0)) {
+ dev_err(ts->dev,
+ "%s: pointer or length error"
+ " buf=%p length=%d\n", __func__, buf, length);
+ retval = -EINVAL;
+ } else {
+ for (tries = 0, retval = -1;
+ tries < CY_NUM_RETRY && (retval < 0);
+ tries++) {
+ retval = ts->bus_ops->write(ts->bus_ops, command,
+ length, buf, i2c_addr, use_subaddr);
+ if (retval < 0)
+ msleep(CY_DELAY_DFLT);
+ }
+
+ if (retval < 0) {
+ dev_err(ts->dev,
+ "%s: bus write block data fail (ret=%d)\n",
+ __func__, retval);
+ }
+ }
+
+ return retval;
+}
+
+#ifdef CY_USE_TMA400
+static int _cyttsp4_wait_ready_int_no_init(struct cyttsp4 *ts,
+ unsigned long timeout_ms)
+{
+ unsigned long uretval;
+ int retval = 0;
+
+ mutex_unlock(&ts->data_lock);
+ uretval = wait_for_completion_interruptible_timeout(
+ &ts->ready_int_running, msecs_to_jiffies(timeout_ms));
+ mutex_lock(&ts->data_lock);
+ if (uretval == 0) {
+ dev_err(ts->dev,
+ "%s: timeout waiting for interrupt\n",
+ __func__);
+ retval = -ETIMEDOUT;
+ }
+
+ return retval;
+}
+#endif /* --CY_USE_TMA400 */
+
+static int _cyttsp4_wait_int_no_init(struct cyttsp4 *ts,
+ unsigned long timeout_ms)
+{
+ unsigned long uretval;
+ int retval = 0;
+
+ mutex_unlock(&ts->data_lock);
+ uretval = wait_for_completion_interruptible_timeout(
+ &ts->int_running, msecs_to_jiffies(timeout_ms));
+ mutex_lock(&ts->data_lock);
+ if (uretval == 0) {
+ dev_err(ts->dev,
+ "%s: timeout waiting for interrupt\n",
+ __func__);
+ retval = -ETIMEDOUT;
+ }
+
+ return retval;
+}
+
+static int _cyttsp4_wait_int(struct cyttsp4 *ts, unsigned long timeout_ms)
+{
+ int retval = 0;
+
+ INIT_COMPLETION(ts->int_running);
+ retval = _cyttsp4_wait_int_no_init(ts, timeout_ms);
+ if (retval < 0) {
+ dev_err(ts->dev,
+ "%s: timeout waiting for interrupt\n",
+ __func__);
+ }
+
+ return retval;
+}
+
+static int _cyttsp4_wait_si_int(struct cyttsp4 *ts, unsigned long timeout_ms)
+{
+ unsigned long uretval;
+ int retval = 0;
+
+ mutex_unlock(&ts->data_lock);
+ uretval = wait_for_completion_interruptible_timeout(
+ &ts->si_int_running, msecs_to_jiffies(timeout_ms));
+ mutex_lock(&ts->data_lock);
+ if (uretval == 0) {
+ dev_err(ts->dev,
+ "%s: timeout waiting for bootloader interrupt\n",
+ __func__);
+ retval = -ETIMEDOUT;
+ }
+
+ return retval;
+}
+
+static void _cyttsp4_queue_startup(struct cyttsp4 *ts, bool was_suspended)
+{
+ ts->was_suspended = was_suspended;
+ queue_work(ts->cyttsp4_wq,
+ &ts->cyttsp4_resume_startup_work);
+ dev_info(ts->dev,
+ "%s: startup queued\n", __func__);
+}
+
+#if defined(CY_AUTO_LOAD_TOUCH_PARAMS) || \
+ defined(CY_AUTO_LOAD_DDATA) || defined(CY_AUTO_LOAD_MDATA) || \
+ defined(CY_USE_DEV_DEBUG_TOOLS) || defined(CY_USE_TMA884) || \
+ defined(FACTORY_TESTING)
+static u16 _cyttsp4_calc_partial_crc(struct cyttsp4 *ts,
+ u8 *pdata, size_t ndata, u16 crc)
+{
+ int i = 0;
+ int j = 0;
+
+ for (i = 0; i < ndata; i++) {
+ crc ^= ((u16)pdata[i] << 8);
+
+ for (j = 8; j > 0; --j) {
+ if (crc & 0x8000)
+ crc = (crc << 1) ^ 0x1021;
+ else
+ crc = crc << 1;
+ }
+ }
+
+ return crc;
+}
+
+static void _cyttsp4_calc_crc(struct cyttsp4 *ts,
+ u8 *pdata, size_t ndata, u8 *crc_h, u8 *crc_l)
+{
+ u16 crc = 0;
+
+ if (pdata == NULL)
+ dev_err(ts->dev,
+ "%s: Null data ptr\n", __func__);
+ else if (ndata == 0)
+ dev_err(ts->dev,
+ "%s: Num data is 0\n", __func__);
+ else {
+ /* Calculate CRC */
+ crc = 0xFFFF;
+ crc = _cyttsp4_calc_partial_crc(ts, pdata, ndata, crc);
+ *crc_h = crc / 256;
+ *crc_l = crc % 256;
+ }
+}
+#endif /* --CY_AUTO_LOAD_TOUCH_PARAMS --CY_AUTO_LOAD_DDATA
+ --CY_AUTO_LOAD_MDATA --CY_USE_DEV_DEBUG_TOOLS --CY_USE_TMA884 */
+
+static bool _cyttsp4_chk_cmd_rdy(struct cyttsp4 *ts, u8 cmd)
+{
+ bool cond = !!(cmd & CY_CMD_RDY_BIT);
+ dev_vdbg(ts->dev,
+ "%s: cmd=%02X cond=%d\n", __func__, cmd, (int)cond);
+
+ return cond;
+}
+
+static bool _cyttsp4_chk_mode_change(struct cyttsp4 *ts, u8 cmd)
+{
+ bool cond = !(cmd & CY_MODE_CHANGE);
+ dev_vdbg(ts->dev,
+ "%s: cmd=%02X cond=%d\n", __func__, cmd, (int)cond);
+
+ return cond;
+}
+
+static void _cyttsp4_change_state(struct cyttsp4 *ts,
+ enum cyttsp4_driver_state new_state)
+{
+ ts->driver_state = new_state;
+ _cyttsp4_pr_state(ts);
+}
+
+static int _cyttsp4_put_cmd_wait(struct cyttsp4 *ts, u16 ofs,
+ size_t cmd_len, const void *cmd_buf, unsigned long timeout_ms,
+ bool (*cond)(struct cyttsp4 *, u8), u8 *retcmd,
+ int i2c_addr, bool use_subaddr)
+{
+ enum cyttsp4_driver_state tmp_state;
+ unsigned long uretval = 0;
+ u8 cmd = 0;
+ int tries = 0;
+ int retval = 0;
+
+ /* unlock here to allow any pending irq to complete */
+ tmp_state = ts->driver_state;
+ _cyttsp4_change_state(ts, CY_TRANSFER_STATE);
+ mutex_unlock(&ts->data_lock);
+ mutex_lock(&ts->data_lock);
+ _cyttsp4_change_state(ts, CY_CMD_STATE);
+ INIT_COMPLETION(ts->int_running);
+ mutex_unlock(&ts->data_lock);
+ retval = _cyttsp4_write_block_data(ts, ofs, cmd_len,
+ cmd_buf, i2c_addr, use_subaddr);
+ if (retval < 0) {
+ dev_err(ts->dev,
+ "%s: Fail writing cmd buf r=%d\n",
+ __func__, retval);
+ mutex_lock(&ts->data_lock);
+ goto _cyttsp4_put_cmd_wait_exit;
+ }
+_cyttsp4_put_cmd_wait_retry:
+ uretval = wait_for_completion_interruptible_timeout(
+ &ts->int_running, msecs_to_jiffies(timeout_ms));
+ mutex_lock(&ts->data_lock);
+
+ retval = _cyttsp4_read_block_data(ts, ofs,
+ sizeof(cmd), &cmd, i2c_addr, use_subaddr);
+ if (retval < 0) {
+ dev_err(ts->dev,
+ "%s: fail read cmd status r=%d\n",
+ __func__, retval);
+ }
+ if ((cond != NULL) && !cond(ts, cmd)) {
+ if (uretval == 0) {
+ dev_err(ts->dev,
+ "%s: timeout waiting for cmd ready\n",
+ __func__);
+ retval = -ETIMEDOUT;
+ } else {
+ if (tries++ < 2) {
+ INIT_COMPLETION(ts->int_running);
+ mutex_unlock(&ts->data_lock);
+ goto _cyttsp4_put_cmd_wait_retry;
+ } else {
+ dev_err(ts->dev,
+ "%s: cmd not ready error"
+ " cmd_stat=0x%02X\n",
+ __func__, cmd);
+ retval = -EIO;
+ }
+ }
+ } else {
+ /* got command ready */
+ if (retcmd != NULL)
+ *retcmd = cmd;
+ retval = 0;
+ dev_vdbg(ts->dev,
+ "%s: got command ready; cmd=%02X retcmd=%p tries=%d\n",
+ __func__, cmd, retcmd, tries);
+ }
+
+_cyttsp4_put_cmd_wait_exit:
+ _cyttsp4_change_state(ts, tmp_state);
+ return retval;
+}
+
+static int _cyttsp4_handshake(struct cyttsp4 *ts, u8 hst_mode)
+{
+ int retval = 0;
+ u8 cmd = 0;
+
+ cmd = hst_mode & CY_HANDSHAKE_BIT ?
+ hst_mode & ~CY_HANDSHAKE_BIT :
+ hst_mode | CY_HANDSHAKE_BIT;
+
+ retval = _cyttsp4_write_block_data(ts, CY_REG_BASE,
+ sizeof(cmd), (u8 *)&cmd,
+ ts->platform_data->addr[CY_TCH_ADDR_OFS], true);
+
+ if (retval < 0) {
+ dev_err(ts->dev,
+ "%s: bus write fail on handshake (ret=%d)\n",
+ __func__, retval);
+ }
+
+ return retval;
+}
+
+static int _cyttsp4_cmd_handshake(struct cyttsp4 *ts)
+{
+ u8 host_mode = 0;
+ int retval = 0;
+
+ retval = _cyttsp4_read_block_data(ts, CY_REG_BASE,
+ sizeof(host_mode), &host_mode,
+ ts->platform_data->addr[CY_TCH_ADDR_OFS], true);
+ if (retval < 0) {
+ dev_err(ts->dev,
+ "%s: Fail read host mode r=%d\n",
+ __func__, retval);
+ } else {
+ retval = _cyttsp4_handshake(ts, host_mode);
+ if (retval < 0) {
+ dev_err(ts->dev,
+ "%s: Fail handshake r=%d\n",
+ __func__, retval);
+ }
+ }
+
+ return retval;
+}
+
+#ifdef CY_USE_TMA400
+#if defined(CY_AUTO_LOAD_TOUCH_PARAMS) || defined(CY_USE_DEV_DEBUG_TOOLS)
+static void _cyttsp_read_table_crc(struct cyttsp4 *ts, const u8 *ptable,
+ u8 *crc_h, u8 *crc_l)
+{
+ size_t crc_loc = (ptable[3] * 256) + ptable[2];
+
+ *crc_l = ptable[crc_loc];
+ *crc_h = ptable[crc_loc + 1];
+}
+#endif
+
+/* Get EBID Row Size is a Config mode command */
+static int _cyttsp4_get_ebid_row_size(struct cyttsp4 *ts)
+{
+ int retval = 0;
+ u8 cmd = 0;
+ u8 cmd_dat[CY_NUM_DAT + 1]; /* +1 for cmd byte */
+
+ memset(cmd_dat, 0, sizeof(cmd_dat));
+ cmd_dat[0] = CY_GET_EBID_ROW_SIZE; /* get EBID row size command */
+
+ retval = _cyttsp4_put_cmd_wait(ts, ts->si_ofs.cmd_ofs,
+ sizeof(cmd_dat), cmd_dat, CY_HALF_SEC_TMO_MS,
+ _cyttsp4_chk_cmd_rdy, &cmd,
+ ts->platform_data->addr[CY_TCH_ADDR_OFS], true);
+ if (retval < 0) {
+ dev_err(ts->dev,
+ "%s: Fail Get EBID row size command r=%d\n",
+ __func__, retval);
+ } else {
+ retval = _cyttsp4_read_block_data(ts, ts->si_ofs.cmd_ofs,
+ sizeof(cmd_dat), cmd_dat,
+ ts->platform_data->addr[CY_TCH_ADDR_OFS], true);
+ if (retval < 0) {
+ dev_err(ts->dev,
+ "%s: Fail get EBID row size r=%d\n",
+ __func__, retval);
+ ts->ebid_row_size = CY_EBID_ROW_SIZE_DFLT;
+ dev_err(ts->dev,
+ "%s: Use default EBID row size=%d\n",
+ __func__, ts->ebid_row_size);
+ } else {
+ ts->ebid_row_size = (cmd_dat[1] * 256) + cmd_dat[2];
+ retval = _cyttsp4_cmd_handshake(ts);
+ if (retval < 0) {
+ dev_err(ts->dev,
+ "%s: Command handshake error r=%d\n",
+ __func__, retval);
+ /* continue anyway; rely on handshake tmo */
+ retval = 0;
+ }
+ }
+ }
+
+ return retval;
+}
+
+static const u8 cyttsp4_security_key[] = {
+ 0xA5, 0x01, 0x02, 0x03, 0xFF, 0xFE, 0xFD, 0x5A
+};
+
+#if defined(CY_AUTO_LOAD_TOUCH_PARAMS) || \
+ defined(CY_AUTO_LOAD_DDATA) || defined(CY_AUTO_LOAD_MDATA) || \
+ defined(CY_USE_DEV_DEBUG_TOOLS) || defined(FACTORY_TESTING)
+/* Get EBID Row Data is a Config mode command */
+static int _cyttsp4_get_ebid_data_tma400(struct cyttsp4 *ts,
+ enum cyttsp4_ic_ebid ebid, size_t row_id, u8 *pdata)
+{
+ int rc = 0;
+ int retval = 0;
+ u8 crc_h = 0;
+ u8 crc_l = 0;
+ u8 cmd = 0;
+ u8 status = 0;
+ u8 cmd_dat[CY_NUM_DAT + 1]; /* +1 for cmd byte */
+
+ memset(cmd_dat, 0, sizeof(cmd_dat));
+ cmd_dat[0] = CY_READ_EBID_DATA; /* get EBID data command */
+ cmd_dat[1] = row_id / 256;
+ cmd_dat[2] = row_id % 256;
+ cmd_dat[3] = ts->ebid_row_size / 256;
+ cmd_dat[4] = ts->ebid_row_size % 256;
+ cmd_dat[5] = ebid;
+
+ if (pdata == NULL) {
+ dev_err(ts->dev,
+ "%s: Get EBID=%d row=%d Data buffer err ptr=%p\n",
+ __func__, ebid, row_id, pdata);
+ goto _cyttsp4_get_ebid_data_tma400_exit;
+ }
+
+ retval = _cyttsp4_put_cmd_wait(ts, ts->si_ofs.cmd_ofs,
+ sizeof(cmd_dat), cmd_dat, CY_HALF_SEC_TMO_MS,
+ _cyttsp4_chk_cmd_rdy, &cmd,
+ ts->platform_data->addr[CY_TCH_ADDR_OFS], true);
+ if (retval < 0) {
+ dev_err(ts->dev,
+ "%s: Fail Get EBID=%d row=%d Data cmd r=%d\n",
+ __func__, ebid, row_id, retval);
+ goto _cyttsp4_get_ebid_data_tma400_exit;
+ }
+
+ retval = _cyttsp4_read_block_data(ts, ts->si_ofs.cmd_ofs + 1,
+ sizeof(status), &status,
+ ts->platform_data->addr[CY_TCH_ADDR_OFS], true);
+ if (retval < 0) {
+ dev_err(ts->dev,
+ "%s: Fail get EBID=%d row=%d status r=%d\n",
+ __func__, ebid, row_id, retval);
+ goto _cyttsp4_get_ebid_data_tma400_exit;
+ }
+
+ if (status != 0x00) {
+ dev_err(ts->dev,
+ "%s: Get EBID=%d row=%d status=%d error\n",
+ __func__, ebid, row_id, status);
+ retval = -EIO;
+ goto _cyttsp4_get_ebid_data_tma400_exit;
+ }
+
+ retval = _cyttsp4_read_block_data(ts, ts->si_ofs.cmd_ofs + 1 + 5,
+ ts->ebid_row_size + 2, pdata,
+ ts->platform_data->addr[CY_TCH_ADDR_OFS], true);
+ if (retval < 0) {
+ dev_err(ts->dev,
+ "%s: fail EBID=%d row=%d data r=%d\n",
+ __func__, ebid, row_id, retval);
+ retval = -EIO;
+ } else {
+ _cyttsp4_calc_crc(ts, pdata, ts->ebid_row_size, &crc_h, &crc_l);
+ if (pdata[ts->ebid_row_size] != crc_h ||
+ pdata[ts->ebid_row_size + 1] != crc_l) {
+ dev_err(ts->dev,
+ "%s: EBID=%d row_id=%d row_data_crc=%02X%02X"
+ " not equal to calc_crc=%02X%02X\n",
+ __func__, ebid, row_id,
+ pdata[ts->ebid_row_size],
+ pdata[ts->ebid_row_size + 1],
+ crc_h, crc_l);
+ /* continue anyway; allow handshake */
+ rc = -EIO;
+ }
+ retval = _cyttsp4_cmd_handshake(ts);
+ if (retval < 0) {
+ dev_err(ts->dev,
+ "%s: Command handshake error r=%d\n",
+ __func__, retval);
+ /* continue anyway; rely on handshake tmo */
+ retval = 0;
+ }
+ retval = rc;
+ }
+
+_cyttsp4_get_ebid_data_tma400_exit:
+ return retval;
+}
+
+/* Put EBID Row Data is a Config mode command */
+static int _cyttsp4_put_ebid_data_tma400(struct cyttsp4 *ts,
+ enum cyttsp4_ic_ebid ebid, size_t row_id, u8 *out_data)
+{
+ u8 calc_crc[2];
+ u8 *pdata = NULL;
+ u8 ret_cmd = 0;
+ size_t psize = 0;
+ u8 status = 0;
+ int retval = 0;
+
+ memset(calc_crc, 0, sizeof(calc_crc));
+ psize = 1 + 5 + ts->ebid_row_size + sizeof(cyttsp4_security_key) + 2;
+ pdata = kzalloc(psize, GFP_KERNEL);
+ if (pdata == NULL || out_data == NULL) {
+ dev_err(ts->dev,
+ "%s: Buffer ptr err EBID=%d row=%d"
+ " alloc_ptr=%p out_data=%p\n",
+ __func__, ebid, row_id, pdata, out_data);
+ retval = -EINVAL;
+ } else {
+ pdata[0] = CY_WRITE_EBID_DATA; /* put ebid data command */
+ pdata[1] = row_id / 256;
+ pdata[2] = row_id % 256;
+ pdata[3] = ts->ebid_row_size / 256;
+ pdata[4] = ts->ebid_row_size % 256;
+ pdata[5] = ebid;
+ memcpy(&pdata[1 + 5], out_data, ts->ebid_row_size);
+ memcpy(&pdata[1 + 5 + ts->ebid_row_size],
+ cyttsp4_security_key, sizeof(cyttsp4_security_key));
+ _cyttsp4_calc_crc(ts, &pdata[1 + 5], ts->ebid_row_size,
+ &calc_crc[0], &calc_crc[1]);
+ memcpy(&pdata[1 + 5 + ts->ebid_row_size +
+ sizeof(cyttsp4_security_key)],
+ calc_crc, sizeof(calc_crc));
+
+ retval = _cyttsp4_put_cmd_wait(ts, ts->si_ofs.cmd_ofs,
+ psize, pdata, CY_HALF_SEC_TMO_MS,
+ _cyttsp4_chk_cmd_rdy, &ret_cmd,
+ ts->platform_data->addr[CY_TCH_ADDR_OFS], true);
+ if (retval < 0) {
+ dev_err(ts->dev,
+ "%s: Fail Put EBID=%d row=%d Data cmd r=%d\n",
+ __func__, ebid, row_id, retval);
+ goto _cyttsp4_put_ebid_data_tma400_exit;
+ }
+
+ retval = _cyttsp4_read_block_data(ts, ts->si_ofs.cmd_ofs + 1,
+ sizeof(status), &status,
+ ts->platform_data->addr[CY_TCH_ADDR_OFS], true);
+ if (retval < 0) {
+ dev_err(ts->dev,
+ "%s: Fail put EBID=%d row=%d"
+ " read status r=%d\n",
+ __func__, ebid, row_id, retval);
+ goto _cyttsp4_put_ebid_data_tma400_exit;
+ }
+
+ retval = _cyttsp4_cmd_handshake(ts);
+ if (retval < 0) {
+ dev_err(ts->dev,
+ "%s: Fail handshake on Put EBID=%d row=%d"
+ " r=%d\n", __func__, ebid, row_id, retval);
+ /* continue; rely on handshake tmo */
+ retval = 0;
+ }
+
+ if (status != 0x00) {
+ dev_err(ts->dev,
+ "%s: Put EBID=%d row=%d status=%d error\n",
+ __func__, ebid, row_id, status);
+ retval = -EIO;
+ } else
+ retval = 0;
+ }
+_cyttsp4_put_ebid_data_tma400_exit:
+ if (pdata != NULL)
+ kfree(pdata);
+ return retval;
+}
+#endif /* --CY_AUTO_LOAD_TOUCH_PARAMS --CY_AUTO_LOAD_DDATA
+ --CY_AUTO_LOAD_MDATA --CY_USE_DEV_DEBUG_TOOLS */
+
+#if defined(CY_AUTO_LOAD_TOUCH_PARAMS) || defined(CY_USE_DEV_DEBUG_TOOLS)
+/* Put All Touch Params is a Config mode command */
+static int _cyttsp4_put_all_params_tma400(struct cyttsp4 *ts)
+{
+ enum cyttsp4_ic_ebid ebid = CY_TCH_PARM_EBID;
+ size_t row_id = 0;
+ size_t num_rows = 0;
+ size_t table_size = 0;
+ size_t residue = 0;
+ u8 *pdata = NULL;
+ u8 *ptable = NULL;
+ int retval = 0;
+
+ pdata = kzalloc(ts->ebid_row_size, GFP_KERNEL);
+ if (pdata == NULL) {
+ dev_err(ts->dev,
+ "%s: Alloc error ebid=%d\n",
+ __func__, ebid);
+ retval = -ENOMEM;
+ } else if (ts->platform_data->sett[CY_IC_GRPNUM_TCH_PARM_VAL] == NULL)
+ dev_err(ts->dev,
+ "%s: NULL param values table\n", __func__);
+ else if (ts->platform_data->sett[CY_IC_GRPNUM_TCH_PARM_VAL]
+ ->data == NULL)
+ dev_err(ts->dev,
+ "%s: NULL param values table data\n", __func__);
+ else if (ts->platform_data->sett[CY_IC_GRPNUM_TCH_PARM_VAL]->size == 0)
+ dev_err(ts->dev,
+ "%s: param values table size is 0\n", __func__);
+ else {
+ ptable = (u8 *)ts->platform_data->sett
+ [CY_IC_GRPNUM_TCH_PARM_VAL]->data;
+ table_size = ts->platform_data->sett
+ [CY_IC_GRPNUM_TCH_PARM_VAL]->size;
+ num_rows = table_size / ts->ebid_row_size;
+ dev_vdbg(ts->dev,
+ "%s: num_rows=%d row_size=%d"
+ " table_size=%d\n", __func__,
+ num_rows, ts->ebid_row_size, table_size);
+ for (row_id = 0; row_id < num_rows;) {
+ memcpy(pdata, ptable, ts->ebid_row_size);
+ dev_vdbg(ts->dev,
+ "%s: row=%d pdata=%p\n",
+ __func__, row_id, pdata);
+ _cyttsp4_pr_buf(ts, pdata, ts->ebid_row_size,
+ "ebid_data");
+ retval = _cyttsp4_put_ebid_data_tma400(ts,
+ ebid, row_id, pdata);
+ if (retval < 0) {
+ dev_err(ts->dev,
+ "%s: Fail put row=%d r=%d\n",
+ __func__, row_id, retval);
+ break;
+ } else {
+ ptable += ts->ebid_row_size;
+ row_id++;
+ }
+ }
+ if (!(retval < 0)) {
+ residue = table_size % ts->ebid_row_size;
+ if (residue) {
+ memset(pdata, 0, ts->ebid_row_size);
+ memcpy(pdata, ptable, residue);
+ dev_vdbg(ts->dev,
+ "%s: ebid=%d row=%d data:\n",
+ __func__, ebid, row_id);
+ _cyttsp4_pr_buf(ts, pdata, ts->ebid_row_size,
+ "ebid_data");
+ retval = _cyttsp4_put_ebid_data_tma400(ts,
+ ebid, row_id, pdata);
+ if (retval < 0) {
+ dev_err(ts->dev,
+ "%s: Fail put row=%d r=%d\n",
+ __func__, row_id, retval);
+ }
+ }
+ }
+ }
+
+ if (pdata != NULL)
+ kfree(pdata);
+
+ return retval;
+}
+#endif /* --CY_AUTO_LOAD_TOUCH_PARAMS */
+
+/* Check MDDATA is a Config mode command */
+static int _cyttsp4_check_mddata_tma400(struct cyttsp4 *ts, bool *updated)
+{
+ bool ddata_updated = false;
+ bool mdata_updated = false;
+#if defined(CY_AUTO_LOAD_DDATA) || defined(CY_AUTO_LOAD_MDATA)
+ enum cyttsp4_ic_ebid ebid = CY_DDATA_EBID;
+ size_t num_data = 0;
+ size_t crc_ofs = 0;
+ u8 crc_h = 0;
+ u8 crc_l = 0;
+#endif
+ u8 *pdata = NULL;
+ u8 *pmddata = NULL;
+ int retval = 0;
+
+ if (ts->ebid_row_size == 0) {
+ dev_err(ts->dev,
+ "%s: fail allocate set MDDATA buffer\n", __func__);
+ retval = -EINVAL;
+ goto _cyttsp4_check_mddata_tma400_exit;
+ }
+ pdata = kzalloc(ts->ebid_row_size, GFP_KERNEL);
+ if (pdata == NULL) {
+ dev_err(ts->dev,
+ "%s: fail allocate set MDDATA buffer\n", __func__);
+ retval = -ENOMEM;
+ goto _cyttsp4_check_mddata_tma400_exit;
+ }
+ pmddata = kzalloc(ts->ebid_row_size, GFP_KERNEL);
+ if (pmddata == NULL) {
+ dev_err(ts->dev,
+ "%s: fail allocate set MDDATA buffer\n", __func__);
+ retval = -ENOMEM;
+ goto _cyttsp4_check_mddata_tma400_exit;
+ }
+
+#ifdef CY_AUTO_LOAD_DDATA
+ /* check for platform_data DDATA */
+ ebid = CY_DDATA_EBID;
+ if (ts->platform_data->sett[CY_IC_GRPNUM_DDATA_REC] == NULL) {
+ dev_vdbg(ts->dev,
+ "%s: No platform DDATA table\n", __func__);
+ goto _cyttsp4_check_mdata_block;
+ }
+ if (ts->platform_data->sett[CY_IC_GRPNUM_DDATA_REC]->data == NULL) {
+ dev_vdbg(ts->dev,
+ "%s: No platform DDATA table data\n", __func__);
+ goto _cyttsp4_check_mdata_block;
+ }
+ if (ts->platform_data->sett[CY_IC_GRPNUM_DDATA_REC]->size == 0) {
+ dev_vdbg(ts->dev,
+ "%s: Platform DDATA table has size=0\n", __func__);
+ goto _cyttsp4_check_mdata_block;
+ }
+
+ dev_vdbg(ts->dev,
+ "%s: call get ebid data for DDATA\n", __func__);
+ retval = _cyttsp4_get_ebid_data_tma400(ts, ebid, 0, pdata);
+ if (retval < 0) {
+ dev_err(ts->dev,
+ "%s: Fail get DDATA r=%d\n", __func__, retval);
+ goto _cyttsp4_check_mdata_block;
+ }
+
+ dev_vdbg(ts->dev,
+ "%s: copy pdata -> pmddata\n", __func__);
+ memcpy(pmddata, pdata, 4);
+ num_data = ts->platform_data->sett
+ [CY_IC_GRPNUM_DDATA_REC]->size < CY_NUM_DDATA ?
+ ts->platform_data->sett
+ [CY_IC_GRPNUM_DDATA_REC]->size : CY_NUM_DDATA;
+ dev_vdbg(ts->dev,
+ "%s: copy %d bytes from platform data to ddata array\n",
+ __func__, num_data);
+ memcpy(&pmddata[4], ts->platform_data->sett
+ [CY_IC_GRPNUM_DDATA_REC]->data, num_data);
+ if (num_data < CY_NUM_DDATA)
+ memset(&pmddata[4 + num_data], 0, CY_NUM_DDATA - num_data);
+ crc_ofs = (pmddata[3] * 256) + pmddata[2];
+ if (crc_ofs == 0)
+ crc_ofs = 126;
+ dev_vdbg(ts->dev,
+ "%s: ddata crc_ofs=%d num_data=%d\n",
+ __func__, crc_ofs, num_data);
+
+ _cyttsp4_calc_crc(ts, pmddata, crc_ofs, &crc_h, &crc_l);
+ pmddata[crc_ofs] = crc_l;
+ pmddata[crc_ofs+1] = crc_h;
+ _cyttsp4_pr_buf(ts, pdata, ts->ebid_row_size, "pdata");
+ _cyttsp4_pr_buf(ts, pmddata, ts->ebid_row_size, "pmddata");
+ if (pmddata[crc_ofs] != pdata[crc_ofs] ||
+ pmddata[crc_ofs+1] != pdata[crc_ofs+1]) {
+ retval = _cyttsp4_put_ebid_data_tma400(ts, ebid, 0, pmddata);
+ if (retval < 0)
+ dev_err(ts->dev,
+ "%s: Fail put DDATA r=%d\n", __func__, retval);
+ else
+ ddata_updated = true;
+ }
+
+_cyttsp4_check_mdata_block:
+#else
+ ddata_updated = false;
+#endif /* --CY_AUTO_LOAD_DDATA */
+
+#ifdef CY_AUTO_LOAD_MDATA
+ /* check for platform_data MDATA */
+ memset(pdata, 0, ts->ebid_row_size);
+ memset(pmddata, 0, ts->ebid_row_size);
+ ebid = CY_MDATA_EBID;
+ if (ts->platform_data->sett[CY_IC_GRPNUM_MDATA_REC] == NULL) {
+ dev_vdbg(ts->dev,
+ "%s: No platform MDATA table\n", __func__);
+ goto _cyttsp4_check_mddata_tma400_exit;
+ }
+ if (ts->platform_data->sett[CY_IC_GRPNUM_MDATA_REC]->data == NULL) {
+ dev_vdbg(ts->dev,
+ "%s: No platform MDATA table data\n", __func__);
+ goto _cyttsp4_check_mddata_tma400_exit;
+ }
+ if (ts->platform_data->sett[CY_IC_GRPNUM_MDATA_REC]->size == 0) {
+ dev_vdbg(ts->dev,
+ "%s: Platform MDATA table has size=0\n", __func__);
+ goto _cyttsp4_check_mddata_tma400_exit;
+ }
+
+ retval = _cyttsp4_get_ebid_data_tma400(ts, ebid, 0, pdata);
+ if (retval < 0) {
+ dev_err(ts->dev,
+ "%s: Fail get MDATA r=%d\n", __func__, retval);
+ goto _cyttsp4_check_mddata_tma400_exit;
+ }
+
+ memcpy(pmddata, pdata, 4);
+ num_data = ts->platform_data->sett
+ [CY_IC_GRPNUM_MDATA_REC]->size < CY_NUM_MDATA ?
+ ts->platform_data->sett
+ [CY_IC_GRPNUM_MDATA_REC]->size : CY_NUM_MDATA;
+ dev_vdbg(ts->dev,
+ "%s: copy %d bytes from platform data to mdata array\n",
+ __func__, num_data);
+ memcpy(&pmddata[4], ts->platform_data->sett
+ [CY_IC_GRPNUM_MDATA_REC]->data, num_data);
+ if (num_data < CY_NUM_MDATA)
+ memset(&pmddata[4 + num_data], 0, CY_NUM_MDATA - num_data);
+ crc_ofs = (pmddata[3] * 256) + pmddata[2];
+ if (crc_ofs == 0)
+ crc_ofs = 124;
+ dev_vdbg(ts->dev,
+ "%s: mdata crc_ofs=%d num_data=%d\n",
+ __func__, crc_ofs, num_data);
+ _cyttsp4_calc_crc(ts, pmddata, crc_ofs, &crc_h, &crc_l);
+ pmddata[crc_ofs] = crc_l;
+ pmddata[crc_ofs+1] = crc_h;
+ _cyttsp4_pr_buf(ts, pdata, ts->ebid_row_size, "pdata");
+ _cyttsp4_pr_buf(ts, pmddata, ts->ebid_row_size, "pmddata");
+ if (pmddata[crc_ofs] != pdata[crc_ofs] ||
+ pmddata[crc_ofs+1] != pdata[crc_ofs+1]) {
+ retval = _cyttsp4_put_ebid_data_tma400(ts, ebid, 0, pmddata);
+ if (retval < 0)
+ dev_err(ts->dev,
+ "%s: Fail put MDATA r=%d\n", __func__, retval);
+ else
+ mdata_updated = true;
+ }
+#else
+ mdata_updated = false;
+#endif /* --CY_AUTO_LOAD_MDATA */
+
+_cyttsp4_check_mddata_tma400_exit:
+ if (pdata != NULL)
+ kfree(pdata);
+ if (pmddata != NULL)
+ kfree(pmddata);
+ if (updated != NULL)
+ *updated = ddata_updated || mdata_updated;
+ return retval;
+}
+#endif /* --CY_USE_TMA400 */
+
+#ifdef CY_USE_TMA884
+static int _cyttsp4_handshake_enable(struct cyttsp4 *ts)
+{
+ int retval = 0;
+ u8 cmd_dat[CY_NUM_DAT + 1]; /* +1 for cmd byte */
+
+ memset(cmd_dat, 0, sizeof(cmd_dat));
+ cmd_dat[0] = 0x26; /* handshake enable operational cmd */
+ cmd_dat[1] = 0x03; /* synchronous level handshake */
+ retval = _cyttsp4_put_cmd_wait(ts, ts->si_ofs.cmd_ofs,
+ sizeof(cmd_dat), cmd_dat, CY_HALF_SEC_TMO_MS,
+ _cyttsp4_chk_cmd_rdy, NULL,
+ ts->platform_data->addr[CY_TCH_ADDR_OFS], true);
+ if (retval < 0) {
+ dev_err(ts->dev,
+ "%s: Fail Enable Handshake command r=%d\n",
+ __func__, retval);
+ goto _cyttsp4_set_handshake_enable_exit;
+ }
+
+ retval = _cyttsp4_read_block_data(ts, ts->si_ofs.cmd_ofs,
+ sizeof(cmd_dat), cmd_dat,
+ ts->platform_data->addr[CY_TCH_ADDR_OFS], true);
+ if (retval < 0) {
+ dev_err(ts->dev,
+ "%s: Fail read Enable Hanshake command status"
+ "r=%d\n", __func__, retval);
+ goto _cyttsp4_set_handshake_enable_exit;
+ }
+
+ if (cmd_dat[6] != cmd_dat[1]) {
+ dev_err(ts->dev,
+ "%s: Fail enable handshake in device\n",
+ __func__);
+ /* return no error and let driver handshake anyway */
+ }
+
+ dev_vdbg(ts->dev,
+ "%s: check cmd ready r=%d"
+ " cmd[]=%02X %02X %02X %02X %02X %02X %02X\n",
+ __func__, retval,
+ cmd_dat[0], cmd_dat[1], cmd_dat[2], cmd_dat[3],
+ cmd_dat[4], cmd_dat[5], cmd_dat[6]);
+
+_cyttsp4_set_handshake_enable_exit:
+ return retval;
+}
+#endif /* --CY_USE_TMA884 */
+
+/*
+ * change device mode - For example, change from
+ * system information mode to operating mode
+ */
+static int _cyttsp4_set_device_mode(struct cyttsp4 *ts,
+ u8 new_mode, u8 new_cur_mode, char *mode)
+{
+ u8 cmd = 0;
+ int retval = 0;
+
+ cmd = new_mode + CY_MODE_CHANGE;
+
+ retval = _cyttsp4_put_cmd_wait(ts, CY_REG_BASE,
+ sizeof(cmd), &cmd, CY_HALF_SEC_TMO_MS,
+ _cyttsp4_chk_mode_change, &cmd,
+ ts->platform_data->addr[CY_TCH_ADDR_OFS], true);
+ if (retval < 0) {
+ dev_err(ts->dev,
+ "%s: Fail Set mode command new_mode=%02X r=%d\n",
+ __func__, new_mode, retval);
+ goto _cyttsp4_set_device_mode_exit;
+ }
+
+ if (cmd != new_mode) {
+ dev_err(ts->dev,
+ "%s: failed to switch to %s mode\n", __func__, mode);
+ retval = -EIO;
+ } else {
+ ts->current_mode = new_cur_mode;
+ retval = _cyttsp4_handshake(ts, cmd);
+ if (retval < 0) {
+ dev_err(ts->dev,
+ "%s: Fail handshake r=%d\n", __func__, retval);
+ /* continue; rely on handshake tmo */
+ retval = 0;
+ }
+ }
+
+ dev_dbg(ts->dev,
+ "%s: check op ready ret=%d host_mode=%02X\n",
+ __func__, retval, cmd);
+
+_cyttsp4_set_device_mode_exit:
+ return retval;
+}
+
+static int _cyttsp4_set_mode(struct cyttsp4 *ts, u8 new_mode)
+{
+ enum cyttsp4_driver_state new_state = CY_TRANSFER_STATE;
+ u8 new_cur_mode = CY_MODE_OPERATIONAL;
+ char *mode = NULL;
+#ifdef CY_USE_TMA400
+ unsigned long uretval = 0;
+#endif /* --CY_USE_TMA400 */
+ int retval = 0;
+
+ switch (new_mode) {
+ case CY_OPERATE_MODE:
+ new_cur_mode = CY_MODE_OPERATIONAL;
+ mode = "operational";
+ INIT_COMPLETION(ts->ready_int_running);
+ _cyttsp4_change_state(ts, CY_READY_STATE);
+ new_state = CY_ACTIVE_STATE;
+ break;
+ case CY_SYSINFO_MODE:
+ new_cur_mode = CY_MODE_SYSINFO;
+ mode = "sysinfo";
+ new_state = CY_SYSINFO_STATE;
+ break;
+ case CY_CONFIG_MODE:
+ new_cur_mode = CY_MODE_OPERATIONAL;
+ mode = "config";
+ new_state = ts->driver_state;
+
+ break;
+ default:
+ dev_err(ts->dev,
+ "%s: invalid mode change request m=0x%02X\n",
+ __func__, new_mode);
+ retval = -EINVAL;
+ goto _cyttsp_set_mode_exit;
+ }
+
+ retval = _cyttsp4_set_device_mode(ts,
+ new_mode, new_cur_mode, mode);
+ if (retval < 0) {
+ dev_err(ts->dev,
+ "%s: Fail switch to %s mode\n", __func__, mode);
+ _cyttsp4_change_state(ts, CY_IDLE_STATE);
+ } else {
+#ifdef CY_USE_TMA400
+ if ((new_mode == CY_OPERATE_MODE) && ts->starting_up) {
+ uretval = _cyttsp4_wait_ready_int_no_init(ts,
+ CY_HALF_SEC_TMO_MS * 5);
+ }
+#endif /* --CY_USE_TMA400 */
+ _cyttsp4_change_state(ts, new_state);
+ }
+
+_cyttsp_set_mode_exit:
+ return retval;
+}
+
+#ifdef CY_USE_TMA884
+static int _cyttsp4_write_config_block(struct cyttsp4 *ts, u8 blockid,
+ const u8 *pdata, size_t ndata, u8 crc_h, u8 crc_l, const char *name)
+{
+ uint8_t *buf = NULL;
+ size_t buf_size = 0;
+ u8 status = 0;
+ int retval = 0;
+
+ /* pre-amble (10) + data (122) + crc (2) + key (8) */
+ buf_size = sizeof(uint8_t) * 142;
+ buf = kzalloc(buf_size, GFP_KERNEL);
+ if (buf == NULL) {
+ dev_err(ts->dev,
+ "%s: Failed to allocate buffer for %s\n",
+ __func__, name);
+ retval = -ENOMEM;
+ goto _cyttsp4_write_config_block_exit;
+ }
+
+ if (pdata == NULL) {
+ dev_err(ts->dev,
+ "%s: bad data pointer\n", __func__);
+ retval = -ENXIO;
+ goto _cyttsp4_write_config_block_exit;
+ }
+
+ if (ndata > 122) {
+ dev_err(ts->dev,
+ "%s: %s is too large n=%d size=%d\n",
+ __func__, name, ndata, 122);
+ retval = -EOVERFLOW;
+ goto _cyttsp4_write_config_block_exit;
+ }
+
+ /* Set command bytes */
+ buf[0] = 0x04; /* cmd */
+ buf[1] = 0x00; /* row offset high */
+ buf[2] = 0x00; /* row offset low */
+ buf[3] = 0x00; /* write block length high */
+ buf[4] = 0x80; /* write block length low */
+ buf[5] = blockid; /* write block id */
+ buf[6] = 0x00; /* num of config bytes + 4 high */
+ buf[7] = 0x7E; /* num of config bytes + 4 low */
+ buf[8] = 0x00; /* max block size w/o crc high */
+ buf[9] = 0x7E; /* max block size w/o crc low */
+
+ /* Copy platform data */
+ memcpy(&(buf[10]), pdata, ndata);
+
+ /* Copy block CRC */
+ buf[132] = crc_h;
+ buf[133] = crc_l;
+
+ /* Set key bytes */
+ buf[134] = 0x45;
+ buf[135] = 0x63;
+ buf[136] = 0x36;
+ buf[137] = 0x6F;
+ buf[138] = 0x34;
+ buf[139] = 0x38;
+ buf[140] = 0x73;
+ buf[141] = 0x77;
+
+ /* Write config block */
+ _cyttsp4_pr_buf(ts, buf, buf_size, name);
+
+ retval = _cyttsp4_write_block_data(ts, ts->si_ofs.cmd_ofs + 1,
+ 141, &(buf[1]),
+ ts->platform_data->addr[CY_TCH_ADDR_OFS], true);
+ if (retval < 0) {
+ dev_err(ts->dev,
+ "%s: Failed to write config %s r=%d\n",
+ __func__, name, retval);
+ goto _cyttsp4_write_config_block_exit;
+ }
+
+ retval = _cyttsp4_put_cmd_wait(ts, ts->si_ofs.cmd_ofs,
+ 1, &(buf[0]), CY_TEN_SEC_TMO_MS,
+ _cyttsp4_chk_cmd_rdy, NULL,
+ ts->platform_data->addr[CY_TCH_ADDR_OFS], true);
+ if (retval < 0) {
+ dev_err(ts->dev,
+ "%s: Fail write config command r=%d\n",
+ __func__, retval);
+ goto _cyttsp4_write_config_block_exit;
+ }
+
+ retval = _cyttsp4_read_block_data(ts, ts->si_ofs.cmd_ofs + 1,
+ sizeof(status), &status,
+ ts->platform_data->addr[CY_TCH_ADDR_OFS], true);
+ if (retval < 0) {
+ dev_err(ts->dev,
+ "%s: Fail read status r=%d\n",
+ __func__, retval);
+ goto _cyttsp4_write_config_block_exit;
+ }
+
+ if (status != 0x00) {
+ dev_err(ts->dev,
+ "%s: Write config status=%d error\n",
+ __func__, status);
+ goto _cyttsp4_write_config_block_exit;
+ }
+
+_cyttsp4_write_config_block_exit:
+ kfree(buf);
+ return retval;
+}
+#endif /* --CY_USE_TMA884 */
+
+
+#ifdef CY_USE_TMA884
+#ifdef CY_AUTO_LOAD_TOUCH_PARAMS
+static int _cyttsp4_set_op_params(struct cyttsp4 *ts, u8 crc_h, u8 crc_l)
+{
+ int retval = 0;
+
+ if (ts->platform_data->sett[CY_IC_GRPNUM_TCH_PARM_VAL] == NULL) {
+ dev_err(ts->dev,
+ "%s: Missing Platform Touch Parameter"
+ " values table\n", __func__);
+ retval = -ENXIO;
+ goto _cyttsp4_set_op_params_exit;
+ }
+
+ if ((ts->platform_data->sett
+ [CY_IC_GRPNUM_TCH_PARM_VAL]->data == NULL) ||
+ (ts->platform_data->sett
+ [CY_IC_GRPNUM_TCH_PARM_VAL]->size == 0)) {
+ dev_err(ts->dev,
+ "%s: Missing Platform Touch Parameter"
+ " values table data\n", __func__);
+ retval = -ENXIO;
+ goto _cyttsp4_set_op_params_exit;
+ }
+
+ /* Change to Config Mode */
+ retval = _cyttsp4_set_mode(ts, CY_CONFIG_MODE);
+ if (retval < 0) {
+ dev_err(ts->dev,
+ "%s: Failed to switch to config mode"
+ " for touch params\n", __func__);
+ goto _cyttsp4_set_op_params_exit;
+ }
+ retval = _cyttsp4_write_config_block(ts, CY_TCH_PARM_EBID,
+ ts->platform_data->sett[CY_IC_GRPNUM_TCH_PARM_VAL]->data,
+ ts->platform_data->sett[CY_IC_GRPNUM_TCH_PARM_VAL]->size,
+ crc_h, crc_l, "platform_touch_param_data");
+
+_cyttsp4_set_op_params_exit:
+ return retval;
+}
+#endif /* --CY_AUTO_LOAD_TOUCH_PARAMS */
+
+static int _cyttsp4_set_data_block(struct cyttsp4 *ts, u8 blkid, u8 *pdata,
+ size_t ndata, const char *name, bool force, bool *data_updated)
+{
+ u8 data_crc[2];
+ u8 ic_crc[2];
+ int retval = 0;
+
+ memset(data_crc, 0, sizeof(data_crc));
+ memset(ic_crc, 0, sizeof(ic_crc));
+ *data_updated = false;
+
+ _cyttsp4_pr_buf(ts, pdata, ndata, name);
+
+ dev_vdbg(ts->dev,
+ "%s: calc %s crc\n", __func__, name);
+ retval = _cyttsp4_calc_data_crc(ts, ndata, pdata,
+ &data_crc[0], &data_crc[1],
+ name);
+ if (retval < 0) {
+ dev_err(ts->dev,
+ "%s: fail calc crc for %s (0x%02X%02X) r=%d\n",
+ __func__, name,
+ data_crc[0], data_crc[1],
+ retval);
+ goto _cyttsp_set_data_block_exit;
+ }
+
+ dev_vdbg(ts->dev,
+ "%s: get ic %s crc\n", __func__, name);
+ retval = _cyttsp4_set_mode(ts, CY_OPERATE_MODE);
+ if (retval < 0) {
+ dev_err(ts->dev,
+ "%s: Failed to switch to operational mode\n", __func__);
+ goto _cyttsp_set_data_block_exit;
+ }
+ retval = _cyttsp4_get_ic_crc(ts, blkid,
+ &ic_crc[0], &ic_crc[1]);
+ if (retval < 0) {
+ dev_err(ts->dev,
+ "%s: fail get ic crc for %s (0x%02X%02X) r=%d\n",
+ __func__, name,
+ ic_crc[0], ic_crc[1],
+ retval);
+ goto _cyttsp_set_data_block_exit;
+ }
+
+ dev_vdbg(ts->dev,
+ "%s: %s calc_crc=0x%02X%02X ic_crc=0x%02X%02X\n",
+ __func__, name,
+ data_crc[0], data_crc[1],
+ ic_crc[0], ic_crc[1]);
+ if ((data_crc[0] != ic_crc[0]) || (data_crc[1] != ic_crc[1]) || force) {
+ /* Change to Config Mode */
+ retval = _cyttsp4_set_mode(ts, CY_CONFIG_MODE);
+ if (retval < 0) {
+ dev_err(ts->dev,
+ "%s: Failed to switch to config mode"
+ " for sysinfo regs\n", __func__);
+ goto _cyttsp_set_data_block_exit;
+ }
+ retval = _cyttsp4_write_config_block(ts, blkid, pdata,
+ ndata, data_crc[0], data_crc[1], name);
+ if (retval < 0) {
+ dev_err(ts->dev,
+ "%s: fail write %s config block r=%d\n",
+ __func__, name, retval);
+ goto _cyttsp_set_data_block_exit;
+ }
+
+ dev_vdbg(ts->dev,
+ "%s: write %s config block ok\n", __func__, name);
+ *data_updated = true;
+ }
+
+_cyttsp_set_data_block_exit:
+ return retval;
+}
+
+static int _cyttsp4_set_sysinfo_regs(struct cyttsp4 *ts, bool *updated)
+{
+ bool ddata_updated = false;
+ bool mdata_updated = false;
+#if defined(CY_AUTO_LOAD_DDATA) || defined(CY_AUTO_LOAD_MDATA)
+ size_t num_data = 0;
+#endif /* --CY_AUTO_LOAD_DDATA || --CY_AUTO_LOAD_DDATA */
+ u8 *pdata = NULL;
+ int retval = 0;
+
+ pdata = kzalloc(CY_NUM_MDATA, GFP_KERNEL);
+ if (pdata == NULL) {
+ dev_err(ts->dev,
+ "%s: fail allocate set sysinfo regs buffer\n",
+ __func__);
+ retval = -ENOMEM;
+ goto _cyttsp4_set_sysinfo_regs_err;
+ }
+
+#ifdef CY_AUTO_LOAD_DDATA
+ /* check for missing DDATA */
+ if (ts->platform_data->sett[CY_IC_GRPNUM_DDATA_REC] == NULL) {
+ dev_vdbg(ts->dev,
+ "%s: No platform_ddata table\n", __func__);
+ dev_vdbg(ts->dev,
+ "%s: Use a zero filled array to compare with device\n",
+ __func__);
+ goto _cyttsp4_set_sysinfo_regs_set_ddata_block;
+ }
+ if ((ts->platform_data->sett[CY_IC_GRPNUM_DDATA_REC]->data == NULL) ||
+ (ts->platform_data->sett[CY_IC_GRPNUM_DDATA_REC]->size == 0)) {
+ dev_vdbg(ts->dev,
+ "%s: No platform_ddata table data\n", __func__);
+ dev_vdbg(ts->dev,
+ "%s: Use a zero filled array to compare with device\n",
+ __func__);
+ goto _cyttsp4_set_sysinfo_regs_set_ddata_block;
+ }
+
+ /* copy platform data design data to the device eeprom */
+ num_data = ts->platform_data->sett
+ [CY_IC_GRPNUM_DDATA_REC]->size < CY_NUM_DDATA ?
+ ts->platform_data->sett
+ [CY_IC_GRPNUM_DDATA_REC]->size : CY_NUM_DDATA;
+ dev_vdbg(ts->dev,
+ "%s: copy %d bytes from platform data to ddata array\n",
+ __func__, num_data);
+ memcpy(pdata, ts->platform_data->sett[CY_IC_GRPNUM_DDATA_REC]->data,
+ num_data);
+
+_cyttsp4_set_sysinfo_regs_set_ddata_block:
+ /* set data block will check CRC match/nomatch */
+ retval = _cyttsp4_set_data_block(ts, CY_DDATA_EBID, pdata,
+ CY_NUM_DDATA, "platform_ddata", false, &ddata_updated);
+ if (retval < 0) {
+ dev_err(ts->dev,
+ "%s: Fail while writing platform_ddata"
+ " block to ic r=%d\n", __func__, retval);
+ }
+#else
+ ddata_updated = false;
+#endif /* --CY_AUTO_LOAD_DDATA */
+
+#ifdef CY_AUTO_LOAD_MDATA
+ /* check for missing MDATA */
+ if (ts->platform_data->sett[CY_IC_GRPNUM_MDATA_REC] == NULL) {
+ dev_vdbg(ts->dev,
+ "%s: No platform_mdata table\n", __func__);
+ dev_vdbg(ts->dev,
+ "%s: Use a zero filled array to compare with device\n",
+ __func__);
+ goto _cyttsp4_set_sysinfo_regs_set_mdata_block;
+ }
+ if ((ts->platform_data->sett[CY_IC_GRPNUM_MDATA_REC]->data == NULL) ||
+ (ts->platform_data->sett[CY_IC_GRPNUM_MDATA_REC]->size == 0)) {
+ dev_vdbg(ts->dev,
+ "%s: No platform_mdata table data\n", __func__);
+ dev_vdbg(ts->dev,
+ "%s: Use a zero filled array to compare with device\n",
+ __func__);
+ goto _cyttsp4_set_sysinfo_regs_set_mdata_block;
+ }
+
+ /* copy platform manufacturing data to the device eeprom */
+ num_data = ts->platform_data->sett
+ [CY_IC_GRPNUM_MDATA_REC]->size < CY_NUM_MDATA ?
+ ts->platform_data->sett
+ [CY_IC_GRPNUM_MDATA_REC]->size : CY_NUM_MDATA;
+ dev_vdbg(ts->dev,
+ "%s: copy %d bytes from platform data to mdata array\n",
+ __func__, num_data);
+ memcpy(pdata, ts->platform_data->sett[CY_IC_GRPNUM_MDATA_REC]->data,
+ num_data);
+
+_cyttsp4_set_sysinfo_regs_set_mdata_block:
+ /* set data block will check CRC match/nomatch */
+ retval = _cyttsp4_set_data_block(ts, CY_MDATA_EBID, pdata,
+ CY_NUM_MDATA, "platform_mdata", false, &mdata_updated);
+ if (retval < 0) {
+ dev_err(ts->dev,
+ "%s: Fail while writing platform_mdata"
+ " block to ic r=%d\n", __func__, retval);
+ }
+#else
+ mdata_updated = false;
+#endif /* --CY_AUTO_LOAD_MDATA */
+
+ kfree(pdata);
+_cyttsp4_set_sysinfo_regs_err:
+ *updated = ddata_updated || mdata_updated;
+ return retval;
+}
+#endif /* --CY_USE_TMA884 */
+
+static int _cyttsp4_bits_2_bytes(struct cyttsp4 *ts, int nbits, int *max)
+{
+ int nbytes;
+
+ *max = 1 << nbits;
+
+ for (nbytes = 0; nbits > 0;) {
+ dev_vdbg(ts->dev,
+ "%s: nbytes=%d nbits=%d\n", __func__, nbytes, nbits);
+ nbytes++;
+ if (nbits > 8)
+ nbits -= 8;
+ else
+ nbits = 0;
+ dev_vdbg(ts->dev,
+ "%s: nbytes=%d nbits=%d\n", __func__, nbytes, nbits);
+ }
+
+ return nbytes;
+}
+
+static int _cyttsp4_get_sysinfo_regs(struct cyttsp4 *ts)
+{
+ int btn = 0;
+ int num_defined_keys = 0;
+ u16 *key_table = NULL;
+ enum cyttsp4_tch_abs abs = 0;
+#ifdef CY_USE_TMA400_SP2
+#ifdef CY_USE_TMA400
+ int i = 0;
+#endif /* --CY_USE_TMA400 */
+#endif /* --CY_USE_TMA400_SP2 */
+ int retval = 0;
+
+ /* pre-clear si_ofs structure */
+ memset(&ts->si_ofs, 0, sizeof(struct cyttsp4_sysinfo_ofs));
+
+ /* get the sysinfo data offsets */
+ retval = _cyttsp4_read_block_data(ts, CY_REG_BASE,
+ sizeof(ts->sysinfo_data), &(ts->sysinfo_data),
+ ts->platform_data->addr[CY_TCH_ADDR_OFS], true);
+ if (retval < 0) {
+ dev_err(ts->dev,
+ "%s: fail read sysinfo data offsets r=%d\n",
+ __func__, retval);
+ goto _cyttsp4_get_sysinfo_regs_exit_no_handshake;
+ } else {
+ /* Print sysinfo data offsets */
+ _cyttsp4_pr_buf(ts, (u8 *)&ts->sysinfo_data,
+ sizeof(ts->sysinfo_data), "sysinfo_data_offsets");
+
+ /* convert sysinfo data offset bytes into integers */
+ ts->si_ofs.map_sz = (ts->sysinfo_data.map_szh * 256) +
+ ts->sysinfo_data.map_szl;
+ ts->si_ofs.cydata_ofs = (ts->sysinfo_data.cydata_ofsh * 256) +
+ ts->sysinfo_data.cydata_ofsl;
+ ts->si_ofs.test_ofs = (ts->sysinfo_data.test_ofsh * 256) +
+ ts->sysinfo_data.test_ofsl;
+ ts->si_ofs.pcfg_ofs = (ts->sysinfo_data.pcfg_ofsh * 256) +
+ ts->sysinfo_data.pcfg_ofsl;
+ ts->si_ofs.opcfg_ofs = (ts->sysinfo_data.opcfg_ofsh * 256) +
+ ts->sysinfo_data.opcfg_ofsl;
+ ts->si_ofs.ddata_ofs = (ts->sysinfo_data.ddata_ofsh * 256) +
+ ts->sysinfo_data.ddata_ofsl;
+ ts->si_ofs.mdata_ofs = (ts->sysinfo_data.mdata_ofsh * 256) +
+ ts->sysinfo_data.mdata_ofsl;
+ dev_err(ts->dev, "%s: ofset.map_sz:%x,cydata_ofs:%x,test_ofs:%x,pcfg_ofs:%x,opcfg_ofs:%x,ddata_ofs:%x,mdata_ofs:%x\n",
+ __func__, ts->si_ofs.map_sz, ts->si_ofs.cydata_ofs, ts->si_ofs.test_ofs, ts->si_ofs.pcfg_ofs, ts->si_ofs.opcfg_ofs, ts->si_ofs.ddata_ofs, ts->si_ofs.mdata_ofs);
+ if (ts->si_ofs.map_sz != 0xc1 || ts->si_ofs.cydata_ofs != 0x10) {
+ dev_err(ts->dev, "%s: ofset data is invalid\n", __func__);
+ goto _cyttsp4_get_sysinfo_regs_exit_no_handshake;
+ }
+ }
+
+ /* get the sysinfo cydata */
+ ts->si_ofs.cydata_size = ts->si_ofs.test_ofs - ts->si_ofs.cydata_ofs;
+ if (ts->sysinfo_ptr.cydata == NULL)
+ ts->sysinfo_ptr.cydata = kzalloc(ts->si_ofs.cydata_size, GFP_KERNEL);
+ if (ts->sysinfo_ptr.cydata == NULL) {
+ retval = -ENOMEM;
+ dev_err(ts->dev,
+ "%s: fail alloc cydata memory r=%d\n",
+ __func__, retval);
+ goto _cyttsp4_get_sysinfo_regs_exit;
+ } else {
+ memset(ts->sysinfo_ptr.cydata, 0, ts->si_ofs.cydata_size);
+ retval = _cyttsp4_read_block_data(ts, ts->si_ofs.cydata_ofs,
+ ts->si_ofs.cydata_size, ts->sysinfo_ptr.cydata,
+ ts->platform_data->addr[CY_TCH_ADDR_OFS], true);
+ if (retval < 0) {
+ dev_err(ts->dev,
+ "%s: fail read cydata r=%d\n",
+ __func__, retval);
+ goto _cyttsp4_get_sysinfo_regs_exit;
+ }
+ /* Print sysinfo cydata */
+ _cyttsp4_pr_buf(ts, (u8 *)ts->sysinfo_ptr.cydata,
+ ts->si_ofs.cydata_size, "sysinfo_cydata");
+ }
+ /* get the sysinfo test data */
+ ts->si_ofs.test_size = ts->si_ofs.pcfg_ofs - ts->si_ofs.test_ofs;
+ if (ts->sysinfo_ptr.test == NULL)
+ ts->sysinfo_ptr.test = kzalloc(ts->si_ofs.test_size, GFP_KERNEL);
+ if (ts->sysinfo_ptr.test == NULL) {
+ retval = -ENOMEM;
+ dev_err(ts->dev,
+ "%s: fail alloc test memory r=%d\n",
+ __func__, retval);
+ goto _cyttsp4_get_sysinfo_regs_exit;
+ } else {
+ memset(ts->sysinfo_ptr.test, 0, ts->si_ofs.test_size);
+ retval = _cyttsp4_read_block_data(ts, ts->si_ofs.test_ofs,
+ ts->si_ofs.test_size, ts->sysinfo_ptr.test,
+ ts->platform_data->addr[CY_TCH_ADDR_OFS], true);
+ if (retval < 0) {
+ dev_err(ts->dev,
+ "%s: fail read test data r=%d\n",
+ __func__, retval);
+ goto _cyttsp4_get_sysinfo_regs_exit;
+ }
+ /* Print sysinfo test data */
+ _cyttsp4_pr_buf(ts, (u8 *)ts->sysinfo_ptr.test,
+ ts->si_ofs.test_size, "sysinfo_test_data");
+#ifdef CY_USE_TMA400
+ if (ts->sysinfo_ptr.test->post_codel & 0x01) {
+ dev_info(ts->dev,
+ "%s: Reset was a WATCHDOG RESET codel=%02X\n",
+ __func__, ts->sysinfo_ptr.test->post_codel);
+ }
+
+ if (!(ts->sysinfo_ptr.test->post_codel & 0x02)) {
+ dev_info(ts->dev,
+ "%s: Config Data CRC FAIL codel=%02X\n",
+ __func__, ts->sysinfo_ptr.test->post_codel);
+ }
+
+ if (!(ts->sysinfo_ptr.test->post_codel & 0x04)) {
+ dev_info(ts->dev,
+ "%s: PANEL TEST FAIL codel=%02X\n",
+ __func__, ts->sysinfo_ptr.test->post_codel);
+ }
+
+ dev_info(ts->dev,
+ "%s: SCANNING is %s codel=%02X\n", __func__,
+ ts->sysinfo_ptr.test->post_codel & 0x08 ? "ENABLED" :
+ "DISABLED", ts->sysinfo_ptr.test->post_codel);
+#endif /* --CY_USE_TMA400 */
+ }
+ /* get the sysinfo pcfg data */
+ ts->si_ofs.pcfg_size = ts->si_ofs.opcfg_ofs - ts->si_ofs.pcfg_ofs;
+ if (ts->sysinfo_ptr.pcfg == NULL)
+ ts->sysinfo_ptr.pcfg = kzalloc(ts->si_ofs.pcfg_size, GFP_KERNEL);
+ if (ts->sysinfo_ptr.pcfg == NULL) {
+ retval = -ENOMEM;
+ dev_err(ts->dev,
+ "%s: fail alloc pcfg memory r=%d\n",
+ __func__, retval);
+ goto _cyttsp4_get_sysinfo_regs_exit;
+ } else {
+ memset(ts->sysinfo_ptr.pcfg, 0, ts->si_ofs.pcfg_size);
+ retval = _cyttsp4_read_block_data(ts, ts->si_ofs.pcfg_ofs,
+ ts->si_ofs.pcfg_size, ts->sysinfo_ptr.pcfg,
+ ts->platform_data->addr[CY_TCH_ADDR_OFS], true);
+ if (retval < 0) {
+ dev_err(ts->dev,
+ "%s: fail read pcfg data r=%d\n",
+ __func__, retval);
+ goto _cyttsp4_get_sysinfo_regs_exit;
+ }
+ /* Print sysinfo pcfg data */
+ _cyttsp4_pr_buf(ts, (u8 *)ts->sysinfo_ptr.pcfg,
+ ts->si_ofs.pcfg_size, "sysinfo_pcfg_data");
+ }
+ /* get the sysinfo opcfg data */
+ ts->si_ofs.opcfg_size = ts->si_ofs.ddata_ofs - ts->si_ofs.opcfg_ofs;
+ if (ts->sysinfo_ptr.opcfg == NULL)
+ ts->sysinfo_ptr.opcfg = kzalloc(ts->si_ofs.opcfg_size, GFP_KERNEL);
+ if (ts->sysinfo_ptr.opcfg == NULL) {
+ retval = -ENOMEM;
+ dev_err(ts->dev,
+ "%s: fail alloc opcfg memory r=%d\n",
+ __func__, retval);
+ goto _cyttsp4_get_sysinfo_regs_exit;
+ } else {
+ memset(ts->sysinfo_ptr.opcfg, 0, ts->si_ofs.opcfg_size);
+ retval = _cyttsp4_read_block_data(ts, ts->si_ofs.opcfg_ofs,
+ ts->si_ofs.opcfg_size, ts->sysinfo_ptr.opcfg,
+ ts->platform_data->addr[CY_TCH_ADDR_OFS], true);
+ if (retval < 0) {
+ dev_err(ts->dev,
+ "%s: fail read opcfg data r=%d\n",
+ __func__, retval);
+ goto _cyttsp4_get_sysinfo_regs_exit;
+ }
+ ts->si_ofs.cmd_ofs = ts->sysinfo_ptr.opcfg->cmd_ofs;
+ ts->si_ofs.rep_ofs = ts->sysinfo_ptr.opcfg->rep_ofs;
+ ts->si_ofs.rep_sz = (ts->sysinfo_ptr.opcfg->rep_szh * 256) +
+ ts->sysinfo_ptr.opcfg->rep_szl;
+ ts->si_ofs.num_btns = ts->sysinfo_ptr.opcfg->num_btns;
+ if (ts->si_ofs.num_btns == 0)
+ ts->si_ofs.num_btn_regs = 0;
+ else {
+ ts->si_ofs.num_btn_regs = ts->si_ofs.num_btns /
+ CY_NUM_BTN_PER_REG;
+ if (ts->si_ofs.num_btns % CY_NUM_BTN_PER_REG)
+ ts->si_ofs.num_btn_regs++;
+ }
+ ts->si_ofs.tt_stat_ofs = ts->sysinfo_ptr.opcfg->tt_stat_ofs;
+ ts->si_ofs.obj_cfg0 = ts->sysinfo_ptr.opcfg->obj_cfg0;
+ ts->si_ofs.max_tchs = ts->sysinfo_ptr.opcfg->max_tchs &
+ CY_SIZE_FIELD_MASK;
+ ts->si_ofs.tch_rec_siz = ts->sysinfo_ptr.opcfg->tch_rec_siz &
+ CY_SIZE_FIELD_MASK;
+
+ /* Get the old touch fields */
+ for (abs = CY_TCH_X; abs < CY_NUM_OLD_TCH_FIELDS; abs++) {
+ ts->si_ofs.tch_abs[abs].ofs =
+ ts->sysinfo_ptr.opcfg->tch_rec_old[abs].loc;
+ ts->si_ofs.tch_abs[abs].size =
+ _cyttsp4_bits_2_bytes(ts,
+ ts->sysinfo_ptr.opcfg->tch_rec_old[abs].size &
+ CY_SIZE_FIELD_MASK,
+ &ts->si_ofs.tch_abs[abs].max);
+ ts->si_ofs.tch_abs[abs].bofs =
+ (ts->sysinfo_ptr.opcfg->tch_rec_old[abs].size &
+ CY_BOFS_MASK) >> CY_BOFS_SHIFT;
+ dev_vdbg(ts->dev,
+ "%s: tch_rec_%s\n", __func__,
+ cyttsp4_tch_abs_string[abs]);
+ dev_vdbg(ts->dev,
+ "%s: ofs =%2d\n", __func__,
+ ts->si_ofs.tch_abs[abs].ofs);
+ dev_vdbg(ts->dev,
+ "%s: siz =%2d\n", __func__,
+ ts->si_ofs.tch_abs[abs].size);
+ dev_vdbg(ts->dev,
+ "%s: max =%2d\n", __func__,
+ ts->si_ofs.tch_abs[abs].max);
+ dev_vdbg(ts->dev,
+ "%s: bofs=%2d\n", __func__,
+ ts->si_ofs.tch_abs[abs].bofs);
+ }
+
+#ifdef CY_USE_TMA400_SP2
+#ifdef CY_USE_TMA400
+ /* skip over the button fields */
+
+ /* Get the new touch fields */
+ for (i = 0; abs < CY_TCH_NUM_ABS; abs++, i++) {
+ ts->si_ofs.tch_abs[abs].ofs =
+ ts->sysinfo_ptr.opcfg->tch_rec_new[i].loc;
+ ts->si_ofs.tch_abs[abs].size =
+ _cyttsp4_bits_2_bytes(ts,
+ ts->sysinfo_ptr.opcfg->tch_rec_new[i].size &
+ CY_SIZE_FIELD_MASK,
+ &ts->si_ofs.tch_abs[abs].max);
+ ts->si_ofs.tch_abs[abs].bofs =
+ (ts->sysinfo_ptr.opcfg->tch_rec_new[i].size &
+ CY_BOFS_MASK) >> CY_BOFS_SHIFT;
+ dev_vdbg(ts->dev,
+ "%s: tch_rec_%s\n", __func__,
+ cyttsp4_tch_abs_string[abs]);
+ dev_vdbg(ts->dev,
+ "%s: ofs =%2d\n", __func__,
+ ts->si_ofs.tch_abs[abs].ofs);
+ dev_vdbg(ts->dev,
+ "%s: siz =%2d\n", __func__,
+ ts->si_ofs.tch_abs[abs].size);
+ dev_vdbg(ts->dev,
+ "%s: max =%2d\n", __func__,
+ ts->si_ofs.tch_abs[abs].max);
+ dev_vdbg(ts->dev,
+ "%s: bofs=%2d\n", __func__,
+ ts->si_ofs.tch_abs[abs].bofs);
+ }
+#endif /* --CY_USE_TMA400 */
+#endif /* --CY_USE_TMA400_SP2 */
+
+ ts->si_ofs.btn_rec_siz = ts->sysinfo_ptr.opcfg->btn_rec_siz;
+ ts->si_ofs.btn_diff_ofs = ts->sysinfo_ptr.opcfg->btn_diff_ofs;
+ ts->si_ofs.btn_diff_siz = ts->sysinfo_ptr.opcfg->btn_diff_siz;
+ ts->si_ofs.mode_size = ts->si_ofs.tt_stat_ofs + 1;
+ ts->si_ofs.data_size = ts->si_ofs.max_tchs *
+ ts->sysinfo_ptr.opcfg->tch_rec_siz;
+ if (ts->si_ofs.num_btns)
+ ts->si_ofs.mode_size += ts->si_ofs.num_btn_regs;
+
+ /* Print sysinfo opcfg data */
+ _cyttsp4_pr_buf(ts, (u8 *)ts->sysinfo_ptr.opcfg,
+ ts->si_ofs.opcfg_size, "sysinfo_opcfg_data");
+ }
+
+ /* get the sysinfo ddata data */
+ ts->si_ofs.ddata_size = ts->si_ofs.mdata_ofs - ts->si_ofs.ddata_ofs;
+ if (ts->sysinfo_ptr.ddata == NULL)
+ ts->sysinfo_ptr.ddata = kzalloc(ts->si_ofs.ddata_size, GFP_KERNEL);
+ if (ts->sysinfo_ptr.ddata == NULL) {
+ dev_err(ts->dev,
+ "%s: fail alloc ddata memory r=%d\n",
+ __func__, retval);
+ /* continue */
+ } else {
+ memset(ts->sysinfo_ptr.ddata, 0, ts->si_ofs.ddata_size);
+ retval = _cyttsp4_read_block_data(ts, ts->si_ofs.ddata_ofs,
+ ts->si_ofs.ddata_size, ts->sysinfo_ptr.ddata,
+ ts->platform_data->addr[CY_TCH_ADDR_OFS], true);
+ if (retval < 0) {
+ dev_err(ts->dev,
+ "%s: fail read ddata data r=%d\n",
+ __func__, retval);
+ goto _cyttsp4_get_sysinfo_regs_exit;
+ }
+ /* Print sysinfo ddata */
+ _cyttsp4_pr_buf(ts, (u8 *)ts->sysinfo_ptr.ddata,
+ ts->si_ofs.ddata_size, "sysinfo_ddata");
+ }
+ /* get the sysinfo mdata data */
+ ts->si_ofs.mdata_size = ts->si_ofs.map_sz - ts->si_ofs.mdata_ofs;
+ if (ts->sysinfo_ptr.mdata == NULL)
+ ts->sysinfo_ptr.mdata = kzalloc(ts->si_ofs.mdata_size, GFP_KERNEL);
+ if (ts->sysinfo_ptr.mdata == NULL) {
+ dev_err(ts->dev,
+ "%s: fail alloc mdata memory r=%d\n",
+ __func__, retval);
+ /* continue */
+ } else {
+ memset(ts->sysinfo_ptr.mdata, 0, ts->si_ofs.mdata_size);
+ retval = _cyttsp4_read_block_data(ts, ts->si_ofs.mdata_ofs,
+ ts->si_ofs.mdata_size, ts->sysinfo_ptr.mdata,
+ ts->platform_data->addr[CY_TCH_ADDR_OFS], true);
+ if (retval < 0) {
+ dev_err(ts->dev,
+ "%s: fail read mdata data r=%d\n",
+ __func__, retval);
+ goto _cyttsp4_get_sysinfo_regs_exit;
+ }
+ /* Print sysinfo mdata */
+ _cyttsp4_pr_buf(ts, (u8 *)ts->sysinfo_ptr.mdata,
+ ts->si_ofs.mdata_size, "sysinfo_mdata");
+ }
+
+ if (ts->si_ofs.num_btns) {
+ ts->si_ofs.btn_keys_size = ts->si_ofs.num_btns *
+ sizeof(struct cyttsp4_btn);
+ if (ts->btn == NULL)
+ ts->btn = kzalloc(ts->si_ofs.btn_keys_size, GFP_KERNEL);
+ if (ts->btn == NULL) {
+ dev_err(ts->dev,
+ "%s: fail alloc btn_keys memory r=%d\n",
+ __func__, retval);
+ } else {
+ if (ts->platform_data->sett
+ [CY_IC_GRPNUM_BTN_KEYS] == NULL)
+ num_defined_keys = 0;
+ else if (ts->platform_data->sett
+ [CY_IC_GRPNUM_BTN_KEYS]->data == NULL)
+ num_defined_keys = 0;
+ else
+ num_defined_keys = ts->platform_data->sett
+ [CY_IC_GRPNUM_BTN_KEYS]->size;
+ for (btn = 0; btn < ts->si_ofs.num_btns &&
+ btn < num_defined_keys; btn++) {
+ key_table = (u16 *)ts->platform_data->sett
+ [CY_IC_GRPNUM_BTN_KEYS]->data;
+ ts->btn[btn].key_code = key_table[btn];
+ ts->btn[btn].enabled = true;
+ }
+ for (; btn < ts->si_ofs.num_btns; btn++) {
+ ts->btn[btn].key_code = KEY_RESERVED;
+ ts->btn[btn].enabled = true;
+ }
+ }
+ } else {
+ ts->si_ofs.btn_keys_size = 0;
+ ts->btn = NULL;
+ }
+
+ dev_vdbg(ts->dev,
+ "%s: cydata_ofs =%4d siz=%4d\n", __func__,
+ ts->si_ofs.cydata_ofs, ts->si_ofs.cydata_size);
+ dev_vdbg(ts->dev,
+ "%s: test_ofs =%4d siz=%4d\n", __func__,
+ ts->si_ofs.test_ofs, ts->si_ofs.test_size);
+ dev_vdbg(ts->dev,
+ "%s: pcfg_ofs =%4d siz=%4d\n", __func__,
+ ts->si_ofs.pcfg_ofs, ts->si_ofs.pcfg_size);
+ dev_vdbg(ts->dev,
+ "%s: opcfg_ofs =%4d siz=%4d\n", __func__,
+ ts->si_ofs.opcfg_ofs, ts->si_ofs.opcfg_size);
+ dev_vdbg(ts->dev,
+ "%s: ddata_ofs =%4d siz=%4d\n", __func__,
+ ts->si_ofs.ddata_ofs, ts->si_ofs.ddata_size);
+ dev_vdbg(ts->dev,
+ "%s: mdata_ofs =%4d siz=%4d\n", __func__,
+ ts->si_ofs.mdata_ofs, ts->si_ofs.mdata_size);
+
+ dev_vdbg(ts->dev,
+ "%s: cmd_ofs =%4d\n", __func__, ts->si_ofs.cmd_ofs);
+ dev_vdbg(ts->dev,
+ "%s: rep_ofs =%4d\n", __func__, ts->si_ofs.rep_ofs);
+ dev_vdbg(ts->dev,
+ "%s: rep_sz =%4d\n", __func__, ts->si_ofs.rep_sz);
+ dev_vdbg(ts->dev,
+ "%s: num_btns =%4d\n", __func__, ts->si_ofs.num_btns);
+ dev_vdbg(ts->dev,
+ "%s: num_btn_regs =%4d\n", __func__, ts->si_ofs.num_btn_regs);
+ dev_vdbg(ts->dev,
+ "%s: tt_stat_ofs =%4d\n", __func__, ts->si_ofs.tt_stat_ofs);
+ dev_vdbg(ts->dev,
+ "%s: tch_rec_siz =%4d\n", __func__, ts->si_ofs.tch_rec_siz);
+ dev_vdbg(ts->dev,
+ "%s: max_tchs =%4d\n", __func__, ts->si_ofs.max_tchs);
+ dev_vdbg(ts->dev,
+ "%s: mode_siz =%4d\n", __func__, ts->si_ofs.mode_size);
+ dev_vdbg(ts->dev,
+ "%s: data_siz =%4d\n", __func__, ts->si_ofs.data_size);
+ dev_vdbg(ts->dev,
+ "%s: map_sz =%4d\n", __func__, ts->si_ofs.map_sz);
+
+ dev_vdbg(ts->dev,
+ "%s: btn_rec_siz =%2d\n", __func__, ts->si_ofs.btn_rec_siz);
+ dev_vdbg(ts->dev,
+ "%s: btn_diff_ofs =%2d\n", __func__, ts->si_ofs.btn_diff_ofs);
+ dev_vdbg(ts->dev,
+ "%s: btn_diff_siz =%2d\n", __func__, ts->si_ofs.btn_diff_siz);
+
+ dev_vdbg(ts->dev,
+ "%s: mode_size =%2d\n", __func__, ts->si_ofs.mode_size);
+ dev_vdbg(ts->dev,
+ "%s: data_size =%2d\n", __func__, ts->si_ofs.data_size);
+
+ if (ts->xy_mode == NULL)
+ ts->xy_mode = kzalloc(ts->si_ofs.mode_size, GFP_KERNEL);
+ if (ts->xy_data == NULL)
+ ts->xy_data = kzalloc(ts->si_ofs.data_size, GFP_KERNEL);
+ if (ts->xy_data_touch1 == NULL) {
+ ts->xy_data_touch1 = kzalloc(ts->si_ofs.tch_rec_siz + 1,
+ GFP_KERNEL);
+ }
+ if (ts->btn_rec_data == NULL) {
+ ts->btn_rec_data = kzalloc(ts->si_ofs.btn_rec_siz *
+ ts->si_ofs.num_btns, GFP_KERNEL);
+ }
+ if ((ts->xy_mode == NULL) || (ts->xy_data == NULL) ||
+ (ts->xy_data_touch1 == NULL) || (ts->btn_rec_data == NULL)) {
+ dev_err(ts->dev,
+ "%s: fail memory alloc xy_mode=%p xy_data=%p"
+ "xy_data_touch1=%p btn_rec_data=%p\n", __func__,
+ ts->xy_mode, ts->xy_data,
+ ts->xy_data_touch1, ts->btn_rec_data);
+ /* continue */
+ }
+
+ dev_vdbg(ts->dev,
+ "%s: xy_mode=%p xy_data=%p xy_data_touch1=%p\n",
+ __func__, ts->xy_mode, ts->xy_data, ts->xy_data_touch1);
+
+_cyttsp4_get_sysinfo_regs_exit:
+ /* provide flow control handshake */
+ retval = _cyttsp4_handshake(ts, ts->sysinfo_data.hst_mode);
+ if (retval < 0) {
+ dev_err(ts->dev,
+ "%s: handshake fail on sysinfo reg\n",
+ __func__);
+ /* continue; rely on handshake tmo */
+ }
+
+_cyttsp4_get_sysinfo_regs_exit_no_handshake:
+ return retval;
+}
+
+static int _cyttsp4_load_status_regs(struct cyttsp4 *ts)
+{
+ int rep_stat_ofs = 0;
+ int retval = 0;
+
+ rep_stat_ofs = ts->si_ofs.rep_ofs + 1;
+ if (ts->xy_mode == NULL) {
+ dev_err(ts->dev,
+ "%s: mode ptr not yet initialized xy_mode=%p\n",
+ __func__, ts->xy_mode);
+ /* continue */
+ } else {
+ retval = _cyttsp4_read_block_data(ts, CY_REG_BASE,
+ ts->si_ofs.mode_size, ts->xy_mode,
+ ts->platform_data->addr[CY_TCH_ADDR_OFS], true);
+ if (retval < 0) {
+ dev_err(ts->dev,
+ "%s: fail read mode regs r=%d\n",
+ __func__, retval);
+ retval = -EIO;
+ }
+ _cyttsp4_pr_buf(ts, ts->xy_mode, ts->si_ofs.mode_size,
+ "xy_mode");
+ }
+ return retval;
+}
+
+static void _cyttsp4_btn_key_release(struct cyttsp4 *ts,
+ int cur_btn, u8 cur_btn_mask, int num_btns)
+{
+ int btn = 0;
+
+ /* Check for button releases */
+ for (btn = 0; btn < num_btns; btn++) {
+ if (ts->btn[cur_btn + btn].enabled) {
+ switch ((cur_btn_mask >> (btn * CY_BITS_PER_BTN)) &
+ (CY_NUM_BTN_EVENT_ID - 1)) {
+ case (CY_BTN_RELEASED):
+ if (ts->btn[cur_btn + btn].state ==
+ CY_BTN_PRESSED) {
+ input_report_key(ts->input,
+ ts->btn[cur_btn + btn].key_code,
+ CY_BTN_RELEASED);
+ ts->btn[cur_btn + btn].state =
+ CY_BTN_RELEASED;
+ input_sync(ts->input);
+ dev_dbg(ts->dev,
+ "%s: btn=%d key_code=%d"
+ " RELEASED\n", __func__,
+ cur_btn + btn, ts->btn
+ [cur_btn + btn].key_code);
+ }
+ break;
+ case (CY_BTN_PRESSED):
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ return;
+}
+
+static void _cyttsp4_btn_key_press(struct cyttsp4 *ts,
+ int cur_btn, u8 cur_btn_mask, int num_btns)
+{
+ int btn = 0;
+
+ /* Check for button presses */
+ for (btn = 0; btn < num_btns; btn++) {
+ if (ts->btn[cur_btn + btn].enabled) {
+ switch ((cur_btn_mask >> (btn * CY_BITS_PER_BTN)) &
+ (CY_NUM_BTN_EVENT_ID - 1)) {
+ case (CY_BTN_RELEASED):
+ break;
+ case (CY_BTN_PRESSED):
+ if (ts->btn[cur_btn + btn].state ==
+ CY_BTN_RELEASED) {
+ input_report_key(ts->input,
+ ts->btn[cur_btn + btn].key_code,
+ CY_BTN_PRESSED);
+ ts->btn[cur_btn + btn].state =
+ CY_BTN_PRESSED;
+ input_sync(ts->input);
+ dev_dbg(ts->dev,
+ "%s: btn=%d key_code=%d"
+ " PRESSED\n", __func__,
+ cur_btn + btn, ts->btn
+ [cur_btn + btn].key_code);
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ return;
+}
+
+static void _cyttsp4_get_touch_axis(struct cyttsp4 *ts,
+ enum cyttsp4_tch_abs abs, int *axis, int size,
+ int max, u8 *xy_data, int bofs)
+{
+ int nbyte = 0;
+ int next = 0;
+
+ for (nbyte = 0, *axis = 0, next = 0; nbyte < size; nbyte++) {
+ dev_vdbg(ts->dev,
+ "%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]);
+ *axis = (*axis * 256) + (xy_data[next] >> bofs);
+ next++;
+ }
+
+ *axis &= max - 1;
+
+#ifdef CY_USE_TMA400_SP2
+#ifdef CY_USE_TMA400
+ /* sign extend signals that can have negative values */
+ if (abs == CY_TCH_OR) {
+ if (*axis >= (max / 2))
+ *axis = -((~(*axis) & (max - 1)) + 1);
+ }
+#endif /* --CY_USE_TMA400 */
+#endif /* --CY_USE_TMA400_SP2 */
+
+ dev_vdbg(ts->dev,
+ "%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]);
+}
+
+static void _cyttsp4_get_touch(struct cyttsp4 *ts,
+ struct cyttsp4_touch *touch, u8 *xy_data)
+{
+ enum cyttsp4_tch_abs abs = 0;
+#ifdef CY_USE_DEBUG_TOOLS
+ int tmp = 0;
+ bool flipped = false;
+#endif /* --CY_USE_DEBUG_TOOLS */
+
+ for (abs = CY_TCH_X; abs < CY_TCH_NUM_ABS; abs++) {
+ _cyttsp4_get_touch_axis(ts, abs, &touch->abs[abs],
+ ts->si_ofs.tch_abs[abs].size,
+ ts->si_ofs.tch_abs[abs].max,
+ xy_data + ts->si_ofs.tch_abs[abs].ofs,
+ ts->si_ofs.tch_abs[abs].bofs);
+ dev_vdbg(ts->dev,
+ "%s: get %s=%08X(%d) size=%d"
+ " ofs=%d max=%d xy_data+ofs=%p bofs=%d\n",
+ __func__, cyttsp4_tch_abs_string[abs],
+ touch->abs[abs], touch->abs[abs],
+ ts->si_ofs.tch_abs[abs].size,
+ ts->si_ofs.tch_abs[abs].ofs,
+ ts->si_ofs.tch_abs[abs].max,
+ xy_data + ts->si_ofs.tch_abs[abs].ofs,
+ ts->si_ofs.tch_abs[abs].bofs);
+ }
+
+#ifdef CY_USE_DEBUG_TOOLS
+ if (ts->flags & CY_FLAG_FLIP) {
+ tmp = touch->abs[CY_TCH_X];
+ touch->abs[CY_TCH_X] =
+ touch->abs[CY_TCH_Y];
+ touch->abs[CY_TCH_Y] = tmp;
+ flipped = true;
+ }
+ if (ts->flags & CY_FLAG_INV_X) {
+ if (!flipped) {
+ touch->abs[CY_TCH_X] =
+ ts->platform_data->frmwrk->abs
+ [(CY_ABS_X_OST * CY_NUM_ABS_SET) + CY_MAX_OST] -
+ touch->abs[CY_TCH_X];
+ } else {
+ touch->abs[CY_TCH_X] =
+ ts->platform_data->frmwrk->abs
+ [(CY_ABS_Y_OST * CY_NUM_ABS_SET) + CY_MAX_OST] -
+ touch->abs[CY_TCH_X];
+ }
+ }
+ if (ts->flags & CY_FLAG_INV_Y) {
+ if (!flipped) {
+ touch->abs[CY_TCH_Y] =
+ ts->platform_data->frmwrk->abs
+ [(CY_ABS_Y_OST * CY_NUM_ABS_SET) + CY_MAX_OST] -
+ touch->abs[CY_TCH_Y];
+ } else {
+ touch->abs[CY_TCH_Y] =
+ ts->platform_data->frmwrk->abs
+ [(CY_ABS_X_OST * CY_NUM_ABS_SET) + CY_MAX_OST] -
+ touch->abs[CY_TCH_Y];
+ }
+ }
+#endif /* --CY_USE_DEBUG_TOOLS */
+}
+
+static void _cyttsp4_get_mt_touches(struct cyttsp4 *ts, int num_cur_tch)
+{
+ struct cyttsp4_touch touch;
+ int signal = CY_IGNORE_VALUE;
+ int i = 0;
+ int j = 0;
+ int t = 0;
+
+ memset(&touch, 0, sizeof(struct cyttsp4_touch));
+ for (i = 0; i < num_cur_tch; i++) {
+ _cyttsp4_get_touch(ts, &touch,
+ ts->xy_data + (i * ts->si_ofs.tch_rec_siz));
+ if ((touch.abs[CY_TCH_T] < ts->platform_data->frmwrk->abs
+ [(CY_ABS_ID_OST * CY_NUM_ABS_SET) + CY_MIN_OST]) ||
+ (touch.abs[CY_TCH_T] > ts->platform_data->frmwrk->abs
+ [(CY_ABS_ID_OST * CY_NUM_ABS_SET) + CY_MAX_OST])) {
+ dev_err(ts->dev,
+ "%s: touch=%d has bad track_id=%d max_id=%d\n",
+ __func__, i, touch.abs[CY_TCH_T],
+ ts->platform_data->frmwrk->abs
+ [(CY_ABS_ID_OST * CY_NUM_ABS_SET) +
+ CY_MAX_OST]);
+ input_mt_sync(ts->input);
+ } else {
+ /* use 0 based track id's */
+ signal = ts->platform_data->frmwrk->abs
+ [(CY_ABS_ID_OST*CY_NUM_ABS_SET)+0];
+ if (signal != CY_IGNORE_VALUE) {
+ t = touch.abs[CY_TCH_T] -
+ ts->platform_data->frmwrk->abs
+ [(CY_ABS_ID_OST * CY_NUM_ABS_SET) +
+ CY_MIN_OST];
+ input_report_abs(ts->input, signal, t);
+ }
+
+ /* all devices: position and pressure fields */
+ for (j = 0; j < CY_ABS_W_OST ; j++) {
+ signal = ts->platform_data->frmwrk->abs
+ [((CY_ABS_X_OST + j) *
+ CY_NUM_ABS_SET) + 0];
+ if (signal != CY_IGNORE_VALUE) {
+ input_report_abs(ts->input, signal,
+ touch.abs[CY_TCH_X + j]);
+ }
+ }
+
+#ifdef CY_USE_TMA884
+ /* TMA884 size field */
+ signal = ts->platform_data->frmwrk->abs
+ [(CY_ABS_W_OST * CY_NUM_ABS_SET) + 0];
+ if (signal != CY_IGNORE_VALUE)
+ input_report_abs(ts->input,
+ signal, touch.abs[CY_TCH_W]);
+#endif /* --CY_USE_TMA884 */
+
+#ifdef CY_USE_TMA400_SP2
+#ifdef CY_USE_TMA400
+ /*
+ * TMA400 size and orientation fields:
+ * if pressure is non-zero and major touch
+ * signal is zero, then set major and minor touch
+ * signal to minimum non-zero value
+ */
+ if ((touch.abs[CY_TCH_P] > 0) &&
+ (touch.abs[CY_TCH_MAJ] == 0)) {
+ touch.abs[CY_TCH_MAJ] = 1;
+ touch.abs[CY_TCH_MIN] = 1;
+ }
+
+ for (j = 0; j < CY_NUM_NEW_TCH_FIELDS; j++) {
+ signal = ts->platform_data->frmwrk->abs
+ [((CY_ABS_MAJ_OST + j) *
+ CY_NUM_ABS_SET) + 0];
+ if (signal != CY_IGNORE_VALUE) {
+ input_report_abs(ts->input, signal,
+ touch.abs[CY_TCH_MAJ + j]);
+ }
+ }
+#endif /* --CY_USE_TMA400 */
+#endif /* --CY_USE_TMA400_SP2 */
+
+ input_mt_sync(ts->input);
+ }
+#ifdef CY_USE_TMA400_SP2
+ dev_dbg(ts->dev,
+ "%s: t=%d x=(%d) y=(%d) z=(%d) M=(%d) m=(%d) o=(%d)\n",
+ __func__, t,
+ touch.abs[CY_TCH_X],
+ touch.abs[CY_TCH_Y],
+ touch.abs[CY_TCH_P],
+ touch.abs[CY_TCH_MAJ],
+ touch.abs[CY_TCH_MIN],
+ touch.abs[CY_TCH_OR]);
+#else
+ dev_dbg(ts->dev,
+ "%s: t=%d x=(%d) y=(%d) z=(%d)\n", __func__,
+ t,
+ touch.abs[CY_TCH_X],
+ touch.abs[CY_TCH_Y],
+ touch.abs[CY_TCH_P]);
+#endif /* --CY_USE_TMA400_SP2 */
+ }
+ input_sync(ts->input);
+ ts->num_prv_tch = num_cur_tch;
+
+ return;
+}
+
+/* read xy_data for all current touches */
+static int _cyttsp4_xy_worker(struct cyttsp4 *ts)
+{
+ struct cyttsp4_touch touch;
+ u8 num_cur_tch = 0;
+ u8 hst_mode = 0;
+ u8 rep_len = 0;
+ u8 rep_stat = 0;
+ u8 tt_stat = 0;
+ int i = 0;
+ int num_cur_btn = 0;
+ int cur_reg = 0;
+ u8 cur_btn_mask = 0;
+ int cur_btn = 0;
+
+ enum cyttsp4_btn_state btn_state = CY_BTN_RELEASED;
+ int retval = 0;
+
+ /*
+ * Get event data from CYTTSP device.
+ * The event data includes all data
+ * for all active touches.
+ */
+ /*
+ * Use 2 reads: first to get mode bytes,
+ * second to get status (touch count) and touch 1 data.
+ * An optional 3rd read to get touch 2 - touch n data.
+ */
+ memset(&touch, 0, sizeof(struct cyttsp4_touch));
+ memset(ts->xy_mode, 0, ts->si_ofs.mode_size);
+ memset(ts->xy_data_touch1, 0, 1 + ts->si_ofs.tch_rec_siz);
+
+ retval = _cyttsp4_load_status_regs(ts);
+ if (retval < 0) {
+ /*
+ * bus failure implies Watchdog -> bootloader running
+ * on TMA884 parts
+ */
+ dev_err(ts->dev,
+ "%s: 1st read fail on mode regs r=%d\n",
+ __func__, retval);
+ retval = -EIO;
+ goto _cyttsp4_xy_worker_exit;
+ }
+ retval = _cyttsp4_read_block_data(ts, ts->si_ofs.tt_stat_ofs,
+ 1+ts->si_ofs.tch_rec_siz, ts->xy_data_touch1,
+ ts->platform_data->addr[CY_TCH_ADDR_OFS], true);
+ if (retval < 0) {
+ /* bus failure may imply bootloader running */
+ dev_err(ts->dev,
+ "%s: read fail on mode regs r=%d\n",
+ __func__, retval);
+ retval = -EIO;
+ goto _cyttsp4_xy_worker_exit;
+ }
+
+ hst_mode = ts->xy_mode[CY_REG_BASE];
+ rep_len = ts->xy_mode[ts->si_ofs.rep_ofs];
+ rep_stat = ts->xy_mode[ts->si_ofs.rep_ofs + 1];
+ tt_stat = ts->xy_data_touch1[0];
+ dev_dbg(ts->dev,
+ "%s: hst_mode=%02X rep_len=%d rep_stat=%02X tt_stat=%02X\n",
+ __func__, hst_mode, rep_len, rep_stat, tt_stat);
+
+ if (rep_len == 0) {
+ dev_err(ts->dev,
+ "%s: report length error rep_len=%d\n",
+ __func__, rep_len);
+ goto _cyttsp4_xy_worker_exit;
+ }
+
+ if (GET_NUM_TOUCHES(tt_stat) > 0) {
+ memcpy(ts->xy_data, ts->xy_data_touch1 + 1,
+ ts->si_ofs.tch_rec_siz);
+ }
+ if (GET_NUM_TOUCHES(tt_stat) > 1) {
+ retval = _cyttsp4_read_block_data(ts, ts->si_ofs.tt_stat_ofs +
+ 1 + ts->si_ofs.tch_rec_siz,
+ (GET_NUM_TOUCHES(tt_stat) - 1) * ts->si_ofs.tch_rec_siz,
+ ts->xy_data + ts->si_ofs.tch_rec_siz,
+ ts->platform_data->addr[CY_TCH_ADDR_OFS], true);
+ if (retval < 0) {
+ dev_err(ts->dev,
+ "%s: read fail on touch regs r=%d\n",
+ __func__, retval);
+ goto _cyttsp4_xy_worker_exit;
+ }
+ }
+
+
+ /* provide flow control handshake */
+ retval = _cyttsp4_handshake(ts, hst_mode);
+ if (retval < 0) {
+ dev_err(ts->dev,
+ "%s: handshake fail on operational reg\n",
+ __func__);
+ /* continue; rely on handshake tmo */
+ retval = 0;
+ }
+
+ /* determine number of currently active touches */
+ num_cur_tch = GET_NUM_TOUCHES(tt_stat);
+
+ /* print xy data */
+ _cyttsp4_pr_buf(ts, ts->xy_data, num_cur_tch *
+ ts->si_ofs.tch_rec_siz, "xy_data");
+
+ /* check for any error conditions */
+ if (ts->driver_state == CY_IDLE_STATE) {
+ dev_err(ts->dev,
+ "%s: IDLE STATE detected\n", __func__);
+ retval = 0;
+ goto _cyttsp4_xy_worker_exit;
+ } else if (IS_BAD_PKT(rep_stat)) {
+ dev_err(ts->dev,
+ "%s: Invalid buffer detected\n", __func__);
+ retval = 0;
+ goto _cyttsp4_xy_worker_exit;
+ } else if (IS_BOOTLOADERMODE(rep_stat)) {
+ dev_info(ts->dev,
+ "%s: BL mode found in ACTIVE state\n",
+ __func__);
+ retval = -EIO;
+ goto _cyttsp4_xy_worker_exit;
+ } else if (GET_HSTMODE(hst_mode) == GET_HSTMODE(CY_SYSINFO_MODE)) {
+ /* if in sysinfo mode switch to op mode */
+ dev_err(ts->dev,
+ "%s: Sysinfo mode=0x%02X detected in ACTIVE state\n",
+ __func__, hst_mode);
+ retval = _cyttsp4_set_mode(ts, CY_OPERATE_MODE);
+ if (retval < 0) {
+ _cyttsp4_change_state(ts, CY_IDLE_STATE);
+ dev_err(ts->dev,
+ "%s: Fail set operational mode (r=%d)\n",
+ __func__, retval);
+ } else {
+ _cyttsp4_change_state(ts, CY_ACTIVE_STATE);
+ dev_vdbg(ts->dev,
+ "%s: enable handshake\n", __func__);
+#ifdef CY_USE_TMA884
+ retval = _cyttsp4_handshake_enable(ts);
+ if (retval < 0) {
+ dev_err(ts->dev,
+ "%s: fail enable handshake r=%d",
+ __func__, retval);
+ }
+#endif /* --CY_USE_TMA884 */
+ }
+ goto _cyttsp4_xy_worker_exit;
+ } else if (IS_LARGE_AREA(tt_stat)) {
+ /* terminate all active tracks */
+ num_cur_tch = 0;
+ dev_dbg(ts->dev, "%s: Large area detected\n", __func__);
+ } else if (num_cur_tch > ts->si_ofs.max_tchs) {
+ if (num_cur_tch == 0x1F) {
+ /* terminate all active tracks */
+ dev_err(ts->dev,
+ "%s: Num touch err detected (n=%d)\n",
+ __func__, num_cur_tch);
+ num_cur_tch = 0;
+ } else {
+ dev_err(ts->dev,
+ "%s: too many tch; set to max tch (n=%d c=%d)\n",
+ __func__, num_cur_tch, CY_NUM_TCH_ID);
+ num_cur_tch = CY_NUM_TCH_ID;
+ }
+ }
+
+ dev_dbg(ts->dev,
+ "%s: num_cur_tch=%d\n", __func__, num_cur_tch);
+
+ /* extract xy_data for all currently reported touches */
+ if (num_cur_tch) {
+ if (ts->num_prv_tch == 0) {
+ /* ICS touch down button press signal */
+ input_report_key(ts->input, BTN_TOUCH, CY_BTN_PRESSED);
+ }
+ _cyttsp4_get_mt_touches(ts, num_cur_tch);
+ } else {
+ if (ts->num_prv_tch != 0) {
+ /* ICS Lift off button release signal and empty mt */
+ input_report_key(ts->input, BTN_TOUCH, CY_BTN_RELEASED);
+ input_mt_sync(ts->input);
+ input_sync(ts->input);
+#if TOUCH_BOOST
+ mod_timer(&ts->dvfs_timer,
+ jiffies + msecs_to_jiffies(500));
+#endif
+ }
+ ts->num_prv_tch = 0;
+ }
+
+ if (ts->si_ofs.num_btns > 0) {
+ for (btn_state = CY_BTN_RELEASED; btn_state < CY_BTN_NUM_STATE;
+ btn_state++) {
+ for (cur_reg = 0, cur_btn = 0,
+ num_cur_btn = ts->si_ofs.num_btns;
+ cur_reg < ts->si_ofs.num_btn_regs;
+ cur_reg++,
+ cur_btn += CY_NUM_BTN_PER_REG,
+ num_cur_btn -= CY_NUM_BTN_PER_REG) {
+ if (num_cur_btn > 0) {
+ cur_btn_mask = ts->xy_mode
+ [ts->si_ofs.rep_ofs +
+ 2 + cur_reg];
+ if (num_cur_btn / CY_NUM_BTN_PER_REG)
+ i = CY_NUM_BTN_PER_REG;
+ else
+ i = num_cur_btn;
+ switch (btn_state) {
+ case CY_BTN_RELEASED:
+ _cyttsp4_btn_key_release(ts,
+ cur_btn,
+ cur_btn_mask, i);
+ break;
+ case CY_BTN_PRESSED:
+ _cyttsp4_btn_key_press(ts,
+ cur_btn,
+ cur_btn_mask, i);
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ dev_dbg(ts->dev,
+ "%s:\n", __func__);
+
+ retval = 0;
+_cyttsp4_xy_worker_exit:
+#ifdef CY_USE_LEVEL_IRQ
+ udelay(500);
+#endif
+ return retval;
+}
+
+#ifdef CY_USE_WATCHDOG
+#define CY_TIMEOUT msecs_to_jiffies(1000)
+static void _cyttsp4_start_wd_timer(struct cyttsp4 *ts)
+{
+ mod_timer(&ts->timer, jiffies + CY_TIMEOUT);
+
+ return;
+}
+
+static void _cyttsp4_stop_wd_timer(struct cyttsp4 *ts)
+{
+ del_timer(&ts->timer);
+ cancel_work_sync(&ts->work);
+
+ return;
+}
+
+static void cyttsp4_timer_watchdog(struct work_struct *work)
+{
+ struct cyttsp4 *ts = container_of(work, struct cyttsp4, work);
+ u8 rep_stat = 0;
+ int retval = 0;
+
+ if (ts == NULL) {
+ dev_err(ts->dev,
+ "%s: NULL context pointer\n", __func__);
+ return;
+ }
+
+ mutex_lock(&ts->data_lock);
+ if (ts->driver_state == CY_ACTIVE_STATE) {
+ retval = _cyttsp4_load_status_regs(ts);
+ if (retval < 0) {
+ dev_err(ts->dev,
+ "%s: failed to access device"
+ " in watchdog timer r=%d\n", __func__, retval);
+ _cyttsp4_queue_startup(ts, false);
+ goto cyttsp4_timer_watchdog_exit_error;
+ }
+ rep_stat = ts->xy_mode[ts->si_ofs.rep_ofs + 1];
+ if (IS_BOOTLOADERMODE(rep_stat)) {
+ dev_err(ts->dev,
+ "%s: device found in bootloader mode"
+ " when operational mode rep_stat=0x%02X\n",
+ __func__, rep_stat);
+ _cyttsp4_queue_startup(ts, false);
+ goto cyttsp4_timer_watchdog_exit_error;
+ }
+ }
+
+ _cyttsp4_start_wd_timer(ts);
+ cyttsp4_timer_watchdog_exit_error:
+ mutex_unlock(&ts->data_lock);
+ return;
+}
+
+static void cyttsp4_timer(unsigned long handle)
+{
+ struct cyttsp4 *ts = (struct cyttsp4 *)handle;
+
+ if (!work_pending(&ts->work))
+ schedule_work(&ts->work);
+
+ return;
+}
+#endif
+
+static int _cyttsp4_soft_reset(struct cyttsp4 *ts)
+{
+ u8 cmd = CY_SOFT_RESET_MODE;
+
+ return _cyttsp4_write_block_data(ts, CY_REG_BASE,
+ sizeof(cmd), &cmd,
+ ts->platform_data->addr[CY_TCH_ADDR_OFS], true);
+}
+
+static int _cyttsp4_reset(struct cyttsp4 *ts)
+{
+ enum cyttsp4_driver_state tmp_state = ts->driver_state;
+ int retval = 0;
+
+ if (ts->platform_data->hw_reset) {
+ retval = ts->platform_data->hw_reset();
+ if (retval == -ENOSYS) {
+ retval = _cyttsp4_soft_reset(ts);
+ ts->soft_reset_asserted = true;
+ } else
+ ts->soft_reset_asserted = false;
+ } else {
+ retval = _cyttsp4_soft_reset(ts);
+ ts->soft_reset_asserted = true;
+ }
+
+ if (retval < 0) {
+ _cyttsp4_pr_state(ts);
+ return retval;
+ } else {
+ ts->current_mode = CY_MODE_BOOTLOADER;
+ ts->driver_state = CY_BL_STATE;
+ if (tmp_state != CY_BL_STATE)
+ _cyttsp4_pr_state(ts);
+ return retval;
+ }
+}
+
+static void cyttsp4_ts_work_func(struct work_struct *work)
+{
+ struct cyttsp4 *ts =
+ container_of(work, struct cyttsp4, cyttsp4_resume_startup_work);
+ int retval = 0;
+ int i;
+
+ dev_err(ts->dev, "%s: %d: wd timer stop\n", __func__, __LINE__);
+#ifdef CY_USE_WATCHDOG
+ _cyttsp4_stop_wd_timer(ts);
+#endif
+ mutex_lock(&ts->data_lock);
+
+ ts->num_prv_tch = 0;
+ for (i = 0; i < ts->si_ofs.max_tchs; i++) {
+ input_mt_sync(ts->input);
+ }
+ input_report_key(ts->input, KEY_MENU, 0);
+ input_report_key(ts->input, KEY_BACK, 0);
+ input_report_key(ts->input, BTN_TOUCH, CY_BTN_RELEASED);
+ input_sync(ts->input);
+ retval = _cyttsp4_startup(ts);
+ if (retval < 0) {
+ dev_err(ts->dev,
+ "%s: Startup failed with error code %d\n",
+ __func__, retval);
+ _cyttsp4_change_state(ts, CY_IDLE_STATE);
+#ifdef CY_USE_WATCHDOG
+ } else {
+ _cyttsp4_start_wd_timer(ts);
+#endif
+ }
+
+ mutex_unlock(&ts->data_lock);
+
+ return;
+}
+
+static int _cyttsp4_enter_sleep(struct cyttsp4 *ts)
+{
+ int retval = 0;
+#if defined(CONFIG_PM_SLEEP) || \
+ defined(CONFIG_PM) || \
+ defined(CONFIG_HAS_EARLYSUSPEND)
+ uint8_t sleep = CY_DEEP_SLEEP_MODE;
+
+ dev_vdbg(ts->dev,
+ "%s: Put the part back to sleep\n", __func__);
+
+ retval = _cyttsp4_write_block_data(ts, CY_REG_BASE,
+ sizeof(sleep), &sleep,
+ ts->platform_data->addr[CY_TCH_ADDR_OFS], true);
+ if (retval < 0) {
+ dev_err(ts->dev,
+ "%s: Failed to write sleep bit r=%d\n",
+ __func__, retval);
+ } else
+ _cyttsp4_change_state(ts, CY_SLEEP_STATE);
+#endif
+ return retval;
+}
+
+static int _cyttsp4_wakeup(struct cyttsp4 *ts)
+{
+ int retval = 0;
+#if defined(CONFIG_PM_SLEEP) || \
+ defined(CONFIG_PM) || \
+ defined(CONFIG_HAS_EARLYSUSPEND)
+ unsigned long timeout = 0;
+ unsigned long uretval = 0;
+ u8 hst_mode = 0;
+#ifdef CY_USE_TMA400
+ u8 rep_stat = 0;
+#endif /* --CY_USE_TMA400 */
+ int wake = CY_WAKE_DFLT;
+
+ _cyttsp4_change_state(ts, CY_CMD_STATE);
+ INIT_COMPLETION(ts->int_running);
+ if (ts->platform_data->hw_recov == NULL) {
+ dev_vdbg(ts->dev,
+ "%s: no hw_recov function\n", __func__);
+ retval = -ENOSYS;
+ } else {
+ /* wake using strobe on host alert pin */
+ retval = ts->platform_data->hw_recov(wake);
+ if (retval < 0) {
+ if (retval == -ENOSYS) {
+ dev_vdbg(ts->dev,
+ "%s: no hw_recov wake code=%d"
+ " function\n", __func__, wake);
+ } else {
+ dev_err(ts->dev,
+ "%s: fail hw_recov(wake=%d)"
+ " function r=%d\n",
+ __func__, wake, retval);
+ retval = -ENOSYS;
+ }
+ }
+ }
+
+ if (retval == -ENOSYS) {
+ /*
+ * Wake the chip with bus traffic
+ * The first few reads should always fail because
+ * the part is not ready to respond,
+ * but the retries should succeed.
+ */
+ /*
+ * Even though this is hardware-specific, it is done
+ * here because the board config file doesn't have
+ * access to the bus read routine
+ */
+ retval = _cyttsp4_read_block_data(ts, CY_REG_BASE,
+ sizeof(hst_mode), &hst_mode,
+ ts->platform_data->addr[CY_TCH_ADDR_OFS],
+ true);
+ if (retval < 0) {
+ /* device may not be ready even with the
+ * bus read retries so just go ahead and
+ * wait for the cmd rdy interrupt or timeout
+ */
+ retval = 0;
+ } else {
+ /* IC is awake but still need to check for
+ * proper mode
+ */
+ }
+ } else
+ retval = 0;
+
+ /* Wait for cmd rdy interrupt to signal device wake */
+ timeout = msecs_to_jiffies(CY_HALF_SEC_TMO_MS);
+ mutex_unlock(&ts->data_lock);
+ uretval = wait_for_completion_interruptible_timeout(
+ &ts->int_running, timeout);
+ mutex_lock(&ts->data_lock);
+
+ /* read registers even if wait ended with timeout */
+ retval = _cyttsp4_read_block_data(ts,
+ CY_REG_BASE, sizeof(hst_mode), &hst_mode,
+ ts->platform_data->addr[CY_TCH_ADDR_OFS], true);
+
+ /* TMA884 indicates bootloader mode by changing addr */
+ if (retval < 0) {
+ dev_err(ts->dev,
+ "%s: failed to resume or in bootloader (r=%d)\n",
+ __func__, retval);
+ } else {
+#ifdef CY_USE_TMA400
+ /* read rep stat register for bootloader status */
+ retval = _cyttsp4_load_status_regs(ts);
+ if (retval < 0) {
+ dev_err(ts->dev,
+ "%s: failed to access device on resume r=%d\n",
+ __func__, retval);
+ goto _cyttsp4_wakeup_exit;
+ }
+ rep_stat = ts->xy_mode[ts->si_ofs.rep_ofs + 1];
+ if (IS_BOOTLOADERMODE(rep_stat)) {
+ dev_err(ts->dev,
+ "%s: device in bootloader mode on wakeup"
+ " rep_stat=0x%02X\n",
+ __func__, rep_stat);
+ retval = -EIO;
+ goto _cyttsp4_wakeup_exit;
+ }
+#endif /* --CY_USE_TMA400 */
+ retval = _cyttsp4_handshake(ts, hst_mode);
+ if (retval < 0) {
+ dev_err(ts->dev,
+ "%s: fail resume INT handshake (r=%d)\n",
+ __func__, retval);
+ /* continue; rely on handshake tmo */
+ retval = 0;
+ }
+ _cyttsp4_change_state(ts, CY_ACTIVE_STATE);
+ }
+#ifdef CY_USE_TMA400
+_cyttsp4_wakeup_exit:
+#endif /* --CY_USE_TMA400 */
+#endif
+ return retval;
+}
+
+#if defined(CONFIG_PM) || \
+ defined(CONFIG_PM_SLEEP) || \
+ defined(CONFIG_HAS_EARLYSUSPEND)
+
+#if defined(CONFIG_HAS_EARLYSUSPEND)
+int cyttsp4_suspend(void *handle)
+{
+ struct cyttsp4 *ts = handle;
+#elif defined(CONFIG_PM_SLEEP)
+static int cyttsp4_suspend(struct device *dev)
+{
+ struct cyttsp4 *ts = dev_get_drvdata(dev);
+#else
+int cyttsp4_suspend(void *handle)
+{
+ struct cyttsp4 *ts = handle;
+#endif
+ int retval = 0;
+ bool on = false;
+
+ if (ts->test.cur_mode != CY_TEST_MODE_NORMAL_OP) {
+ retval = -EBUSY;
+ dev_err(ts->dev,
+ "%s: Suspend Blocked while in test mode=%d\n",
+ __func__, ts->test.cur_mode);
+ } else {
+ switch (ts->driver_state) {
+ case CY_ACTIVE_STATE:
+#if defined(CY_USE_FORCE_LOAD) || defined(CONFIG_TOUCHSCREEN_DEBUG)
+ if (ts->waiting_for_fw) {
+ retval = -EBUSY;
+ dev_err(ts->dev,
+ "%s: Suspend Blocked while waiting for"
+ " fw load in %s state\n", __func__,
+ cyttsp4_driver_state_string
+ [ts->driver_state]);
+ break;
+ }
+#endif
+ dev_vdbg(ts->dev,
+ "%s: Suspending...\n", __func__);
+#ifdef CY_USE_WATCHDOG
+ _cyttsp4_stop_wd_timer(ts);
+#endif
+ if (ts->irq_enabled)
+ disable_irq(ts->irq);
+
+ ts->platform_data->hw_power(on);
+ _cyttsp4_change_state(ts, CY_SLEEP_STATE);
+
+ break;
+ case CY_SLEEP_STATE:
+ dev_err(ts->dev,
+ "%s: already in Sleep state\n", __func__);
+ break;
+ /*
+ * These states could be changing the device state
+ * Some of these states don't directly change device state
+ * but the next state could happen at any time and that
+ * state DOES modify the device state
+ * they must complete before allowing suspend.
+ */
+ case CY_BL_STATE:
+ case CY_CMD_STATE:
+ case CY_SYSINFO_STATE:
+ case CY_READY_STATE:
+ case CY_TRANSFER_STATE:
+ retval = -EBUSY;
+ dev_err(ts->dev,
+ "%s: Suspend Blocked while in %s state\n",
+ __func__, cyttsp4_driver_state_string
+ [ts->driver_state]);
+ break;
+ case CY_IDLE_STATE:
+ case CY_INVALID_STATE:
+ default:
+ dev_err(ts->dev,
+ "%s: Cannot enter suspend from %s state\n",
+ __func__, cyttsp4_driver_state_string
+ [ts->driver_state]);
+ break;
+ }
+ }
+#if TOUCH_BOOST
+ if (true == boost) {
+ omap_cpufreq_min_limit_free(DVFS_LOCK_ID_TSP);
+ boost = false;
+ del_timer_sync(&ts->dvfs_timer);
+ }
+#endif
+
+ return retval;
+}
+EXPORT_SYMBOL_GPL(cyttsp4_suspend);
+
+#if defined(CONFIG_HAS_EARLYSUSPEND)
+int cyttsp4_resume(void *handle)
+{
+ struct cyttsp4 *ts = handle;
+#elif defined(CONFIG_PM_SLEEP)
+static int cyttsp4_resume(struct device *dev)
+{
+ struct cyttsp4 *ts = dev_get_drvdata(dev);
+#else
+int cyttsp4_resume(void *handle)
+{
+ struct cyttsp4 *ts = handle;
+#endif
+ int retval = 0;
+
+ dev_vdbg(ts->dev, "%s: Resuming...\n", __func__);
+
+ mutex_lock(&ts->data_lock);
+ if (ts->irq_enabled)
+ enable_irq(ts->irq);
+ retval = _cyttsp4_startup(ts);
+
+ mutex_unlock(&ts->data_lock);
+
+ dev_vdbg(ts->dev,
+ "%s: exit Resume r=%d\n", __func__, retval);
+ return retval;
+}
+EXPORT_SYMBOL_GPL(cyttsp4_resume);
+#endif
+#if !defined(CONFIG_HAS_EARLYSUSPEND) && defined(CONFIG_PM_SLEEP)
+const struct dev_pm_ops cyttsp4_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(cyttsp4_suspend, cyttsp4_resume)
+};
+EXPORT_SYMBOL_GPL(cyttsp4_pm_ops);
+#endif
+
+
+#if defined(CONFIG_HAS_EARLYSUSPEND)
+void cyttsp4_early_suspend(struct early_suspend *h)
+{
+ struct cyttsp4 *ts = container_of(h, struct cyttsp4, early_suspend);
+ int retval = 0;
+
+ dev_vdbg(ts->dev, "%s: EARLY SUSPEND ts=%p\n",
+ __func__, ts);
+ retval = cyttsp4_suspend(ts);
+ if (retval < 0) {
+ dev_err(ts->dev,
+ "%s: Early suspend failed with error code %d\n",
+ __func__, retval);
+ }
+}
+void cyttsp4_late_resume(struct early_suspend *h)
+{
+ struct cyttsp4 *ts = container_of(h, struct cyttsp4, early_suspend);
+ int retval = 0;
+
+ dev_vdbg(ts->dev, "%s: LATE RESUME ts=%p\n",
+ __func__, ts);
+ retval = cyttsp4_resume(ts);
+ if (retval < 0) {
+ dev_err(ts->dev,
+ "%s: Late resume failed with error code %d\n",
+ __func__, retval);
+ }
+}
+#endif
+
+#ifdef CY_AUTO_LOAD_FW
+static int _cyttsp4_boot_loader(struct cyttsp4 *ts, bool *upgraded)
+{
+ int retval = 0;
+ int i = 0;
+ u32 fw_vers_platform = 0;
+ u32 fw_vers_img = 0;
+ u32 fw_revctrl_platform_h = 0;
+ u32 fw_revctrl_platform_l = 0;
+ u32 fw_revctrl_img_h = 0;
+ u32 fw_revctrl_img_l = 0;
+ bool new_fw_vers = false;
+ bool new_fw_revctrl = false;
+ bool new_vers = false;
+
+ *upgraded = false;
+ if (ts->driver_state == CY_SLEEP_STATE) {
+ dev_err(ts->dev,
+ "%s: cannot load firmware in sleep state\n",
+ __func__);
+ retval = 0;
+ } else if ((ts->platform_data->fw->ver == NULL) ||
+ (ts->platform_data->fw->img == NULL)) {
+ dev_err(ts->dev,
+ "%s: empty version list or no image\n",
+ __func__);
+ retval = 0;
+ } else if (ts->platform_data->fw->vsize != CY_BL_VERS_SIZE) {
+ dev_err(ts->dev,
+ "%s: bad fw version list size=%d\n",
+ __func__, ts->platform_data->fw->vsize);
+ retval = 0;
+ } else {
+ /* automatically update firmware if new version detected */
+ fw_vers_img = (ts->sysinfo_ptr.cydata->fw_ver_major * 256);
+ fw_vers_img += ts->sysinfo_ptr.cydata->fw_ver_minor;
+ fw_vers_platform = ts->platform_data->fw->ver[2] * 256;
+ fw_vers_platform += ts->platform_data->fw->ver[3];
+#ifdef CY_ANY_DIFF_NEW_VER
+ if (fw_vers_platform != fw_vers_img)
+ new_fw_vers = true;
+ else
+ new_fw_vers = false;
+#else
+ if (fw_vers_platform > fw_vers_img)
+ new_fw_vers = true;
+ else
+ new_fw_vers = false;
+#endif
+ dev_vdbg(ts->dev,
+ "%s: fw_vers_platform=%04X fw_vers_img=%04X\n",
+ __func__, fw_vers_platform, fw_vers_img);
+
+ fw_revctrl_img_h = ts->sysinfo_ptr.cydata->revctrl[0];
+ fw_revctrl_img_l = ts->sysinfo_ptr.cydata->revctrl[4];
+ fw_revctrl_platform_h = ts->platform_data->fw->ver[4];
+ fw_revctrl_platform_l = ts->platform_data->fw->ver[8];
+ for (i = 1; i < 4; i++) {
+ fw_revctrl_img_h = (fw_revctrl_img_h * 256) +
+ ts->sysinfo_ptr.cydata->revctrl[0+i];
+ fw_revctrl_img_l = (fw_revctrl_img_l * 256) +
+ ts->sysinfo_ptr.cydata->revctrl[4+i];
+ fw_revctrl_platform_h = (fw_revctrl_platform_h * 256) +
+ ts->platform_data->fw->ver[4+i];
+ fw_revctrl_platform_l = (fw_revctrl_platform_l * 256) +
+ ts->platform_data->fw->ver[8+i];
+ }
+#ifdef CY_ANY_DIFF_NEW_VER
+ if (fw_revctrl_platform_h != fw_revctrl_img_h)
+ new_fw_revctrl = true;
+ else if (fw_revctrl_platform_h == fw_revctrl_img_h) {
+ if (fw_revctrl_platform_l != fw_revctrl_img_l)
+ new_fw_revctrl = true;
+ else
+ new_fw_revctrl = false;
+ } else
+ new_fw_revctrl = false;
+#else
+ if (fw_revctrl_platform_h > fw_revctrl_img_h)
+ new_fw_revctrl = true;
+ else if (fw_revctrl_platform_h == fw_revctrl_img_h) {
+ if (fw_revctrl_platform_l > fw_revctrl_img_l)
+ new_fw_revctrl = true;
+ else
+ new_fw_revctrl = false;
+ } else
+ new_fw_revctrl = false;
+#endif
+ if (new_fw_vers || new_fw_revctrl)
+ new_vers = true;
+
+ dev_vdbg(ts->dev,
+ "%s: fw_revctrl_platform_h=%08X"
+ " fw_revctrl_img_h=%08X\n", __func__,
+ fw_revctrl_platform_h, fw_revctrl_img_h);
+ dev_vdbg(ts->dev,
+ "%s: fw_revctrl_platform_l=%08X"
+ " fw_revctrl_img_l=%08X\n", __func__,
+ fw_revctrl_platform_l, fw_revctrl_img_l);
+ dev_vdbg(ts->dev,
+ "%s: new_fw_vers=%d new_fw_revctrl=%d new_vers=%d\n",
+ __func__,
+ (int)new_fw_vers, (int)new_fw_revctrl, (int)new_vers);
+
+ if (new_vers) {
+ dev_info(ts->dev,
+ "%s: upgrading firmware...\n", __func__);
+ retval = _cyttsp4_load_app(ts,
+ ts->platform_data->fw->img,
+ ts->platform_data->fw->size);
+ if (retval < 0) {
+ dev_err(ts->dev,
+ "%s: communication fail"
+ " on load fw r=%d\n",
+ __func__, retval);
+ _cyttsp4_change_state(ts, CY_IDLE_STATE);
+ retval = -EIO;
+ } else
+ *upgraded = true;
+ } else {
+ dev_vdbg(ts->dev,
+ "%s: No auto firmware upgrade required\n",
+ __func__);
+ }
+ }
+
+ return retval;
+}
+#endif /* --CY_AUTO_LOAD_FW */
+
+static ssize_t cyttsp4_ic_ver_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct cyttsp4 *ts = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%s: 0x%02X 0x%02X\n%s: 0x%02X\n%s: 0x%02X\n%s: "
+ "0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X\n",
+ "TrueTouch Product ID",
+ ts->sysinfo_ptr.cydata->ttpidh,
+ ts->sysinfo_ptr.cydata->ttpidl,
+ "Firmware Major Version", ts->sysinfo_ptr.cydata->fw_ver_major,
+ "Firmware Minor Version", ts->sysinfo_ptr.cydata->fw_ver_minor,
+ "Revision Control Number", ts->sysinfo_ptr.cydata->revctrl[0],
+ ts->sysinfo_ptr.cydata->revctrl[1],
+ ts->sysinfo_ptr.cydata->revctrl[2],
+ ts->sysinfo_ptr.cydata->revctrl[3],
+ ts->sysinfo_ptr.cydata->revctrl[4],
+ ts->sysinfo_ptr.cydata->revctrl[5],
+ ts->sysinfo_ptr.cydata->revctrl[6],
+ ts->sysinfo_ptr.cydata->revctrl[7]);
+}
+static DEVICE_ATTR(ic_ver, S_IRUGO, cyttsp4_ic_ver_show, NULL);
+
+/* Driver version */
+static ssize_t cyttsp4_drv_ver_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct cyttsp4 *ts = dev_get_drvdata(dev);
+
+ return snprintf(buf, CY_MAX_PRBUF_SIZE,
+ "Driver: %s\nVersion: %s\nDate: %s\n",
+ ts->input->name, CY_DRIVER_VERSION, CY_DRIVER_DATE);
+}
+static DEVICE_ATTR(drv_ver, S_IRUGO, cyttsp4_drv_ver_show, NULL);
+
+
+/* Driver status */
+static ssize_t cyttsp4_drv_stat_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct cyttsp4 *ts = dev_get_drvdata(dev);
+
+ return snprintf(buf, CY_MAX_PRBUF_SIZE,
+ "Driver state is %s\n",
+ cyttsp4_driver_state_string[ts->driver_state]);
+}
+static DEVICE_ATTR(drv_stat, S_IRUGO, cyttsp4_drv_stat_show, NULL);
+
+
+
+
+#ifdef CY_USE_REG_ACCESS
+static ssize_t cyttsp_drv_rw_regid_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct cyttsp4 *ts = dev_get_drvdata(dev);
+
+ return snprintf(buf, CY_MAX_PRBUF_SIZE,
+ "Current Read/Write Regid=%02X(%d)\n",
+ ts->rw_regid, ts->rw_regid);
+}
+static ssize_t cyttsp_drv_rw_regid_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t size)
+{
+ struct cyttsp4 *ts = dev_get_drvdata(dev);
+ int retval = 0;
+ unsigned long value;
+
+ mutex_lock(&ts->data_lock);
+ retval = strict_strtoul(buf, 10, &value);
+ if (retval < 0) {
+ retval = strict_strtoul(buf, 16, &value);
+ if (retval < 0) {
+ dev_err(ts->dev,
+ "%s: Failed to convert value\n",
+ __func__);
+ goto cyttsp_drv_rw_regid_store_exit;
+ }
+ }
+
+ if (value > CY_RW_REGID_MAX) {
+ ts->rw_regid = CY_RW_REGID_MAX;
+ dev_err(ts->dev,
+ "%s: Invalid Read/Write Regid; set to max=%d\n",
+ __func__, ts->rw_regid);
+ } else
+ ts->rw_regid = value;
+
+ retval = size;
+
+cyttsp_drv_rw_regid_store_exit:
+ mutex_unlock(&ts->data_lock);
+ return retval;
+}
+static DEVICE_ATTR(drv_rw_regid, S_IWUSR | S_IRUGO,
+ cyttsp_drv_rw_regid_show, cyttsp_drv_rw_regid_store);
+
+
+static ssize_t cyttsp_drv_rw_reg_data_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct cyttsp4 *ts = dev_get_drvdata(dev);
+ int retval;
+ u8 reg_data;
+
+ retval = _cyttsp4_read_block_data(ts, ts->rw_regid,
+ sizeof(reg_data), &reg_data,
+ ts->platform_data->addr[CY_TCH_ADDR_OFS], true);
+
+ if (retval < 0)
+ return snprintf(buf, CY_MAX_PRBUF_SIZE,
+ "Read/Write Regid(%02X(%d) Failed\n",
+ ts->rw_regid, ts->rw_regid);
+ else
+ return snprintf(buf, CY_MAX_PRBUF_SIZE,
+ "Read/Write Regid=%02X(%d) Data=%02X(%d)\n",
+ ts->rw_regid, ts->rw_regid, reg_data, reg_data);
+}
+static ssize_t cyttsp_drv_rw_reg_data_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t size)
+{
+ struct cyttsp4 *ts = dev_get_drvdata(dev);
+ int retval = 0;
+ unsigned long value;
+ u8 reg_data = 0;
+
+ retval = strict_strtoul(buf, 10, &value);
+ if (retval < 0) {
+ retval = strict_strtoul(buf, 16, &value);
+ if (retval < 0) {
+ dev_err(ts->dev,
+ "%s: Failed to convert value\n",
+ __func__);
+ goto cyttsp_drv_rw_reg_data_store_exit;
+ }
+ }
+
+ if (value > CY_RW_REG_DATA_MAX) {
+ dev_err(ts->dev,
+ "%s: Invalid Register Data Range; no write\n",
+ __func__);
+ } else {
+ reg_data = (u8)value;
+ retval = _cyttsp4_write_block_data(ts, ts->rw_regid,
+ sizeof(reg_data), &reg_data,
+ ts->platform_data->addr[CY_TCH_ADDR_OFS], true);
+ if (retval < 0) {
+ dev_err(ts->dev,
+ "%s: Failed write to Regid=%02X(%d)\n",
+ __func__, ts->rw_regid, ts->rw_regid);
+ }
+ }
+
+ retval = size;
+
+cyttsp_drv_rw_reg_data_store_exit:
+ return retval;
+}
+static DEVICE_ATTR(drv_rw_reg_data, S_IWUSR | S_IRUGO,
+ cyttsp_drv_rw_reg_data_show, cyttsp_drv_rw_reg_data_store);
+#endif
+
+#ifdef FACTORY_TESTING
+
+static void set_node_data(struct cyttsp4 *ts_data, const u8 data_type,
+ int *max_value, int *min_value)
+{
+ /* TODO : fix later
+ * Add factory func. for cypress GEN4
+ */
+}
+
+static void set_default_result(struct factory_data *data)
+{
+ char delim = ':';
+
+ memset(data->cmd_result, 0x00, ARRAY_SIZE(data->cmd_result));
+ memset(data->cmd_buff, 0x00, ARRAY_SIZE(data->cmd_buff));
+ memcpy(data->cmd_result, data->cmd, strlen(data->cmd));
+ strncat(data->cmd_result, &delim, 1);
+}
+
+static void set_cmd_result(struct factory_data *data, char *buff, int len)
+{
+ strncat(data->cmd_result, buff, len);
+}
+
+static void not_support_cmd(void *device_data)
+{
+ struct cyttsp4 *ts_data = (struct cyttsp4 *)device_data;
+ struct factory_data *data = ts_data->factory_data;
+
+ set_default_result(data);
+ sprintf(data->cmd_buff, "%s", "NA");
+ set_cmd_result(data, data->cmd_buff, strlen(data->cmd_buff));
+ data->cmd_state = NOT_APPLICABLE;
+ pr_info("tsp factory : %s: \"%s(%d)\"\n", __func__,
+ data->cmd_buff, strlen(data->cmd_buff));
+ return;
+}
+
+static void fw_update(void *device_data)
+{
+ /* TODO : fix later
+ * Add factory func. for cypress GEN4
+ */
+}
+
+static void get_fw_ver_bin(void *device_data)
+{
+ struct cyttsp4 *ts_data = (struct cyttsp4 *)device_data;
+ struct factory_data *data = ts_data->factory_data;
+
+ data->cmd_state = RUNNING;
+
+ set_default_result(data);
+ sprintf(data->cmd_buff, "%.4x", FW_VERSION);
+ set_cmd_result(data, data->cmd_buff, strlen(data->cmd_buff));
+
+ data->cmd_state = OK;
+ return;
+}
+
+static void get_fw_ver_ic(void *device_data)
+{
+ struct cyttsp4 *ts_data = (struct cyttsp4 *)device_data;
+ struct factory_data *data = ts_data->factory_data;
+
+ u8 buf[2];
+
+ data->cmd_state = RUNNING;
+ buf[0] = *((u8 *)ts_data->sysinfo_ptr.ddata+21);
+ buf[1] = *((u8 *)ts_data->sysinfo_ptr.ddata+20);
+
+ set_default_result(data);
+ sprintf(data->cmd_buff, "%.2x%.2x", buf[1], buf[0]);
+ set_cmd_result(data, data->cmd_buff, strlen(data->cmd_buff));
+
+ data->cmd_state = OK;
+ return;
+}
+
+static void get_config_ver(void *device_data)
+{
+ struct cyttsp4 *ts_data = (struct cyttsp4 *)device_data;
+ struct factory_data *data = ts_data->factory_data;
+
+ u8 buf = 0;
+
+ data->cmd_state = RUNNING;
+ buf = *((u8 *)ts_data->sysinfo_ptr.ddata+22);
+ set_default_result(data);
+ sprintf(data->cmd_buff, "%.2x", buf);
+ set_cmd_result(data, data->cmd_buff, strlen(data->cmd_buff));
+
+ data->cmd_state = OK;
+ return;
+}
+
+static void get_threshold(void *device_data)
+{
+ struct cyttsp4 *ts_data = (struct cyttsp4 *)device_data;
+ struct factory_data *data = ts_data->factory_data;
+ enum cyttsp4_ic_ebid ebid = CY_TCH_PARM_EBID;
+ u8 *pdata = NULL;
+ u8 buf = 0;
+ int retval = 0;
+
+ data->cmd_state = RUNNING;
+
+ pdata = kzalloc(ts_data->ebid_row_size, GFP_KERNEL);
+ memset(pdata, 0, ts_data->ebid_row_size);
+ mutex_lock(&ts_data->data_lock);
+ retval = _cyttsp4_set_mode(ts_data, CY_CONFIG_MODE);
+ if (retval < 0) {
+ dev_err(ts_data->dev,
+ "%s: Fail set config mode 1 r=%d\n", __func__, retval);
+ goto cyttsp_threshold_get_fail;
+ }
+
+ retval = _cyttsp4_get_ebid_data_tma400(ts_data, ebid, 3, pdata);
+ if (retval < 0) {
+ dev_err(ts_data->dev,
+ "%s: Fail get threshold value r=%d\n", __func__, retval);
+ goto cyttsp_threshold_get_fail;
+ }
+
+ retval = _cyttsp4_set_mode(ts_data, CY_OPERATE_MODE);
+ if (retval < 0) {
+ dev_err(ts_data->dev,
+ "%s: Fail set operational mode 1 (r=%d)\n",
+ __func__, retval);
+ goto cyttsp_threshold_get_fail;
+ }
+
+ mutex_unlock(&ts_data->data_lock);
+
+ buf = *(pdata+23);
+
+cyttsp_threshold_get_fail:
+ set_default_result(data);
+
+ sprintf(data->cmd_buff, "%.2x", buf);
+ set_cmd_result(data, data->cmd_buff, strlen(data->cmd_buff));
+
+ data->cmd_state = OK;
+ if (pdata != NULL)
+ kfree(pdata);
+
+ return;
+}
+
+static void module_off_master(void *device_data)
+{
+
+}
+
+static void module_on_master(void *device_data)
+{
+
+}
+
+static void get_chip_vendor(void *device_data)
+{
+ struct cyttsp4 *ts_data = (struct cyttsp4 *)device_data;
+ struct factory_data *data = ts_data->factory_data;
+
+ data->cmd_state = RUNNING;
+
+ set_default_result(data);
+ sprintf(data->cmd_buff, "%s", TSP_VENDOR);
+ set_cmd_result(data, data->cmd_buff, strlen(data->cmd_buff));
+
+ data->cmd_state = OK;
+ return;
+}
+
+static void get_chip_name(void *device_data)
+{
+ struct cyttsp4 *ts_data = (struct cyttsp4 *)device_data;
+ struct factory_data *data = ts_data->factory_data;
+
+ data->cmd_state = RUNNING;
+
+ set_default_result(data);
+ sprintf(data->cmd_buff, "%s", TSP_IC);
+ set_cmd_result(data, data->cmd_buff, strlen(data->cmd_buff));
+
+ data->cmd_state = OK;
+ return;
+}
+
+static void get_x_num(void *device_data)
+{
+ struct cyttsp4 *ts_data = (struct cyttsp4 *)device_data;
+ struct factory_data *data = ts_data->factory_data;
+
+ data->cmd_state = RUNNING;
+
+ set_default_result(data);
+
+ set_cmd_result(data, data->cmd_buff, strlen(data->cmd_buff));
+
+ data->cmd_state = OK;
+ return;
+}
+
+static void get_y_num(void *device_data)
+{
+ struct cyttsp4 *ts_data = (struct cyttsp4 *)device_data;
+ struct factory_data *data = ts_data->factory_data;
+
+ data->cmd_state = RUNNING;
+
+ set_default_result(data);
+
+ set_cmd_result(data, data->cmd_buff, strlen(data->cmd_buff));
+
+ data->cmd_state = OK;
+ return;
+}
+
+static void get_reference(void *device_data)
+{
+ /* TODO : fix later
+ * Add factory func. for cypress GEN4
+ */
+}
+
+static void get_cm_abs(void *device_data)
+{
+ /* TODO : fix later
+ * Add factory func. for cypress GEN4
+ */
+}
+
+static void get_cm_delta(void *device_data)
+{
+ /* TODO : fix later
+ * Add factory func. for cypress GEN4
+ */
+}
+
+static void get_intensity(void *device_data)
+{
+ /* TODO : fix later
+ * Add factory func. for cypress GEN4
+ */
+}
+
+static void run_reference_read(void *device_data)
+{
+ /* TODO : fix later
+ * Add factory func. for cypress GEN4
+ */
+}
+
+static void run_cm_abs_read(void *device_data)
+{
+ /* TODO : fix later
+ * Add factory func. for cypress GEN4
+ */
+}
+
+static void run_cm_delta_read(void *device_data)
+{
+ /* TODO : fix later
+ * Add factory func. for cypress GEN4
+ */
+}
+
+static void run_intensity_read(void *device_data)
+{
+ /* TODO : fix later
+ * Add factory func. for cypress GEN4
+ */
+}
+
+struct tsp_cmd tsp_cmds[] = {
+ {TSP_CMD("fw_update", fw_update),},
+ {TSP_CMD("get_fw_ver_bin", get_fw_ver_bin),},
+ {TSP_CMD("get_fw_ver_ic", get_fw_ver_ic),},
+ {TSP_CMD("get_config_ver", get_config_ver),},
+ {TSP_CMD("get_threshold", get_threshold),},
+ {TSP_CMD("module_off_master", module_off_master),},
+ {TSP_CMD("module_on_master", module_on_master),},
+ {TSP_CMD("module_off_slave", not_support_cmd),},
+ {TSP_CMD("module_on_slave", not_support_cmd),},
+ {TSP_CMD("get_chip_vendor", get_chip_vendor),},
+ {TSP_CMD("get_chip_name", get_chip_name),},
+ {TSP_CMD("get_x_num", get_x_num),},
+ {TSP_CMD("get_y_num", get_y_num),},
+ {TSP_CMD("get_reference", get_reference),},
+ {TSP_CMD("get_cm_abs", get_cm_abs),},
+ {TSP_CMD("get_cm_delta", get_cm_delta),},
+ {TSP_CMD("get_intensity", get_intensity),},
+ {TSP_CMD("run_reference_read", run_reference_read),},
+ {TSP_CMD("run_cm_abs_read", run_cm_abs_read),},
+ {TSP_CMD("run_cm_delta_read", run_cm_delta_read),},
+ {TSP_CMD("run_intensity_read", run_intensity_read),},
+ {TSP_CMD("not_support_cmd", not_support_cmd),},
+};
+
+static ssize_t cmd_store(struct device *dev, struct device_attribute *devattr,
+ const char *buf, size_t count)
+{
+ struct cyttsp4 *ts_data = dev_get_drvdata(dev);
+ struct factory_data *data = ts_data->factory_data;
+ char *cur, *start, *end;
+ char buff[TSP_CMD_STR_LEN] = {0, };
+ int len, i;
+ struct tsp_cmd *tsp_cmd_ptr = NULL;
+ char delim = ',';
+ bool cmd_found = false;
+ int param_cnt = 0;
+
+ if (data == NULL) {
+ pr_err("factory_data is NULL.\n");
+ goto err_out;
+ }
+
+ if (data->cmd_is_running == true) {
+ pr_err("tsp cmd: other cmd is running.\n");
+ goto err_out;
+ }
+
+ /* check lock */
+ mutex_lock(&data->cmd_lock);
+ data->cmd_is_running = true;
+ mutex_unlock(&data->cmd_lock);
+
+ data->cmd_state = RUNNING;
+
+ for (i = 0; i < ARRAY_SIZE(data->cmd_param); i++)
+ data->cmd_param[i] = 0;
+
+ len = (int)count;
+ if (*(buf + len - 1) == '\n')
+ len--;
+ memset(data->cmd, 0x00, ARRAY_SIZE(data->cmd));
+ memcpy(data->cmd, buf, len);
+
+ cur = strchr(buf, (int)delim);
+ if (cur)
+ memcpy(buff, buf, cur - buf);
+ else
+ memcpy(buff, buf, len);
+
+ /* find command */
+ list_for_each_entry(tsp_cmd_ptr, &data->cmd_list_head, list) {
+ if (!strcmp(buff, tsp_cmd_ptr->cmd_name)) {
+ cmd_found = true;
+ break;
+ }
+ }
+
+ /* set not_support_cmd */
+ if (!cmd_found) {
+ list_for_each_entry(tsp_cmd_ptr, &data->cmd_list_head, list) {
+ if (!strcmp("not_support_cmd", tsp_cmd_ptr->cmd_name))
+ break;
+ }
+ }
+
+ /* parsing parameters */
+ if (cur && cmd_found) {
+ cur++;
+ start = cur;
+ do {
+ memset(buff, 0x00, ARRAY_SIZE(buff));
+ if (*cur == delim || cur - buf == len) {
+ end = cur;
+ memcpy(buff, start, end - start);
+ *(buff + strlen(buff)) = '\0';
+ if (kstrtoint(buff, 10,
+ data->cmd_param + param_cnt) < 0)
+ break;
+ start = cur + 1;
+ param_cnt++;
+ }
+ cur++;
+ } while (cur - buf <= len);
+ }
+
+ pr_info("cmd = %s\n", tsp_cmd_ptr->cmd_name);
+ for (i = 0; i < param_cnt; i++)
+ pr_info("cmd param %d= %d\n", i, data->cmd_param[i]);
+
+ tsp_cmd_ptr->cmd_func(ts_data);
+
+err_out:
+ return count;
+}
+
+static ssize_t cmd_status_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct cyttsp4 *ts_data = dev_get_drvdata(dev);
+ struct factory_data *data = ts_data->factory_data;
+ char buff[16];
+
+ pr_info("tsp cmd: status:%d\n", data->cmd_state);
+
+ switch (data->cmd_state) {
+ case WAITING:
+ sprintf(buff, "%s", TOSTRING(WAITING));
+ break;
+ case RUNNING:
+ sprintf(buff, "%s", TOSTRING(RUNNING));
+ break;
+ case OK:
+ sprintf(buff, "%s", TOSTRING(OK));
+ break;
+ case FAIL:
+ sprintf(buff, "%s", TOSTRING(FAIL));
+ break;
+ case NOT_APPLICABLE:
+ sprintf(buff, "%s", TOSTRING(NOT_APPLICABLE));
+ break;
+ default:
+ sprintf(buff, "%s", TOSTRING(NOT_APPLICABLE));
+ break;
+ }
+
+ return sprintf(buf, "%s\n", buff);
+}
+
+static ssize_t cmd_result_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct cyttsp4 *ts_data = dev_get_drvdata(dev);
+ struct factory_data *data = ts_data->factory_data;
+
+ pr_info("tsp factory : tsp cmd: result: \"%s(%d)\"\n",
+ data->cmd_result, strlen(data->cmd_result));
+
+ mutex_lock(&data->cmd_lock);
+ data->cmd_is_running = false;
+ mutex_unlock(&data->cmd_lock);
+
+ data->cmd_state = WAITING;
+
+ return sprintf(buf, "%s\n", data->cmd_result);
+}
+
+static DEVICE_ATTR(cmd, S_IWUSR | S_IWGRP, NULL, cmd_store);
+static DEVICE_ATTR(cmd_status, S_IRUGO, cmd_status_show, NULL);
+static DEVICE_ATTR(cmd_result, S_IRUGO, cmd_result_show, NULL);
+
+static struct attribute *touchscreen_attributes[] = {
+ &dev_attr_cmd.attr,
+ &dev_attr_cmd_status.attr,
+ &dev_attr_cmd_result.attr,
+ NULL,
+};
+
+static struct attribute_group touchscreen_attr_group = {
+ .attrs = touchscreen_attributes,
+};
+#endif
+
+#if TOUCH_BOOST
+static void disable_dvfs(struct work_struct *unused)
+{
+ omap_cpufreq_min_limit_free(DVFS_LOCK_ID_TSP);
+ boost = false;
+ return;
+}
+static DECLARE_WORK(tsp_wq, disable_dvfs);
+
+static void timer_cb(unsigned long data)
+{
+ schedule_work(&tsp_wq);
+ return;
+}
+#endif
+
+#define CY_CMD_I2C_ADDR 0
+#define CY_STATUS_SIZE_BYTE 1
+#define CY_STATUS_TYP_DELAY 2
+#define CY_CMD_TAIL_LEN 3
+#define CY_CMD_BYTE 1
+#define CY_STATUS_BYTE 1
+#define CY_MAX_STATUS_SIZE 32
+#define CY_MIN_STATUS_SIZE 5
+#define CY_START_OF_PACKET 0x01
+#define CY_END_OF_PACKET 0x17
+#define CY_DATA_ROW_SIZE 288
+#define CY_DATA_ROW_SIZE_TMA400 128
+#define CY_PACKET_DATA_LEN 96
+#define CY_MAX_PACKET_LEN 512
+#define CY_COMM_BUSY 0xFF
+#define CY_CMD_BUSY 0xFE
+#define CY_SEPARATOR_OFFSET 0
+#define CY_ARRAY_ID_OFFSET 0
+#define CY_ROW_NUM_OFFSET 1
+#define CY_ROW_SIZE_OFFSET 3
+#define CY_ROW_DATA_OFFSET 5
+#define CY_FILE_SILICON_ID_OFFSET 0
+#define CY_FILE_REV_ID_OFFSET 4
+#define CY_CMD_LDR_HOST_SYNC 0xFF /* tma400 */
+#define CY_CMD_LDR_EXIT 0x3B
+#define CY_CMD_LDR_EXIT_CMD_SIZE 7
+#define CY_CMD_LDR_EXIT_STAT_SIZE 7
+
+enum ldr_status {
+ ERROR_SUCCESS = 0,
+ ERROR_COMMAND = 1,
+ ERROR_FLASH_ARRAY = 2,
+ ERROR_PACKET_DATA = 3,
+ ERROR_PACKET_LEN = 4,
+ ERROR_PACKET_CHECKSUM = 5,
+ ERROR_FLASH_PROTECTION = 6,
+ ERROR_FLASH_CHECKSUM = 7,
+ ERROR_VERIFY_IMAGE = 8,
+ ERROR_UKNOWN1 = 9,
+ ERROR_UKNOWN2 = 10,
+ ERROR_UKNOWN3 = 11,
+ ERROR_UKNOWN4 = 12,
+ ERROR_UKNOWN5 = 13,
+ ERROR_UKNOWN6 = 14,
+ ERROR_INVALID_COMMAND = 15,
+ ERROR_INVALID
+};
+
+static u16 _cyttsp4_compute_crc(struct cyttsp4 *ts, u8 *buf, int size)
+{
+ u16 crc = 0xffff;
+ u16 tmp;
+ int i;
+
+ /* RUN CRC */
+
+ if (size == 0)
+ crc = ~crc;
+ else {
+
+ do {
+ for (i = 0, tmp = 0x00ff & *buf++; i < 8;
+ i++, tmp >>= 1) {
+ if ((crc & 0x0001) ^ (tmp & 0x0001))
+ crc = (crc >> 1) ^ 0x8408;
+ else
+ crc >>= 1;
+ }
+ } while (--size);
+
+ crc = ~crc;
+ tmp = crc;
+ crc = (crc << 8) | (tmp >> 8 & 0xFF);
+ }
+
+ return crc;
+}
+
+static int _cyttsp4_get_status(struct cyttsp4 *ts,
+ u8 *buf, int size, unsigned long timeout_ms)
+{
+ unsigned long uretval = 0;
+ int tries = 0;
+ int retval = 0;
+
+ if (timeout_ms != 0) {
+ /* wait until status ready interrupt or timeout occurs */
+ uretval = wait_for_completion_interruptible_timeout(
+ &ts->int_running, msecs_to_jiffies(timeout_ms));
+
+ /* read the status packet */
+ if (buf == NULL) {
+ dev_err(ts->dev,
+ "%s: Status buf ptr is NULL\n", __func__);
+ retval = -EINVAL;
+ goto _cyttsp4_get_status_exit;
+ }
+ for (tries = 0; tries < 2; tries++) {
+ retval = _cyttsp4_read_block_data(ts, CY_REG_BASE, size,
+ buf, ts->platform_data->addr[CY_LDR_ADDR_OFS],
+#ifdef CY_USE_TMA400
+ true);
+#endif /* --CY_USE_TMA400 */
+#ifdef CY_USE_TMA884
+ false);
+#endif /* --CY_USE_TMA884 */
+ /*
+ * retry if bus read error or
+ * status byte shows not ready
+ */
+ if ((buf[1] == CY_COMM_BUSY) || (buf[1] == CY_CMD_BUSY))
+ msleep(CY_DELAY_DFLT);
+ else
+ break;
+ }
+ dev_vdbg(ts->dev,
+ "%s: tries=%d ret=%d status=%02X\n",
+ __func__, tries, retval, buf[1]);
+ }
+
+_cyttsp4_get_status_exit:
+ mutex_lock(&ts->data_lock);
+ return retval;
+}
+
+/*
+ * Send a bootloader command to the device;
+ * Wait for the ISR to execute indicating command
+ * was received and status is ready;
+ * Releases data_lock mutex to allow ISR to run,
+ * then locks it again.
+ */
+static int _cyttsp4_send_cmd(struct cyttsp4 *ts, const u8 *cmd_buf,
+ int cmd_size, u8 *stat_ret, size_t num_stat_byte,
+ size_t status_size, unsigned long timeout_ms)
+{
+ u8 *status_buf = NULL;
+ int retval = 0;
+
+ if (timeout_ms > 0) {
+ status_buf = kzalloc(CY_MAX_STATUS_SIZE, GFP_KERNEL);
+ if (status_buf == NULL) {
+ dev_err(ts->dev,
+ "%s: Fail alloc status buffer=%p\n",
+ __func__, status_buf);
+ goto _cyttsp4_send_cmd_exit;
+ }
+ }
+
+ if (cmd_buf == NULL) {
+ dev_err(ts->dev,
+ "%s: bad cmd_buf=%p\n", __func__, cmd_buf);
+ goto _cyttsp4_send_cmd_exit;
+ }
+
+ if (cmd_size == 0) {
+ dev_err(ts->dev,
+ "%s: bad cmd_size=%d\n", __func__, cmd_size);
+ goto _cyttsp4_send_cmd_exit;
+ }
+
+ _cyttsp4_pr_buf(ts, (u8 *)cmd_buf, cmd_size, "send_cmd");
+
+ mutex_unlock(&ts->data_lock);
+ if (timeout_ms > 0)
+ INIT_COMPLETION(ts->int_running);
+ retval = _cyttsp4_write_block_data(ts, CY_REG_BASE, cmd_size, cmd_buf,
+ ts->platform_data->addr[CY_LDR_ADDR_OFS],
+#ifdef CY_USE_TMA400
+ true);
+#endif /* --CY_USE_TMA400 */
+#ifdef CY_USE_TMA884
+ false);
+#endif /* --CY_USE_TMA884 */
+ if (retval < 0) {
+ dev_err(ts->dev,
+ "%s: Fail writing command=%02X\n",
+ __func__, cmd_buf[CY_CMD_BYTE]);
+ mutex_lock(&ts->data_lock);
+ goto _cyttsp4_send_cmd_exit;
+ }
+
+ /* get the status and lock the mutex */
+ if (timeout_ms > 0) {
+ retval = _cyttsp4_get_status(ts, status_buf,
+ status_size, timeout_ms);
+ if ((retval < 0) || (status_buf[0] != CY_START_OF_PACKET)) {
+ dev_err(ts->dev,
+ "%s: Error getting status r=%d"
+ " status_buf[0]=%02X\n",
+ __func__, retval, status_buf[0]);
+ if (!(retval < 0))
+ retval = -EIO;
+ goto _cyttsp4_send_cmd_exit;
+ } else {
+ if (status_buf[CY_STATUS_BYTE] != ERROR_SUCCESS) {
+ dev_err(ts->dev,
+ "%s: Status=0x%02X error\n",
+ __func__, status_buf[CY_STATUS_BYTE]);
+ retval = -EIO;
+ } else if (stat_ret != NULL) {
+ if (num_stat_byte < status_size)
+ *stat_ret = status_buf[num_stat_byte];
+ else
+ *stat_ret = 0;
+ }
+ }
+ } else {
+ if (stat_ret != NULL)
+ *stat_ret = ERROR_SUCCESS;
+ mutex_lock(&ts->data_lock);
+ }
+
+_cyttsp4_send_cmd_exit:
+ if (status_buf != NULL)
+ kfree(status_buf);
+ return retval;
+}
+
+struct cyttsp4_dev_id {
+ u32 silicon_id;
+ u8 rev_id;
+ u32 bl_ver;
+};
+
+#if defined(CY_AUTO_LOAD_FW) || \
+ defined(CY_USE_FORCE_LOAD) || \
+ defined(CONFIG_TOUCHSCREEN_DEBUG)
+#define CY_CMD_LDR_ENTER 0x38
+#define CY_CMD_LDR_ENTER_CMD_SIZE 7
+#define CY_CMD_LDR_ENTER_STAT_SIZE 15
+#define CY_CMD_LDR_INIT 0x48
+#define CY_CMD_LDR_INIT_CMD_SIZE 15
+#define CY_CMD_LDR_INIT_STAT_SIZE 7
+#define CY_CMD_LDR_ERASE_ROW 0x34
+#define CY_CMD_LDR_ERASE_ROW_CMD_SIZE 10
+#define CY_CMD_LDR_ERASE_ROW_STAT_SIZE 7
+#define CY_CMD_LDR_SEND_DATA 0x37
+#define CY_CMD_LDR_SEND_DATA_CMD_SIZE 4 /* hdr bytes only */
+#define CY_CMD_LDR_SEND_DATA_STAT_SIZE 8
+#define CY_CMD_LDR_PROG_ROW 0x39
+#define CY_CMD_LDR_PROG_ROW_CMD_SIZE 7 /* hdr bytes only */
+#define CY_CMD_LDR_PROG_ROW_STAT_SIZE 7
+#define CY_CMD_LDR_VERIFY_ROW 0x3A
+#define CY_CMD_LDR_VERIFY_ROW_STAT_SIZE 8
+#define CY_CMD_LDR_VERIFY_ROW_CMD_SIZE 10
+#define CY_CMD_LDR_VERIFY_CHKSUM 0x31
+#define CY_CMD_LDR_VERIFY_CHKSUM_CMD_SIZE 7
+#define CY_CMD_LDR_VERIFY_CHKSUM_STAT_SIZE 8
+
+
+static u16 _cyttsp4_get_short(u8 *buf)
+{
+ return ((u16)(*buf) << 8) + *(buf+1);
+}
+
+static u8 *_cyttsp4_get_row(struct cyttsp4 *ts,
+ u8 *row_buf, u8 *image_buf, int size)
+{
+ int i;
+ for (i = 0; i < size; i++) {
+ /* copy a row from the image */
+ row_buf[i] = image_buf[i];
+ }
+
+ image_buf = image_buf + size;
+ return image_buf;
+}
+
+static int _cyttsp4_ldr_enter(struct cyttsp4 *ts, struct cyttsp4_dev_id *dev_id)
+{
+ u16 crc;
+ int i = 0;
+ size_t cmd_size;
+ u8 status_buf[CY_MAX_STATUS_SIZE];
+ u8 status = 0;
+ int retval = 0;
+ /* +1 for TMA400 host sync byte */
+ u8 ldr_enter_cmd[CY_CMD_LDR_ENTER_CMD_SIZE+1];
+
+ memset(status_buf, 0, sizeof(status_buf));
+ dev_id->bl_ver = 0;
+ dev_id->rev_id = 0;
+ dev_id->silicon_id = 0;
+
+#ifdef CY_USE_TMA400
+ ldr_enter_cmd[i++] = CY_CMD_LDR_HOST_SYNC;
+#endif /* --CY_USE_TMA400 */
+ ldr_enter_cmd[i++] = CY_START_OF_PACKET;
+ ldr_enter_cmd[i++] = CY_CMD_LDR_ENTER;
+ ldr_enter_cmd[i++] = 0x00; /* data len lsb */
+ ldr_enter_cmd[i++] = 0x00; /* data len msb */
+#ifdef CY_USE_TMA400
+ crc = _cyttsp4_compute_crc(ts, &ldr_enter_cmd[1], i - 1);
+ cmd_size = sizeof(ldr_enter_cmd);
+#endif /* --CY_USE_TMA400 */
+#ifdef CY_USE_TMA884
+ crc = _cyttsp4_compute_crc(ts, ldr_enter_cmd, i);
+ cmd_size = sizeof(ldr_enter_cmd) - 1;
+#endif /* --CY_USE_TMA884 */
+ ldr_enter_cmd[i++] = (u8)crc;
+ ldr_enter_cmd[i++] = (u8)(crc >> 8);
+ ldr_enter_cmd[i++] = CY_END_OF_PACKET;
+
+ mutex_unlock(&ts->data_lock);
+ INIT_COMPLETION(ts->int_running);
+ retval = _cyttsp4_write_block_data(ts, CY_REG_BASE, cmd_size,
+ ldr_enter_cmd, ts->platform_data->addr[CY_LDR_ADDR_OFS],
+#ifdef CY_USE_TMA400
+ true);
+#endif /* --CY_USE_TMA400 */
+#ifdef CY_USE_TMA884
+ false);
+#endif /* --CY_USE_TMA884 */
+ if (retval < 0) {
+ dev_err(ts->dev,
+ "%s: write block failed %d\n", __func__, retval);
+ goto _cyttsp4_ldr_enter_exit;
+ }
+
+ /* Wait for ISR, get status and lock mutex */
+ retval = _cyttsp4_get_status(ts, status_buf,
+ CY_CMD_LDR_ENTER_STAT_SIZE, CY_HALF_SEC_TMO_MS);
+
+ if (retval < 0) {
+ dev_err(ts->dev,
+ "%s: Fail get status to Enter Loader command r=%d\n",
+ __func__, retval);
+ } else {
+ status = status_buf[CY_STATUS_BYTE];
+ if (status == ERROR_SUCCESS) {
+ dev_id->bl_ver =
+ status_buf[11] << 16 |
+ status_buf[10] << 8 |
+ status_buf[9] << 0;
+ dev_id->rev_id =
+ status_buf[8] << 0;
+ dev_id->silicon_id =
+ status_buf[7] << 24 |
+ status_buf[6] << 16 |
+ status_buf[5] << 8 |
+ status_buf[4] << 0;
+ retval = 0;
+ } else
+ retval = -EIO;
+ dev_vdbg(ts->dev,
+ "%s: status=%d "
+ "bl_ver=%08X rev_id=%02X silicon_id=%08X\n",
+ __func__, status,
+ dev_id->bl_ver, dev_id->rev_id, dev_id->silicon_id);
+ }
+
+_cyttsp4_ldr_enter_exit:
+ return retval;
+}
+
+#ifdef CY_USE_TMA400
+static int _cyttsp4_ldr_init(struct cyttsp4 *ts)
+{
+ u16 crc;
+ int i = 0;
+ int retval = 0;
+ /* +1 for TMA400 host sync byte */
+ u8 ldr_init_cmd[CY_CMD_LDR_INIT_CMD_SIZE+1];
+
+ ldr_init_cmd[i++] = CY_CMD_LDR_HOST_SYNC;
+ ldr_init_cmd[i++] = CY_START_OF_PACKET;
+ ldr_init_cmd[i++] = CY_CMD_LDR_INIT;
+ ldr_init_cmd[i++] = 0x08; /* data len lsb */
+ ldr_init_cmd[i++] = 0x00; /* data len msb */
+ memcpy(&ldr_init_cmd[i], cyttsp4_security_key,
+ sizeof(cyttsp4_security_key));
+ i += sizeof(cyttsp4_security_key);
+ crc = _cyttsp4_compute_crc(ts, &ldr_init_cmd[1], i - 1);
+ ldr_init_cmd[i++] = (u8)crc;
+ ldr_init_cmd[i++] = (u8)(crc >> 8);
+ ldr_init_cmd[i++] = CY_END_OF_PACKET;
+
+ retval = _cyttsp4_send_cmd(ts, ldr_init_cmd, i, NULL, 0,
+ CY_CMD_LDR_INIT_STAT_SIZE, CY_TEN_SEC_TMO_MS);
+ if (retval < 0) {
+ dev_err(ts->dev,
+ "%s: Fail ldr init r=%d\n",
+ __func__, retval);
+ }
+
+ return retval;
+}
+#endif /* --CY_USE_TMA400 */
+
+struct cyttsp4_hex_image {
+ u8 array_id;
+ u16 row_num;
+ u16 row_size;
+ u8 row_data[CY_DATA_ROW_SIZE];
+} __packed;
+
+#ifdef CY_USE_TMA884
+static int _cyttsp4_ldr_erase_row(struct cyttsp4 *ts,
+ struct cyttsp4_hex_image *row_image)
+{
+ u16 crc;
+ int i = 0;
+ int retval = 0;
+ /* +1 for TMA400 host sync byte */
+ u8 ldr_erase_row_cmd[CY_CMD_LDR_ERASE_ROW_CMD_SIZE+1];
+
+#ifdef CY_USE_TMA400
+ ldr_erase_row_cmd[i++] = CY_CMD_LDR_HOST_SYNC;
+#endif /* --CY_USE_TMA400 */
+ ldr_erase_row_cmd[i++] = CY_START_OF_PACKET;
+ ldr_erase_row_cmd[i++] = CY_CMD_LDR_ERASE_ROW;
+ ldr_erase_row_cmd[i++] = 0x03; /* data len lsb */
+ ldr_erase_row_cmd[i++] = 0x00; /* data len msb */
+ ldr_erase_row_cmd[i++] = row_image->array_id;
+ ldr_erase_row_cmd[i++] = (u8)row_image->row_num;
+ ldr_erase_row_cmd[i++] = (u8)(row_image->row_num >> 8);
+#ifdef CY_USE_TMA400
+ crc = _cyttsp4_compute_crc(ts, &ldr_erase_row_cmd[1], i - 1);
+#endif /* --CY_USE_TMA400 */
+#ifdef CY_USE_TMA884
+ crc = _cyttsp4_compute_crc(ts, ldr_erase_row_cmd, i);
+#endif /* --CY_USE_TMA884 */
+ ldr_erase_row_cmd[i++] = (u8)crc;
+ ldr_erase_row_cmd[i++] = (u8)(crc >> 8);
+ ldr_erase_row_cmd[i++] = CY_END_OF_PACKET;
+
+ retval = _cyttsp4_send_cmd(ts, ldr_erase_row_cmd, i, NULL, 0,
+ CY_CMD_LDR_ERASE_ROW_STAT_SIZE, CY_HALF_SEC_TMO_MS);
+
+ if (retval < 0) {
+ dev_err(ts->dev,
+ "%s: Fail erase row=%d r=%d\n",
+ __func__, row_image->row_num, retval);
+ }
+ return retval;
+}
+#endif
+
+static int _cyttsp4_ldr_parse_row(struct cyttsp4 *ts, u8 *row_buf,
+ struct cyttsp4_hex_image *row_image)
+{
+ u16 i, j;
+ int retval = 0;
+
+ if (!row_buf) {
+ dev_err(ts->dev,
+ "%s parse row error - buf is null\n", __func__);
+ retval = -EINVAL;
+ goto cyttsp4_ldr_parse_row_exit;
+ }
+
+ row_image->array_id = row_buf[CY_ARRAY_ID_OFFSET];
+ row_image->row_num = _cyttsp4_get_short(&row_buf[CY_ROW_NUM_OFFSET]);
+ row_image->row_size = _cyttsp4_get_short(&row_buf[CY_ROW_SIZE_OFFSET]);
+
+ if (row_image->row_size > ARRAY_SIZE(row_image->row_data)) {
+ dev_err(ts->dev,
+ "%s: row data buffer overflow\n", __func__);
+ retval = -EOVERFLOW;
+ goto cyttsp4_ldr_parse_row_exit;
+ }
+
+ for (i = 0, j = CY_ROW_DATA_OFFSET;
+ i < row_image->row_size; i++)
+ row_image->row_data[i] = row_buf[j++];
+
+ retval = 0;
+
+cyttsp4_ldr_parse_row_exit:
+ return retval;
+}
+
+static int _cyttsp4_ldr_prog_row(struct cyttsp4 *ts,
+ struct cyttsp4_hex_image *row_image)
+{
+ u16 crc;
+ int next;
+ int data;
+ int row_data;
+ u16 row_sum = 0;
+ size_t data_len;
+#ifdef CY_USE_TMA884
+ int segment;
+#endif /* --CY_USE_TMA884 */
+ int retval = 0;
+
+ u8 *cmd = kzalloc(CY_MAX_PACKET_LEN, GFP_KERNEL);
+
+ if (cmd != NULL) {
+ row_data = 0;
+ row_sum = 0;
+
+#ifdef CY_USE_TMA884
+ for (segment = 0; segment <
+ (CY_DATA_ROW_SIZE/CY_PACKET_DATA_LEN)-1;
+ segment++) {
+ next = 0;
+ cmd[next++] = CY_START_OF_PACKET;
+ cmd[next++] = CY_CMD_LDR_SEND_DATA;
+ cmd[next++] = (u8)CY_PACKET_DATA_LEN;
+ cmd[next++] = (u8)(CY_PACKET_DATA_LEN >> 8);
+
+ for (data = 0;
+ data < CY_PACKET_DATA_LEN; data++) {
+ cmd[next] = row_image->row_data
+ [row_data++];
+ row_sum += cmd[next];
+ next++;
+ }
+
+ crc = _cyttsp4_compute_crc(ts, cmd, next);
+ cmd[next++] = (u8)crc;
+ cmd[next++] = (u8)(crc >> 8);
+ cmd[next++] = CY_END_OF_PACKET;
+
+ retval = _cyttsp4_send_cmd(ts, cmd, next, NULL,
+ 0, CY_CMD_LDR_SEND_DATA_STAT_SIZE,
+ CY_HALF_SEC_TMO_MS);
+
+ if (retval < 0) {
+ dev_err(ts->dev,
+ "%s: send row=%d segment=%d"
+ " fail r=%d\n",
+ __func__, row_image->row_num,
+ segment, retval);
+ goto cyttsp4_ldr_prog_row_exit;
+ }
+ }
+#endif /* --CY_USE_TMA884 */
+
+ next = 0;
+#ifdef CY_USE_TMA400
+ cmd[next++] = CY_CMD_LDR_HOST_SYNC;
+#endif /* --CY_USE_TMA400 */
+ cmd[next++] = CY_START_OF_PACKET;
+ cmd[next++] = CY_CMD_LDR_PROG_ROW;
+ /*
+ * include array id size and row id size in CY_PACKET_DATA_LEN
+ */
+#ifdef CY_USE_TMA400
+ data_len = CY_DATA_ROW_SIZE_TMA400;
+#endif /* --CY_USE_TMA400 */
+#ifdef CY_USE_TMA884
+ data_len = CY_PACKET_DATA_LEN;
+#endif /* --CY_USE_TMA884 */
+ cmd[next++] = (u8)(data_len+3);
+ cmd[next++] = (u8)((data_len+3) >> 8);
+ cmd[next++] = row_image->array_id;
+ cmd[next++] = (u8)row_image->row_num;
+ cmd[next++] = (u8)(row_image->row_num >> 8);
+
+ for (data = 0;
+ data < data_len; data++) {
+ cmd[next] = row_image->row_data[row_data++];
+ row_sum += cmd[next];
+ next++;
+ }
+
+#ifdef CY_USE_TMA400
+ crc = _cyttsp4_compute_crc(ts, &cmd[1], next - 1);
+#endif /* --CY_USE_TMA400 */
+#ifdef CY_USE_TMA884
+ crc = _cyttsp4_compute_crc(ts, cmd, next);
+#endif /* --CY_USE_TMA884 */
+ cmd[next++] = (u8)crc;
+ cmd[next++] = (u8)(crc >> 8);
+ cmd[next++] = CY_END_OF_PACKET;
+
+ retval = _cyttsp4_send_cmd(ts, cmd, next, NULL, 0,
+ CY_CMD_LDR_PROG_ROW_STAT_SIZE, CY_HALF_SEC_TMO_MS);
+
+ if (retval < 0) {
+ dev_err(ts->dev,
+ "%s: prog row=%d fail r=%d\n",
+ __func__, row_image->row_num, retval);
+ goto cyttsp4_ldr_prog_row_exit;
+ }
+
+ } else {
+ dev_err(ts->dev,
+ "%s prog row error - cmd buf is NULL\n", __func__);
+ retval = -EIO;
+ }
+
+cyttsp4_ldr_prog_row_exit:
+ if (cmd != NULL)
+ kfree(cmd);
+ return retval;
+}
+
+static int _cyttsp4_ldr_verify_row(struct cyttsp4 *ts,
+ struct cyttsp4_hex_image *row_image)
+{
+ u16 crc;
+ int i = 0;
+ u8 verify_checksum;
+ int retval = 0;
+ /* +1 for TMA400 host sync byte */
+ u8 ldr_verify_row_cmd[CY_CMD_LDR_VERIFY_ROW_CMD_SIZE+1];
+
+#ifdef CY_USE_TMA400
+ ldr_verify_row_cmd[i++] = CY_CMD_LDR_HOST_SYNC;
+#endif /* --CY_USE_TMA400 */
+ ldr_verify_row_cmd[i++] = CY_START_OF_PACKET;
+ ldr_verify_row_cmd[i++] = CY_CMD_LDR_VERIFY_ROW;
+ ldr_verify_row_cmd[i++] = 0x03; /* data len lsb */
+ ldr_verify_row_cmd[i++] = 0x00; /* data len msb */
+ ldr_verify_row_cmd[i++] = row_image->array_id;
+ ldr_verify_row_cmd[i++] = (u8)row_image->row_num;
+ ldr_verify_row_cmd[i++] = (u8)(row_image->row_num >> 8);
+#ifdef CY_USE_TMA400
+ crc = _cyttsp4_compute_crc(ts, &ldr_verify_row_cmd[1], i - 1);
+#endif /* --CY_USE_TMA400 */
+#ifdef CY_USE_TMA884
+ crc = _cyttsp4_compute_crc(ts, ldr_verify_row_cmd, i);
+#endif /* --CY_USE_TMA884 */
+ ldr_verify_row_cmd[i++] = (u8)crc;
+ ldr_verify_row_cmd[i++] = (u8)(crc >> 8);
+ ldr_verify_row_cmd[i++] = CY_END_OF_PACKET;
+
+ retval = _cyttsp4_send_cmd(ts, ldr_verify_row_cmd, i,
+ &verify_checksum, 4,
+ CY_CMD_LDR_VERIFY_ROW_STAT_SIZE, CY_HALF_SEC_TMO_MS);
+
+ if (retval < 0) {
+ dev_err(ts->dev,
+ "%s: verify row=%d fail r=%d\n",
+ __func__, row_image->row_num, retval);
+ }
+
+ return retval;
+}
+
+static int _cyttsp4_ldr_verify_chksum(struct cyttsp4 *ts, u8 *app_chksum)
+{
+ u16 crc;
+ int i = 0;
+ int retval = 0;
+ /* +1 for TMA400 host sync byte */
+ u8 ldr_verify_chksum_cmd[CY_CMD_LDR_VERIFY_CHKSUM_CMD_SIZE+1];
+
+#ifdef CY_USE_TMA400
+ ldr_verify_chksum_cmd[i++] = CY_CMD_LDR_HOST_SYNC;
+#endif /* --CY_USE_TMA400 */
+ ldr_verify_chksum_cmd[i++] = CY_START_OF_PACKET;
+ ldr_verify_chksum_cmd[i++] = CY_CMD_LDR_VERIFY_CHKSUM;
+ ldr_verify_chksum_cmd[i++] = 0x00; /* data len lsb */
+ ldr_verify_chksum_cmd[i++] = 0x00; /* data len msb */
+#ifdef CY_USE_TMA400
+ crc = _cyttsp4_compute_crc(ts, &ldr_verify_chksum_cmd[1], i - 1);
+#endif /* --CY_USE_TMA400 */
+#ifdef CY_USE_TMA884
+ crc = _cyttsp4_compute_crc(ts, ldr_verify_chksum_cmd, i);
+#endif /* --CY_USE_TMA884 */
+ ldr_verify_chksum_cmd[i++] = (u8)crc;
+ ldr_verify_chksum_cmd[i++] = (u8)(crc >> 8);
+ ldr_verify_chksum_cmd[i++] = CY_END_OF_PACKET;
+
+ retval = _cyttsp4_send_cmd(ts, ldr_verify_chksum_cmd, i,
+ app_chksum, 4,
+ CY_CMD_LDR_VERIFY_CHKSUM_STAT_SIZE, CY_HALF_SEC_TMO_MS);
+
+ if (retval < 0) {
+ dev_err(ts->dev,
+ "%s: verify checksum fail r=%d\n",
+ __func__, retval);
+ }
+
+ return retval;
+}
+
+static int _cyttsp4_load_app(struct cyttsp4 *ts, const u8 *fw, int fw_size)
+{
+ u8 *p;
+#ifdef CY_USE_TMA884
+ u8 tries;
+#endif
+ int ret;
+ int retval; /* need separate return value at exit stage */
+ struct cyttsp4_dev_id *file_id = NULL;
+ struct cyttsp4_dev_id *dev_id = NULL;
+ struct cyttsp4_hex_image *row_image = NULL;
+ u8 app_chksum;
+
+ u8 *row_buf = NULL;
+ size_t image_rec_size;
+ size_t row_buf_size = 1024 > CY_MAX_PRBUF_SIZE ?
+ 1024 : CY_MAX_PRBUF_SIZE;
+ int row_count = 0;
+
+#ifdef CY_USE_TMA400
+ image_rec_size = CY_DATA_ROW_SIZE_TMA400 +
+ (sizeof(struct cyttsp4_hex_image) - CY_DATA_ROW_SIZE);
+#endif /* --CY_USE_TMA400 */
+#ifdef CY_USE_TMA884
+ image_rec_size = sizeof(struct cyttsp4_hex_image);
+#endif /* --CY_USE_TMA884 */
+
+ if (!fw_size || (fw_size % image_rec_size != 0)) {
+ dev_err(ts->dev,
+ "%s: Firmware image is misaligned\n", __func__);
+ retval = -EINVAL;
+ goto _cyttsp4_load_app_exit;
+ }
+
+#ifdef CY_USE_WATCHDOG
+ _cyttsp4_stop_wd_timer(ts);
+#endif
+
+ dev_info(ts->dev,
+ "%s: start load app\n", __func__);
+
+ row_buf = kzalloc(row_buf_size, GFP_KERNEL);
+ row_image = kzalloc(sizeof(struct cyttsp4_hex_image), GFP_KERNEL);
+ file_id = kzalloc(sizeof(struct cyttsp4_dev_id), GFP_KERNEL);
+ dev_id = kzalloc(sizeof(struct cyttsp4_dev_id), GFP_KERNEL);
+ if ((row_buf == NULL) || (row_image == NULL) ||
+ (file_id == NULL) || (dev_id == NULL)) {
+ dev_err(ts->dev,
+ "%s: Unable to alloc row buffers(%p %p %p %p)\n",
+ __func__, row_buf, row_image, file_id, dev_id);
+ retval = -ENOMEM;
+ goto _cyttsp4_load_app_error_exit;
+ }
+
+ p = (u8 *)fw;
+ /* Enter Loader and return Silicon ID and Rev */
+
+ retval = _cyttsp4_reset(ts);
+ if (retval < 0) {
+ dev_err(ts->dev,
+ "%s: Fail reset device r=%d\n", __func__, retval);
+ goto _cyttsp4_load_app_exit;
+ }
+ retval = _cyttsp4_wait_int(ts, CY_TEN_SEC_TMO_MS);
+ if (retval < 0) {
+ dev_err(ts->dev,
+ "%s: Fail waiting for bootloader interrupt\n",
+ __func__);
+ goto _cyttsp4_load_app_exit;
+ }
+
+ _cyttsp4_change_state(ts, CY_BL_STATE);
+ dev_info(ts->dev,
+ "%s: Send BL Loader Enter\n", __func__);
+ retval = _cyttsp4_ldr_enter(ts, dev_id);
+ if (retval < 0) {
+ dev_err(ts->dev,
+ "%s: Error cannot start Loader (ret=%d)\n",
+ __func__, retval);
+ goto _cyttsp4_load_app_error_exit;
+ }
+
+ dev_vdbg(ts->dev,
+ "%s: dev: silicon id=%08X rev=%02X bl=%08X\n",
+ __func__, dev_id->silicon_id,
+ dev_id->rev_id, dev_id->bl_ver);
+
+#ifdef CY_USE_TMA400
+ udelay(1000);
+ retval = _cyttsp4_ldr_init(ts);
+ if (retval < 0) {
+ dev_err(ts->dev,
+ "%s: Error cannot init Loader (ret=%d)\n",
+ __func__, retval);
+ goto _cyttsp4_load_app_error_exit;
+ }
+#endif /* --CY_USE_TMA400 */
+
+ dev_info(ts->dev,
+ "%s: Send BL Loader Blocks\n", __func__);
+ while (p < (fw + fw_size)) {
+ /* Get row */
+ dev_dbg(ts->dev,
+ "%s: read row=%d\n", __func__, ++row_count);
+ memset(row_buf, 0, row_buf_size);
+ p = _cyttsp4_get_row(ts, row_buf, p, image_rec_size);
+
+ /* Parse row */
+ dev_vdbg(ts->dev,
+ "%s: p=%p buf=%p buf[0]=%02X\n", __func__,
+ p, row_buf, row_buf[0]);
+ retval = _cyttsp4_ldr_parse_row(ts, row_buf, row_image);
+ dev_vdbg(ts->dev,
+ "%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 (retval < 0) {
+ dev_err(ts->dev,
+ "%s: Parse Row Error "
+ "(a=%d r=%d ret=%d\n",
+ __func__, row_image->array_id,
+ row_image->row_num,
+ retval);
+ goto bl_exit;
+ } else {
+ dev_vdbg(ts->dev,
+ "%s: Parse Row "
+ "(a=%d r=%d ret=%d\n",
+ __func__, row_image->array_id,
+ row_image->row_num, retval);
+ }
+
+#ifdef CY_USE_TMA884
+ /* erase row */
+ tries = 0;
+ do {
+ retval = _cyttsp4_ldr_erase_row(ts, row_image);
+ if (retval < 0) {
+ dev_err(ts->dev,
+ "%s: Erase Row Error "
+ "(array=%d row=%d ret=%d try=%d)\n",
+ __func__, row_image->array_id,
+ row_image->row_num, retval, tries);
+ }
+ } while (retval && tries++ < 5);
+
+ if (retval < 0)
+ goto _cyttsp4_load_app_error_exit;
+#endif
+
+ /* program row */
+ retval = _cyttsp4_ldr_prog_row(ts, row_image);
+ if (retval < 0) {
+ dev_err(ts->dev,
+ "%s: Program Row Error "
+ "(array=%d row=%d ret=%d)\n",
+ __func__, row_image->array_id,
+ row_image->row_num, retval);
+ goto _cyttsp4_load_app_error_exit;
+ }
+
+ /* verify row */
+ retval = _cyttsp4_ldr_verify_row(ts, row_image);
+ if (retval < 0) {
+ dev_err(ts->dev,
+ "%s: Verify Row Error "
+ "(array=%d row=%d ret=%d)\n",
+ __func__, row_image->array_id,
+ row_image->row_num, retval);
+ goto _cyttsp4_load_app_error_exit;
+ }
+
+ dev_vdbg(ts->dev,
+ "%s: array=%d row_cnt=%d row_num=%04X\n",
+ __func__, row_image->array_id, row_count,
+ row_image->row_num);
+ }
+
+ /* verify app checksum */
+ retval = _cyttsp4_ldr_verify_chksum(ts, &app_chksum);
+ dev_dbg(ts->dev,
+ "%s: Application Checksum = %02X r=%d\n",
+ __func__, app_chksum, retval);
+ if (retval < 0) {
+ dev_err(ts->dev,
+ "%s: ldr_verify_chksum fail r=%d\n", __func__, retval);
+ retval = 0;
+ }
+
+ /* exit loader */
+bl_exit:
+ dev_info(ts->dev,
+ "%s: Send BL Loader Terminate\n", __func__);
+ ret = _cyttsp4_ldr_exit(ts);
+ if (ret) {
+ dev_err(ts->dev,
+ "%s: Error on exit Loader (ret=%d)\n",
+ __func__, ret);
+ retval = ret;
+ goto _cyttsp4_load_app_error_exit;
+ }
+
+ /*
+ * this is a temporary parking state;
+ * the driver will always run startup
+ * after the loader has completed
+ */
+ _cyttsp4_change_state(ts, CY_TRANSFER_STATE);
+ goto _cyttsp4_load_app_exit;
+
+_cyttsp4_load_app_error_exit:
+ _cyttsp4_change_state(ts, CY_BL_STATE);
+_cyttsp4_load_app_exit:
+ kfree(row_buf);
+ kfree(row_image);
+ kfree(file_id);
+ kfree(dev_id);
+ return retval;
+}
+#endif /* CY_AUTO_LOAD_FW || CY_USE_FORCE_LOAD || CONFIG_TOUCHSCREEN_DEBUG */
+
+/* Constructs loader exit command and sends via _cyttsp4_send_cmd() */
+static int _cyttsp4_ldr_exit(struct cyttsp4 *ts)
+{
+ u16 crc;
+ int i = 0;
+ int retval = 0;
+ /* +1 for TMA400 host sync byte */
+ u8 ldr_exit_cmd[CY_CMD_LDR_EXIT_CMD_SIZE+1];
+
+#ifdef CY_USE_TMA400
+ ldr_exit_cmd[i++] = CY_CMD_LDR_HOST_SYNC;
+#endif /* --CY_USE_TMA400 */
+ ldr_exit_cmd[i++] = CY_START_OF_PACKET;
+ ldr_exit_cmd[i++] = CY_CMD_LDR_EXIT;
+ ldr_exit_cmd[i++] = 0x00; /* data len lsb */
+ ldr_exit_cmd[i++] = 0x00; /* data len msb */
+#ifdef CY_USE_TMA400
+ crc = _cyttsp4_compute_crc(ts, &ldr_exit_cmd[1], i - 1);
+#endif /* --CY_USE_TMA400 */
+#ifdef CY_USE_TMA884
+ crc = _cyttsp4_compute_crc(ts, ldr_exit_cmd, i);
+#endif /* --CY_USE_TMA884 */
+ ldr_exit_cmd[i++] = (u8)crc;
+ ldr_exit_cmd[i++] = (u8)(crc >> 8);
+ ldr_exit_cmd[i++] = CY_END_OF_PACKET;
+
+ retval = _cyttsp4_send_cmd(ts, ldr_exit_cmd, i, NULL, 0,
+ CY_CMD_LDR_EXIT_STAT_SIZE, 0);
+
+ if (retval < 0) {
+ dev_err(ts->dev,
+ "%s: BL Loader exit fail r=%d\n",
+ __func__, retval);
+ }
+
+ dev_vdbg(ts->dev,
+ "%s: Exit BL Loader r=%d\n", __func__, retval);
+
+ return retval;
+}
+
+#if defined(CY_USE_FORCE_LOAD) || defined(CONFIG_TOUCHSCREEN_DEBUG)
+/* Force firmware upgrade */
+static void cyttsp4_firmware_cont(const struct firmware *fw, void *context)
+{
+ int retval = 0;
+ struct device *dev = context;
+ struct cyttsp4 *ts = dev_get_drvdata(dev);
+ u8 header_size = 0;
+
+ mutex_lock(&ts->data_lock);
+
+ if (fw == NULL) {
+ dev_err(ts->dev,
+ "%s: Firmware not found\n", __func__);
+ goto cyttsp4_firmware_cont_exit;
+ }
+
+ if ((fw->data == NULL) || (fw->size == 0)) {
+ dev_err(ts->dev,
+ "%s: No firmware received\n", __func__);
+ goto cyttsp4_firmware_cont_release_exit;
+ }
+
+ header_size = fw->data[0];
+ if (header_size >= (fw->size + 1)) {
+ dev_err(ts->dev,
+ "%s: Firmware format is invalid\n", __func__);
+ goto cyttsp4_firmware_cont_release_exit;
+ }
+ retval = _cyttsp4_load_app(ts, &(fw->data[header_size + 1]),
+ fw->size - (header_size + 1));
+ if (retval < 0) {
+ dev_err(ts->dev,
+ "%s: Firmware update failed with error code %d\n",
+ __func__, retval);
+ _cyttsp4_change_state(ts, CY_IDLE_STATE);
+ retval = -EIO;
+ goto cyttsp4_firmware_cont_release_exit;
+ }
+
+
+ retval = _cyttsp4_startup(ts);
+ if (retval < 0) {
+ dev_err(ts->dev,
+ "%s: Failed to restart IC with error code %d\n",
+ __func__, retval);
+ _cyttsp4_change_state(ts, CY_IDLE_STATE);
+ }
+
+cyttsp4_firmware_cont_release_exit:
+ release_firmware(fw);
+
+cyttsp4_firmware_cont_exit:
+ ts->waiting_for_fw = false;
+ mutex_unlock(&ts->data_lock);
+ return;
+}
+static ssize_t cyttsp4_ic_reflash_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ static const char *wait_fw_ld = "Driver is waiting for firmware load\n";
+ static const char *no_fw_ld = "No firmware loading in progress\n";
+ struct cyttsp4 *ts = dev_get_drvdata(dev);
+
+ if (ts->waiting_for_fw)
+ return snprintf(buf, strlen(wait_fw_ld)+1, wait_fw_ld);
+ else
+ return snprintf(buf, strlen(no_fw_ld)+1, no_fw_ld);
+}
+static ssize_t cyttsp4_ic_reflash_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t size)
+{
+ int i;
+ int retval = 0;
+ struct cyttsp4 *ts = dev_get_drvdata(dev);
+
+ if (ts->waiting_for_fw) {
+ dev_err(ts->dev,
+ "%s: Driver is already waiting for firmware\n",
+ __func__);
+ retval = -EALREADY;
+ goto cyttsp4_ic_reflash_store_exit;
+ }
+
+ /*
+ * must configure FW_LOADER in .config file
+ * CONFIG_HOTPLUG=y
+ * CONFIG_FW_LOADER=y
+ * CONFIG_FIRMWARE_IN_KERNEL=y
+ * CONFIG_EXTRA_FIRMWARE=""
+ * CONFIG_EXTRA_FIRMWARE_DIR=""
+ */
+
+ if (size > CY_BL_FW_NAME_SIZE) {
+ dev_err(ts->dev,
+ "%s: Filename too long\n", __func__);
+ retval = -ENAMETOOLONG;
+ goto cyttsp4_ic_reflash_store_exit;
+ } else {
+ /*
+ * name string must be in alloc() memory
+ * or is lost on context switch
+ * strip off any line feed character(s)
+ * at the end of the buf string
+ */
+ for (i = 0; buf[i]; i++) {
+ if (buf[i] < ' ')
+ ts->fwname[i] = 0;
+ else
+ ts->fwname[i] = buf[i];
+ }
+ }
+
+ dev_vdbg(ts->dev,
+ "%s: Enabling firmware class loader\n", __func__);
+
+ retval = request_firmware_nowait(THIS_MODULE,
+ FW_ACTION_NOHOTPLUG, (const char *)ts->fwname, ts->dev,
+ GFP_KERNEL, ts->dev, cyttsp4_firmware_cont);
+ if (retval < 0) {
+ dev_err(ts->dev,
+ "%s: Fail request firmware class file load\n",
+ __func__);
+ ts->waiting_for_fw = false;
+ goto cyttsp4_ic_reflash_store_exit;
+ } else {
+ ts->waiting_for_fw = true;
+ retval = size;
+ }
+
+cyttsp4_ic_reflash_store_exit:
+ return retval;
+}
+static DEVICE_ATTR(ic_reflash, S_IRUSR | S_IWUSR,
+ cyttsp4_ic_reflash_show, cyttsp4_ic_reflash_store);
+#endif /* CY_USE_FORCE_LOAD || CONFIG_TOUCHSCREEN_DEBUG */
+
+#ifdef CY_USE_TMA884
+static int _cyttsp4_calc_data_crc(struct cyttsp4 *ts, size_t ndata, u8 *pdata,
+ u8 *crc_h, u8 *crc_l, const char *name)
+{
+ int retval = 0;
+ u8 *buf = NULL;
+
+ *crc_h = 0;
+ *crc_l = 0;
+
+ buf = kzalloc(sizeof(uint8_t) * 126, GFP_KERNEL);
+ if (buf == NULL) {
+ dev_err(ts->dev,
+ "%s: Failed to allocate buf\n", __func__);
+ retval = -ENOMEM;
+ goto _cyttsp4_calc_data_crc_exit;
+ }
+
+ if (pdata == NULL) {
+ dev_err(ts->dev,
+ "%s: bad data pointer\n", __func__);
+ retval = -ENXIO;
+ goto _cyttsp4_calc_data_crc_exit;
+ }
+
+ if (ndata > 122) {
+ dev_err(ts->dev,
+ "%s: %s is too large n=%d size=%d\n",
+ __func__, name, ndata, 126);
+ retval = -EOVERFLOW;
+ goto _cyttsp4_calc_data_crc_exit;
+ }
+
+ buf[0] = 0x00; /* num of config bytes + 4 high */
+ buf[1] = 0x7E; /* num of config bytes + 4 low */
+ buf[2] = 0x00; /* max block size w/o crc high */
+ buf[3] = 0x7E; /* max block size w/o crc low */
+
+ /* Copy platform data */
+ memcpy(&(buf[4]), pdata, ndata);
+
+ /* Calculate CRC */
+ _cyttsp4_calc_crc(ts, buf, 126, crc_h, crc_l);
+
+ dev_vdbg(ts->dev,
+ "%s: crc=%02X%02X\n", __func__, *crc_h, *crc_l);
+
+_cyttsp4_calc_data_crc_exit:
+ kfree(buf);
+ return retval;
+}
+#endif /* --CY_USE_TMA884 */
+
+
+#ifdef CY_USE_TMA884
+#ifdef CY_AUTO_LOAD_TOUCH_PARAMS
+static int _cyttsp4_calc_settings_crc(struct cyttsp4 *ts, u8 *crc_h, u8 *crc_l)
+{
+ int retval = 0;
+ u8 *buf = NULL;
+ u8 size = 0;
+
+ buf = kzalloc(sizeof(uint8_t) * 126, GFP_KERNEL);
+ if (buf == NULL) {
+ dev_err(ts->dev,
+ "%s: Failed to allocate buf\n", __func__);
+ retval = -ENOMEM;
+ goto _cyttsp4_calc_settings_crc_exit;
+ }
+
+ if (ts->platform_data->sett[CY_IC_GRPNUM_TCH_PARM_VAL] == NULL) {
+ dev_err(ts->dev,
+ "%s: Missing Platform Touch Parameter"
+ " values table\n", __func__);
+ retval = -ENXIO;
+ goto _cyttsp4_calc_settings_crc_exit;
+ }
+ if ((ts->platform_data->sett
+ [CY_IC_GRPNUM_TCH_PARM_VAL]->data == NULL) ||
+ (ts->platform_data->sett
+ [CY_IC_GRPNUM_TCH_PARM_VAL]->size == 0)) {
+ dev_err(ts->dev,
+ "%s: Missing Platform Touch Parameter"
+ " values table data\n", __func__);
+ retval = -ENXIO;
+ goto _cyttsp4_calc_settings_crc_exit;
+ }
+
+ size = ts->platform_data->sett[CY_IC_GRPNUM_TCH_PARM_VAL]->size;
+
+ if (size > 122) {
+ dev_err(ts->dev,
+ "%s: Platform data is too large\n", __func__);
+ retval = -EOVERFLOW;
+ goto _cyttsp4_calc_settings_crc_exit;
+ }
+
+ buf[0] = 0x00; /* num of config bytes + 4 high */
+ buf[1] = 0x7E; /* num of config bytes + 4 low */
+ buf[2] = 0x00; /* max block size w/o crc high */
+ buf[3] = 0x7E; /* max block size w/o crc low */
+
+ /* Copy platform data */
+ memcpy(&(buf[4]),
+ ts->platform_data->sett[CY_IC_GRPNUM_TCH_PARM_VAL]->data,
+ size);
+
+ /* Calculate CRC */
+ _cyttsp4_calc_crc(ts, buf, 126, crc_h, crc_l);
+
+_cyttsp4_calc_settings_crc_exit:
+ kfree(buf);
+ return retval;
+}
+#endif /* --CY_AUTO_LOAD_TOUCH_PARAMS */
+#endif /* --CY_USE_TMA884 */
+
+/* Get IC CRC is operational mode command */
+static int _cyttsp4_get_ic_crc(struct cyttsp4 *ts,
+ enum cyttsp4_ic_ebid ebid, u8 *crc_h, u8 *crc_l)
+{
+ int retval = 0;
+ u8 cmd_dat[CY_NUM_DAT + 1]; /* +1 for cmd byte */
+
+ memset(cmd_dat, 0, sizeof(cmd_dat));
+ cmd_dat[0] = CY_GET_CFG_BLK_CRC;/* pack cmd */
+ cmd_dat[1] = ebid; /* pack EBID id */
+
+ retval = _cyttsp4_put_cmd_wait(ts, ts->si_ofs.cmd_ofs,
+ sizeof(cmd_dat), cmd_dat, CY_HALF_SEC_TMO_MS,
+ _cyttsp4_chk_cmd_rdy, NULL,
+ ts->platform_data->addr[CY_TCH_ADDR_OFS], true);
+ if (retval < 0) {
+ dev_err(ts->dev,
+ "%s: Fail Get CRC command r=%d\n",
+ __func__, retval);
+ goto _cyttsp4_get_ic_crc_exit;
+ }
+
+ memset(cmd_dat, 0, sizeof(cmd_dat));
+ retval = _cyttsp4_read_block_data(ts, ts->si_ofs.cmd_ofs,
+ sizeof(cmd_dat), cmd_dat,
+ ts->platform_data->addr[CY_TCH_ADDR_OFS], true);
+ if (retval < 0) {
+ dev_err(ts->dev,
+ "%s: Fail Get CRC status r=%d\n",
+ __func__, retval);
+ goto _cyttsp4_get_ic_crc_exit;
+ }
+
+ /* Check CRC status and assign values */
+ if (cmd_dat[1] != 0) {
+ dev_err(ts->dev,
+ "%s: Get CRC status=%d error\n",
+ __func__, cmd_dat[1]);
+ retval = -EIO;
+ goto _cyttsp4_get_ic_crc_exit;
+ }
+
+ *crc_h = cmd_dat[2];
+ *crc_l = cmd_dat[3];
+
+#ifdef CY_USE_TMA400
+ retval = _cyttsp4_cmd_handshake(ts);
+ if (retval < 0) {
+ dev_err(ts->dev,
+ "%s: Command handshake error r=%d\n",
+ __func__, retval);
+ /* continue anyway; rely on handshake tmo */
+ retval = 0;
+ }
+#endif /* --CY_USE_TMA400 */
+
+_cyttsp4_get_ic_crc_exit:
+ return retval;
+}
+
+#ifdef CY_USE_TMA400
+static int _cyttsp4_startup(struct cyttsp4 *ts)
+{
+ int tries;
+ int retval = 0;
+ u8 ic_crc[2];
+#ifdef CY_AUTO_LOAD_TOUCH_PARAMS
+ u8 table_crc[2];
+#endif /* --CY_AUTO_LOAD_TOUCH_PARAMS */
+ bool put_all_params_done = false;
+ bool upgraded = false;
+ bool mddata_updated = false;
+ bool sleep_state = false;
+ tries = 0;
+ ts->starting_up = true;
+ memset(&ts->test, 0, sizeof(struct cyttsp4_test_mode));
+ if (ts->driver_state == CY_SLEEP_STATE)
+ sleep_state = true;
+_cyttsp4_startup_tma400_restart:
+
+ dev_err(ts->dev,
+ "%s: enter driver_state=%d\n", __func__, ts->driver_state);
+ ts->current_mode = CY_MODE_BOOTLOADER;
+ retval = _cyttsp4_reset(ts);
+ if (retval < 0) {
+ dev_err(ts->dev,
+ "%s: Fail reset device r=%d\n", __func__, retval);
+ /* continue anyway in case device was already in bootloader */
+ }
+ /*
+ * Wait for heartbeat interrupt. If we didn't get the CPU quickly, this
+ * may not be the first interupt.
+ */
+ dev_vdbg(ts->dev,
+ "%s: wait for first bootloader interrupt\n", __func__);
+ retval = _cyttsp4_wait_int(ts, CY_HALF_SEC_TMO_MS);
+ if (retval < 0) {
+ dev_err(ts->dev,
+ "%s: Fail waiting for bootloader interrupt\n",
+ __func__);
+ goto _cyttsp4_startup_tma400_exit;
+ }
+
+ /*
+ * exit BL mode and eliminate race between heartbeat and
+ * command / response interrupts
+ */
+ _cyttsp4_change_state(ts, CY_EXIT_BL_STATE);
+ ts->switch_flag = true;
+ retval = _cyttsp4_wait_si_int(ts, CY_TEN_SEC_TMO_MS);
+ if (retval < 0) {
+ dev_err(ts->dev,
+ "%s: Fail wait switch to Sysinfo r=%d\n",
+ __func__, retval);
+ /* continue anyway in case sync missed */
+ }
+
+ if (!sleep_state) {
+
+ if (ts->driver_state != CY_SYSINFO_STATE) {
+ dev_err(ts->dev,
+ "%s: Fail set sysinfo mode; switch to sysinfo anyway\r",
+ __func__);
+ _cyttsp4_change_state(ts, CY_SYSINFO_STATE);
+ } else {
+ dev_vdbg(ts->dev,
+ "%s: Exit BL ok; now in sysinfo mode\n", __func__);
+ _cyttsp4_pr_state(ts);
+ }
+
+ dev_vdbg(ts->dev,
+ "%s: Read Sysinfo regs and get rev numbers try=%d\n",
+ __func__, tries);
+ retval = _cyttsp4_get_sysinfo_regs(ts);
+ if (retval < 0) {
+ dev_err(ts->dev,
+ "%s: Read Block fail -get sys regs (r=%d)\n",
+ __func__, retval);
+ dev_err(ts->dev,
+ "%s: Fail to switch from Bootloader "
+ "to Application r=%d\n",
+ __func__, retval);
+
+ _cyttsp4_change_state(ts, CY_BL_STATE);
+
+ if (upgraded) {
+ dev_err(ts->dev,
+ "%s: app failed to launch after"
+ " platform firmware upgrade\n", __func__);
+ retval = -EIO;
+ goto _cyttsp4_startup_tma400_exit;
+ }
+
+#ifdef CY_AUTO_LOAD_FW
+ if (!ts->powered) {
+ dev_info(ts->dev,
+ "%s: attempting to reflash IC...\n", __func__);
+ if (ts->platform_data->fw->img == NULL ||
+ ts->platform_data->fw->size == 0) {
+ dev_err(ts->dev,
+ "%s: no platform firmware available"
+ " for reflashing\n", __func__);
+ _cyttsp4_change_state(ts, CY_INVALID_STATE);
+ retval = -ENODATA;
+ goto _cyttsp4_startup_tma400_exit;
+ }
+ retval = _cyttsp4_load_app(ts,
+ ts->platform_data->fw->img,
+ ts->platform_data->fw->size);
+ if (retval) {
+ dev_err(ts->dev,
+ "%s: failed to reflash IC (r=%d)\n",
+ __func__, retval);
+ _cyttsp4_change_state(ts, CY_INVALID_STATE);
+ retval = -EIO;
+ goto _cyttsp4_startup_tma400_exit;
+ }
+ upgraded = true;
+ dev_info(ts->dev,
+ "%s: resetting IC after reflashing\n", __func__);
+ goto _cyttsp4_startup_tma400_restart; /* Reset the part */
+ }
+#endif /* --CY_AUTO_LOAD_FW */
+ }
+
+#ifdef CY_AUTO_LOAD_FW
+ if (!ts->powered) {
+ retval = _cyttsp4_boot_loader(ts, &upgraded);
+ if (retval < 0) {
+ dev_err(ts->dev,
+ "%s: fail boot loader r=%d)\n",
+ __func__, retval);
+ _cyttsp4_change_state(ts, CY_IDLE_STATE);
+ goto _cyttsp4_startup_tma400_exit;
+ }
+ if (upgraded)
+ goto _cyttsp4_startup_tma400_restart;
+ }
+#endif /* --CY_AUTO_LOAD_FW */
+
+ retval = _cyttsp4_set_mode(ts, CY_CONFIG_MODE);
+ if (retval < 0) {
+ dev_err(ts->dev,
+ "%s: Fail set config mode 1 r=%d\n", __func__, retval);
+ goto _cyttsp4_startup_tma400_bypass_crc_check;
+ }
+
+ retval = _cyttsp4_get_ebid_row_size(ts);
+ if (retval < 0) {
+ dev_err(ts->dev,
+ "%s: Fail get EBID row size; using default r=%d\n",
+ __func__, retval);
+ }
+ dev_vdbg(ts->dev,
+ "%s: get EBID row size=%d\n", __func__, ts->ebid_row_size);
+
+ memset(ic_crc, 0, sizeof(ic_crc));
+ dev_vdbg(ts->dev,
+ "%s: Read IC CRC values\n", __func__);
+ /* Get settings CRC from touch IC */
+ retval = _cyttsp4_set_mode(ts, CY_OPERATE_MODE);
+ if (retval < 0) {
+ dev_err(ts->dev,
+ "%s: Fail set operational mode 1 (r=%d)\n",
+ __func__, retval);
+ goto _cyttsp4_startup_tma400_exit;
+ }
+ retval = _cyttsp4_get_ic_crc(ts, CY_TCH_PARM_EBID,
+ &ic_crc[0], &ic_crc[1]);
+ if (retval < 0) {
+ dev_err(ts->dev,
+ "%s: Fail read ic crc r=%d\n",
+ __func__, retval);
+ }
+
+ _cyttsp4_pr_buf(ts, ic_crc, sizeof(ic_crc), "read_ic_crc");
+
+ retval = _cyttsp4_set_mode(ts, CY_CONFIG_MODE);
+ if (retval < 0) {
+ dev_err(ts->dev,
+ "%s: Fail set config mode 2 r=%d\n", __func__, retval);
+ goto _cyttsp4_startup_tma400_exit;
+ }
+#ifdef CY_AUTO_LOAD_TOUCH_PARAMS
+ if (!ts->powered) {
+ if (!put_all_params_done) {
+ if (ts->platform_data->sett
+ [CY_IC_GRPNUM_TCH_PARM_VAL] == NULL) {
+ dev_err(ts->dev,
+ "%s: missing param table\n", __func__);
+ goto _cyttsp4_startup_tma400_bypass_crc_check;
+ } else if (ts->platform_data->sett
+ [CY_IC_GRPNUM_TCH_PARM_VAL]->data == NULL) {
+ dev_err(ts->dev,
+ "%s: missing param table data\n", __func__);
+ goto _cyttsp4_startup_tma400_bypass_crc_check;
+ } else if (ts->platform_data->sett
+ [CY_IC_GRPNUM_TCH_PARM_VAL]->size == 0) {
+ dev_err(ts->dev,
+ "%s: param values table size is 0\n", __func__);
+ goto _cyttsp4_startup_tma400_bypass_crc_check;
+ }
+ _cyttsp_read_table_crc(ts, ts->platform_data->sett
+ [CY_IC_GRPNUM_TCH_PARM_VAL]->data,
+ &table_crc[0], &table_crc[1]);
+ _cyttsp4_pr_buf(ts, table_crc, sizeof(table_crc),
+ "read_table_crc");
+ if ((ic_crc[0] != table_crc[0]) ||
+ (ic_crc[1] != table_crc[1])) {
+ retval = _cyttsp4_put_all_params_tma400(ts);
+ if (retval < 0) {
+ dev_err(ts->dev,
+ "%s: Fail put all params r=%d\n",
+ __func__, retval);
+ goto _cyttsp4_startup_tma400_bypass_crc_check;
+ }
+ put_all_params_done = true;
+ goto _cyttsp4_startup_tma400_restart;
+ }
+ }
+ }
+#else
+ put_all_params_done = true;
+#endif /* --CY_AUTO_LOAD_TOUCH_PARAMS */
+
+_cyttsp4_startup_tma400_bypass_crc_check:
+ if (!mddata_updated) {
+ retval = _cyttsp4_check_mddata_tma400(ts, &mddata_updated);
+ if (retval < 0) {
+ dev_err(ts->dev,
+ "%s: Fail update MDDATA r=%d\n",
+ __func__, retval);
+ } else if (mddata_updated)
+ goto _cyttsp4_startup_tma400_restart;
+ }
+
+ }
+
+ dev_vdbg(ts->dev,
+ "%s: enter operational mode\n", __func__);
+ /* mode=operational mode, state = active_state */
+ retval = _cyttsp4_set_mode(ts, CY_OPERATE_MODE);
+ if (retval < 0) {
+ dev_err(ts->dev,
+ "%s: Fail set operational mode 2 (r=%d)\n",
+ __func__, retval);
+ goto _cyttsp4_startup_tma400_exit;
+ }
+
+ if (ts->was_suspended) {
+ ts->was_suspended = false;
+ retval = _cyttsp4_enter_sleep(ts);
+ if (retval < 0) {
+ dev_err(ts->dev,
+ "%s: fail resume sleep r=%d\n",
+ __func__, retval);
+ }
+ } else {
+#ifdef CY_USE_WATCHDOG
+ _cyttsp4_start_wd_timer(ts);
+#endif
+ }
+_cyttsp4_startup_tma400_exit:
+ ts->starting_up = false;
+ return retval;
+}
+#endif /* --CY_USE_TMA400 */
+
+#ifdef CY_USE_TMA884
+#define CY_IRQ_DEASSERT 1
+#define CY_IRQ_ASSERT 0
+static int _cyttsp4_startup(struct cyttsp4 *ts)
+{
+ int retval = 0;
+ int i = 0;
+ u8 pdata_crc[2];
+ u8 ic_crc[2];
+ bool upgraded = false;
+ bool mddata_updated = false;
+ bool wrote_sysinfo_regs = false;
+ bool wrote_settings = false;
+
+ memset(&ts->test, 0, sizeof(struct cyttsp4_test_mode));
+#ifdef CY_USE_WATCHDOG
+ _cyttsp4_stop_wd_timer(ts);
+#endif
+_cyttsp4_startup_start:
+ memset(pdata_crc, 0, sizeof(pdata_crc));
+ memset(ic_crc, 0, sizeof(ic_crc));
+ dev_vdbg(ts->dev,
+ "%s: enter driver_state=%d\n", __func__, ts->driver_state);
+ _cyttsp4_change_state(ts, CY_BL_STATE);
+
+ retval = _cyttsp4_reset(ts);
+ if (retval < 0) {
+ dev_err(ts->dev,
+ "%s: Fail reset device r=%d\n", __func__, retval);
+ /* continue anyway in case device was already in bootloader */
+ }
+
+ /* wait for interrupt to set ready completion */
+ retval = _cyttsp4_wait_int(ts, CY_HALF_SEC_TMO_MS);
+ if (retval < 0) {
+ dev_err(ts->dev,
+ "%s: Fail waiting for bootloader interrupt\n",
+ __func__);
+ goto _cyttsp4_startup_exit;
+ }
+
+ INIT_COMPLETION(ts->si_int_running);
+ _cyttsp4_change_state(ts, CY_EXIT_BL_STATE);
+ ts->switch_flag = true;
+ retval = _cyttsp4_wait_si_int(ts, CY_TEN_SEC_TMO_MS);
+ if (retval < 0) {
+ dev_err(ts->dev,
+ "%s: Fail wait switch to Sysinfo r=%d\n",
+ __func__, retval);
+ /* continue anyway in case sync missed */
+ }
+ if (ts->driver_state != CY_SYSINFO_STATE)
+ _cyttsp4_change_state(ts, CY_SYSINFO_STATE);
+ else
+ _cyttsp4_pr_state(ts);
+
+ /*
+ * TODO: remove this wait for toggle high when
+ * startup from ES10 firmware is no longer required
+ */
+ /* Wait for IRQ to toggle high */
+ dev_vdbg(ts->dev,
+ "%s: wait for irq toggle high\n", __func__);
+ retval = -ETIMEDOUT;
+ for (i = 0; i < CY_DELAY_MAX * 10 * 5; i++) {
+ if (ts->platform_data->irq_stat() == CY_IRQ_DEASSERT) {
+ retval = 0;
+ break;
+ }
+ mdelay(CY_DELAY_DFLT);
+ }
+ if (retval < 0) {
+ dev_err(ts->dev,
+ "%s: timeout waiting for irq to de-assert\n",
+ __func__);
+ goto _cyttsp4_startup_exit;
+ }
+
+ dev_vdbg(ts->dev,
+ "%s: read sysinfo 1\n", __func__);
+ memset(&ts->sysinfo_data, 0,
+ sizeof(struct cyttsp4_sysinfo_data));
+ retval = _cyttsp4_read_block_data(ts, CY_REG_BASE,
+ sizeof(struct cyttsp4_sysinfo_data), &ts->sysinfo_data,
+ ts->platform_data->addr[CY_TCH_ADDR_OFS], true);
+ if (retval < 0) {
+ dev_err(ts->dev,
+ "%s: Fail to switch from Bootloader "
+ "to Application r=%d\n",
+ __func__, retval);
+
+ _cyttsp4_change_state(ts, CY_BL_STATE);
+
+ if (upgraded) {
+ dev_err(ts->dev,
+ "%s: app failed to launch after"
+ " platform firmware upgrade\n", __func__);
+ retval = -EIO;
+ goto _cyttsp4_startup_exit;
+ }
+
+#ifdef CY_AUTO_LOAD_FW
+ dev_info(ts->dev,
+ "%s: attempting to reflash IC...\n", __func__);
+ if (ts->platform_data->fw->img == NULL ||
+ ts->platform_data->fw->size == 0) {
+ dev_err(ts->dev,
+ "%s: no platform firmware available"
+ " for reflashing\n", __func__);
+ _cyttsp4_change_state(ts, CY_INVALID_STATE);
+ retval = -ENODATA;
+ goto _cyttsp4_startup_exit;
+ }
+ retval = _cyttsp4_load_app(ts,
+ ts->platform_data->fw->img,
+ ts->platform_data->fw->size);
+ if (retval) {
+ dev_err(ts->dev,
+ "%s: failed to reflash IC (r=%d)\n",
+ __func__, retval);
+ _cyttsp4_change_state(ts, CY_INVALID_STATE);
+ retval = -EIO;
+ goto _cyttsp4_startup_exit;
+ }
+ upgraded = true;
+ dev_info(ts->dev,
+ "%s: resetting IC after reflashing\n", __func__);
+ goto _cyttsp4_startup_start; /* Reset the part */
+#endif /* --CY_AUTO_LOAD_FW */
+ }
+
+ /*
+ * read system information registers
+ * get version numbers and fill sysinfo regs
+ */
+ dev_vdbg(ts->dev,
+ "%s: Read Sysinfo regs and get version numbers\n", __func__);
+ retval = _cyttsp4_get_sysinfo_regs(ts);
+ if (retval < 0) {
+ dev_err(ts->dev,
+ "%s: Read Block fail -get sys regs (r=%d)\n",
+ __func__, retval);
+ _cyttsp4_change_state(ts, CY_IDLE_STATE);
+ goto _cyttsp4_startup_exit;
+ }
+
+#ifdef CY_AUTO_LOAD_FW
+ retval = _cyttsp4_boot_loader(ts, &upgraded);
+ if (retval < 0) {
+ dev_err(ts->dev,
+ "%s: fail boot loader r=%d)\n",
+ __func__, retval);
+ _cyttsp4_change_state(ts, CY_IDLE_STATE);
+ goto _cyttsp4_startup_exit;
+ }
+ if (upgraded)
+ goto _cyttsp4_startup_start;
+#endif /* --CY_AUTO_LOAD_FW */
+
+ if (!wrote_sysinfo_regs) {
+ dev_vdbg(ts->dev,
+ "%s: Set Sysinfo regs\n", __func__);
+ retval = _cyttsp4_set_mode(ts, CY_SYSINFO_MODE);
+ if (retval < 0) {
+ dev_err(ts->dev,
+ "%s: Set SysInfo Mode fail r=%d\n",
+ __func__, retval);
+ _cyttsp4_change_state(ts, CY_IDLE_STATE);
+ goto _cyttsp4_startup_exit;
+ }
+ retval = _cyttsp4_set_sysinfo_regs(ts, &mddata_updated);
+ if (retval < 0) {
+ dev_err(ts->dev,
+ "%s: Set SysInfo Regs fail r=%d\n",
+ __func__, retval);
+ _cyttsp4_change_state(ts, CY_IDLE_STATE);
+ goto _cyttsp4_startup_exit;
+ } else
+ wrote_sysinfo_regs = true;
+ }
+
+ dev_vdbg(ts->dev,
+ "%s: enter operational mode\n", __func__);
+ retval = _cyttsp4_set_mode(ts, CY_OPERATE_MODE);
+ if (retval < 0) {
+ _cyttsp4_change_state(ts, CY_IDLE_STATE);
+ dev_err(ts->dev,
+ "%s: Fail set operational mode (r=%d)\n",
+ __func__, retval);
+ goto _cyttsp4_startup_exit;
+ } else {
+#ifdef CY_AUTO_LOAD_TOUCH_PARAMS
+ /* Calculate settings CRC from platform settings */
+ dev_vdbg(ts->dev,
+ "%s: Calculate settings CRC and get IC CRC\n",
+ __func__);
+ retval = _cyttsp4_calc_settings_crc(ts,
+ &pdata_crc[0], &pdata_crc[1]);
+ if (retval < 0) {
+ dev_err(ts->dev,
+ "%s: Unable to calculate settings CRC\n",
+ __func__);
+ goto _cyttsp4_startup_exit;
+ }
+
+ /* Get settings CRC from touch IC */
+ retval = _cyttsp4_get_ic_crc(ts, CY_TCH_PARM_EBID,
+ &ic_crc[0], &ic_crc[1]);
+ if (retval < 0) {
+ dev_err(ts->dev,
+ "%s: Unable to get settings CRC\n", __func__);
+ goto _cyttsp4_startup_exit;
+ }
+
+ /* Compare CRC values */
+ dev_vdbg(ts->dev,
+ "%s: PDATA CRC = 0x%02X%02X, IC CRC = 0x%02X%02X\n",
+ __func__, pdata_crc[0], pdata_crc[1],
+ ic_crc[0], ic_crc[1]);
+
+ if ((pdata_crc[0] == ic_crc[0]) &&
+ (pdata_crc[1] == ic_crc[1]))
+ goto _cyttsp4_startup_settings_valid;
+
+ /* Update settings */
+ dev_info(ts->dev,
+ "%s: Updating IC settings...\n", __func__);
+
+ if (wrote_settings) {
+ dev_err(ts->dev,
+ "%s: Already updated IC settings\n",
+ __func__);
+ goto _cyttsp4_startup_settings_valid;
+ }
+
+ retval = _cyttsp4_set_op_params(ts, pdata_crc[0], pdata_crc[1]);
+ if (retval < 0) {
+ dev_err(ts->dev,
+ "%s: Set Operational Params fail r=%d\n",
+ __func__, retval);
+ goto _cyttsp4_startup_exit;
+ }
+
+ wrote_settings = true;
+#else
+ wrote_settings = false;
+#endif /* --CY_AUTO_LOAD_TOUCH_PARAMS */
+ }
+
+#ifdef CY_AUTO_LOAD_TOUCH_PARAMS
+_cyttsp4_startup_settings_valid:
+#endif /* --CY_AUTO_LOAD_TOUCH_PARAMS */
+ if (mddata_updated || wrote_settings) {
+ dev_info(ts->dev,
+ "%s: Resetting IC after writing settings\n",
+ __func__);
+ mddata_updated = false;
+ wrote_settings = false;
+ goto _cyttsp4_startup_start; /* Reset the part */
+ }
+ dev_vdbg(ts->dev,
+ "%s: enable handshake\n", __func__);
+ retval = _cyttsp4_handshake_enable(ts);
+ if (retval < 0)
+ dev_err(ts->dev,
+ "%s: fail enable handshake r=%d", __func__, retval);
+
+ _cyttsp4_change_state(ts, CY_ACTIVE_STATE);
+
+ if (ts->was_suspended) {
+ ts->was_suspended = false;
+ retval = _cyttsp4_enter_sleep(ts);
+ if (retval < 0) {
+ dev_err(ts->dev,
+ "%s: fail resume sleep r=%d\n",
+ __func__, retval);
+ }
+ } else {
+#ifdef CY_USE_WATCHDOG
+ _cyttsp4_start_wd_timer(ts);
+#endif
+ }
+
+_cyttsp4_startup_exit:
+ return retval;
+}
+#endif /* --CY_USE_TMA884 */
+
+static irqreturn_t cyttsp4_irq(int irq, void *handle)
+{
+ struct cyttsp4 *ts = handle;
+ u8 rep_stat = 0;
+ int retval = 0;
+
+#if TOUCH_BOOST
+ if (false == boost) {
+ omap_cpufreq_min_limit(DVFS_LOCK_ID_TSP, 800000);
+ boost = true;
+ }
+#endif
+
+ dev_vdbg(ts->dev,
+ "%s: GOT IRQ ps=%d\n", __func__, ts->driver_state);
+ mutex_lock(&ts->data_lock);
+
+ dev_vdbg(ts->dev,
+ "%s: DO IRQ ps=%d\n", __func__, ts->driver_state);
+
+ switch (ts->driver_state) {
+ case CY_BL_STATE:
+ case CY_CMD_STATE:
+ complete(&ts->int_running);
+#ifdef CY_USE_LEVEL_IRQ
+ udelay(1000);
+#endif
+ break;
+ case CY_SYSINFO_STATE:
+ complete(&ts->si_int_running);
+#ifdef CY_USE_LEVEL_IRQ
+ udelay(500);
+#endif
+ break;
+ case CY_EXIT_BL_STATE:
+#ifdef CY_USE_LEVEL_IRQ
+ udelay(1000);
+#endif
+ if (ts->switch_flag == true) {
+ ts->switch_flag = false;
+ retval = _cyttsp4_ldr_exit(ts);
+ if (retval < 0) {
+ dev_err(ts->dev,
+ "%s: Fail bl exit r=%d\n",
+ __func__, retval);
+ } else
+ ts->driver_state = CY_SYSINFO_STATE;
+ }
+ break;
+ case CY_SLEEP_STATE:
+ dev_vdbg(ts->dev,
+ "%s: Attempt to process touch after enter sleep or"
+ " unexpected wake event\n", __func__);
+ retval = _cyttsp4_wakeup(ts); /* in case its really asleep */
+ if (retval < 0) {
+ dev_err(ts->dev,
+ "%s: wakeup fail r=%d\n",
+ __func__, retval);
+ _cyttsp4_pr_state(ts);
+ _cyttsp4_queue_startup(ts, true);
+ break;
+ }
+ /* Put the part back to sleep */
+ retval = _cyttsp4_enter_sleep(ts);
+ if (retval < 0) {
+ dev_err(ts->dev,
+ "%s: fail resume sleep r=%d\n",
+ __func__, retval);
+ _cyttsp4_pr_state(ts);
+ _cyttsp4_queue_startup(ts, true);
+ }
+ break;
+ case CY_IDLE_STATE:
+ if (ts->xy_mode == NULL) {
+ /* initialization is not complete; invalid pointers */
+ break;
+ }
+
+ /* device now available; signal initialization */
+ dev_info(ts->dev,
+ "%s: Received IRQ in IDLE state\n",
+ __func__);
+ /* Try to determine the IC's current state */
+ retval = _cyttsp4_load_status_regs(ts);
+ if (retval < 0) {
+ dev_err(ts->dev,
+ "%s: Still unable to access IC after IRQ r=%d\n",
+ __func__, retval);
+ _cyttsp4_queue_startup(ts, false);
+ break;
+ }
+ rep_stat = ts->xy_mode[ts->si_ofs.rep_ofs + 1];
+ if (IS_BOOTLOADERMODE(rep_stat)) {
+ dev_info(ts->dev,
+ "%s: BL mode found in IDLE state\n",
+ __func__);
+ _cyttsp4_queue_startup(ts, false);
+ break;
+ }
+ dev_err(ts->dev,
+ "%s: interrupt received in IDLE state -"
+ " try processing touch\n",
+ __func__);
+ _cyttsp4_change_state(ts, CY_ACTIVE_STATE);
+#ifdef CY_USE_WATCHDOG
+ _cyttsp4_start_wd_timer(ts);
+#endif
+ retval = _cyttsp4_xy_worker(ts);
+ if (retval < 0) {
+ dev_err(ts->dev,
+ "%s: xy_worker IDLE fail r=%d\n",
+ __func__, retval);
+ _cyttsp4_queue_startup(ts, false);
+ break;
+ }
+
+#ifdef CY_USE_LEVEL_IRQ
+ udelay(500);
+#endif
+ break;
+ case CY_READY_STATE:
+ complete(&ts->ready_int_running);
+ /* do not break; do worker */
+ case CY_ACTIVE_STATE:
+ if (ts->test.cur_mode == CY_TEST_MODE_CAT) {
+ complete(&ts->int_running);
+#ifdef CY_USE_LEVEL_IRQ
+ udelay(500);
+#endif
+ } else {
+ /* process the touches */
+ retval = _cyttsp4_xy_worker(ts);
+ if (retval < 0) {
+ dev_err(ts->dev,
+ "%s: XY Worker fail r=%d\n",
+ __func__, retval);
+ _cyttsp4_queue_startup(ts, false);
+ }
+ }
+ break;
+ default:
+ break;
+ }
+
+ mutex_unlock(&ts->data_lock);
+ dev_vdbg(ts->dev,
+ "%s: DONE IRQ ps=%d\n", __func__, ts->driver_state);
+
+ return IRQ_HANDLED;
+}
+
+static void _cyttsp4_file_init(struct cyttsp4 *ts)
+{
+
+ if (device_create_file(ts->dev, &dev_attr_drv_stat))
+ dev_err(ts->dev,
+ "%s: Error, could not create drv_stat\n", __func__);
+
+ if (device_create_file(ts->dev, &dev_attr_drv_ver))
+ dev_err(ts->dev,
+ "%s: Error, could not create drv_ver\n", __func__);
+
+#if defined(CY_USE_FORCE_LOAD) || defined(CONFIG_TOUCHSCREEN_DEBUG)
+ if (device_create_file(ts->dev, &dev_attr_ic_reflash))
+ dev_err(ts->dev,
+ "%s: Error, could not create ic_reflash\n", __func__);
+#endif
+
+
+ if (device_create_file(ts->dev, &dev_attr_ic_ver))
+ dev_err(ts->dev,
+ "%s: Cannot create ic_ver\n", __func__);
+
+#ifdef CY_USE_REG_ACCESS
+ if (device_create_file(ts->dev, &dev_attr_drv_rw_regid))
+ dev_err(ts->dev,
+ "%s: Cannot create drv_rw_regid\n", __func__);
+
+ if (device_create_file(ts->dev, &dev_attr_drv_rw_reg_data))
+ dev_err(ts->dev,
+ "%s: Cannot create drv_rw_reg_data\n", __func__);
+#endif
+
+ return;
+}
+
+static void _cyttsp4_file_free(struct cyttsp4 *ts)
+{
+ device_remove_file(ts->dev, &dev_attr_drv_ver);
+ device_remove_file(ts->dev, &dev_attr_drv_stat);
+ device_remove_file(ts->dev, &dev_attr_ic_ver);
+#if defined(CY_USE_FORCE_LOAD) || defined(CONFIG_TOUCHSCREEN_DEBUG)
+ device_remove_file(ts->dev, &dev_attr_ic_reflash);
+#endif
+#ifdef CY_USE_REG_ACCESS
+ device_remove_file(ts->dev, &dev_attr_drv_rw_regid);
+ device_remove_file(ts->dev, &dev_attr_drv_rw_reg_data);
+#endif
+}
+
+static int cyttsp4_open(struct input_dev *dev)
+{
+ int retval = 0;
+
+ struct cyttsp4 *ts = input_get_drvdata(dev);
+ dev_dbg(ts->dev, "%s: Open call ts=%p\n", __func__, ts);
+ mutex_lock(&ts->data_lock);
+ if (!ts->powered) {
+ /*
+ * execute complete startup procedure. After this
+ * call the device is in active state and the worker
+ * is running
+ */
+ retval = _cyttsp4_startup(ts);
+
+ /* powered if no hard failure */
+ if (retval < 0) {
+ ts->powered = false;
+ _cyttsp4_change_state(ts, CY_IDLE_STATE);
+ dev_err(ts->dev,
+ "%s: startup fail at power on r=%d\n",
+ __func__, retval);
+ } else
+ ts->powered = true;
+
+ dev_info(ts->dev,
+ "%s: Powered ON(%d) r=%d\n",
+ __func__, (int)ts->powered, retval);
+ }
+ mutex_unlock(&ts->data_lock);
+ return 0;
+}
+
+static void cyttsp4_close(struct input_dev *dev)
+{
+ /*
+ * close() normally powers down the device
+ * this call simply returns unless power
+ * to the device can be controlled by the driver
+ */
+ return;
+}
+
+void cyttsp4_core_release(void *handle)
+{
+ struct cyttsp4 *ts = handle;
+
+ dev_dbg(ts->dev, "%s: Release call ts=%p\n",
+ __func__, ts);
+ if (ts == NULL) {
+ dev_err(ts->dev,
+ "%s: Null context pointer on driver release\n",
+ __func__);
+ goto cyttsp4_core_release_exit;
+ }
+
+#ifdef FACTORY_TESTING
+ kfree(ts->node_data->reference_data);
+ kfree(ts->node_data->intensity_data);
+ kfree(ts->node_data->cm_abs_data);
+ kfree(ts->node_data->cm_delta_data);
+ kfree(ts->node_data);
+ kfree(ts->factory_data);
+#endif
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ unregister_early_suspend(&ts->early_suspend);
+#endif
+ _cyttsp4_file_free(ts);
+ if (mutex_is_locked(&ts->data_lock))
+ mutex_unlock(&ts->data_lock);
+ mutex_destroy(&ts->data_lock);
+ free_irq(ts->irq, ts);
+ input_unregister_device(ts->input);
+ if (ts->cyttsp4_wq) {
+ destroy_workqueue(ts->cyttsp4_wq);
+ ts->cyttsp4_wq = NULL;
+ }
+
+ if (ts->sysinfo_ptr.cydata != NULL)
+ kfree(ts->sysinfo_ptr.cydata);
+ if (ts->sysinfo_ptr.test != NULL)
+ kfree(ts->sysinfo_ptr.test);
+ if (ts->sysinfo_ptr.pcfg != NULL)
+ kfree(ts->sysinfo_ptr.pcfg);
+ if (ts->sysinfo_ptr.opcfg != NULL)
+ kfree(ts->sysinfo_ptr.opcfg);
+ if (ts->sysinfo_ptr.ddata != NULL)
+ kfree(ts->sysinfo_ptr.ddata);
+ if (ts->sysinfo_ptr.mdata != NULL)
+ kfree(ts->sysinfo_ptr.mdata);
+ if (ts->xy_mode != NULL)
+ kfree(ts->xy_mode);
+ if (ts->xy_data != NULL)
+ kfree(ts->xy_data);
+ if (ts->xy_data_touch1 != NULL)
+ kfree(ts->xy_data_touch1);
+
+#if TOUCH_BOOST
+ del_timer_sync(&ts->dvfs_timer);
+#endif
+
+ kfree(ts);
+cyttsp4_core_release_exit:
+ return;
+}
+EXPORT_SYMBOL_GPL(cyttsp4_core_release);
+
+void *cyttsp4_core_init(struct cyttsp4_bus_ops *bus_ops,
+ struct device *dev, int irq, char *name)
+{
+ unsigned long irq_flags = 0;
+ int i = 0;
+ int min = 0;
+ int max = 0;
+ u16 signal = 0;
+ int retval = 0;
+ struct input_dev *input_device = NULL;
+ struct cyttsp4 *ts = NULL;
+#ifdef FACTORY_TESTING
+ struct device *fac_dev_ts;
+ struct factory_data *factory_data;
+ struct node_data *node_data;
+ u32 rx, tx;
+#endif
+
+ if (dev == NULL) {
+ pr_err("%s: Error, dev pointer is Null\n", __func__);
+ goto error_alloc_data;
+ }
+
+ if (bus_ops == NULL) {
+ dev_err(dev,
+ "%s: Error, bus_ops Pointer is Null\n", __func__);
+ goto error_alloc_data;
+ }
+ ts = kzalloc(sizeof(*ts), GFP_KERNEL);
+ if (ts == NULL) {
+ dev_err(dev,
+ "%s: Error, kzalloc context memory\n", __func__);
+ goto error_alloc_data;
+ }
+
+#if defined(CY_USE_FORCE_LOAD) || defined(CONFIG_TOUCHSCREEN_DEBUG)
+ ts->fwname = kzalloc(CY_BL_FW_NAME_SIZE, GFP_KERNEL);
+ if (ts->fwname == NULL) {
+ dev_err(dev,
+ "%s: Error, kzalloc fwname\n", __func__);
+ goto error_alloc_failed;
+ }
+#endif
+
+ ts->cyttsp4_wq =
+ create_singlethread_workqueue("cyttsp4_resume_startup_wq");
+ if (ts->cyttsp4_wq == NULL) {
+ dev_err(dev,
+ "%s: No memory for cyttsp4_resume_startup_wq\n",
+ __func__);
+ goto error_alloc_failed;
+ }
+
+ ts->driver_state = CY_INVALID_STATE;
+ ts->current_mode = CY_MODE_BOOTLOADER;
+ ts->powered = false;
+ ts->was_suspended = false;
+ ts->switch_flag = false;
+ ts->soft_reset_asserted = false;
+ ts->num_prv_tch = 0;
+
+ ts->xy_data = NULL;
+ ts->xy_mode = NULL;
+ ts->xy_data_touch1 = NULL;
+ ts->btn_rec_data = NULL;
+ memset(&ts->test, 0, sizeof(struct cyttsp4_test_mode));
+
+ ts->dev = dev;
+ ts->bus_ops = bus_ops;
+ ts->platform_data = dev->platform_data;
+ if (ts->platform_data == NULL) {
+ dev_err(ts->dev,
+ "%s: Error, platform data is Null\n", __func__);
+ goto error_alloc_failed;
+ }
+
+ if (ts->platform_data->frmwrk == NULL) {
+ dev_err(ts->dev,
+ "%s: Error, platform data framework is Null\n",
+ __func__);
+ goto error_alloc_failed;
+ }
+
+ if (ts->platform_data->frmwrk->abs == NULL) {
+ dev_err(ts->dev,
+ "%s: Error, platform data framework array is Null\n",
+ __func__);
+ goto error_alloc_failed;
+ }
+
+ mutex_init(&ts->data_lock);
+ init_completion(&ts->int_running);
+ init_completion(&ts->si_int_running);
+ init_completion(&ts->ready_int_running);
+ ts->flags = ts->platform_data->flags;
+#if defined(CY_USE_FORCE_LOAD) || defined(CONFIG_TOUCHSCREEN_DEBUG)
+ ts->waiting_for_fw = false;
+#endif
+
+#ifdef CY_USE_TMA400
+ ts->max_config_bytes = CY_TMA400_MAX_BYTES;
+#endif /* --CY_USE_TMA400 */
+#ifdef CY_USE_TMA884
+ ts->max_config_bytes = CY_TMA884_MAX_BYTES;
+#endif /* --CY_USE_TMA884 */
+
+ ts->irq = irq;
+ if (ts->irq <= 0) {
+ dev_vdbg(ts->dev,
+ "%s: Error, failed to allocate irq\n", __func__);
+ goto error_init;
+ }
+
+ /* Create the input device and register it. */
+ dev_vdbg(ts->dev,
+ "%s: Create the input device and register it\n", __func__);
+ input_device = input_allocate_device();
+ if (input_device == NULL) {
+ dev_err(ts->dev,
+ "%s: Error, failed to allocate input device\n",
+ __func__);
+ goto error_init;
+ }
+
+ ts->input = input_device;
+ input_device->name = name;
+ snprintf(ts->phys, sizeof(ts->phys)-1, "%s", dev_name(dev));
+ input_device->phys = ts->phys;
+ input_device->dev.parent = ts->dev;
+ ts->bus_type = bus_ops->dev->bus;
+#ifdef CY_USE_WATCHDOG
+ INIT_WORK(&ts->work, cyttsp4_timer_watchdog);
+ setup_timer(&ts->timer, cyttsp4_timer, (unsigned long)ts);
+#endif
+
+ dev_vdbg(ts->dev,
+ "%s: Initialize irq\n", __func__);
+#ifdef CY_USE_LEVEL_IRQ
+ irq_flags = IRQF_TRIGGER_LOW | IRQF_ONESHOT;
+#else
+ irq_flags = IRQF_TRIGGER_FALLING | IRQF_ONESHOT;
+#endif
+ retval = request_threaded_irq(ts->irq, NULL, cyttsp4_irq,
+ irq_flags, ts->input->name, ts);
+ if (retval < 0) {
+ dev_err(ts->dev,
+ "%s: failed to init irq r=%d name=%s\n",
+ __func__, retval, ts->input->name);
+ ts->irq_enabled = false;
+ goto error_init;
+ } else {
+ ts->irq_enabled = true;
+ }
+
+
+ input_device->open = cyttsp4_open;
+ input_device->close = cyttsp4_close;
+ input_set_drvdata(input_device, ts);
+ dev_set_drvdata(dev, ts);
+
+ dev_vdbg(ts->dev,
+ "%s: Initialize event signals\n", __func__);
+ __set_bit(EV_ABS, input_device->evbit);
+ __set_bit(EV_KEY, input_device->evbit);
+ bitmap_fill(input_device->absbit, ABS_MAX);
+ /* key led */
+ __set_bit(EV_LED, input_device->evbit);
+ __set_bit(LED_MISC, input_device->ledbit);
+
+ /* ICS touch down button press signal */
+ __set_bit(BTN_TOUCH, input_device->keybit);
+ __set_bit(KEY_BACK, input_device->keybit);
+ __set_bit(KEY_MENU, input_device->keybit);
+
+ __set_bit(INPUT_PROP_DIRECT, input_device->propbit);
+
+ for (i = 0; i < (ts->platform_data->frmwrk->size / CY_NUM_ABS_SET);
+ i++) {
+ signal = ts->platform_data->frmwrk->abs[
+ (i * CY_NUM_ABS_SET) + CY_SIGNAL_OST];
+ if (signal != CY_IGNORE_VALUE) {
+ min = ts->platform_data->frmwrk->abs
+ [(i * CY_NUM_ABS_SET) + CY_MIN_OST];
+ max = ts->platform_data->frmwrk->abs
+ [(i * CY_NUM_ABS_SET) + CY_MAX_OST];
+ if (i == CY_ABS_ID_OST) {
+ /* shift track ids down to start at 0 */
+ max = max - min;
+ min = min - min;
+ }
+ input_set_abs_params(input_device,
+ signal,
+ min,
+ max,
+ ts->platform_data->frmwrk->abs[
+ (i * CY_NUM_ABS_SET) + CY_FUZZ_OST],
+ ts->platform_data->frmwrk->abs[
+ (i * CY_NUM_ABS_SET) + CY_FLAT_OST]);
+ dev_vdbg(ts->dev,
+ "%s: s=%02X min=%d max=%d fuzz=%d flat=%d\n",
+ __func__, signal, min, max,
+ ts->platform_data->frmwrk->abs[
+ (i * CY_NUM_ABS_SET) + CY_FUZZ_OST],
+ ts->platform_data->frmwrk->abs[
+ (i * CY_NUM_ABS_SET) + CY_FLAT_OST]);
+
+ }
+ }
+
+#ifdef CY_USE_DEBUG_TOOLS
+ if (ts->flags & CY_FLAG_FLIP) {
+ input_set_abs_params(input_device,
+ ABS_MT_POSITION_X,
+ ts->platform_data->frmwrk->abs
+ [(CY_ABS_Y_OST * CY_NUM_ABS_SET) + CY_MIN_OST],
+ ts->platform_data->frmwrk->abs
+ [(CY_ABS_Y_OST * CY_NUM_ABS_SET) + CY_MAX_OST],
+ ts->platform_data->frmwrk->abs
+ [(CY_ABS_Y_OST * CY_NUM_ABS_SET) + CY_FUZZ_OST],
+ ts->platform_data->frmwrk->abs
+ [(CY_ABS_Y_OST * CY_NUM_ABS_SET) + CY_FLAT_OST]);
+
+ input_set_abs_params(input_device,
+ ABS_MT_POSITION_Y,
+ ts->platform_data->frmwrk->abs
+ [(CY_ABS_X_OST * CY_NUM_ABS_SET) + CY_MIN_OST],
+ ts->platform_data->frmwrk->abs
+ [(CY_ABS_X_OST * CY_NUM_ABS_SET) + CY_MAX_OST],
+ ts->platform_data->frmwrk->abs
+ [(CY_ABS_X_OST * CY_NUM_ABS_SET) + CY_FUZZ_OST],
+ ts->platform_data->frmwrk->abs
+ [(CY_ABS_X_OST * CY_NUM_ABS_SET) + CY_FLAT_OST]);
+ }
+#endif /* --CY_USE_DEBUG_TOOLS */
+
+ input_set_events_per_packet(input_device, 6 * CY_NUM_TCH_ID);
+
+ retval = input_register_device(input_device);
+ if (retval < 0) {
+ dev_err(ts->dev,
+ "%s: Error, failed to register input device r=%d\n",
+ __func__, retval);
+ goto error_init;
+ }
+
+ /* add /sys files */
+ _cyttsp4_file_init(ts);
+
+#if TOUCH_BOOST
+ setup_timer(&ts->dvfs_timer, timer_cb, 0);
+#endif
+
+#ifdef FACTORY_TESTING
+ node_data = kzalloc(sizeof(struct node_data), GFP_KERNEL);
+ if (unlikely(node_data == NULL)) {
+ retval = -ENOMEM;
+ goto err_alloc_node_data_failed;
+ }
+ factory_data = kzalloc(sizeof(struct factory_data), GFP_KERNEL);
+ if (unlikely(factory_data == NULL)) {
+ retval = -ENOMEM;
+ goto err_alloc_factory_data_failed;
+ }
+
+ INIT_LIST_HEAD(&factory_data->cmd_list_head);
+ for (i = 0; i < ARRAY_SIZE(tsp_cmds); i++)
+ list_add_tail(&tsp_cmds[i].list, &factory_data->cmd_list_head);
+
+ mutex_init(&factory_data->cmd_lock);
+ factory_data->cmd_is_running = false;
+
+ fac_dev_ts = device_create(sec_class, NULL, 0, ts, "tsp");
+ if (!fac_dev_ts)
+ pr_err("[TSP_FACTORY] Failed to create fac tsp dev\n");
+
+ if (sysfs_create_group(&fac_dev_ts->kobj, &touchscreen_attr_group))
+ pr_err("[TSP_FACTORY] Failed to create sysfs (touchscreen_attr_group).\n");
+
+ ts->factory_data = factory_data;
+ ts->node_data = node_data;
+
+#endif
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ ts->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1;
+ ts->early_suspend.suspend = cyttsp4_early_suspend;
+ ts->early_suspend.resume = cyttsp4_late_resume;
+ register_early_suspend(&ts->early_suspend);
+#endif
+
+ INIT_WORK(&ts->cyttsp4_resume_startup_work, cyttsp4_ts_work_func);
+
+ goto no_error;
+
+#ifdef FACTORY_TESTING
+
+err_alloc_factory_data_failed:
+ pr_err("tsp: ts_probe: err_alloc_factory_data failed.\n");
+
+err_alloc_node_data_failed:
+ pr_err("tsp: ts_probe: err_alloc_node_data failed.\n");
+ kfree(ts->node_data->reference_data);
+ kfree(ts->node_data->intensity_data);
+ kfree(ts->node_data->cm_abs_data);
+ kfree(ts->node_data->cm_delta_data);
+ kfree(ts->node_data);
+
+#endif
+
+error_init:
+ mutex_destroy(&ts->data_lock);
+ if (ts->cyttsp4_wq) {
+ destroy_workqueue(ts->cyttsp4_wq);
+ ts->cyttsp4_wq = NULL;
+ }
+error_alloc_failed:
+ if (ts != NULL) {
+ kfree(ts);
+ ts = NULL;
+ }
+error_alloc_data:
+ dev_err(ts->dev,
+ "%s: Failed Initialization\n", __func__);
+no_error:
+ return ts;
+}
+EXPORT_SYMBOL_GPL(cyttsp4_core_init);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Cypress TrueTouch(R) Standard touchscreen driver core");
+MODULE_AUTHOR("Cypress");