| /* |
| * mms_ts.c - Touchscreen driver for Melfas MMS-series touch controllers |
| * |
| * Copyright (C) 2011 Google Inc. |
| * Author: Dima Zavin <dima@android.com> |
| * Simon Wilson <simonwilson@google.com> |
| * |
| * ISP reflashing code based on original code from Melfas. |
| * |
| * This program is free software; you can redistribute it and/or modify it |
| * under the terms of the GNU General Public License as published by the |
| * Free Software Foundation; either version 2 of the License, or (at your |
| * option) any later version. |
| * |
| */ |
| |
| #define DEBUG |
| /* #define VERBOSE_DEBUG */ |
| #define SEC_TSP_DEBUG |
| #define SEC_TSP_VERBOSE_DEBUG |
| |
| |
| /// #define MMS_144_RESET_PIN //Define it only if the TSP uses RST Pin under the particular model flag |
| |
| /* #define FORCE_FW_FLASH */ |
| /* #define FORCE_FW_PASS */ |
| /* #define ESD_DEBUG */ |
| |
| #define W1_DUMMY |
| #define SEC_TSP_FACTORY_TEST |
| #define SEC_TSP_FW_UPDATE |
| #define TSP_BUF_SIZE 1024 |
| #define FAIL -1 |
| #include <linux/delay.h> |
| #include <linux/firmware.h> |
| #include <linux/gpio.h> |
| #include <linux/i2c.h> |
| #include <linux/init.h> |
| #include <linux/input.h> |
| #include <linux/input/mt.h> |
| #include <linux/interrupt.h> |
| #include <linux/irq.h> |
| #include <linux/module.h> |
| #include <linux/mutex.h> |
| #include <linux/slab.h> |
| #include <linux/uaccess.h> |
| #include <linux/cpufreq.h> |
| |
| #include <linux/regulator/consumer.h> |
| #include <linux/of_gpio.h> |
| |
| #include <linux/i2c/mms144.h> |
| |
| #include <asm/unaligned.h> |
| #include "mms_ts_fw.h" |
| |
| #define MAX_FINGERS 10 |
| #define MAX_WIDTH 30 |
| #define MAX_PRESSURE 255 |
| #define MAX_ANGLE 90 |
| #define MIN_ANGLE -90 |
| |
| /* Registers */ |
| #define MMS_MODE_CONTROL 0x01 |
| #define MMS_XYRES_HI 0x02 |
| #define MMS_XRES_LO 0x03 |
| #define MMS_YRES_LO 0x04 |
| |
| #define MMS_INPUT_EVENT_PKT_SZ 0x0F |
| #define MMS_INPUT_EVENT0 0x10 |
| #define FINGER_EVENT_SZ 8 |
| |
| #define MMS_TSP_REVISION 0xF0 |
| #define MMS_HW_REVISION 0xF1 |
| #define MMS_COMPAT_GROUP 0xF2 |
| #define MMS_FW_VERSION 0xF3 |
| |
| enum { |
| ISP_MODE_FLASH_ERASE = 0x59F3, |
| ISP_MODE_FLASH_WRITE = 0x62CD, |
| ISP_MODE_FLASH_READ = 0x6AC9, |
| }; |
| |
| /* each address addresses 4-byte words */ |
| #define ISP_MAX_FW_SIZE (0x1F00 * 4) |
| #define ISP_IC_INFO_ADDR 0x1F00 |
| |
| #ifdef CONFIG_SEC_DVFS |
| #define TOUCH_BOOSTER 1 |
| #define TOUCH_BOOSTER_OFF_TIME 100 |
| #define TOUCH_BOOSTER_CHG_TIME 200 |
| #endif |
| |
| #define COVER_OPEN 0 |
| #define COVER_CLOSED 3 |
| |
| #ifdef SEC_TSP_FW_UPDATE |
| |
| #define WORD_SIZE 4 |
| #define MMS_I2C_VTG_MIN_UV 1800000 |
| #define MMS_I2C_VTG_MAX_UV 1800000 |
| #define MMS_I2C_LOAD_UA 10000 |
| |
| #define ISC_PKT_SIZE 1029 |
| #define ISC_PKT_DATA_SIZE 1024 |
| #define ISC_PKT_HEADER_SIZE 3 |
| #define ISC_PKT_NUM 31 |
| |
| #define ISC_ENTER_ISC_CMD 0x5F |
| #define ISC_ENTER_ISC_DATA 0x01 |
| #define ISC_CMD 0xAE |
| #define ISC_ENTER_UPDATE_DATA 0x55 |
| #define ISC_ENTER_UPDATE_DATA_LEN 9 |
| #define ISC_DATA_WRITE_SUB_CMD 0xF1 |
| #define ISC_EXIT_ISC_SUB_CMD 0x0F |
| #define ISC_EXIT_ISC_SUB_CMD2 0xF0 |
| #define ISC_CHECK_STATUS_CMD 0xAF |
| #define ISC_CONFIRM_CRC 0x03 |
| #define ISC_DEFAULT_CRC 0xFFFF |
| |
| #endif |
| |
| #ifdef SEC_TSP_FACTORY_TEST |
| #define TX_NUM 26 |
| #define RX_NUM 14 |
| #define NODE_NUM 364 /* 26x14 */ |
| |
| /* VSC(Vender Specific Command) */ |
| #define MMS_VSC_CMD 0xB0 /* vendor specific command */ |
| #define MMS_VSC_MODE 0x1A /* mode of vendor */ |
| |
| #define MMS_VSC_CMD_ENTER 0X01 |
| #define MMS_VSC_CMD_CM_DELTA 0X02 |
| #define MMS_VSC_CMD_CM_ABS 0X03 |
| #define MMS_VSC_CMD_EXIT 0X05 |
| #define MMS_VSC_CMD_INTENSITY 0X04 |
| #define MMS_VSC_CMD_RAW 0X06 |
| #define MMS_VSC_CMD_REFER 0X07 |
| |
| #define TSP_CMD_STR_LEN 32 |
| #define TSP_CMD_RESULT_STR_LEN 512 |
| #define TSP_CMD_PARAM_NUM 8 |
| #endif /* SEC_TSP_FACTORY_TEST */ |
| |
| #if defined(CONFIG_MACH_STRETTO) || defined(CONFIG_MACH_SUPERIORLTE_SKT) |
| #define ISC_DL_MODE 0 |
| #else |
| #define ISC_DL_MODE 1 |
| #endif |
| |
| #define MMS_COORDS_ARR_SIZE 4 |
| |
| #if ISC_DL_MODE |
| /* Default configuration of ISC mode */ |
| |
| #define DEFAULT_SLAVE_ADDR 0x48 |
| |
| #define SECTION_NUM 3 |
| #define SECTION_NAME_LEN 5 |
| |
| #define PAGE_HEADER 3 |
| #define PAGE_DATA 1024 |
| #define PAGE_TAIL 2 |
| #define PACKET_SIZE (PAGE_HEADER + PAGE_DATA + PAGE_TAIL) |
| #define TS_WRITE_REGS_LEN 1030 |
| |
| #define TIMEOUT_CNT 10 |
| #define STRING_BUF_LEN 100 |
| |
| |
| /* State Registers */ |
| #define MIP_ADDR_INPUT_INFORMATION 0x01 |
| |
| #define ISC_ADDR_VERSION 0xE1 |
| #define ISC_ADDR_SECTION_PAGE_INFO 0xE5 |
| |
| /* Config Update Commands */ |
| #define ISC_CMD_ENTER_ISC 0x5F |
| #define ISC_CMD_ENTER_ISC_PARA1 0x01 |
| #define ISC_CMD_UPDATE_MODE 0xAE |
| #define ISC_SUBCMD_ENTER_UPDATE 0x55 |
| #define ISC_SUBCMD_DATA_WRITE 0XF1 |
| #define ISC_SUBCMD_LEAVE_UPDATE_PARA1 0x0F |
| #define ISC_SUBCMD_LEAVE_UPDATE_PARA2 0xF0 |
| #define ISC_CMD_CONFIRM_STATUS 0xAF |
| |
| #define ISC_STATUS_UPDATE_MODE 0x01 |
| #define ISC_STATUS_CRC_CHECK_SUCCESS 0x03 |
| |
| #define ISC_CHAR_2_BCD(num) (((num/10)<<4) + (num%10)) |
| #define ISC_MAX(x, y) (((x) > (y)) ? (x) : (y)) |
| |
| static const char section_name[SECTION_NUM][SECTION_NAME_LEN] = { |
| "BOOT", "CORE", "CONF" |
| }; |
| |
| static const unsigned char crc0_buf[31] = { |
| 0x1D, 0x2C, 0x05, 0x34, 0x95, 0xA4, 0x8D, 0xBC, |
| 0x59, 0x68, 0x41, 0x70, 0xD1, 0xE0, 0xC9, 0xF8, |
| 0x3F, 0x0E, 0x27, 0x16, 0xB7, 0x86, 0xAF, 0x9E, |
| 0x7B, 0x4A, 0x63, 0x52, 0xF3, 0xC2, 0xEB |
| }; |
| |
| static const unsigned char crc1_buf[31] = { |
| 0x1E, 0x9C, 0xDF, 0x5D, 0x76, 0xF4, 0xB7, 0x35, |
| 0x2A, 0xA8, 0xEB, 0x69, 0x42, 0xC0, 0x83, 0x01, |
| 0x04, 0x86, 0xC5, 0x47, 0x6C, 0xEE, 0xAD, 0x2F, |
| 0x30, 0xB2, 0xF1, 0x73, 0x58, 0xDA, 0x99 |
| }; |
| |
| struct mms_bin_hdr |
| { |
| char tag[8]; |
| u16 core_version; |
| u16 section_num; |
| u16 contains_full_binary; |
| u16 reserved0; |
| |
| u32 binary_offset; |
| u32 binary_length; |
| |
| u32 extention_offset; |
| u32 reserved1; |
| |
| }__attribute__ ((packed)); |
| |
| struct mms_fw_img |
| { |
| u16 type; |
| u16 version; |
| |
| u16 start_page; |
| u16 end_page; |
| |
| u32 offset; |
| u32 length; |
| |
| }__attribute__ ((packed)); |
| |
| struct mms_firmware |
| { |
| u8 *data; |
| size_t size; |
| }; |
| |
| |
| typedef enum { |
| ISC_NONE = -1, |
| ISC_SUCCESS = 0, |
| ISC_FILE_OPEN_ERROR, |
| ISC_FILE_CLOSE_ERROR, |
| ISC_FILE_FORMAT_ERROR, |
| ISC_WRITE_BUFFER_ERROR, |
| ISC_I2C_ERROR, |
| ISC_UPDATE_MODE_ENTER_ERROR, |
| ISC_CRC_ERROR, |
| ISC_VALIDATION_ERROR, |
| ISC_COMPATIVILITY_ERROR, |
| ISC_UPDATE_SECTION_ERROR, |
| ISC_SLAVE_ERASE_ERROR, |
| ISC_SLAVE_DOWNLOAD_ERROR, |
| ISC_DOWNLOAD_WHEN_SLAVE_IS_UPDATED_ERROR, |
| ISC_INITIAL_PACKET_ERROR, |
| ISC_NO_NEED_UPDATE_ERROR, |
| ISC_LIMIT |
| } eISCRet_t; |
| |
| typedef enum { |
| EC_NONE = -1, |
| EC_DEPRECATED = 0, |
| EC_BOOTLOADER_RUNNING = 1, |
| EC_BOOT_ON_SUCCEEDED = 2, |
| EC_ERASE_END_MARKER_ON_SLAVE_FINISHED = 3, |
| EC_SLAVE_DOWNLOAD_STARTS = 4, |
| EC_SLAVE_DOWNLOAD_FINISHED = 5, |
| EC_2CHIP_HANDSHAKE_FAILED = 0x0E, |
| EC_ESD_PATTERN_CHECKED = 0x0F, |
| EC_LIMIT |
| } eErrCode_t; |
| |
| typedef enum { |
| SEC_NONE = -1, |
| SEC_BOOTLOADER = 0, |
| SEC_CORE, |
| SEC_CONFIG, |
| SEC_LIMIT |
| } eSectionType_t; |
| |
| typedef struct tISCFWInfo_t { |
| unsigned char version; |
| unsigned char compatible_version; |
| unsigned char start_addr; |
| unsigned char end_addr; |
| int bin_offset; |
| u32 crc; |
| } tISCFWInfo_t; |
| |
| //static tISCFWInfo_t mbin_info[SECTION_NUM]; |
| static tISCFWInfo_t mfsb_info[SECTION_NUM]; |
| |
| static tISCFWInfo_t ts_info[SECTION_NUM]; /* read F/W version from IC */ |
| static bool section_update_flag[SECTION_NUM]; |
| |
| const struct firmware *fw_mbin; |
| |
| static struct mms_firmware fw_mfsb; |
| static struct mms_bin_hdr *fw_hdr; |
| static struct mms_fw_img **img; |
| |
| static unsigned char g_wr_buf[1024 + 3 + 2]; |
| #endif |
| |
| int touch_is_pressed; |
| EXPORT_SYMBOL(touch_is_pressed); |
| |
| enum fw_flash_mode { |
| ISP_FLASH, |
| ISC_FLASH, |
| }; |
| |
| enum { |
| BUILT_IN = 0, |
| UMS, |
| }; |
| |
| struct tsp_callbacks { |
| void (*inform_charger)(struct tsp_callbacks *tsp_cb, bool mode); |
| }; |
| |
| struct mms_ts_info { |
| struct i2c_client *client; |
| struct input_dev *input_dev; |
| char phys[32]; |
| |
| int max_x; |
| int max_y; |
| |
| bool invert_x; |
| bool invert_y; |
| const u8 *config_fw_version; |
| int irq; |
| struct regulator *vcc_i2c; |
| struct mms_ts_platform_data *pdata; |
| |
| char *fw_name; |
| #ifdef CONFIG_HAS_EARLYSUSPEND |
| struct early_suspend early_suspend; |
| #endif |
| #ifdef TOUCH_BOOSTER |
| #if TOUCH_BOOSTER |
| struct delayed_work work_dvfs_off; |
| struct delayed_work work_dvfs_chg; |
| bool dvfs_lock_status; |
| struct mutex dvfs_lock; |
| #endif |
| #endif |
| |
| /* protects the enabled flag */ |
| struct mutex lock; |
| bool enabled; |
| |
| enum fw_flash_mode fw_flash_mode; |
| void (*register_cb)(void *); |
| struct tsp_callbacks callbacks; |
| bool ta_status; |
| bool noise_mode; |
| |
| #if defined(SEC_TSP_DEBUG) || defined(SEC_TSP_VERBOSE_DEBUG) |
| unsigned char finger_state[MAX_FINGERS]; |
| #endif |
| |
| #if defined(SEC_TSP_FW_UPDATE) |
| u8 fw_update_state; |
| #endif |
| u8 fw_ic_ver; |
| |
| #if defined(SEC_TSP_FACTORY_TEST) |
| 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]; |
| struct mutex cmd_lock; |
| bool cmd_is_running; |
| |
| unsigned int reference[NODE_NUM]; |
| unsigned int raw[NODE_NUM]; /* CM_ABS */ |
| unsigned int inspection[NODE_NUM];/* CM_DELTA */ |
| unsigned int intensity[NODE_NUM]; |
| bool ft_flag; |
| int cover_state; |
| #endif /* SEC_TSP_FACTORY_TEST */ |
| }; |
| |
| struct mms_fw_image { |
| __le32 hdr_len; |
| __le32 data_len; |
| __le32 fw_ver; |
| __le32 hdr_ver; |
| u8 data[0]; |
| } __packed; |
| |
| #ifdef CONFIG_HAS_EARLYSUSPEND |
| static void mms_ts_early_suspend(struct early_suspend *h); |
| static void mms_ts_late_resume(struct early_suspend *h); |
| #endif |
| |
| #define USE_OPEN_CLOSE |
| |
| #ifdef USE_OPEN_CLOSE |
| static void melfas_ts_close(struct input_dev *dev); |
| static int melfas_ts_open(struct input_dev *dev); |
| #endif |
| |
| #if defined(SEC_TSP_FACTORY_TEST) |
| #define TSP_CMD(name, func) .cmd_name = name, .cmd_func = func |
| |
| struct tsp_cmd { |
| struct list_head list; |
| const char *cmd_name; |
| void (*cmd_func)(void *device_data); |
| }; |
| |
| static void fw_update(void *device_data); |
| static void get_fw_ver_bin(void *device_data); |
| static void get_fw_ver_ic(void *device_data); |
| static void get_config_ver(void *device_data); |
| static void get_threshold(void *device_data); |
| static void module_off_master(void *device_data); |
| static void module_on_master(void *device_data); |
| static void get_chip_vendor(void *device_data); |
| static void get_chip_name(void *device_data); |
| static void get_reference(void *device_data); |
| static void get_cm_abs(void *device_data); |
| static void get_cm_delta(void *device_data); |
| static void get_intensity(void *device_data); |
| static void get_x_num(void *device_data); |
| static void get_y_num(void *device_data); |
| static void run_reference_read(void *device_data); |
| static void run_cm_abs_read(void *device_data); |
| static void run_cm_delta_read(void *device_data); |
| static void run_intensity_read(void *device_data); |
| static void not_support_cmd(void *device_data); |
| static void clear_cover_mode(void *device_data); |
| |
| 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),}, |
| {TSP_CMD("clear_cover_mode", clear_cover_mode),}, |
| }; |
| #endif |
| |
| #if 0 /* firmware call removal */ |
| static int melfas_mux_fw_flash(struct mms_ts_platform_data *pdata, |
| bool to_gpios) |
| { |
| if (to_gpios) { |
| gpio_direction_output(pdata->gpio_int, 0); |
| gpio_tlmm_config(GPIO_CFG(pdata->gpio_int, 0, |
| GPIO_CFG_INPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA), 1); |
| gpio_direction_output(pdata->gpio_scl, 0); |
| gpio_tlmm_config(GPIO_CFG(pdata->gpio_scl, 0, |
| GPIO_CFG_INPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA), 1); |
| gpio_direction_output(pdata->gpio_sda, 0); |
| gpio_tlmm_config(GPIO_CFG(pdata->gpio_sda, 0, |
| GPIO_CFG_INPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA), 1); |
| } else { |
| gpio_direction_output(pdata->gpio_int, 1); |
| gpio_tlmm_config(GPIO_CFG(pdata->gpio_int, 0, |
| GPIO_CFG_INPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA), 1); |
| gpio_direction_output(pdata->gpio_scl, 1); |
| gpio_tlmm_config(GPIO_CFG(pdata->gpio_scl, 3, |
| GPIO_CFG_INPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA), 1); |
| gpio_direction_output(pdata->gpio_sda, 1); |
| gpio_tlmm_config(GPIO_CFG(pdata->gpio_sda, 3, |
| GPIO_CFG_INPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA), 1); |
| } |
| return 0; |
| } |
| #endif /* firmware call removal */ |
| |
| #if 0 |
| static int reg_set_optimum_mode_check(struct regulator *reg, int load_uA) |
| { |
| return (regulator_count_voltages(reg) > 0) ? |
| regulator_set_optimum_mode(reg, load_uA) : 0; |
| } |
| #endif |
| |
| void melfas_vdd_on(struct mms_ts_info *info, bool onoff) |
| { |
| int ret = 0, rc = 0; |
| pr_info("[TSP] power %s\n", onoff ? "on" : "off"); |
| |
| if (!info->vcc_i2c) { |
| if (info->pdata->i2c_pull_up) { |
| info->vcc_i2c = regulator_get(&info->client->dev, |
| "vdd_ldo2"); |
| if (IS_ERR(info->vcc_i2c)) { |
| rc = PTR_ERR(info->vcc_i2c); |
| dev_err(&info->client->dev, |
| "Regulator get failed rc=%d\n", rc); |
| goto error_get_vtg_i2c; |
| } |
| /* |
| if (regulator_count_voltages(info->vcc_i2c) > 0) { |
| rc = regulator_set_voltage(info->vcc_i2c, |
| MMS_I2C_VTG_MIN_UV, MMS_I2C_VTG_MAX_UV); |
| if (rc) { |
| dev_err(&info->client->dev, |
| "regulator set_vtg failed rc=%d\n", |
| rc); |
| goto error_set_vtg_i2c; |
| } |
| } |
| */ |
| } |
| } |
| |
| if (onoff) { |
| if (info->pdata->i2c_pull_up) { |
| /* |
| rc = reg_set_optimum_mode_check(info->vcc_i2c, |
| MMS_I2C_LOAD_UA); |
| if (rc < 0) { |
| dev_err(&info->client->dev, |
| "Regulator vcc_i2c set_opt failed rc=%d\n", |
| rc); |
| goto error_reg_opt_i2c; |
| } |
| */ |
| |
| rc = regulator_enable(info->vcc_i2c); |
| if (rc) { |
| dev_err(&info->client->dev, |
| "Regulator vcc_i2c enable failed rc=%d\n", |
| rc); |
| // goto error_reg_en_vcc_i2c; |
| } |
| } |
| } else { |
| if (info->pdata->i2c_pull_up) { |
| //reg_set_optimum_mode_check(info->vcc_i2c, 0); |
| regulator_disable(info->vcc_i2c); |
| } |
| } |
| //msleep(50); |
| |
| /* |
| ret = gpio_direction_output(info->pdata->vdd_en, onoff); |
| if (ret) { |
| pr_err("[TSP]%s: unable to set_direction for mms_vdd_en [%d]\n", |
| __func__, info->pdata->vdd_en); |
| } |
| */ |
| if (onoff) { |
| ret = regulator_enable(info->pdata->vdd_en_reg); |
| if (ret) { |
| pr_err("[TSP]%s: unable to enable for mms_vdd_en_reg [%p]\n", |
| __func__, info->pdata->vdd_en_reg); |
| } |
| } |
| else { |
| regulator_disable(info->pdata->vdd_en_reg); |
| if (ret) { |
| pr_err("[TSP]%s: unable to disable for mms_vdd_en_reg [%p]\n", |
| __func__, info->pdata->vdd_en_reg); |
| } |
| } |
| //msleep(30); |
| return; |
| #if 0 |
| error_reg_en_vcc_i2c: |
| if (info->pdata->i2c_pull_up) |
| reg_set_optimum_mode_check(info->vcc_i2c, 0); |
| //error_reg_opt_i2c: |
| //error_set_vtg_i2c: |
| regulator_put(info->vcc_i2c); |
| #endif |
| error_get_vtg_i2c: |
| return; |
| |
| } |
| |
| int is_melfas_vdd_on(struct mms_ts_info *info) |
| { |
| int ret; |
| |
| //ret = gpio_get_value(info->pdata->vdd_en); |
| ret = regulator_is_enabled(info->pdata->vdd_en_reg); |
| pr_info("[TSP] %s = %d\n", __func__, ret); |
| |
| if (ret) |
| return 1; |
| return 0; |
| } |
| |
| #ifdef TOUCH_BOOSTER |
| #if TOUCH_BOOSTER |
| static void change_dvfs_lock(struct work_struct *work) |
| { |
| struct mms_ts_info *info = container_of(work, |
| struct mms_ts_info, work_dvfs_chg.work); |
| int ret; |
| |
| mutex_lock(&info->dvfs_lock); |
| ret = set_freq_limit(DVFS_TOUCH_ID, 998400); |
| mutex_unlock(&info->dvfs_lock); |
| |
| if (ret < 0) |
| pr_err("%s: 1booster stop failed(%d)\n",\ |
| __func__, __LINE__); |
| else |
| pr_info("[TSP] %s", __func__); |
| } |
| |
| static void set_dvfs_off(struct work_struct *work) |
| { |
| struct mms_ts_info *info = container_of(work, |
| struct mms_ts_info, work_dvfs_off.work); |
| mutex_lock(&info->dvfs_lock); |
| set_freq_limit(DVFS_TOUCH_ID, -1); |
| info->dvfs_lock_status = false; |
| mutex_unlock(&info->dvfs_lock); |
| //pr_info("[TSP] DVFS Off!"); |
| } |
| |
| static void set_dvfs_lock(struct mms_ts_info *info, uint32_t on) |
| { |
| int ret = 0; |
| |
| mutex_lock(&info->dvfs_lock); |
| if (on == 0) { |
| if (info->dvfs_lock_status) { |
| schedule_delayed_work(&info->work_dvfs_off, |
| msecs_to_jiffies(TOUCH_BOOSTER_OFF_TIME)); |
| } |
| } else if (on == 1) { |
| cancel_delayed_work(&info->work_dvfs_off); |
| if (!info->dvfs_lock_status) { |
| ret = set_freq_limit(DVFS_TOUCH_ID, 998400); |
| if (ret < 0) |
| printk(KERN_ERR "%s: cpu lock failed(%d)\n",\ |
| __func__, ret); |
| |
| info->dvfs_lock_status = true; |
| //pr_info("[TSP] DVFS On!"); |
| } |
| } else if (on == 2) { |
| cancel_delayed_work(&info->work_dvfs_off); |
| schedule_work(&info->work_dvfs_off.work); |
| } |
| mutex_unlock(&info->dvfs_lock); |
| } |
| #endif |
| #endif |
| |
| static void release_all_fingers(struct mms_ts_info *info) |
| { |
| #ifdef SEC_TSP_DEBUG |
| struct i2c_client *client = info->client; |
| #endif |
| int i; |
| |
| printk(KERN_DEBUG "[TSP] %s\n", __func__); |
| |
| for (i = 0; i < MAX_FINGERS; i++) { |
| #ifdef SEC_TSP_DEBUG |
| if (info->finger_state[i] == 1) { |
| dev_notice(&client->dev, "finger %d up(force)\n", i); |
| } |
| #endif |
| info->finger_state[i] = 0; |
| input_mt_slot(info->input_dev, i); |
| input_mt_report_slot_state(info->input_dev, MT_TOOL_FINGER, |
| false); |
| } |
| input_sync(info->input_dev); |
| #ifdef TOUCH_BOOSTER |
| #if TOUCH_BOOSTER |
| set_dvfs_lock(info, 2); |
| pr_info("[TSP] dvfs_lock free.\n "); |
| #endif |
| #endif |
| } |
| |
| static void mms_set_noise_mode(struct mms_ts_info *info) |
| { |
| struct i2c_client *client = info->client; |
| |
| if (!(info->noise_mode && info->enabled)) |
| return; |
| dev_notice(&client->dev, "%s\n", __func__); |
| |
| if (info->ta_status) { |
| dev_notice(&client->dev, "noise_mode & TA connect!!!\n"); |
| i2c_smbus_write_byte_data(info->client, 0x30, 0x1); |
| } else { |
| dev_notice(&client->dev, "noise_mode & TA disconnect!!!\n"); |
| i2c_smbus_write_byte_data(info->client, 0x30, 0x2); |
| } |
| } |
| |
| static inline void mms_pwr_on_reset(struct mms_ts_info *info); |
| |
| static void reset_mms_ts(struct mms_ts_info *info) |
| { |
| struct i2c_client *client = info->client; |
| if (info->enabled == false) |
| return; |
| |
| dev_notice(&client->dev, "%s++\n", __func__); |
| disable_irq_nosync(info->irq); |
| info->enabled = false; |
| touch_is_pressed = 0; |
| |
| release_all_fingers(info); |
| |
| mms_pwr_on_reset(info); |
| enable_irq(info->irq); |
| info->enabled = true; |
| |
| if (info->ta_status) { |
| dev_notice(&client->dev, "TA connect!!!\n"); |
| i2c_smbus_write_byte_data(info->client, 0x33, 0x1); |
| } else { |
| dev_notice(&client->dev, "TA disconnect!!!\n"); |
| i2c_smbus_write_byte_data(info->client, 0x33, 0x2); |
| mms_set_noise_mode(info); |
| } |
| |
| dev_notice(&client->dev, "%s--\n", __func__); |
| } |
| |
| static void melfas_ta_cb(struct tsp_callbacks *cb, bool ta_status) |
| { |
| struct mms_ts_info *info = |
| container_of(cb, struct mms_ts_info, callbacks); |
| struct i2c_client *client = info->client; |
| |
| dev_notice(&client->dev, "%s\n", __func__); |
| |
| info->ta_status = ta_status; |
| |
| if (info->enabled) { |
| if (info->ta_status) { |
| dev_notice(&client->dev, "TA connect!!!\n"); |
| i2c_smbus_write_byte_data(info->client, 0x33, 0x1); |
| } else { |
| dev_notice(&client->dev, "TA disconnect!!!\n"); |
| i2c_smbus_write_byte_data(info->client, 0x33, 0x2); |
| } |
| mms_set_noise_mode(info); |
| } |
| } |
| |
| struct tsp_callbacks *charger_callbacks; |
| void tsp_charger_infom(bool en) |
| { |
| if (charger_callbacks && charger_callbacks->inform_charger) |
| charger_callbacks->inform_charger(charger_callbacks, en); |
| } |
| static void melfas_register_callback(void *cb) |
| { |
| charger_callbacks = cb; |
| pr_info("melfas-ts : melfas_register_callback"); |
| } |
| static irqreturn_t mms_ts_interrupt(int irq, void *dev_id) |
| { |
| struct mms_ts_info *info = dev_id; |
| struct i2c_client *client = info->client; |
| u8 buf[MAX_FINGERS*FINGER_EVENT_SZ] = { 0 }; |
| int ret; |
| int i; |
| int sz; |
| u8 reg = MMS_INPUT_EVENT0; |
| struct i2c_msg msg[] = { |
| { |
| .addr = client->addr, |
| .flags = 0, |
| .buf = ®, |
| .len = 1, |
| }, { |
| .addr = client->addr, |
| .flags = I2C_M_RD, |
| .buf = buf, |
| }, |
| }; |
| |
| sz = i2c_smbus_read_byte_data(client, MMS_INPUT_EVENT_PKT_SZ); |
| if (sz < 0) { |
| dev_err(&client->dev, "%s bytes=%d\n", __func__, sz); |
| for (i = 0; i < 50; i++) { |
| sz = i2c_smbus_read_byte_data(client, |
| MMS_INPUT_EVENT_PKT_SZ); |
| if (sz > 0) |
| break; |
| } |
| |
| if (i == 50) { |
| dev_dbg(&client->dev, "i2c failed... reset!!\n"); |
| reset_mms_ts(info); |
| goto out; |
| } |
| } |
| /* BUG_ON(sz > MAX_FINGERS*FINGER_EVENT_SZ); */ |
| if (sz == 0) |
| goto out; |
| |
| if (sz > MAX_FINGERS*FINGER_EVENT_SZ) { |
| dev_err(&client->dev, "[TSP] abnormal data inputed.\n"); |
| goto out; |
| } |
| |
| msg[1].len = sz; |
| ret = i2c_transfer(client->adapter, msg, ARRAY_SIZE(msg)); |
| if (ret != ARRAY_SIZE(msg)) { |
| dev_err(&client->dev, |
| "failed to read %d bytes of touch data (%d)\n", |
| sz, ret); |
| goto out; |
| } |
| |
| #if defined(VERBOSE_DEBUG) |
| print_hex_dump(KERN_DEBUG, "mms_ts raw: ", |
| DUMP_PREFIX_OFFSET, 32, 1, buf, sz, false); |
| |
| #endif |
| if (buf[0] == 0x0F) { /* ESD */ |
| dev_dbg(&client->dev, "ESD DETECT.... reset!!\n"); |
| reset_mms_ts(info); |
| goto out; |
| } |
| |
| if (buf[0] == 0x0E) { /* NOISE MODE */ |
| dev_dbg(&client->dev, "[TSP] noise mode enter!!\n"); |
| info->noise_mode = 1 ; |
| mms_set_noise_mode(info); |
| goto out; |
| } |
| |
| for (i = 0; i < sz; i += FINGER_EVENT_SZ) { |
| u8 *tmp = &buf[i]; |
| int id = (tmp[0] & 0xf) - 1; |
| int x = tmp[2] | ((tmp[1] & 0xf) << 8); |
| int y = tmp[3] | (((tmp[1] >> 4) & 0xf) << 8); |
| int angle = (tmp[5] >= 127) ? (-(256 - tmp[5])) : tmp[5]; |
| int palm = (buf[0] & 0x10) >> 4; |
| if (info->invert_x) { |
| x = info->max_x - x; |
| if (x < 0) |
| x = 0; |
| } |
| if (info->invert_y) { |
| y = info->max_y - y; |
| if (y < 0) |
| y = 0; |
| } |
| if (id >= MAX_FINGERS) { |
| dev_notice(&client->dev, \ |
| "finger id error [%d]\n", id); |
| reset_mms_ts(info); |
| goto out; |
| } |
| |
| if ((tmp[0] & 0x80) == 0) { |
| #if defined(SEC_TSP_DEBUG) |
| dev_dbg(&client->dev, |
| "finger id[%d]: x=%d y=%d p=%d w=%d major=%d minor=%d angle=%d palm=%d\n" |
| , id, x, y, tmp[5], tmp[4], tmp[6], tmp[7] |
| , angle, palm); |
| #else |
| dev_notice(&client->dev, "finger [%d] up\n", id); |
| #endif |
| input_mt_slot(info->input_dev, id); |
| input_mt_report_slot_state(info->input_dev, |
| MT_TOOL_FINGER, false); |
| |
| #if defined(SEC_TSP_DEBUG) || defined(SEC_TSP_VERBOSE_DEBUG) |
| info->finger_state[id] = 0; |
| #endif |
| continue; |
| } |
| |
| input_mt_slot(info->input_dev, id); |
| input_mt_report_slot_state(info->input_dev, |
| MT_TOOL_FINGER, true); |
| input_report_abs(info->input_dev, ABS_MT_WIDTH_MAJOR, tmp[4]); |
| #if defined(CONFIG_MACH_M2_KDI) |
| input_report_abs(info->input_dev, ABS_MT_POSITION_X, |
| (info->max_x - x)); |
| input_report_abs(info->input_dev, ABS_MT_POSITION_Y, |
| (info->max_y - y)); |
| #else |
| input_report_abs(info->input_dev, ABS_MT_POSITION_X, x); |
| input_report_abs(info->input_dev, ABS_MT_POSITION_Y, y); |
| #endif |
| input_report_abs(info->input_dev, ABS_MT_TOUCH_MAJOR, tmp[6]); |
| input_report_abs(info->input_dev, ABS_MT_TOUCH_MINOR, tmp[7]); |
| // input_report_abs(info->input_dev, ABS_MT_ANGLE, angle); |
| input_report_abs(info->input_dev, ABS_MT_PALM, palm); |
| #if defined(SEC_TSP_DEBUG) |
| if (info->finger_state[id] == 0) { |
| info->finger_state[id] = 1; |
| dev_dbg(&client->dev, |
| "finger id[%d]: x=%d y=%d p=%d w=%d major=%d minor=%d angle=%d palm=%d\n" |
| , id, x, y, tmp[5], tmp[4], tmp[6], tmp[7] |
| , angle, palm); |
| } |
| #else |
| if (info->finger_state[id] == 0) { |
| info->finger_state[id] = 1; |
| dev_notice(&client->dev, "finger [%d] down\n", id); |
| } |
| #endif |
| } |
| |
| input_sync(info->input_dev); |
| touch_is_pressed = 0; |
| |
| for (i = 0; i < MAX_FINGERS; i++) { |
| if (info->finger_state[i] == 1) |
| touch_is_pressed++; |
| } |
| |
| #ifdef TOUCH_BOOSTER |
| #if TOUCH_BOOSTER |
| set_dvfs_lock(info, !!touch_is_pressed); |
| #endif |
| #endif |
| |
| out: |
| return IRQ_HANDLED; |
| } |
| |
| #if ISC_DL_MODE |
| static int mms100_i2c_read(struct i2c_client *client, |
| u16 addr, u16 length, u8 *value) |
| { |
| struct i2c_adapter *adapter = client->adapter; |
| struct i2c_msg msg; |
| int ret = -1; |
| |
| msg.addr = client->addr; |
| msg.flags = 0x00; |
| msg.len = 1; |
| msg.buf = (u8 *) &addr; |
| |
| ret = i2c_transfer(adapter, &msg, 1); |
| |
| if (ret >= 0) { |
| msg.addr = client->addr; |
| msg.flags = I2C_M_RD; |
| msg.len = length; |
| msg.buf = (u8 *) value; |
| |
| ret = i2c_transfer(adapter, &msg, 1); |
| } |
| |
| if (ret < 0) |
| pr_err("[TSP] : read error : [%d]", ret); |
| |
| return ret; |
| } |
| |
| static int mms100_i2c_write(struct i2c_client *client, char *buf, int length) |
| { |
| int i; |
| char *data; |
| data = kzalloc(sizeof(char)*TS_WRITE_REGS_LEN, GFP_KERNEL); |
| if (length > TS_WRITE_REGS_LEN) |
| { |
| pr_err("[TSP] %s :size error \n", __FUNCTION__); |
| return -EINVAL; |
| } |
| |
| for (i = 0; i < length; i++) |
| data[i] = *buf++; |
| |
| i = i2c_master_send(client, (char *) data, length); |
| |
| if (i == length){ |
| kfree(data); |
| return length; |
| } |
| else |
| { |
| kfree(data); |
| pr_err("[TSP] :write error : [%d]", i); |
| return -EIO; |
| } |
| } |
| |
| static int mms100_reset(struct mms_ts_info *info) |
| { |
| melfas_vdd_on(info, 0); |
| msleep(30); |
| melfas_vdd_on(info, 1); |
| msleep(300); |
| |
| return ISC_SUCCESS; |
| } |
| /* |
| static eISCRet_t mms100_check_operating_mode(struct i2c_client *_client, const eErrCode_t _error_code) |
| { |
| int ret; |
| unsigned char rd_buf = 0x00; |
| |
| pr_info("[TSP ISC] %s\n", __func__); |
| |
| // Config versionÀ» ÀÐ¾î¼ booting È®ÀÎ... |
| ret = mms100_i2c_read(_client, ISC_ADDR_VERSION, 1, &rd_buf); |
| |
| if (ret < 0) |
| { |
| pr_info("[TSP ISC] %s,%d: i2c read fail[%d] \n", __FUNCTION__, __LINE__, ret); |
| return _error_code; |
| } |
| |
| pr_info("End mms100_check_operating_mode()\n"); |
| |
| return ISC_SUCCESS; |
| } |
| */ |
| |
| static eISCRet_t mms100_get_version_info(struct i2c_client *_client) |
| { |
| int i, ret; |
| unsigned char rd_buf[8]; |
| |
| pr_info("[TSP ISC] %s\n", __func__); |
| |
| // config version brust read (core, private, public) |
| ret = mms100_i2c_read(_client, ISC_ADDR_VERSION, SECTION_NUM, rd_buf); |
| |
| if (ret < 0) |
| { |
| pr_info("[TSP ISC] %s,%d: i2c read fail[%d] \n", __FUNCTION__, __LINE__, ret); |
| return ISC_I2C_ERROR; |
| } |
| |
| for (i = 0; i < SECTION_NUM; i++) |
| ts_info[i].version = rd_buf[i]; |
| |
| ts_info[SEC_CORE].compatible_version = ts_info[SEC_BOOTLOADER].version; |
| ts_info[SEC_CONFIG].compatible_version = ts_info[SEC_CORE].version; |
| |
| ret = mms100_i2c_read(_client, ISC_ADDR_SECTION_PAGE_INFO, 8, rd_buf); |
| |
| if (ret < 0) |
| { |
| pr_info("[TSP ISC] %s,%d: i2c read fail[%d] \n", __FUNCTION__, __LINE__, ret); |
| return ISC_I2C_ERROR; |
| } |
| |
| for (i = 0; i < SECTION_NUM; i++) |
| { |
| ts_info[i].start_addr = rd_buf[i]; |
| |
| /* |
| * previous core binary had 4 sections while current version contains 3 of them |
| * for compatibleness, register address was not modified so we get 1,2,3,5,6,7th |
| * data of read buffer |
| */ |
| ts_info[i].end_addr = rd_buf[i + SECTION_NUM + 1]; |
| } |
| |
| for (i = 0; i < SECTION_NUM; i++) |
| { |
| pr_info("\tTS : Section(%d) version: 0x%02X\n", i, ts_info[i].version); |
| pr_info("\tTS : Section(%d) Start Address: 0x%02X\n", i, ts_info[i].start_addr); |
| pr_info("\tTS : Section(%d) End Address: 0x%02X\n", i, ts_info[i].end_addr); |
| pr_info("\tTS : Section(%d) Compatibility: 0x%02X\n", i, ts_info[i].compatible_version); |
| } |
| |
| pr_info("End mms100_get_version_info()\n"); |
| |
| return ISC_SUCCESS; |
| } |
| |
| static eISCRet_t mms100_seek_section_info(void) |
| { |
| #define STRING_BUF_LEN 100 |
| |
| int i; |
| int offset = sizeof(struct mms_bin_hdr); |
| |
| pr_info("[TSP ISC] %s\n", __func__); |
| |
| fw_hdr = (struct mms_bin_hdr *) fw_mfsb.data; |
| img = kzalloc(sizeof(*img) * fw_hdr->section_num, GFP_KERNEL); |
| for (i = 0; i < fw_hdr->section_num; i++, offset += sizeof(struct mms_fw_img)) |
| { |
| img[i] = (struct mms_fw_img *) (fw_mfsb.data + offset); |
| mfsb_info[i].version = img[i]->version; |
| mfsb_info[i].start_addr = img[i]->start_page; |
| mfsb_info[i].end_addr = img[i]->end_page; |
| if (i == 0) |
| { |
| mfsb_info[i].compatible_version = img[i]->version; |
| } |
| else |
| { |
| mfsb_info[i].compatible_version = img[i - 1]->version; |
| } |
| } |
| |
| for (i = 0; i < SECTION_NUM; i++) |
| { |
| pr_info("\tMFSB : Section(%d) Version: 0x%02X\n", i, mfsb_info[i].version); |
| pr_info("\tMFSB : Section(%d) Start Address: 0x%02X\n", i, mfsb_info[i].start_addr); |
| pr_info("\tMFSB : Section(%d) End Address: 0x%02X\n", i, mfsb_info[i].end_addr); |
| pr_info("\tMFSB : Section(%d) Compatibility: 0x%02X\n", i, mfsb_info[i].compatible_version); |
| } |
| |
| pr_info("End mms100_seek_section_info()\n"); |
| |
| return ISC_SUCCESS; |
| } |
| |
| |
| static eISCRet_t mms100_compare_version_info(struct i2c_client *_client) |
| { |
| int i; |
| //unsigned char expected_compatibility[SECTION_NUM]; |
| int target_ver[SECTION_NUM]; |
| int fw_up_to_date = true; |
| |
| pr_info("[TSP ISC] %s\n", __func__); |
| |
| if (mms100_get_version_info(_client) != ISC_SUCCESS) |
| return ISC_I2C_ERROR; |
| |
| mms100_seek_section_info(); |
| |
| for (i = 0; i < SECTION_NUM; i++) |
| { |
| |
| if (mfsb_info[i].version != ts_info[i].version) |
| { |
| fw_up_to_date = false; |
| section_update_flag[i] = true; |
| target_ver[i] = mfsb_info[i].version; |
| |
| if (mfsb_info[0].version != ts_info[0].version) |
| { |
| section_update_flag[0] = true; |
| section_update_flag[1] = true; |
| section_update_flag[2] = true; |
| } |
| |
| if (mfsb_info[1].version != ts_info[1].version) |
| { |
| section_update_flag[0] = false; |
| section_update_flag[1] = true; |
| section_update_flag[2] = true; |
| } |
| |
| } |
| else |
| { |
| target_ver[i] = ts_info[i].version; |
| } |
| } |
| |
| if (fw_up_to_date) |
| { |
| pr_info("mms_ts firmware version is up to date\n"); |
| return ISC_NO_NEED_UPDATE_ERROR; |
| } |
| |
| for (i = 1; i < SECTION_NUM; i++) |
| { |
| if (target_ver[i - 1] != mfsb_info[i].compatible_version) |
| { |
| pr_info("compatibility version mismatch(%d), 0x%02x, 0x%02x\n", i, target_ver[i - 1], mfsb_info[i].compatible_version); |
| return ISC_COMPATIVILITY_ERROR; |
| } |
| } |
| |
| pr_info("End mms100_compare_version_info()\n"); |
| |
| return ISC_SUCCESS; |
| } |
| |
| static eISCRet_t mms100_enter_ISC_mode(struct i2c_client *_client) |
| { |
| int ret; |
| unsigned char wr_buf[2]; |
| |
| pr_info("[TSP ISC] %s\n", __func__); |
| |
| wr_buf[0] = ISC_CMD_ENTER_ISC; // command |
| wr_buf[1] = ISC_CMD_ENTER_ISC_PARA1; // sub_command |
| |
| ret = mms100_i2c_write(_client, wr_buf, 2); |
| |
| if (ret < 0) |
| { |
| pr_info("[TSP ISC] %s,%d: i2c write fail[%d] \n", __FUNCTION__, __LINE__, ret); |
| return ISC_I2C_ERROR; |
| } |
| |
| //mms100_msdelay(50); |
| mdelay(50); |
| |
| pr_info("End mms100_enter_ISC_mode()\n"); |
| |
| return ISC_SUCCESS; |
| } |
| |
| static eISCRet_t mms100_enter_config_update(struct i2c_client *_client) |
| { |
| int ret; |
| unsigned char wr_buf[10] = |
| { 0, }; |
| unsigned char rd_buf; |
| |
| pr_info("[TSP ISC] %s\n", __func__); |
| |
| wr_buf[0] = ISC_CMD_UPDATE_MODE; |
| wr_buf[1] = ISC_SUBCMD_ENTER_UPDATE; |
| |
| ret = mms100_i2c_write(_client, wr_buf, 10); |
| |
| if (ret < 0) |
| { |
| pr_info("[TSP ISC] %s,%d: i2c write fail[%d] \n", __FUNCTION__, __LINE__, ret); |
| return ISC_I2C_ERROR; |
| } |
| |
| ret = mms100_i2c_read(_client, ISC_CMD_CONFIRM_STATUS, 1, &rd_buf); |
| |
| if (ret < 0) |
| { |
| pr_info("[TSP ISC] %s,%d: i2c read fail[%d] \n", __FUNCTION__, __LINE__, ret); |
| return ISC_I2C_ERROR; |
| } |
| |
| if (rd_buf != ISC_STATUS_UPDATE_MODE) |
| return ISC_UPDATE_MODE_ENTER_ERROR; |
| |
| pr_info("End mms100_enter_config_update()\n"); |
| |
| return ISC_SUCCESS; |
| } |
| |
| static eISCRet_t mms100_ISC_clear_page(struct i2c_client *_client, unsigned char _page_addr) |
| { |
| int ret; |
| unsigned char rd_buf; |
| |
| pr_info("[TSP ISC] %s\n", __func__); |
| memset(&g_wr_buf[3], 0xFF, PAGE_DATA); |
| |
| g_wr_buf[0] = ISC_CMD_UPDATE_MODE; // command |
| g_wr_buf[1] = ISC_SUBCMD_DATA_WRITE; // sub_command |
| g_wr_buf[2] = _page_addr; |
| |
| g_wr_buf[PAGE_HEADER + PAGE_DATA] = crc0_buf[_page_addr]; |
| g_wr_buf[PAGE_HEADER + PAGE_DATA + 1] = crc1_buf[_page_addr]; |
| |
| ret = mms100_i2c_write(_client, g_wr_buf, PACKET_SIZE); |
| |
| if (ret < 0) |
| { |
| pr_info("[TSP ISC] %s,%d: i2c write fail[%d] \n", __FUNCTION__, __LINE__, ret); |
| return ISC_I2C_ERROR; |
| } |
| |
| ret = mms100_i2c_read(_client, ISC_CMD_CONFIRM_STATUS, 1, &rd_buf); |
| |
| if (ret < 0) |
| { |
| pr_info("[TSP ISC] %s,%d: i2c read fail[%d] \n", __FUNCTION__, __LINE__, ret); |
| return ISC_I2C_ERROR; |
| } |
| |
| if (rd_buf != ISC_STATUS_CRC_CHECK_SUCCESS) |
| return ISC_UPDATE_MODE_ENTER_ERROR; |
| |
| pr_info("End mms100_ISC_clear_page()\n"); |
| |
| return ISC_SUCCESS; |
| |
| } |
| |
| static eISCRet_t mms100_ISC_clear_validate_markers(struct i2c_client *_client) |
| { |
| eISCRet_t ret_msg; |
| int i, j; |
| bool is_matched_address; |
| |
| pr_info("[TSP ISC] %s\n", __func__); |
| |
| for (i = SEC_CORE; i <= SEC_CONFIG; i++) |
| { |
| if (section_update_flag[i]) |
| { |
| if (ts_info[i].end_addr <= 30 && ts_info[i].end_addr > 0) |
| { |
| ret_msg = mms100_ISC_clear_page(_client, ts_info[i].end_addr); |
| |
| if (ret_msg != ISC_SUCCESS) |
| return ret_msg; |
| } |
| } |
| } |
| |
| for (i = SEC_CORE; i <= SEC_CONFIG; i++) |
| { |
| if (section_update_flag[i]) |
| { |
| is_matched_address = false; |
| for (j = SEC_CORE; j <= SEC_CONFIG; j++) |
| { |
| if (mfsb_info[i].end_addr == ts_info[i].end_addr) |
| { |
| is_matched_address = true; |
| break; |
| } |
| } |
| |
| if (!is_matched_address) |
| { |
| if (mfsb_info[i].end_addr <= 30 && mfsb_info[i].end_addr > 0) |
| { |
| ret_msg = mms100_ISC_clear_page(_client, mfsb_info[i].end_addr); |
| |
| if (ret_msg != ISC_SUCCESS) |
| return ret_msg; |
| } |
| } |
| } |
| } |
| |
| pr_info("End mms100_ISC_clear_validate_markers()\n"); |
| |
| return ISC_SUCCESS; |
| } |
| |
| static void mms100_calc_crc(unsigned char *crc, int page_addr, unsigned char* ptr_fw) |
| { |
| int i, j; |
| |
| unsigned char ucData; |
| |
| unsigned short SeedValue; |
| unsigned short CRC_check_buf; |
| unsigned short CRC_send_buf; |
| unsigned short IN_data; |
| unsigned short XOR_bit_1; |
| unsigned short XOR_bit_2; |
| unsigned short XOR_bit_3; |
| |
| // Seed |
| |
| CRC_check_buf = 0xFFFF; |
| SeedValue = (unsigned short) page_addr; |
| |
| for (i = 7; i >= 0; i--) |
| { |
| IN_data = (SeedValue >> i) & 0x01; |
| XOR_bit_1 = (CRC_check_buf & 0x0001) ^ IN_data; |
| XOR_bit_2 = XOR_bit_1 ^ (CRC_check_buf >> 11 & 0x01); |
| XOR_bit_3 = XOR_bit_1 ^ (CRC_check_buf >> 4 & 0x01); |
| CRC_send_buf = (XOR_bit_1 << 4) | (CRC_check_buf >> 12 & 0x0F); |
| CRC_send_buf = (CRC_send_buf << 7) | (XOR_bit_2 << 6) | (CRC_check_buf >> 5 & 0x3F); |
| CRC_send_buf = (CRC_send_buf << 4) | (XOR_bit_3 << 3) | (CRC_check_buf >> 1 & 0x0007); |
| CRC_check_buf = CRC_send_buf; |
| } |
| |
| for (i = 0; i < 1024; i++) |
| { |
| ucData = ptr_fw[i]; |
| |
| for (j = 7; j >= 0; j--) |
| { |
| IN_data = (ucData >> j) & 0x0001; |
| XOR_bit_1 = (CRC_check_buf & 0x0001) ^ IN_data; |
| XOR_bit_2 = XOR_bit_1 ^ (CRC_check_buf >> 11 & 0x01); |
| XOR_bit_3 = XOR_bit_1 ^ (CRC_check_buf >> 4 & 0x01); |
| CRC_send_buf = (XOR_bit_1 << 4) | (CRC_check_buf >> 12 & 0x0F); |
| CRC_send_buf = (CRC_send_buf << 7) | (XOR_bit_2 << 6) | (CRC_check_buf >> 5 & 0x3F); |
| CRC_send_buf = (CRC_send_buf << 4) | (XOR_bit_3 << 3) | (CRC_check_buf >> 1 & 0x0007); |
| CRC_check_buf = CRC_send_buf; |
| } |
| } |
| |
| crc[0] = (unsigned char) ((CRC_check_buf >> 8) & 0xFF); |
| crc[1] = (unsigned char) ((CRC_check_buf >> 0) & 0xFF); |
| } |
| |
| static eISCRet_t mms100_update_section_data(struct i2c_client *_client) |
| { |
| #define STRING_BUF_LEN 100 |
| |
| int i, j, ret; // 2012.08.30 |
| unsigned char rd_buf; |
| unsigned char crc[2]; // 2012.08.30 |
| int ptr; |
| const u8 *ptr_fw; |
| int page_addr; |
| const u8 *fw_data; |
| pr_info("[TSP ISC] %s\n", __func__); |
| |
| fw_data = (u8 *)fw_mfsb.data + fw_hdr->binary_offset; |
| for (i = 0; i < fw_hdr->section_num; i++) |
| { |
| pr_info("update flag (%d)\n", section_update_flag[i]); |
| ptr = img[i]->offset; |
| if (section_update_flag[i]) |
| { |
| ptr_fw = fw_data + ptr; |
| pr_info("binary found\n"); |
| |
| for (page_addr = mfsb_info[i].start_addr; page_addr <= mfsb_info[i].end_addr; page_addr++) |
| { |
| if (page_addr - mfsb_info[i].start_addr > 0) |
| ptr_fw += 1024; |
| |
| g_wr_buf[0] = ISC_CMD_UPDATE_MODE; |
| g_wr_buf[1] = ISC_SUBCMD_DATA_WRITE; |
| g_wr_buf[2] = (unsigned char) page_addr; |
| |
| for (j = 0; j < 1024; j += 4) |
| { |
| g_wr_buf[3 + j] = ptr_fw[j + 3]; |
| g_wr_buf[3 + j + 1] = ptr_fw[j + 2]; |
| g_wr_buf[3 + j + 2] = ptr_fw[j + 1]; |
| g_wr_buf[3 + j + 3] = ptr_fw[j + 0]; |
| } |
| |
| mms100_calc_crc(crc, page_addr, &g_wr_buf[3]); |
| |
| g_wr_buf[1027] = crc[0]; |
| g_wr_buf[1028] = crc[1]; |
| |
| pr_info("crc val : %X%X\n", crc[0], crc[1]); |
| ret = mms100_i2c_write(_client, g_wr_buf, PACKET_SIZE); |
| |
| if (ret < 0) |
| { |
| pr_info("[TSP ISC] %s,%d: i2c write fail[%d] \n", __FUNCTION__, __LINE__, ret); |
| return ISC_I2C_ERROR; |
| } |
| |
| ret = mms100_i2c_read(_client, ISC_CMD_CONFIRM_STATUS, 1, &rd_buf); |
| |
| if (ret < 0) |
| { |
| pr_info("[TSP ISC] %s,%d: i2c read fail[%d] \n", __FUNCTION__, __LINE__, ret); |
| return ISC_I2C_ERROR; |
| } |
| |
| if (rd_buf != ISC_STATUS_CRC_CHECK_SUCCESS) |
| return ISC_CRC_ERROR; |
| |
| section_update_flag[i] = false; |
| pr_info("section(%d) updated.\n", i); |
| } |
| } |
| } |
| |
| pr_info("End mms100_update_section_data()\n"); |
| |
| return ISC_SUCCESS; |
| } |
| |
| static int mms100_open_mbinary(struct i2c_client *_client) |
| { |
| int ret = 0; |
| |
| /* |
| ret += request_firmware(&(fw_mbin[1]), |
| "tsp_melfas/s3/CORE.fw", &_client->dev); |
| ret += request_firmware(&(fw_mbin[2]), |
| "tsp_melfas/s3/PRIV.fw", &_client->dev); |
| ret += request_firmware(&(fw_mbin[3]), |
| "tsp_melfas/s3/PUBL.fw", &_client->dev); |
| */ |
| |
| ret = request_firmware(&fw_mbin, |
| "tsp_melfas/DIABLO_CoreV54_V35_master.fw", &_client->dev); |
| |
| if (!ret) { |
| fw_mfsb.data = kzalloc(fw_mbin->size, GFP_KERNEL); |
| memcpy(fw_mfsb.data, fw_mbin->data, fw_mbin->size); |
| fw_mfsb.size = fw_mbin->size; |
| return ISC_SUCCESS; |
| } |
| else { |
| pr_info("[TSP ISC] request_firmware fail"); |
| return ISC_FILE_OPEN_ERROR; |
| } |
| } |
| |
| static int mms100_close_mbinary(void) |
| { |
| /* |
| int i; |
| |
| for (i = 0; i < SECTION_NUM; i++) { |
| if (fw_mbin[i] != NULL) |
| release_firmware(fw_mbin[i]); |
| } |
| */ |
| release_firmware(fw_mbin); |
| kfree(img); |
| kfree(fw_mfsb.data); |
| |
| return ISC_SUCCESS; |
| } |
| |
| eISCRet_t mms100_ISC_download_mbinary(struct mms_ts_info *info) |
| { |
| struct i2c_client *_client = info->client; |
| int ret_msg = ISC_NONE; |
| bool force_update = 0; |
| |
| pr_info("[TSP ISC] %s\n", __func__); |
| |
| mms100_reset(info); |
| /* |
| ret_msg = mms100_check_operating_mode(_client, EC_BOOT_ON_SUCCEEDED); |
| if (ret_msg != ISC_SUCCESS) |
| goto ISC_ERROR_HANDLE; |
| */ |
| ret_msg = mms100_open_mbinary(_client); |
| if (ret_msg != ISC_SUCCESS) |
| goto ISC_ERROR_HANDLE; |
| |
| /* Config version Check */ |
| if (force_update) |
| { |
| int i; |
| ret_msg = mms100_compare_version_info(_client); |
| |
| for (i = 0; i < SECTION_NUM; i++) |
| section_update_flag[i] = true; |
| } |
| else |
| { |
| ret_msg = mms100_compare_version_info(_client); |
| if (ret_msg != ISC_SUCCESS) |
| goto ISC_ERROR_HANDLE; |
| } |
| |
| ret_msg = mms100_enter_ISC_mode(_client); |
| if (ret_msg != ISC_SUCCESS) |
| goto ISC_ERROR_HANDLE; |
| |
| ret_msg = mms100_enter_config_update(_client); |
| if (ret_msg != ISC_SUCCESS) |
| goto ISC_ERROR_HANDLE; |
| |
| ret_msg = mms100_ISC_clear_validate_markers(_client); |
| if (ret_msg != ISC_SUCCESS) |
| goto ISC_ERROR_HANDLE; |
| |
| pr_info("[TSP ISC]mms100_update_section_data start"); |
| |
| ret_msg = mms100_update_section_data(_client); |
| if (ret_msg != ISC_SUCCESS) |
| goto ISC_ERROR_HANDLE; |
| |
| pr_info("[TSP ISC]mms100_update_section_data end"); |
| |
| pr_info("[TSP ISC]FIRMWARE_UPDATE_FINISHED!!!\n"); |
| |
| ret_msg = ISC_SUCCESS; |
| |
| ISC_ERROR_HANDLE: |
| if(ret_msg == ISC_NO_NEED_UPDATE_ERROR){ |
| ret_msg = ISC_SUCCESS; |
| }else if (ret_msg != ISC_SUCCESS){ |
| pr_info("ISC_ERROR_CODE: %d\n", ret_msg); |
| } |
| |
| mms100_reset(info); |
| mms100_close_mbinary(); |
| |
| return ret_msg; |
| } |
| #endif /* ISC_DL_MODE start */ |
| |
| static void hw_reboot(struct mms_ts_info *info, bool bootloader) |
| { |
| melfas_vdd_on(info, 0); |
| gpio_direction_output(info->pdata->gpio_sda, bootloader ? 0 : 1); |
| gpio_direction_output(info->pdata->gpio_scl, bootloader ? 0 : 1); |
| #ifdef MMS_144_RESET_PIN |
| printk("[TSP] %s : Reset pin Output value 0 \n",__func__); |
| gpio_direction_output(info->pdata->gpio_resetb, 0); |
| #endif |
| |
| msleep(30); |
| melfas_vdd_on(info, 1); |
| msleep(30); |
| |
| if (bootloader) { |
| gpio_set_value(info->pdata->gpio_scl, 0); |
| gpio_set_value(info->pdata->gpio_sda, 1); |
| } else { |
| #ifdef MMS_144_RESET_PIN |
| printk("[TSP] %s : Reset pin Set Value 1 \n",__func__); |
| gpio_set_value(info->pdata->gpio_resetb, 1); |
| printk("[TSP] %s : reset pin direction input \n",__func__); |
| gpio_direction_input(info->pdata->gpio_resetb); |
| #endif |
| gpio_direction_input(info->pdata->gpio_scl); |
| gpio_direction_input(info->pdata->gpio_sda); |
| } |
| msleep(40); |
| } |
| |
| static inline void hw_reboot_bootloader(struct mms_ts_info *info) |
| { |
| hw_reboot(info, true); |
| } |
| |
| static inline void hw_reboot_normal(struct mms_ts_info *info) |
| { |
| hw_reboot(info, false); |
| } |
| |
| static inline void mms_pwr_on_reset(struct mms_ts_info *info) |
| { |
| #if 0 /* firmware call removal */ |
| struct i2c_adapter *adapter = to_i2c_adapter(info->client->dev.parent); |
| |
| /* |
| if (!info->pdata->mux_fw_flash) { |
| dev_info(&info->client->dev, |
| "missing platform data, can't do power-on-reset\n"); |
| return; |
| } |
| */ |
| i2c_lock_adapter(adapter); |
| melfas_mux_fw_flash(info->pdata, true); |
| |
| melfas_vdd_on(info, 0); |
| gpio_direction_output(info->pdata->gpio_sda, 1); |
| gpio_direction_output(info->pdata->gpio_scl, 1); |
| #ifdef MMS_144_RESET_PIN |
| printk("[TSP] %s : Reset pin Output value 1 \n",__func__); |
| gpio_direction_output(info->pdata->gpio_resetb, 1); |
| #endif |
| msleep(50); |
| melfas_vdd_on(info, 1); |
| msleep(50); |
| |
| melfas_mux_fw_flash(info->pdata, false); |
| i2c_unlock_adapter(adapter); |
| |
| /* TODO: Seems long enough for the firmware to boot. |
| * Find the right value */ |
| #endif /* fw call removal */ |
| msleep(250); |
| } |
| |
| #if 0 /* firmware call removal */ |
| static void isp_toggle_clk(struct mms_ts_info *info, int start_lvl, int end_lvl, |
| int hold_us) |
| { |
| gpio_set_value(info->pdata->gpio_scl, start_lvl); |
| udelay(hold_us); |
| gpio_set_value(info->pdata->gpio_scl, end_lvl); |
| udelay(hold_us); |
| } |
| |
| /* 1 <= cnt <= 32 bits to write */ |
| static void isp_send_bits(struct mms_ts_info *info, u32 data, int cnt) |
| { |
| #ifdef MMS_144_RESET_PIN |
| printk("[TSP] %s : Reset pin Output value 0 \n",__func__); |
| gpio_direction_output(info->pdata->gpio_resetb, 0); |
| #endif |
| gpio_direction_output(info->pdata->gpio_scl, 0); |
| gpio_direction_output(info->pdata->gpio_sda, 0); |
| |
| /* clock out the bits, msb first */ |
| while (cnt--) { |
| gpio_set_value(info->pdata->gpio_sda, (data >> cnt) & 1); |
| udelay(3); |
| isp_toggle_clk(info, 1, 0, 3); |
| } |
| } |
| |
| /* 1 <= cnt <= 32 bits to read */ |
| static u32 isp_recv_bits(struct mms_ts_info *info, int cnt) |
| { |
| u32 data = 0; |
| #ifdef MMS_144_RESET_PIN |
| printk("[TSP] %s : Reset pin Output value 0 \n",__func__); |
| gpio_direction_output(info->pdata->gpio_resetb, 0); |
| #endif |
| gpio_direction_output(info->pdata->gpio_scl, 0); |
| gpio_set_value(info->pdata->gpio_sda, 0); |
| gpio_direction_input(info->pdata->gpio_sda); |
| |
| /* clock in the bits, msb first */ |
| while (cnt--) { |
| isp_toggle_clk(info, 0, 1, 1); |
| data = (data << 1) | (!!gpio_get_value(info->pdata->gpio_sda)); |
| } |
| |
| gpio_direction_output(info->pdata->gpio_sda, 0); |
| return data; |
| } |
| |
| static void isp_enter_mode(struct mms_ts_info *info, u32 mode) |
| { |
| int cnt; |
| unsigned long flags; |
| |
| local_irq_save(flags); |
| #ifdef MMS_144_RESET_PIN |
| printk("[TSP] %s : Reset pin Output value 0\n",__func__); |
| gpio_direction_output(info->pdata->gpio_resetb, 0); |
| #endif |
| gpio_direction_output(info->pdata->gpio_scl, 0); |
| gpio_direction_output(info->pdata->gpio_sda, 1); |
| |
| mode &= 0xffff; |
| for (cnt = 15; cnt >= 0; cnt--) { |
| #ifdef MMS_144_RESET_PIN |
| printk("[TSP] %s : Reset pin Set Value \n",__func__); |
| gpio_set_value(info->pdata->gpio_resetb, (mode >> cnt) & 1); |
| #endif |
| udelay(3); |
| isp_toggle_clk(info, 1, 0, 3); |
| } |
| #ifdef MMS_144_RESET_PIN |
| printk("[TSP] %s : Reset pin Set Value 0 \n",__func__); |
| gpio_set_value(info->pdata->gpio_resetb, 0); |
| #endif |
| local_irq_restore(flags); |
| } |
| |
| static void isp_exit_mode(struct mms_ts_info *info) |
| { |
| int i; |
| unsigned long flags; |
| |
| local_irq_save(flags); |
| #ifdef MMS_144_RESET_PIN |
| printk("[TSP] %s : Reset pin Output value 0 \n",__func__); |
| gpio_direction_output(info->pdata->gpio_resetb, 0); |
| #endif |
| udelay(3); |
| |
| for (i = 0; i < 10; i++) |
| isp_toggle_clk(info, 1, 0, 3); |
| local_irq_restore(flags); |
| } |
| |
| static void flash_set_address(struct mms_ts_info *info, u16 addr) |
| { |
| /* Only 13 bits of addr are valid. |
| * The addr is in bits 13:1 of cmd */ |
| isp_send_bits(info, (u32)(addr & 0x1fff) << 1, 18); |
| } |
| |
| static void flash_erase(struct mms_ts_info *info) |
| { |
| isp_enter_mode(info, ISP_MODE_FLASH_ERASE); |
| |
| #ifdef MMS_144_RESET_PIN |
| printk("[TSP] %s : Reset pin Output value 0 \n",__func__); |
| gpio_direction_output(info->pdata->gpio_resetb, 0); |
| #endif |
| gpio_direction_output(info->pdata->gpio_scl, 0); |
| gpio_direction_output(info->pdata->gpio_sda, 1); |
| |
| /* 4 clock cycles with different timings for the erase to |
| * get processed, clk is already 0 from above */ |
| udelay(7); |
| isp_toggle_clk(info, 1, 0, 3); |
| udelay(7); |
| isp_toggle_clk(info, 1, 0, 3); |
| usleep_range(25000, 35000); |
| isp_toggle_clk(info, 1, 0, 3); |
| usleep_range(150, 200); |
| isp_toggle_clk(info, 1, 0, 3); |
| |
| gpio_set_value(info->pdata->gpio_sda, 0); |
| |
| isp_exit_mode(info); |
| } |
| |
| static u32 flash_readl(struct mms_ts_info *info, u16 addr) |
| { |
| int i; |
| u32 val; |
| unsigned long flags; |
| |
| local_irq_save(flags); |
| isp_enter_mode(info, ISP_MODE_FLASH_READ); |
| flash_set_address(info, addr); |
| |
| gpio_direction_output(info->pdata->gpio_scl, 0); |
| gpio_direction_output(info->pdata->gpio_sda, 0); |
| udelay(40); |
| |
| /* data load cycle */ |
| for (i = 0; i < 6; i++) |
| isp_toggle_clk(info, 1, 0, 10); |
| |
| val = isp_recv_bits(info, 32); |
| isp_exit_mode(info); |
| local_irq_restore(flags); |
| |
| return val; |
| } |
| |
| static void flash_writel(struct mms_ts_info *info, u16 addr, u32 val) |
| { |
| unsigned long flags; |
| |
| local_irq_save(flags); |
| isp_enter_mode(info, ISP_MODE_FLASH_WRITE); |
| flash_set_address(info, addr); |
| isp_send_bits(info, val, 32); |
| |
| gpio_direction_output(info->pdata->gpio_sda, 1); |
| /* 6 clock cycles with different timings for the data to get written |
| * into flash */ |
| isp_toggle_clk(info, 0, 1, 3); |
| isp_toggle_clk(info, 0, 1, 3); |
| isp_toggle_clk(info, 0, 1, 6); |
| isp_toggle_clk(info, 0, 1, 12); |
| isp_toggle_clk(info, 0, 1, 3); |
| isp_toggle_clk(info, 0, 1, 3); |
| |
| isp_toggle_clk(info, 1, 0, 1); |
| |
| gpio_direction_output(info->pdata->gpio_sda, 0); |
| isp_exit_mode(info); |
| local_irq_restore(flags); |
| usleep_range(300, 400); |
| } |
| |
| static bool flash_is_erased(struct mms_ts_info *info) |
| { |
| struct i2c_client *client = info->client; |
| u32 val; |
| u16 addr; |
| |
| for (addr = 0; addr < (ISP_MAX_FW_SIZE / 4); addr++) { |
| udelay(40); |
| val = flash_readl(info, addr); |
| |
| if (val != 0xffffffff) { |
| dev_dbg(&client->dev, |
| "addr 0x%x not erased: 0x%08x != 0xffffffff\n", |
| addr, val); |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| static int fw_write_image(struct mms_ts_info *info, const u8 *data, size_t len) |
| { |
| struct i2c_client *client = info->client; |
| u16 addr = 0; |
| |
| for (addr = 0; addr < (len / 4); addr++, data += 4) { |
| u32 val = get_unaligned_le32(data); |
| u32 verify_val; |
| int retries = 3; |
| |
| while (retries--) { |
| flash_writel(info, addr, val); |
| verify_val = flash_readl(info, addr); |
| if (val == verify_val) |
| break; |
| dev_err(&client->dev, |
| "mismatch @ addr 0x%x: 0x%x != 0x%x\n", |
| addr, verify_val, val); |
| continue; |
| } |
| if (retries < 0) |
| return -ENXIO; |
| } |
| |
| return 0; |
| } |
| |
| static int fw_download(struct mms_ts_info *info, const u8 *data, size_t len) |
| { |
| struct i2c_client *client = info->client; |
| u32 val; |
| int ret = 0; |
| |
| if (len % 4) { |
| dev_err(&client->dev, |
| "fw image size (%d) must be a multiple of 4 bytes\n", |
| len); |
| return -EINVAL; |
| } else if (len > ISP_MAX_FW_SIZE) { |
| dev_err(&client->dev, |
| "fw image is too big, %d > %d\n", len, ISP_MAX_FW_SIZE); |
| return -EINVAL; |
| } |
| |
| dev_info(&client->dev, "fw download start\n"); |
| |
| melfas_vdd_on(info, 0); |
| gpio_direction_output(info->pdata->gpio_sda, 0); |
| gpio_direction_output(info->pdata->gpio_scl, 0); |
| #ifdef MMS_144_RESET_PIN |
| printk("[TSP] %s : Reset pin Output value 0 \n",__func__); |
| gpio_direction_output(info->pdata->gpio_resetb, 0); |
| #endif |
| |
| hw_reboot_bootloader(info); |
| |
| val = flash_readl(info, ISP_IC_INFO_ADDR); |
| dev_info(&client->dev, "IC info: 0x%02x (%x)\n", val & 0xff, val); |
| |
| dev_info(&client->dev, "fw erase...\n"); |
| flash_erase(info); |
| if (!flash_is_erased(info)) { |
| ret = -ENXIO; |
| goto err; |
| } |
| |
| dev_info(&client->dev, "fw write...\n"); |
| /* XXX: what does this do?! */ |
| flash_writel(info, ISP_IC_INFO_ADDR, 0xffffff00 | (val & 0xff)); |
| usleep_range(1000, 1500); |
| ret = fw_write_image(info, data, len); |
| if (ret) |
| goto err; |
| usleep_range(1000, 1500); |
| |
| hw_reboot_normal(info); |
| usleep_range(1000, 1500); |
| dev_info(&client->dev, "fw download done...\n"); |
| return 0; |
| |
| err: |
| dev_err(&client->dev, "fw download failed...\n"); |
| hw_reboot_normal(info); |
| return ret; |
| } |
| #endif /* fw call removal */ |
| |
| #if defined(SEC_TSP_ISC_FW_UPDATE) |
| static u16 gen_crc(u8 data, u16 pre_crc) |
| { |
| u16 crc; |
| u16 cur; |
| u16 temp; |
| u16 bit_1; |
| u16 bit_2; |
| int i; |
| |
| crc = pre_crc; |
| for (i = 7; i >= 0; i--) { |
| cur = ((data >> i) & 0x01) ^ (crc & 0x0001); |
| bit_1 = cur ^ (crc >> 11 & 0x01); |
| bit_2 = cur ^ (crc >> 4 & 0x01); |
| temp = (cur << 4) | (crc >> 12 & 0x0F); |
| temp = (temp << 7) | (bit_1 << 6) | (crc >> 5 & 0x3F); |
| temp = (temp << 4) | (bit_2 << 3) | (crc >> 1 & 0x0007); |
| crc = temp; |
| } |
| return crc; |
| } |
| |
| static int isc_fw_download(struct mms_ts_info *info, const u8 *data, |
| size_t len) |
| { |
| u8 *buff; |
| u16 crc_buf; |
| int src_idx; |
| int dest_idx; |
| int ret; |
| int i, j; |
| |
| buff = kzalloc(ISC_PKT_SIZE, GFP_KERNEL); |
| if (!buff) { |
| dev_err(&info->client->dev, "%s: failed to allocate memory\n", |
| __func__); |
| ret = -1; |
| goto err_mem_alloc; |
| } |
| |
| /* enterring ISC mode */ |
| *buff = ISC_ENTER_ISC_DATA; |
| ret = i2c_smbus_write_byte_data(info->client, |
| ISC_ENTER_ISC_CMD, *buff); |
| if (ret < 0) { |
| dev_err(&info->client->dev, |
| "fail to enter ISC mode(err=%d)\n", ret); |
| goto fail_to_isc_enter; |
| } |
| usleep_range(10000, 20000); |
| dev_info(&info->client->dev, "Enter ISC mode\n"); |
| |
| /*enter ISC update mode */ |
| *buff = ISC_ENTER_UPDATE_DATA; |
| ret = i2c_smbus_write_i2c_block_data(info->client, |
| ISC_CMD, |
| ISC_ENTER_UPDATE_DATA_LEN, buff); |
| if (ret < 0) { |
| dev_err(&info->client->dev, |
| "fail to enter ISC update mode(err=%d)\n", ret); |
| goto fail_to_isc_update; |
| } |
| dev_info(&info->client->dev, "Enter ISC update mode\n"); |
| |
| /* firmware write */ |
| *buff = ISC_CMD; |
| *(buff + 1) = ISC_DATA_WRITE_SUB_CMD; |
| for (i = 0; i < ISC_PKT_NUM; i++) { |
| *(buff + 2) = i; |
| crc_buf = gen_crc(*(buff + 2), ISC_DEFAULT_CRC); |
| |
| for (j = 0; j < ISC_PKT_DATA_SIZE; j++) { |
| dest_idx = ISC_PKT_HEADER_SIZE + j; |
| src_idx = i * ISC_PKT_DATA_SIZE + |
| ((int)(j / WORD_SIZE)) * WORD_SIZE - |
| (j % WORD_SIZE) + 3; |
| *(buff + dest_idx) = *(data + src_idx); |
| crc_buf = gen_crc(*(buff + dest_idx), crc_buf); |
| } |
| |
| *(buff + ISC_PKT_DATA_SIZE + ISC_PKT_HEADER_SIZE + 1) = |
| crc_buf & 0xFF; |
| *(buff + ISC_PKT_DATA_SIZE + ISC_PKT_HEADER_SIZE) = |
| crc_buf >> 8 & 0xFF; |
| |
| ret = i2c_master_send(info->client, buff, ISC_PKT_SIZE); |
| if (ret < 0) { |
| dev_err(&info->client->dev, |
| "fail to firmware writing on packet %d.(%d)\n", |
| i, ret); |
| goto fail_to_fw_write; |
| } |
| usleep_range(1, 5); |
| |
| /* confirm CRC */ |
| ret = i2c_smbus_read_byte_data(info->client, |
| ISC_CHECK_STATUS_CMD); |
| if (ret == ISC_CONFIRM_CRC) { |
| dev_info(&info->client->dev, |
| "updating %dth firmware data packet.\n", i); |
| } else { |
| dev_err(&info->client->dev, |
| "fail to firmware update on %dth (%X).\n", |
| i, ret); |
| ret = -1; |
| goto fail_to_confirm_crc; |
| } |
| } |
| |
| ret = 0; |
| |
| fail_to_confirm_crc: |
| fail_to_fw_write: |
| /* exit ISC mode */ |
| *buff = ISC_EXIT_ISC_SUB_CMD; |
| *(buff + 1) = ISC_EXIT_ISC_SUB_CMD2; |
| i2c_smbus_write_i2c_block_data(info->client, ISC_CMD, 2, buff); |
| usleep_range(10000, 20000); |
| fail_to_isc_update: |
| hw_reboot_normal(info); |
| fail_to_isc_enter: |
| kfree(buff); |
| err_mem_alloc: |
| return ret; |
| } |
| #endif /* SEC_TSP_ISC_FW_UPDATE */ |
| |
| static int get_fw_version(struct mms_ts_info *info) |
| { |
| int ret; |
| int retries = 3; |
| |
| /* this seems to fail sometimes after a reset.. retry a few times */ |
| do { |
| ret = i2c_smbus_read_byte_data(info->client, MMS_FW_VERSION); |
| if(retries==1) |
| { |
| mms_pwr_on_reset(info); |
| msleep(30); |
| } |
| } while (ret < 0 && retries-- > 0); |
| |
| return ret; |
| } |
| |
| static int get_hw_version(struct mms_ts_info *info) |
| { |
| int ret; |
| int retries = 3; |
| |
| /* this seems to fail sometimes after a reset.. retry a few times */ |
| do { |
| ret = i2c_smbus_read_byte_data(info->client, MMS_HW_REVISION); |
| } while (ret < 0 && retries-- > 0); |
| |
| return ret; |
| } |
| |
| static int mms_ts_enable(struct mms_ts_info *info, int wakeupcmd) |
| { |
| //mutex_lock(&info->lock); |
| if (info->enabled) |
| goto out; |
| /* wake up the touch controller. */ |
| if (wakeupcmd == 1) { |
| i2c_smbus_write_byte_data(info->client, 0, 0); |
| usleep_range(3000, 5000); |
| } |
| info->enabled = true; |
| enable_irq(info->irq); |
| out: |
| //mutex_unlock(&info->lock); |
| return 0; |
| } |
| |
| static int mms_ts_disable(struct mms_ts_info *info, int sleepcmd) |
| { |
| //mutex_lock(&info->lock); |
| if (!info->enabled) |
| goto out; |
| disable_irq(info->irq); |
| if (sleepcmd == 1) { |
| i2c_smbus_write_byte_data(info->client, MMS_MODE_CONTROL, 0); |
| usleep_range(10000, 12000); |
| } |
| info->enabled = false; |
| touch_is_pressed = 0; |
| out: |
| //mutex_unlock(&info->lock); |
| return 0; |
| } |
| |
| #if 0 |
| static int mms_ts_finish_config(struct mms_ts_info *info) |
| { |
| struct i2c_client *client = info->client; |
| int ret; |
| |
| ret = request_threaded_irq(client->irq, NULL, mms_ts_interrupt, |
| IRQF_TRIGGER_LOW | IRQF_ONESHOT, |
| "mms_ts", info); |
| if (ret < 0) { |
| ret = 1; |
| dev_err(&client->dev, "Failed to register interrupt\n"); |
| goto err_req_irq; |
| } |
| |
| info->irq = client->irq; |
| barrier(); |
| |
| dev_info(&client->dev, |
| "Melfas MMS-series touch controller initialized\n"); |
| |
| return 0; |
| |
| err_req_irq: |
| return ret; |
| } |
| #endif |
| |
| #if !ISC_DL_MODE |
| static int mms_ts_fw_info(struct mms_ts_info *info) |
| { |
| struct i2c_client *client = info->client; |
| int ret = 0; |
| int ver, hw_rev; |
| |
| ver = get_fw_version(info); |
| info->fw_ic_ver = ver; |
| dev_info(&client->dev, |
| "[TSP]fw version 0x%02x !!!!\n", ver); |
| |
| hw_rev = get_hw_version(info); |
| dev_info(&client->dev, |
| "[TSP] hw rev = %x\n", hw_rev); |
| |
| if (ver < 0 || hw_rev < 0) { |
| ret = 1; |
| dev_err(&client->dev, |
| "i2c fail...tsp driver unload.\n"); |
| return ret; |
| } |
| /* |
| if (!info->pdata || !info->pdata->mux_fw_flash) { |
| ret = 1; |
| dev_err(&client->dev, |
| "fw cannot be updated, missing platform data\n"); |
| return ret; |
| } |
| */ |
| // ret = mms_ts_finish_config(info); |
| |
| return ret; |
| } |
| #endif |
| |
| #if ISC_DL_MODE |
| static int mms_ts_fw_load(struct mms_ts_info *info) |
| { |
| |
| struct i2c_client *client = info->client; |
| /*struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);*/ |
| int ret = 0; |
| int ver, hw_rev; |
| /*int retries = 3;*/ |
| |
| ver = get_fw_version(info); |
| info->fw_ic_ver = ver; |
| dev_info(&client->dev, |
| "[TSP]fw version 0x%02x !!!!\n", ver); |
| |
| hw_rev = get_hw_version(info); |
| dev_info(&client->dev, |
| "[TSP]hw rev = 0x%02x\n", hw_rev); |
| |
| if (ver < 0 || hw_rev < 0) { |
| ret = 1; |
| dev_err(&client->dev, |
| "i2c fail...tsp driver unload.\n"); |
| } |
| /* |
| if (!info->pdata || !info->pdata->mux_fw_flash) { |
| ret = 1; |
| dev_err(&client->dev, |
| "fw cannot be updated, missing platform data\n"); |
| goto out; |
| } |
| */ |
| if (hw_rev == 0xC) { |
| dev_err(&client->dev, |
| "[TSP] support only 4.8 inch panel. Do not update"); |
| goto done; |
| } |
| |
| dev_err(&client->dev, |
| "[TSP] ISC Ver [0x%02x] [0x%02x] [0x%02x]", |
| i2c_smbus_read_byte_data(info->client, 0xF3), |
| i2c_smbus_read_byte_data(info->client, 0xF4), |
| i2c_smbus_read_byte_data(info->client, 0xF5)); |
| |
| #if 0 |
| if (ver >= FW_VERSION && ver != 0xFF |
| && ver != 0x00 && ver != 0x45) { |
| dev_err(&client->dev, |
| "[TSP] fw is latest. Do not update."); |
| goto done; |
| } else { |
| dev_err(&client->dev, |
| "[TSP] fw update [0x%02x -> 0x%02x]", |
| ver, FW_VERSION); |
| } |
| #endif |
| dev_err(&client->dev, |
| "[TSP] fw update [0x%02x -> 0x%02x]", |
| ver, FW_VERSION); |
| |
| |
| ret = mms100_ISC_download_mbinary(info); |
| |
| if (ret == 0) { |
| dev_err(&client->dev, |
| "[TSP] mms100_ISC_download_mbinary success"); |
| goto done; |
| } else { |
| dev_err(&client->dev, |
| "[TSP] mms100_ISC_download_mbinary fail [%d]", ret); |
| ret = 1; |
| } |
| //out: |
| return ret; |
| |
| done: |
| // ret = mms_ts_finish_config(info); |
| return ret; |
| } |
| #endif |
| |
| #ifdef SEC_TSP_FACTORY_TEST |
| static void set_default_result(struct mms_ts_info *info) |
| { |
| char delim = ':'; |
| |
| memset(info->cmd_result, 0x00, ARRAY_SIZE(info->cmd_result)); |
| memcpy(info->cmd_result, info->cmd, strlen(info->cmd)); |
| strncat(info->cmd_result, &delim, 1); |
| } |
| |
| static void set_cmd_result(struct mms_ts_info *info, char *buff, int len) |
| { |
| strncat(info->cmd_result, buff, len); |
| } |
| |
| /* |
| static inline int msm_irq_to_gpio(unsigned irq) |
| { |
| // TODO : Need to verify chip->base=0 |
| return irq - MSM_GPIO_TO_INT(0); |
| } |
| */ |
| static void get_raw_data_all(struct mms_ts_info *info, u8 cmd) |
| { |
| u8 w_buf[6]; |
| u8 read_buffer[2]; /* 52 */ |
| char buff[TSP_CMD_STR_LEN] = {0}; |
| int gpio; |
| int ret; |
| int i, j; |
| u32 max_value, min_value; |
| u32 raw_data; |
| gpio = info->pdata->gpio_int; |
| // gpio = msm_irq_to_gpio(info->irq); |
| disable_irq(info->irq); |
| |
| w_buf[0] = MMS_VSC_CMD; /* vendor specific command id */ |
| w_buf[1] = MMS_VSC_MODE; /* mode of vendor */ |
| w_buf[2] = 0; /* tx line */ |
| w_buf[3] = 0; /* rx line */ |
| w_buf[4] = 0; /* reserved */ |
| w_buf[5] = 0; /* sub command */ |
| |
| if (cmd == MMS_VSC_CMD_EXIT) { |
| w_buf[5] = MMS_VSC_CMD_EXIT; /* exit test mode */ |
| |
| ret = i2c_smbus_write_i2c_block_data(info->client, |
| w_buf[0], 5, &w_buf[1]); |
| if (ret < 0) |
| goto err_i2c; |
| touch_is_pressed = 0; |
| release_all_fingers(info); |
| #if defined(CONFIG_MIPI_SAMSUNG_ESD_REFRESH) |
| set_esd_disable(); |
| #endif |
| melfas_vdd_on(info, 0); |
| msleep(30); |
| melfas_vdd_on(info, 1); |
| msleep(120); |
| enable_irq(info->irq); |
| #if defined(CONFIG_MIPI_SAMSUNG_ESD_REFRESH) |
| set_esd_enable(); |
| #endif |
| return ; |
| } |
| |
| /* MMS_VSC_CMD_CM_DELTA or MMS_VSC_CMD_CM_ABS |
| * this two mode need to enter the test mode |
| * exit command must be followed by testing. |
| */ |
| if (cmd == MMS_VSC_CMD_CM_DELTA || cmd == MMS_VSC_CMD_CM_ABS) { |
| /* enter the debug mode */ |
| w_buf[2] = 0x0; /* tx */ |
| w_buf[3] = 0x0; /* rx */ |
| w_buf[5] = MMS_VSC_CMD_ENTER; |
| |
| ret = i2c_smbus_write_i2c_block_data(info->client, |
| w_buf[0], 5, &w_buf[1]); |
| if (ret < 0) |
| goto err_i2c; |
| |
| /* wating for the interrupt */ |
| while (gpio_get_value(gpio)) |
| udelay(100); |
| } |
| |
| max_value = 0; |
| min_value = 0; |
| |
| for (i = 0; i < RX_NUM; i++) { |
| for (j = 0; j < TX_NUM; j++) { |
| |
| w_buf[2] = j; /* tx */ |
| w_buf[3] = i; /* rx */ |
| w_buf[5] = cmd; |
| |
| ret = i2c_smbus_write_i2c_block_data(info->client, |
| w_buf[0], 5, &w_buf[1]); |
| if (ret < 0) |
| goto err_i2c; |
| |
| usleep_range(1, 5); |
| |
| ret = i2c_smbus_read_i2c_block_data(info->client, 0xBF, |
| 2, read_buffer); |
| if (ret < 0) |
| goto err_i2c; |
| |
| raw_data = ((u16)read_buffer[1] << 8) | read_buffer[0]; |
| |
| if (i == 0 && j == 0) { |
| max_value = min_value = raw_data; |
| } else { |
| max_value = max(max_value, raw_data); |
| min_value = min(min_value, raw_data); |
| } |
| |
| if (cmd == MMS_VSC_CMD_INTENSITY) { |
| info->intensity[j * RX_NUM + i] = raw_data; |
| dev_dbg(&info->client->dev, "[TSP] intensity[%d][%d] = %d\n", |
| i, j, info->intensity[j * RX_NUM + i]); |
| } else if (cmd == MMS_VSC_CMD_CM_DELTA) { |
| info->inspection[j * RX_NUM + i] = raw_data; |
| dev_dbg(&info->client->dev, "[TSP] delta[%d][%d] = %d\n", |
| i, j, info->inspection[j * RX_NUM + i]); |
| } else if (cmd == MMS_VSC_CMD_CM_ABS) { |
| info->raw[j * RX_NUM + i] = raw_data; |
| dev_dbg(&info->client->dev, "[TSP] raw[%d][%d] = %d\n", |
| i, j, info->raw[j * RX_NUM + i]); |
| } else if (cmd == MMS_VSC_CMD_REFER) { |
| info->reference[j * RX_NUM + i] = |
| raw_data >> 3; |
| dev_dbg(&info->client->dev, "[TSP] reference[%d][%d] = %d\n", |
| i, j, info->reference[j * RX_NUM + i]); |
| } |
| } |
| } |
| |
| snprintf(buff, sizeof(buff), "%d,%d", min_value, max_value); |
| set_cmd_result(info, buff, strnlen(buff, sizeof(buff))); |
| |
| enable_irq(info->irq); |
| |
| err_i2c: |
| dev_err(&info->client->dev, "%s: fail to i2c (cmd=%d)\n", |
| __func__, cmd); |
| } |
| |
| #ifdef ESD_DEBUG |
| static u32 get_raw_data_one(struct mms_ts_info *info, u16 rx_idx, u16 tx_idx, |
| u8 cmd) |
| { |
| u8 w_buf[6]; |
| u8 read_buffer[2]; |
| int ret; |
| u32 raw_data; |
| |
| w_buf[0] = MMS_VSC_CMD; /* vendor specific command id */ |
| w_buf[1] = MMS_VSC_MODE; /* mode of vendor */ |
| w_buf[2] = 0; /* tx line */ |
| w_buf[3] = 0; /* rx line */ |
| w_buf[4] = 0; /* reserved */ |
| w_buf[5] = 0; /* sub command */ |
| |
| if (cmd != MMS_VSC_CMD_INTENSITY && cmd != MMS_VSC_CMD_RAW && |
| cmd != MMS_VSC_CMD_REFER) { |
| dev_err(&info->client->dev, "%s: not profer command(cmd=%d)\n", |
| __func__, cmd); |
| return FAIL; |
| } |
| |
| w_buf[2] = tx_idx; /* tx */ |
| w_buf[3] = rx_idx; /* rx */ |
| w_buf[5] = cmd; /* sub command */ |
| |
| ret = i2c_smbus_write_i2c_block_data(info->client, w_buf[0], 5, |
| &w_buf[1]); |
| if (ret < 0) |
| goto err_i2c; |
| |
| ret = i2c_smbus_read_i2c_block_data(info->client, 0xBF, 2, |
| read_buffer); |
| if (ret < 0) |
| goto err_i2c; |
| |
| raw_data = ((u16)read_buffer[1] << 8) | read_buffer[0]; |
| if (cmd == MMS_VSC_CMD_REFER) |
| raw_data = raw_data >> 4; |
| |
| return raw_data; |
| |
| err_i2c: |
| dev_err(&info->client->dev, "%s: fail to i2c (cmd=%d)\n", |
| __func__, cmd); |
| return FAIL; |
| } |
| #endif |
| |
| #if 0 /* sysfs function call */ |
| static ssize_t show_close_tsp_test(struct device *dev, |
| struct device_attribute *attr, char *buf) |
| { |
| struct mms_ts_info *info = dev_get_drvdata(dev); |
| |
| get_raw_data_all(info, MMS_VSC_CMD_EXIT); |
| info->ft_flag = 0; |
| |
| return snprintf(buf, TSP_BUF_SIZE, "%u\n", 0); |
| } |
| #endif /* sysfs function call */ |
| |
| static int check_rx_tx_num(void *device_data) |
| { |
| struct mms_ts_info *info = (struct mms_ts_info *)device_data; |
| |
| char buff[TSP_CMD_STR_LEN] = {0}; |
| int node; |
| |
| if (info->cmd_param[0] < 0 || |
| info->cmd_param[0] >= TX_NUM || |
| info->cmd_param[1] < 0 || |
| info->cmd_param[1] >= RX_NUM) { |
| snprintf(buff, sizeof(buff) , "%s", "NG"); |
| set_cmd_result(info, buff, strnlen(buff, sizeof(buff))); |
| info->cmd_state = 3; |
| |
| dev_info(&info->client->dev, "%s: parameter error: %u,%u\n", |
| __func__, info->cmd_param[0], |
| info->cmd_param[1]); |
| node = -1; |
| return node; |
| } |
| node = info->cmd_param[0] * RX_NUM + info->cmd_param[1]; |
| dev_info(&info->client->dev, "%s: node = %d\n", __func__, |
| node); |
| return node; |
| |
| } |
| |
| static void cover_set(struct mms_ts_info *info, int state){ |
| if(state == COVER_OPEN){ |
| dev_info(&info->client->dev, "[TSP] %s: opened\n", __func__); |
| i2c_smbus_write_byte_data(info->client, 0x34, 0x0); |
| |
| } else if(state == COVER_CLOSED) { |
| dev_info(&info->client->dev, "[TSP] %s: closed\n", __func__); |
| i2c_smbus_write_byte_data(info->client, 0x34, 0x1); |
| } |
| } |
| |
| static void clear_cover_mode(void *device_data) |
| { |
| struct mms_ts_info *info = (struct mms_ts_info *)device_data; |
| char buff[TSP_CMD_STR_LEN] = {0}; |
| int arg = info->cmd_param[0]; |
| |
| set_default_result(info); |
| snprintf(buff, sizeof(buff), "%u", arg); |
| |
| dev_info(&info->client->dev, "[TSP] %s: arg=%d\n", __func__,arg); |
| |
| info->cover_state = arg; |
| |
| if(info->enabled) |
| cover_set(info, arg); |
| |
| set_cmd_result(info, buff, strnlen(buff, sizeof(buff))); |
| |
| info->cmd_is_running = false; |
| |
| info->cmd_state = 2; |
| |
| return; |
| } |
| |
| static void not_support_cmd(void *device_data) |
| { |
| struct mms_ts_info *info = (struct mms_ts_info *)device_data; |
| char buff[16] = {0}; |
| |
| set_default_result(info); |
| sprintf(buff, "%s", "NA"); |
| set_cmd_result(info, buff, strnlen(buff, sizeof(buff))); |
| info->cmd_state = 4; |
| dev_info(&info->client->dev, "%s: \"%s(%d)\"\n", __func__, |
| buff, (int)strnlen(buff, sizeof(buff))); |
| return; |
| } |
| |
| static void fw_update(void *device_data) |
| { |
| struct mms_ts_info *info = (struct mms_ts_info *)device_data; |
| struct i2c_client *client = info->client; |
| struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); |
| int ret = 0; |
| int fw_ver = 0, ver = 0, hw_rev = 0, fw_bin_ver = 0; |
| int retries = 5; |
| const u8 *buff = 0; |
| mm_segment_t old_fs = {0}; |
| struct file *fp = NULL; |
| long fsize = 0, nread = 0; |
| char result_buff[16] = {0}; |
| |
| #define MMS_TS "/sdcard/melfas_fw.bin" |
| |
| set_default_result(info); |
| |
| hw_rev = get_hw_version(info); |
| if (hw_rev == 0x1 || hw_rev == 0x32) |
| fw_bin_ver = FW_VERSION; |
| else if (hw_rev == 0x0C) |
| fw_bin_ver = FW_465_VERSION; |
| |
| fw_ver = get_fw_version(info); |
| dev_info(&client->dev, |
| "fw_ic_ver = 0x%02x, fw_bin_ver = 0x%02x\n", |
| fw_ver, fw_bin_ver); |
| |
| if (info->cmd_param[0] == 0 |
| && fw_ver >= fw_bin_ver) { |
| dev_info(&client->dev, |
| "fw version update does not need\n"); |
| goto do_not_need_update; |
| } |
| |
| ret=mms_ts_fw_load(info); |
| if (ret){ |
| snprintf(result_buff, sizeof(result_buff), "FAIL"); |
| set_cmd_result(info, result_buff, strnlen(result_buff, sizeof(result_buff))); |
| info->cmd_state = 3; |
| return; |
| } |
| else { |
| snprintf(result_buff, sizeof(result_buff), "OK"); |
| set_cmd_result(info, result_buff, strnlen(result_buff, sizeof(result_buff))); |
| info->cmd_state = 2; |
| return; |
| } |
| switch (info->cmd_param[0]) { |
| case BUILT_IN: |
| if (hw_rev == 0x1 || hw_rev == 0x32) { |
| buff = MELFAS_binary; |
| fsize = MELFAS_binary_nLength; |
| dev_info(&client->dev, "built in 4.8 fw is loaded!!\n"); |
| } else if (hw_rev == 0x0C) { |
| buff = MELFAS_465_binary; |
| fsize = MELFAS_465_binary_nLength; |
| dev_info(&client->dev, "built in 4.65 fw is loaded!!\n"); |
| } |
| break; |
| |
| case UMS: |
| old_fs = get_fs(); |
| set_fs(get_ds()); |
| |
| fp = filp_open(MMS_TS, O_RDONLY, 0); |
| if (IS_ERR(fp)) { |
| dev_err(&client->dev, |
| "file %s open error\n", MMS_TS); |
| info->cmd_state = 3; |
| goto err_open; |
| } |
| |
| fsize = fp->f_path.dentry->d_inode->i_size; |
| |
| buff = kzalloc((size_t)fsize, GFP_KERNEL); |
| if (!buff) { |
| dev_err(&client->dev, "fail to alloc buffer for fw\n"); |
| info->cmd_state = 3; |
| goto err_alloc; |
| } |
| |
| nread = vfs_read(fp, (char __user *)buff, fsize, &fp->f_pos); |
| if (nread != fsize) { |
| dev_err(&client->dev, "fail to read file %s (nread = %ld)\n", |
| MMS_TS, nread); |
| info->cmd_state = 3; |
| goto err_fw_size; |
| } |
| |
| filp_close(fp, current->files); |
| set_fs(old_fs); |
| dev_info(&client->dev, "ums fw is loaded!!\n"); |
| break; |
| |
| default: |
| dev_err(&client->dev, "invalid fw file type!!\n"); |
| goto not_support; |
| } |
| |
| disable_irq(info->irq); |
| while (retries--) { |
| i2c_lock_adapter(adapter); |
| #if 0 /* firmware call removal */ |
| melfas_mux_fw_flash(info->pdata, true); |
| |
| ret = fw_download(info, (const u8 *)buff, |
| (const size_t)fsize); |
| |
| melfas_mux_fw_flash(info->pdata, false); |
| #endif /* firmware call removal */ |
| i2c_unlock_adapter(adapter); |
| |
| if (ret < 0) { |
| dev_err(&client->dev, "retrying flashing\n"); |
| continue; |
| } |
| |
| ver = get_fw_version(info); |
| info->fw_ic_ver = ver; |
| |
| if (info->cmd_param[0] == 1) { |
| dev_info(&client->dev, |
| "fw update done. ver = 0x%02x\n", ver); |
| info->cmd_state = 2; |
| enable_irq(info->irq); |
| return; |
| } else if (ver == fw_bin_ver) { |
| dev_info(&client->dev, |
| "fw update done. ver = 0x%02x\n", ver); |
| info->cmd_state = 2; |
| enable_irq(info->irq); |
| return; |
| } else { |
| dev_err(&client->dev, |
| "ERROR : fw version is still wrong (0x%x != 0x%x)\n", |
| ver, FW_VERSION); |
| } |
| dev_err(&client->dev, "retrying flashing\n"); |
| } |
| |
| err_fw_size: |
| kfree(buff); |
| err_alloc: |
| filp_close(fp, NULL); |
| err_open: |
| set_fs(old_fs); |
| not_support: |
| do_not_need_update: |
| info->cmd_state = 2; |
| return; |
| } |
| |
| static void get_fw_ver_bin(void *device_data) |
| { |
| struct mms_ts_info *info = (struct mms_ts_info *)device_data; |
| |
| char buff[16] = {0}; |
| int hw_rev; |
| |
| set_default_result(info); |
| hw_rev = get_hw_version(info); |
| if (hw_rev == 0x01) |
| snprintf(buff, sizeof(buff), "ME0000%x", FW_VERSION); |
| else |
| snprintf(buff, sizeof(buff), "ME0000%x", FW_465_VERSION); |
| |
| set_cmd_result(info, buff, strnlen(buff, sizeof(buff))); |
| info->cmd_state = 2; |
| dev_info(&info->client->dev, "%s: %s(%d)\n", __func__, |
| buff, (int)strnlen(buff, sizeof(buff))); |
| } |
| |
| static void get_fw_ver_ic(void *device_data) |
| { |
| struct mms_ts_info *info = (struct mms_ts_info *)device_data; |
| |
| char buff[16] = {0}; |
| int ver; |
| |
| set_default_result(info); |
| |
| ver = get_fw_version(info); |
| |
| snprintf(buff, sizeof(buff), "ME0000%x", ver); |
| |
| set_cmd_result(info, buff, strnlen(buff, sizeof(buff))); |
| info->cmd_state = 2; |
| dev_info(&info->client->dev, "%s: %s(%d)\n", __func__, |
| buff, (int)strnlen(buff, sizeof(buff))); |
| } |
| |
| static void get_config_ver(void *device_data) |
| { |
| struct mms_ts_info *info = (struct mms_ts_info *)device_data; |
| |
| char buff[20] = {0}; |
| |
| set_default_result(info); |
| |
| // snprintf(buff, sizeof(buff), "%s", info->config_fw_version); |
| snprintf(buff, sizeof(buff), "N/A"); |
| set_cmd_result(info, buff, strnlen(buff, sizeof(buff))); |
| info->cmd_state = 2; |
| dev_info(&info->client->dev, "%s: %s(%d)\n", __func__, |
| buff, (int)strnlen(buff, sizeof(buff))); |
| } |
| |
| static void get_threshold(void *device_data) |
| { |
| struct mms_ts_info *info = (struct mms_ts_info *)device_data; |
| |
| char buff[16] = {0}; |
| int threshold; |
| |
| set_default_result(info); |
| |
| threshold = i2c_smbus_read_byte_data(info->client, 0x05); |
| if (threshold < 0) { |
| snprintf(buff, sizeof(buff), "%s", "NG"); |
| set_cmd_result(info, buff, (int)strnlen(buff, sizeof(buff))); |
| info->cmd_state = 3; |
| return; |
| } |
| snprintf(buff, sizeof(buff), "%d", threshold); |
| |
| set_cmd_result(info, buff, strnlen(buff, sizeof(buff))); |
| info->cmd_state = 2; |
| dev_info(&info->client->dev, "%s: %s(%d)\n", __func__, |
| buff, (int)strnlen(buff, sizeof(buff))); |
| } |
| |
| static void module_off_master(void *device_data) |
| { |
| struct mms_ts_info *info = (struct mms_ts_info *)device_data; |
| |
| char buff[3] = {0}; |
| |
| //mutex_lock(&info->lock); |
| if (info->enabled) { |
| disable_irq(info->irq); |
| info->enabled = false; |
| touch_is_pressed = 0; |
| } |
| //mutex_unlock(&info->lock); |
| |
| melfas_vdd_on(info, 0); |
| |
| if (is_melfas_vdd_on(info) == 0) |
| snprintf(buff, sizeof(buff), "%s", "OK"); |
| else |
| snprintf(buff, sizeof(buff), "%s", "NG"); |
| |
| set_default_result(info); |
| set_cmd_result(info, buff, strnlen(buff, sizeof(buff))); |
| |
| if (strncmp(buff, "OK", 2) == 0) |
| info->cmd_state = 2; |
| else |
| info->cmd_state = 3; |
| |
| dev_info(&info->client->dev, "%s: %s\n", __func__, buff); |
| } |
| |
| static void module_on_master(void *device_data) |
| { |
| struct mms_ts_info *info = (struct mms_ts_info *)device_data; |
| |
| char buff[3] = {0}; |
| |
| mms_pwr_on_reset(info); |
| |
| //mutex_lock(&info->lock); |
| if (!info->enabled) { |
| enable_irq(info->irq); |
| info->enabled = true; |
| } |
| //mutex_unlock(&info->lock); |
| |
| if (is_melfas_vdd_on(info) == 1) |
| snprintf(buff, sizeof(buff), "%s", "OK"); |
| else |
| snprintf(buff, sizeof(buff), "%s", "NG"); |
| |
| set_default_result(info); |
| set_cmd_result(info, buff, strnlen(buff, sizeof(buff))); |
| |
| if (strncmp(buff, "OK", 2) == 0) |
| info->cmd_state = 2; |
| else |
| info->cmd_state = 3; |
| |
| dev_info(&info->client->dev, "%s: %s\n", __func__, buff); |
| |
| } |
| |
| static void get_chip_vendor(void *device_data) |
| { |
| struct mms_ts_info *info = (struct mms_ts_info *)device_data; |
| |
| char buff[16] = {0}; |
| |
| set_default_result(info); |
| |
| snprintf(buff, sizeof(buff), "%s", "MELFAS"); |
| set_cmd_result(info, buff, strnlen(buff, sizeof(buff))); |
| info->cmd_state = 2; |
| dev_info(&info->client->dev, "%s: %s(%d)\n", __func__, |
| buff, (int)strnlen(buff, sizeof(buff))); |
| } |
| |
| static void get_chip_name(void *device_data) |
| { |
| struct mms_ts_info *info = (struct mms_ts_info *)device_data; |
| |
| char buff[16] = {0}; |
| |
| set_default_result(info); |
| |
| snprintf(buff, sizeof(buff), "%s", "MMS144"); |
| set_cmd_result(info, buff, strnlen(buff, sizeof(buff))); |
| info->cmd_state = 2; |
| dev_info(&info->client->dev, "%s: %s(%d)\n", __func__, |
| buff, (int)strnlen(buff, sizeof(buff))); |
| } |
| |
| static void get_reference(void *device_data) |
| { |
| struct mms_ts_info *info = (struct mms_ts_info *)device_data; |
| |
| char buff[16] = {0}; |
| unsigned int val; |
| int node; |
| |
| set_default_result(info); |
| node = check_rx_tx_num(info); |
| |
| if (node < 0) |
| return; |
| |
| val = info->reference[node]; |
| snprintf(buff, sizeof(buff), "%u", val); |
| set_cmd_result(info, buff, strnlen(buff, sizeof(buff))); |
| |
| info->cmd_state = 2; |
| |
| dev_info(&info->client->dev, "%s: %s(%d)\n", __func__, |
| buff, (int)strnlen(buff, sizeof(buff))); |
| |
| } |
| |
| static void get_cm_abs(void *device_data) |
| { |
| struct mms_ts_info *info = (struct mms_ts_info *)device_data; |
| |
| char buff[16] = {0}; |
| unsigned int val; |
| int node; |
| |
| set_default_result(info); |
| node = check_rx_tx_num(info); |
| |
| if (node < 0) |
| return; |
| |
| val = info->raw[node]; |
| snprintf(buff, sizeof(buff), "%u", val); |
| set_cmd_result(info, buff, strnlen(buff, sizeof(buff))); |
| info->cmd_state = 2; |
| |
| dev_info(&info->client->dev, "%s: %s(%d)\n", __func__, buff, |
| (int)strnlen(buff, sizeof(buff))); |
| } |
| |
| static void get_cm_delta(void *device_data) |
| { |
| struct mms_ts_info *info = (struct mms_ts_info *)device_data; |
| |
| char buff[16] = {0}; |
| unsigned int val; |
| int node; |
| |
| set_default_result(info); |
| node = check_rx_tx_num(info); |
| |
| if (node < 0) |
| return; |
| |
| val = info->inspection[node]; |
| snprintf(buff, sizeof(buff), "%u", val); |
| set_cmd_result(info, buff, strnlen(buff, sizeof(buff))); |
| info->cmd_state = 2; |
| |
| dev_info(&info->client->dev, "%s: %s(%d)\n", __func__, buff, |
| (int)strnlen(buff, sizeof(buff))); |
| } |
| |
| static void get_intensity(void *device_data) |
| { |
| struct mms_ts_info *info = (struct mms_ts_info *)device_data; |
| |
| char buff[16] = {0}; |
| unsigned int val; |
| int node; |
| |
| set_default_result(info); |
| node = check_rx_tx_num(info); |
| |
| if (node < 0) |
| return; |
| |
| val = info->intensity[node]; |
| |
| snprintf(buff, sizeof(buff), "%u", val); |
| set_cmd_result(info, buff, strnlen(buff, sizeof(buff))); |
| info->cmd_state = 2; |
| |
| dev_info(&info->client->dev, "%s: %s(%d)\n", __func__, buff, |
| (int)strnlen(buff, sizeof(buff))); |
| } |
| |
| static void get_x_num(void *device_data) |
| { |
| struct mms_ts_info *info = (struct mms_ts_info *)device_data; |
| |
| char buff[16] = {0}; |
| int val; |
| |
| set_default_result(info); |
| val = i2c_smbus_read_byte_data(info->client, 0xEF); |
| if (val < 0) { |
| snprintf(buff, sizeof(buff), "%s", "NG"); |
| set_cmd_result(info, buff, strnlen(buff, sizeof(buff))); |
| info->cmd_state = 3; |
| |
| dev_info(&info->client->dev, |
| "%s: fail to read num of x (%d).\n", __func__, val); |
| |
| return ; |
| } |
| snprintf(buff, sizeof(buff), "%u", val); |
| set_cmd_result(info, buff, strnlen(buff, sizeof(buff))); |
| info->cmd_state = 2; |
| |
| dev_info(&info->client->dev, "%s: %s(%d)\n", __func__, buff, |
| (int)strnlen(buff, sizeof(buff))); |
| } |
| |
| static void get_y_num(void *device_data) |
| { |
| struct mms_ts_info *info = (struct mms_ts_info *)device_data; |
| |
| char buff[16] = {0}; |
| int val; |
| |
| set_default_result(info); |
| val = i2c_smbus_read_byte_data(info->client, 0xEE); |
| if (val < 0) { |
| snprintf(buff, sizeof(buff), "%s", "NG"); |
| set_cmd_result(info, buff, strnlen(buff, sizeof(buff))); |
| info->cmd_state = 3; |
| |
| dev_info(&info->client->dev, |
| "%s: fail to read num of y (%d).\n", __func__, val); |
| |
| return ; |
| } |
| snprintf(buff, sizeof(buff), "%u", val); |
| set_cmd_result(info, buff, strnlen(buff, sizeof(buff))); |
| info->cmd_state = 2; |
| |
| dev_info(&info->client->dev, "%s: %s(%d)\n", __func__, buff, |
| (int)strnlen(buff, sizeof(buff))); |
| } |
| |
| static void run_reference_read(void *device_data) |
| { |
| struct mms_ts_info *info = (struct mms_ts_info *)device_data; |
| |
| set_default_result(info); |
| get_raw_data_all(info, MMS_VSC_CMD_REFER); |
| info->cmd_state = 2; |
| |
| dev_info(&info->client->dev, "%s\n", __func__); |
| } |
| |
| static void run_cm_abs_read(void *device_data) |
| { |
| struct mms_ts_info *info = (struct mms_ts_info *)device_data; |
| |
| set_default_result(info); |
| get_raw_data_all(info, MMS_VSC_CMD_CM_ABS); |
| get_raw_data_all(info, MMS_VSC_CMD_EXIT); |
| info->cmd_state = 2; |
| |
| dev_info(&info->client->dev, "%s\n", __func__); |
| } |
| |
| static void run_cm_delta_read(void *device_data) |
| { |
| struct mms_ts_info *info = (struct mms_ts_info *)device_data; |
| |
| set_default_result(info); |
| get_raw_data_all(info, MMS_VSC_CMD_CM_DELTA); |
| get_raw_data_all(info, MMS_VSC_CMD_EXIT); |
| info->cmd_state = 2; |
| |
| dev_info(&info->client->dev, "%s\n", __func__); |
| } |
| |
| static void run_intensity_read(void *device_data) |
| { |
| struct mms_ts_info *info = (struct mms_ts_info *)device_data; |
| |
| set_default_result(info); |
| get_raw_data_all(info, MMS_VSC_CMD_INTENSITY); |
| info->cmd_state = 2; |
| |
| dev_info(&info->client->dev, "%s\n", __func__); |
| } |
| |
| #if 0 /* sysfs device function */ |
| static ssize_t store_cmd(struct device *dev, struct device_attribute |
| *devattr, const char *buf, size_t count) |
| { |
| struct mms_ts_info *info = dev_get_drvdata(dev); |
| struct i2c_client *client = info->client; |
| |
| 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 (info->cmd_is_running == true) { |
| dev_err(&info->client->dev, "tsp_cmd: other cmd is running.\n"); |
| goto err_out; |
| } |
| |
| |
| /* check lock */ |
| mutex_lock(&info->cmd_lock); |
| info->cmd_is_running = true; |
| mutex_unlock(&info->cmd_lock); |
| |
| info->cmd_state = 1; |
| |
| for (i = 0; i < ARRAY_SIZE(info->cmd_param); i++) |
| info->cmd_param[i] = 0; |
| |
| len = (int)count; |
| if (*(buf + len - 1) == '\n') |
| len--; |
| memset(info->cmd, 0x00, ARRAY_SIZE(info->cmd)); |
| memcpy(info->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, &info->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, &info->cmd_list_head, list) { |
| if (!strcmp("not_support_cmd", tsp_cmd_ptr->cmd_name)) |
| break; |
| } |
| } |
| |
| /* parsing parameters */ |
| if (cur && cmd_found) { |
| cur++; |
| start = cur; |
| memset(buff, 0x00, ARRAY_SIZE(buff)); |
| do { |
| if (*cur == delim || cur - buf == len) { |
| end = cur; |
| memcpy(buff, start, end - start); |
| *(buff + strlen(buff)) = '\0'; |
| if (kstrtoint(buff, 10, |
| info->cmd_param + param_cnt) < 0) |
| goto err_out; |
| start = cur + 1; |
| memset(buff, 0x00, ARRAY_SIZE(buff)); |
| param_cnt++; |
| } |
| cur++; |
| } while (cur - buf <= len); |
| } |
| |
| dev_info(&client->dev, "cmd = %s\n", tsp_cmd_ptr->cmd_name); |
| for (i = 0; i < param_cnt; i++) |
| dev_info(&client->dev, "cmd param %d= %d\n", i, |
| info->cmd_param[i]); |
| |
| tsp_cmd_ptr->cmd_func(info); |
| |
| |
| err_out: |
| return count; |
| } |
| |
| static ssize_t show_cmd_status(struct device *dev, |
| struct device_attribute *devattr, char *buf) |
| { |
| struct mms_ts_info *info = dev_get_drvdata(dev); |
| char buff[16] = {0}; |
| |
| dev_info(&info->client->dev, "tsp cmd: status:%d\n", |
| info->cmd_state); |
| |
| if (info->cmd_state == 0) |
| snprintf(buff, sizeof(buff), "WAITING"); |
| |
| else if (info->cmd_state == 1) |
| snprintf(buff, sizeof(buff), "RUNNING"); |
| |
| else if (info->cmd_state == 2) |
| snprintf(buff, sizeof(buff), "OK"); |
| |
| else if (info->cmd_state == 3) |
| snprintf(buff, sizeof(buff), "FAIL"); |
| |
| else if (info->cmd_state == 4) |
| snprintf(buff, sizeof(buff), "NOT_APPLICABLE"); |
| |
| return snprintf(buf, TSP_BUF_SIZE, "%s\n", buff); |
| } |
| |
| static ssize_t show_cmd_result(struct device *dev, struct device_attribute |
| *devattr, char *buf) |
| { |
| struct mms_ts_info *info = dev_get_drvdata(dev); |
| |
| dev_info(&info->client->dev, "tsp cmd: result: %s\n", info->cmd_result); |
| |
| mutex_lock(&info->cmd_lock); |
| info->cmd_is_running = false; |
| mutex_unlock(&info->cmd_lock); |
| |
| info->cmd_state = 0; |
| |
| return snprintf(buf, TSP_BUF_SIZE, "%s\n", info->cmd_result); |
| } |
| #endif /* sysfs device function */ |
| |
| #ifdef ESD_DEBUG |
| |
| static bool intensity_log_flag; |
| |
| static ssize_t show_intensity_logging_on(struct device *dev, |
| struct device_attribute *devattr, char *buf) |
| { |
| struct mms_ts_info *info = dev_get_drvdata(dev); |
| struct i2c_client *client = info->client; |
| struct file *fp; |
| char log_data[160] = {0,}; |
| char buff[16] = {0,}; |
| mm_segment_t old_fs; |
| long nwrite; |
| u32 val; |
| int i, y, c; |
| |
| old_fs = get_fs(); |
| set_fs(KERNEL_DS); |
| |
| #define MELFAS_DEBUG_LOG_PATH "/sdcard/melfas_log" |
| |
| dev_info(&client->dev, "%s: start.\n", __func__); |
| fp = filp_open(MELFAS_DEBUG_LOG_PATH, O_RDWR|O_CREAT, |
| S_IRWXU|S_IRWXG|S_IRWXO); |
| if (IS_ERR(fp)) { |
| dev_err(&client->dev, "%s: fail to open log file\n", __func__); |
| goto open_err; |
| } |
| |
| intensity_log_flag = 1; |
| do { |
| for (y = 0; y < 3; y++) { |
| /* for tx chanel 0~2 */ |
| memset(log_data, 0x00, 160); |
| |
| snprintf(buff, 16, "%1u: ", y); |
| strncat(log_data, buff, strnlen(buff, 16)); |
| |
| for (i = 0; i < RX_NUM; i++) { |
| val = get_raw_data_one(info, i, y, |
| MMS_VSC_CMD_INTENSITY); |
| snprintf(buff, 16, "%5u, ", val); |
| strncat(log_data, buff, strnlen(buff, 16)); |
| } |
| memset(buff, '\n', 2); |
| c = (y == 2) ? 2 : 1; |
| strncat(log_data, buff, c); |
| nwrite = vfs_write(fp, (const char __user *)log_data, |
| strnlen(log_data, 160), &fp->f_pos); |
| } |
| usleep_range(5000); |
| } while (intensity_log_flag); |
| |
| filp_close(fp, current->files); |
| set_fs(old_fs); |
| |
| return 0; |
| |
| open_err: |
| set_fs(old_fs); |
| return FAIL; |
| } |
| |
| static ssize_t show_intensity_logging_off(struct device *dev, |
| struct device_attribute *devattr, char *buf) |
| { |
| struct mms_ts_info *info = dev_get_drvdata(dev); |
| intensity_log_flag = 0; |
| usleep_range(10000); |
| get_raw_data_all(info, MMS_VSC_CMD_EXIT); |
| return 0; |
| } |
| |
| #endif |
| |
| #if 0 /* ignore sysfs device creation, for now */ |
| static DEVICE_ATTR(close_tsp_test, S_IRUGO, show_close_tsp_test, NULL); |
| static DEVICE_ATTR(cmd, S_IWUSR | S_IWGRP, NULL, store_cmd); |
| static DEVICE_ATTR(cmd_status, S_IRUGO, show_cmd_status, NULL); |
| static DEVICE_ATTR(cmd_result, S_IRUGO, show_cmd_result, NULL); |
| #ifdef ESD_DEBUG |
| static DEVICE_ATTR(intensity_logging_on, S_IRUGO, show_intensity_logging_on, |
| NULL); |
| static DEVICE_ATTR(intensity_logging_off, S_IRUGO, show_intensity_logging_off, |
| NULL); |
| #endif |
| |
| static struct attribute *sec_touch_facotry_attributes[] = { |
| &dev_attr_close_tsp_test.attr, |
| &dev_attr_cmd.attr, |
| &dev_attr_cmd_status.attr, |
| &dev_attr_cmd_result.attr, |
| #ifdef ESD_DEBUG |
| &dev_attr_intensity_logging_on.attr, |
| &dev_attr_intensity_logging_off.attr, |
| #endif |
| NULL, |
| }; |
| |
| static struct attribute_group sec_touch_factory_attr_group = { |
| .attrs = sec_touch_facotry_attributes, |
| }; |
| #endif /* ignore sysfs device creation, for now */ |
| #endif /* SEC_TSP_FACTORY_TEST */ |
| |
| #ifdef CONFIG_OF |
| static void melfas_request_gpio(struct mms_ts_platform_data *pdata) |
| { |
| int ret; |
| pr_info("[TSP] request gpio\n"); |
| |
| ret = gpio_request(pdata->gpio_scl, "melfas_tsp_scl"); |
| if (ret) { |
| pr_err("[TSP]%s: unable to request melfas_tsp_scl [%d]\n", |
| __func__, pdata->gpio_scl); |
| return; |
| } |
| |
| ret = gpio_request(pdata->gpio_sda, "melfas_tsp_sda"); |
| if (ret) { |
| pr_err("[TSP]%s: unable to request melfas_tsp_sda [%d]\n", |
| __func__, pdata->gpio_sda); |
| return; |
| } |
| |
| ret = gpio_request(pdata->gpio_int, "melfas_tsp_irq"); |
| if (ret) { |
| pr_err("[TSP]%s: unable to request melfas_tsp_irq [%d]\n", |
| __func__, pdata->gpio_int); |
| return; |
| } |
| |
| /* |
| pr_err("Requesting VDD GPIO : %d\n", pdata->vdd_en); |
| ret = gpio_request(pdata->vdd_en, "melfas_vdd_en"); |
| if (ret) { |
| pr_err("[TSP]%s: unable to request melfas_vdd_en [%d]\n", |
| __func__, pdata->vdd_en); |
| return; |
| } |
| */ |
| } |
| |
| |
| |
| static int mms_get_dt_coords(struct device *dev, char *name, |
| struct mms_ts_platform_data *pdata) |
| { |
| u32 coords[MMS_COORDS_ARR_SIZE]; |
| struct property *prop; |
| struct device_node *np = dev->of_node; |
| int coords_size, rc; |
| |
| prop = of_find_property(np, name, NULL); |
| if (!prop) |
| return -EINVAL; |
| if (!prop->value) |
| return -ENODATA; |
| |
| coords_size = prop->length / sizeof(u32); |
| if (coords_size != MMS_COORDS_ARR_SIZE) { |
| dev_err(dev, "invalid %s\n", name); |
| return -EINVAL; |
| } |
| |
| rc = of_property_read_u32_array(np, name, coords, coords_size); |
| if (rc && (rc != -EINVAL)) { |
| dev_err(dev, "Unable to read %s\n", name); |
| return rc; |
| } |
| |
| if (strncmp(name, "melfas,panel-coords", |
| sizeof("melfas,panel-coords")) == 0) { |
| pdata->invert_x = coords[0]; |
| pdata->invert_y = coords[1]; |
| pdata->max_x = coords[2]; |
| pdata->max_y = coords[3]; |
| } else { |
| dev_err(dev, "unsupported property %s\n", name); |
| return -EINVAL; |
| } |
| |
| return 0; |
| } |
| |
| static int mms_parse_dt(struct device *dev, |
| struct mms_ts_platform_data *pdata) |
| { |
| int rc; |
| struct device_node *np = dev->of_node; |
| |
| rc = mms_get_dt_coords(dev, "melfas,panel-coords", pdata); |
| if (rc) |
| return rc; |
| |
| /* regulator info */ |
| pdata->i2c_pull_up = of_property_read_bool(np, "melfas,i2c-pull-up"); |
| //pdata->vdd_en = of_get_named_gpio(np, "vdd_en-gpio", 0); |
| pdata->vdd_en_reg = regulator_get(dev, "vdd_ldo19"); |
| if (pdata->vdd_en_reg == NULL) { |
| pr_err("ERROR: Cannot find vdd_ldo19\n"); |
| } |
| |
| /* reset, irq gpio info */ |
| pdata->gpio_scl = of_get_named_gpio_flags(np, "melfas,scl-gpio", |
| 0, &pdata->scl_gpio_flags); |
| pdata->gpio_sda = of_get_named_gpio_flags(np, "melfas,sda-gpio", |
| 0, &pdata->sda_gpio_flags); |
| pdata->gpio_int = of_get_named_gpio_flags(np, "melfas,irq-gpio", |
| 0, &pdata->irq_gpio_flags); |
| pdata->config_fw_version = of_get_property(np, |
| "melfas,config_fw_version", NULL); |
| return 0; |
| } |
| #else |
| static int mms_parse_dt(struct device *dev, |
| struct mms_ts_platform_data *pdata) |
| { |
| return -ENODEV; |
| } |
| #endif |
| |
| #ifdef W1_DUMMY |
| static struct device w1_dev = { |
| .init_name = "w1_bus_master1", |
| }; |
| |
| static ssize_t w1_show(struct device *dev, |
| struct device_attribute *devattr, char *buf) |
| { |
| return snprintf(buf, 5, "1\n"); |
| } |
| |
| static DEVICE_ATTR(w1_master_check_id, S_IRUGO, w1_show, NULL); |
| #endif |
| |
| //static int __devinit mms_ts_probe(struct i2c_client *client, |
| int __devinit mms_ts_probe(struct i2c_client *client, |
| const struct i2c_device_id *id) |
| { |
| struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); |
| struct mms_ts_platform_data *pdata; |
| struct mms_ts_info *info; |
| struct input_dev *input_dev; |
| int ret = 0; |
| // int val1 = 0, val2 = 0; |
| // int fw_core = 0, fw_boot = 0, fw_config = 0; |
| // char buf[4] = { 0, }; |
| int error; |
| |
| #ifdef SEC_TSP_FACTORY_TEST |
| int i; |
| #if 0 /* ignore sysfs device creation, for now */ |
| struct device *fac_dev_ts; |
| #endif /* ignore sysfs device creation, for now */ |
| #endif |
| touch_is_pressed = 0; |
| printk(KERN_ERR "%s: [TSP] probe START!!\n", __func__); |
| |
| if (!i2c_check_functionality(adapter, I2C_FUNC_I2C)) |
| return -EIO; |
| |
| #ifdef CONFIG_OF |
| if (client->dev.of_node) { |
| pdata = devm_kzalloc(&client->dev, |
| sizeof(struct mms_ts_platform_data), GFP_KERNEL); |
| if (!pdata) { |
| dev_err(&client->dev, "Failed to allocate memory\n"); |
| return -ENOMEM; |
| } |
| |
| error = mms_parse_dt(&client->dev, pdata); |
| if (error) |
| return error; |
| } else |
| pdata = client->dev.platform_data; |
| |
| if (!pdata) |
| return -EINVAL; |
| |
| melfas_request_gpio(pdata); |
| #endif |
| info = kzalloc(sizeof(struct mms_ts_info), GFP_KERNEL); |
| if (!info) { |
| dev_err(&client->dev, "Failed to allocate memory\n"); |
| ret = -ENOMEM; |
| goto err_alloc; |
| } |
| |
| input_dev = input_allocate_device(); |
| if (!input_dev) { |
| dev_err(&client->dev, "Failed to allocate memory for input device\n"); |
| ret = -ENOMEM; |
| goto err_input_alloc; |
| } |
| |
| info->client = client; |
| info->input_dev = input_dev; |
| // info->pdata = client->dev.platform_data; |
| info->pdata = pdata; |
| |
| if (NULL == info->pdata) { |
| pr_err("failed to get platform data\n"); |
| goto err_config; |
| } |
| |
| |
| info->irq = -1; |
| //mutex_init(&info->lock); |
| if (info->pdata) { |
| info->max_x = info->pdata->max_x; |
| info->max_y = info->pdata->max_y; |
| info->invert_x = info->pdata->invert_x; |
| info->invert_y = info->pdata->invert_y; |
| info->config_fw_version = info->pdata->config_fw_version; |
| info->register_cb = melfas_register_callback; |
| } else { |
| info->max_x = 720; |
| info->max_y = 1280; |
| } |
| |
| melfas_vdd_on(info, 1); |
| msleep(100); |
| i2c_set_clientdata(client, info); |
| |
| info->callbacks.inform_charger = melfas_ta_cb; |
| if (info->register_cb) |
| info->register_cb(&info->callbacks); |
| |
| input_mt_init_slots(input_dev, MAX_FINGERS, 0); |
| /* |
| snprintf(info->phys, sizeof(info->phys), |
| "%s/input0", dev_name(&client->dev)); |
| */ |
| input_dev->name = "sec_touchscreen"; /*= "Melfas MMSxxx Touchscreen";*/ |
| input_dev->phys = info->phys; |
| input_dev->id.bustype = BUS_I2C; |
| input_dev->dev.parent = &client->dev; |
| __set_bit(EV_ABS, input_dev->evbit); |
| __set_bit(INPUT_PROP_DIRECT, input_dev->propbit); |
| input_set_abs_params(input_dev, ABS_MT_WIDTH_MAJOR, 0, MAX_WIDTH, 0, 0); |
| input_set_abs_params(input_dev, ABS_MT_POSITION_X, |
| 0, info->max_x, 0, 0); |
| input_set_abs_params(input_dev, ABS_MT_POSITION_Y, |
| 0, info->max_y, 0, 0); |
| input_set_abs_params(info->input_dev, ABS_MT_TOUCH_MAJOR, |
| 0, MAX_PRESSURE, 0, 0); |
| input_set_abs_params(info->input_dev, ABS_MT_TOUCH_MINOR, |
| 0, MAX_PRESSURE, 0, 0); |
| // input_set_abs_params(input_dev, ABS_MT_ANGLE, |
| // MIN_ANGLE, MAX_ANGLE, 0, 0); |
| input_set_abs_params(input_dev, ABS_MT_PALM, |
| 0, 1, 0, 0); |
| input_set_drvdata(input_dev, info); |
| |
| ret = input_register_device(input_dev); |
| if (ret) { |
| dev_err(&client->dev, "failed to register input dev (%d)\n", |
| ret); |
| goto err_reg_input_dev; |
| } |
| |
| #ifdef TOUCH_BOOSTER |
| #if TOUCH_BOOSTER |
| mutex_init(&info->dvfs_lock); |
| INIT_DELAYED_WORK(&info->work_dvfs_off, set_dvfs_off); |
| INIT_DELAYED_WORK(&info->work_dvfs_chg, change_dvfs_lock); |
| info->dvfs_lock_status = false; |
| #endif |
| #endif |
| |
| client->irq = gpio_to_irq(pdata->gpio_int); |
| #if ISC_DL_MODE |
| ret = mms_ts_fw_load(info); |
| #else |
| ret = mms_ts_fw_info(info); |
| #endif |
| if (ret) |
| dev_err(&client->dev, "failed to check the firmware"); |
| |
| info->enabled = true; |
| |
| printk(KERN_ERR "%s: tsp : gpio_to_irq : %d\n", __func__, client->irq); |
| |
| ret = request_threaded_irq(client->irq, NULL, mms_ts_interrupt, |
| IRQF_TRIGGER_LOW | IRQF_ONESHOT, |
| MELFAS_TS_NAME, info); |
| if (ret < 0) { |
| dev_err(&client->dev, "%s:Failed to register interrupt, ret = %d\n", |
| __func__, ret); |
| goto err_req_irq; |
| } |
| info->irq = client->irq; |
| |
| #ifdef CONFIG_HAS_EARLYSUSPEND |
| info->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1; |
| info->early_suspend.suspend = mms_ts_early_suspend; |
| info->early_suspend.resume = mms_ts_late_resume; |
| register_early_suspend(&info->early_suspend); |
| #endif |
| |
| #ifdef USE_OPEN_CLOSE |
| input_dev->open = melfas_ts_open; |
| input_dev->close = melfas_ts_close; |
| #endif |
| |
| #ifdef SEC_TSP_FACTORY_TEST |
| INIT_LIST_HEAD(&info->cmd_list_head); |
| for (i = 0; i < ARRAY_SIZE(tsp_cmds); i++) |
| list_add_tail(&tsp_cmds[i].list, &info->cmd_list_head); |
| |
| mutex_init(&info->cmd_lock); |
| info->cmd_is_running = false; |
| #if 0 /* ignore sysfs device creation, for now */ |
| fac_dev_ts = device_create(sec_class, |
| NULL, 0, info, "tsp"); |
| if (IS_ERR(fac_dev_ts)) |
| dev_err(&client->dev, "Failed to create device for the sysfs\n"); |
| |
| ret = sysfs_create_group(&fac_dev_ts->kobj, |
| &sec_touch_factory_attr_group); |
| if (ret) |
| dev_err(&client->dev, "Failed to create sysfs group\n"); |
| #endif /* ignore sysfs device creation, for now */ |
| #endif |
| |
| #ifdef W1_DUMMY |
| ret = device_register(&w1_dev); |
| if(ret){ |
| pr_err("[W1] error register dummy w1 device\n"); |
| goto w1_out; |
| } |
| ret = sysfs_create_file(&w1_dev.kobj, &dev_attr_w1_master_check_id.attr); |
| if(ret) |
| pr_err("[W1] couldn't create sysfs\n"); |
| |
| pr_info("[W1] created dummy w1 sysfs\n"); |
| w1_out: |
| #endif |
| |
| printk(KERN_ERR "%s: [TSP] probe END!!\n", __func__); |
| |
| return 0; |
| |
| err_req_irq: |
| input_unregister_device(input_dev); |
| err_reg_input_dev: |
| err_config: |
| input_free_device(input_dev); |
| err_input_alloc: |
| kfree(info->fw_name); |
| kfree(info); |
| err_alloc: |
| return ret; |
| } |
| |
| static int __devexit mms_ts_remove(struct i2c_client *client) |
| { |
| struct mms_ts_info *info = i2c_get_clientdata(client); |
| |
| if (info->irq >= 0) |
| free_irq(info->irq, info); |
| input_unregister_device(info->input_dev); |
| kfree(info->fw_name); |
| kfree(info); |
| |
| return 0; |
| } |
| |
| #if defined(CONFIG_PM) || defined(CONFIG_HAS_EARLYSUSPEND) || defined(USE_OPEN_CLOSE) |
| static int mms_ts_suspend(struct device *dev) |
| { |
| struct i2c_client *client = to_i2c_client(dev); |
| struct mms_ts_info *info = i2c_get_clientdata(client); |
| //#ifdef SEC_TSP_DEBUG |
| // int mt_val; |
| //#endif |
| |
| dev_notice(&info->client->dev, "%s: users=%d\n", __func__, |
| info->input_dev->users); |
| // mutex_lock(&info->input_dev->mutex); |
| // if (!info->input_dev->users) |
| // goto out; |
| |
| mms_ts_disable(info, 0); |
| touch_is_pressed = 0; |
| release_all_fingers(info); |
| melfas_vdd_on(info, 0); |
| msleep(50); |
| |
| //out: |
| // mutex_unlock(&info->input_dev->mutex); |
| return 0; |
| } |
| |
| static int mms_ts_resume(struct device *dev) |
| { |
| struct i2c_client *client = to_i2c_client(dev); |
| struct mms_ts_info *info = i2c_get_clientdata(client); |
| int ret = 0; |
| |
| dev_notice(&info->client->dev, "%s: users=%d\n", __func__, |
| info->input_dev->users); |
| melfas_vdd_on(info, 1); |
| msleep(50); |
| |
| if (info->ta_status) { |
| dev_notice(&client->dev, "TA connect!!!\n"); |
| i2c_smbus_write_byte_data(info->client, 0x33, 0x1); |
| } else { |
| dev_notice(&client->dev, "TA disconnect!!!\n"); |
| i2c_smbus_write_byte_data(info->client, 0x33, 0x2); |
| } |
| |
| mms_set_noise_mode(info); |
| // mutex_lock(&info->input_dev->mutex); |
| // if (info->input_dev->users) |
| #ifdef SEC_TSP_FACTORY_TEST |
| cover_set(info, info->cover_state); |
| #endif |
| ret = mms_ts_enable(info, 0); |
| // mutex_unlock(&info->input_dev->mutex); |
| |
| return ret; |
| } |
| #endif |
| |
| #ifdef CONFIG_HAS_EARLYSUSPEND |
| static void mms_ts_early_suspend(struct early_suspend *h) |
| { |
| struct mms_ts_info *info; |
| info = container_of(h, struct mms_ts_info, early_suspend); |
| mms_ts_suspend(&info->client->dev); |
| } |
| |
| static void mms_ts_late_resume(struct early_suspend *h) |
| { |
| struct mms_ts_info *info; |
| info = container_of(h, struct mms_ts_info, early_suspend); |
| mms_ts_resume(&info->client->dev); |
| } |
| #endif |
| |
| #ifdef USE_OPEN_CLOSE |
| static void melfas_ts_close(struct input_dev *dev) |
| { |
| struct mms_ts_info *info = input_get_drvdata(dev); |
| printk(KERN_ERR "[TSP], %s, %d\n",__func__, __LINE__); |
| mms_ts_suspend(&info->client->dev); |
| } |
| static int melfas_ts_open(struct input_dev *dev) |
| { |
| struct mms_ts_info *info = input_get_drvdata(dev); |
| printk(KERN_ERR "[TSP], %s, %d\n",__func__, __LINE__); |
| mms_ts_resume(&info->client->dev); |
| return 0; |
| } |
| #endif |
| |
| #if defined(CONFIG_PM) && !defined(CONFIG_HAS_EARLYSUSPEND) && !defined(USE_OPEN_CLOSE) |
| static const struct dev_pm_ops mms_ts_pm_ops = { |
| .suspend = mms_ts_suspend, |
| .resume = mms_ts_resume, |
| }; |
| #endif |
| |
| static const struct i2c_device_id mms_ts_id[] = { |
| {MELFAS_TS_NAME, 0}, |
| { } |
| }; |
| MODULE_DEVICE_TABLE(i2c, mms_ts_id); |
| |
| #ifdef CONFIG_OF |
| static struct of_device_id mms_match_table[] = { |
| { .compatible = "melfas,mms-ts",}, |
| { }, |
| }; |
| #else |
| #define mms_match_table NULL |
| #endif |
| |
| static struct i2c_driver mms_ts_driver = { |
| .probe = mms_ts_probe, |
| .remove = __devexit_p(mms_ts_remove), |
| .driver = { |
| .name = MELFAS_TS_NAME, |
| #ifdef CONFIG_OF |
| .of_match_table = mms_match_table, |
| #endif |
| #if defined(CONFIG_PM) && !defined(CONFIG_HAS_EARLYSUSPEND) && !defined(USE_OPEN_CLOSE) |
| .pm = &mms_ts_pm_ops, |
| #endif |
| }, |
| .id_table = mms_ts_id, |
| }; |
| |
| static int __init mms_ts_init(void) |
| { |
| #if 0 /* reference to battery driver variable */ |
| if (poweroff_charging) { |
| pr_info("%s : LPM Charging Mode!!\n", __func__); |
| return 0; |
| } |
| #endif /* reference to battery driver variable */ |
| return i2c_add_driver(&mms_ts_driver); |
| } |
| |
| static void __exit mms_ts_exit(void) |
| { |
| i2c_del_driver(&mms_ts_driver); |
| } |
| |
| module_init(mms_ts_init); |
| module_exit(mms_ts_exit); |
| |
| /* Module information */ |
| MODULE_DESCRIPTION("Touchscreen driver for Melfas MMS-series controllers"); |
| MODULE_LICENSE("GPL"); |