diff options
Diffstat (limited to 'drivers/input/touchscreen/cyttsp4_core.c')
-rw-r--r-- | drivers/input/touchscreen/cyttsp4_core.c | 6685 |
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), ®_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), ®_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"); |