/* * 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 * */ #define TOUCH_BOOST 0 #include "cyttsp4_core.h" #include #include #include #include #include #include /* This enables firmware class loader code */ #if TOUCH_BOOST #include #include #include #ifdef CONFIG_DVFS_LIMIT #include #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");