/* * MELFAS MIP4 Touchkey * * Copyright (C) 2016 MELFAS Inc. * * mip4.c : Main functions * * Version : 2016.03.15 */ #include "mip4.h" /* * Reboot chip */ void mip4_tk_reboot(struct mip4_tk_info *info) { input_info(true, &info->client->dev, "%s [START]\n", __func__); disable_irq_nosync(info->irq); info->enabled = false; mip4_tk_clear_input(info); mip4_tk_power_off(info); mip4_tk_power_on(info); msleep(100); info->enabled = true; enable_irq(info->irq); input_info(true, &info->client->dev, "%s [DONE]\n", __func__); } /* * I2C Read */ int mip4_tk_i2c_read(struct mip4_tk_info *info, char *write_buf, unsigned int write_len, char *read_buf, unsigned int read_len) { int retry = I2C_RETRY_COUNT; int res; struct i2c_msg msg[] = { { .addr = info->client->addr, .flags = 0, .buf = write_buf, .len = write_len, }, { .addr = info->client->addr, .flags = I2C_M_RD, .buf = read_buf, .len = read_len, }, }; if (!info->enabled) { input_err(true, &info->client->dev, "%s [ERROR] device disabled\n", __func__); return 1; } mutex_lock(&info->lock); while (retry--) { res = i2c_transfer(info->client->adapter, msg, ARRAY_SIZE(msg)); if (res == ARRAY_SIZE(msg)) goto DONE; else if (res < 0) input_err(true, &info->client->dev, "%s [ERROR] i2c_transfer - errno[%d]\n", __func__, res); else if (res != ARRAY_SIZE(msg)) input_err(true, &info->client->dev, "%s [ERROR] i2c_transfer - size[%ld] result[%d]\n", __func__, ARRAY_SIZE(msg), res); else input_err(true, &info->client->dev, "%s [ERROR] unknown error [%d]\n", __func__, res); } mutex_unlock(&info->lock); #if RESET_ON_I2C_ERROR mip4_tk_reboot(info); #endif return 1; DONE: mutex_unlock(&info->lock); return 0; } /* * I2C Write */ int mip4_tk_i2c_write(struct mip4_tk_info *info, char *write_buf, unsigned int write_len) { int retry = I2C_RETRY_COUNT; int res; if (!info->enabled) { input_err(true, &info->client->dev, "%s [ERROR] device disabled\n", __func__); return 1; } mutex_lock(&info->lock); while (retry--) { res = i2c_master_send(info->client, write_buf, write_len); if (res == write_len) goto DONE; else if (res < 0) input_err(true, &info->client->dev, "%s [ERROR] i2c_master_send - errno [%d]\n", __func__, res); else if (res != write_len) input_err(true, &info->client->dev, "%s [ERROR] length mismatch - write[%d] result[%d]\n", __func__, write_len, res); else input_err(true, &info->client->dev, "%s [ERROR] unknown error [%d]\n", __func__, res); } mutex_unlock(&info->lock); #if RESET_ON_I2C_ERROR mip4_tk_reboot(info); #endif return 1; DONE: mutex_unlock(&info->lock); return 0; } /* * Enable device */ int mip4_tk_enable(struct mip4_tk_info *info) { if (info->enabled) { input_err(true, &info->client->dev, "%s [ERROR] device already enabled\n", __func__); goto EXIT; } mip4_tk_power_on(info); msleep(100); #if 0 if(info->disable_esd == true){ /* Disable ESD alert */ mip4_tk_disable_esd_alert(info); } #endif mutex_lock(&info->device); if (info->irq_enabled == false) { enable_irq(info->irq); info->irq_enabled = true; } info->enabled = true; mutex_unlock(&info->device); EXIT: input_info(true, &info->client->dev, MIP_DEV_NAME" - Enabled\n"); return 0; } /* * Disable device */ int mip4_tk_disable(struct mip4_tk_info *info) { if (!info->enabled) { input_err(true, &info->client->dev, "%s [ERROR] device already disabled\n", __func__); goto EXIT; } mutex_lock(&info->device); disable_irq(info->irq); info->irq_enabled = false; info->enabled = false; mip4_tk_power_off(info); mip4_tk_clear_input(info); mutex_unlock(&info->device); EXIT: input_info(true, &info->client->dev, MIP_DEV_NAME" - Disabled\n"); return 0; } #if MIP_USE_INPUT_OPEN_CLOSE /* * Open input device */ #ifdef OPEN_CLOSE_WORK static void mip4_tk_resume_work(struct work_struct *work) { struct mip4_tk_info *info = container_of(work, struct mip4_tk_info, resume_work.work); input_info(true, &info->client->dev, "%s\n", __func__); mip4_tk_enable(info); } #endif static int mip4_tk_input_open(struct input_dev *dev) { struct mip4_tk_info *info = input_get_drvdata(dev); if (info->init == false) { input_err(true, &info->client->dev, "%s: Touchkey is not probed\n", __func__); return 0; } #ifdef OPEN_CLOSE_WORK schedule_delayed_work(&info->resume_work, msecs_to_jiffies(1)); #else mip4_tk_enable(info); #endif return 0; } /* * Close input device */ static void mip4_tk_input_close(struct input_dev *dev) { struct mip4_tk_info *info = input_get_drvdata(dev); if (info->init == false) { input_err(true, &info->client->dev, "%s: Touchkey is not probed\n", __func__); return; } #ifdef OPEN_CLOSE_WORK cancel_delayed_work(&info->resume_work); #endif mip4_tk_disable(info); return; } #endif /* * Get ready status */ int mip4_tk_get_ready_status(struct mip4_tk_info *info) { u8 wbuf[16]; u8 rbuf[16]; int ret = 0; wbuf[0] = MIP_R0_CTRL; wbuf[1] = MIP_R1_CTRL_READY_STATUS; if (mip4_tk_i2c_read(info, wbuf, 2, rbuf, 1)) { input_err(true, &info->client->dev, "%s [ERROR] mip4_tk_i2c_read\n", __func__); goto ERROR; } ret = rbuf[0]; /* check status */ if ((ret == MIP_CTRL_STATUS_NONE) || (ret == MIP_CTRL_STATUS_LOG) || (ret == MIP_CTRL_STATUS_READY)) { input_info(true, &info->client->dev, "%s - status [0x%02X]\n", __func__, ret); } else { input_err(true, &info->client->dev, "%s [ERROR] Unknown status [0x%02X]\n", __func__, ret); goto ERROR; } if (ret == MIP_CTRL_STATUS_LOG) { /* skip log event */ wbuf[0] = MIP_R0_LOG; wbuf[1] = MIP_R1_LOG_TRIGGER; wbuf[2] = 0; if (mip4_tk_i2c_write(info, wbuf, 3)) { input_err(true, &info->client->dev, "%s [ERROR] mip4_tk_i2c_write\n", __func__); } } return ret; ERROR: input_err(true, &info->client->dev, "%s [ERROR]\n", __func__); return -1; } /* * Read chip firmware version */ int mip4_tk_get_fw_version(struct mip4_tk_info *info, u8 *ver_buf) { u8 rbuf[8]; u8 wbuf[2]; int i; wbuf[0] = MIP_R0_INFO; wbuf[1] = MIP_R1_INFO_VERSION_BOOT; if (mip4_tk_i2c_read(info, wbuf, 2, rbuf, 8)) goto ERROR; for (i = 0; i < MIP_FW_MAX_SECT_NUM; i++) { ver_buf[0 + i * 2] = rbuf[1 + i * 2]; ver_buf[1 + i * 2] = rbuf[0 + i * 2]; } return 0; ERROR: input_err(true, &info->client->dev, "%s [ERROR]\n", __func__); return 1; } /* * Read chip firmware version for u16 */ int mip4_tk_get_fw_version_u16(struct mip4_tk_info *info, u16 *ver_buf_u16) { u8 rbuf[8]; int i; if (mip4_tk_get_fw_version(info, rbuf)) goto ERROR; for (i = 0; i < MIP_FW_MAX_SECT_NUM; i++) ver_buf_u16[i] = (rbuf[0 + i * 2] << 8) | rbuf[1 + i * 2]; return 0; ERROR: input_err(true, &info->client->dev, "%s [ERROR]\n", __func__); return 1; } /* * Read bin(file) firmware version */ int mip4_tk_get_fw_version_from_bin(struct mip4_tk_info *info, u8 *ver_buf) { const struct firmware *fw; const char *fw_name = info->pdata->firmware_name; request_firmware(&fw, fw_name, &info->client->dev); if (!fw) { input_err(true, &info->client->dev,"%s [ERROR] request_firmware\n", __func__); goto ERROR; } if (mip4_tk_bin_fw_version(info, fw->data, fw->size, ver_buf)) { input_err(true, &info->client->dev,"%s [ERROR] mip4_tk_bin_fw_version\n", __func__); goto ERROR; } release_firmware(fw); return 0; ERROR: input_err(true, &info->client->dev,"%s [ERROR]\n", __func__); return 1; } /* * Set power state */ int mip4_tk_set_power_state(struct mip4_tk_info *info, u8 mode) { u8 wbuf[3]; input_dbg(true, &info->client->dev, "%s [START]\n", __func__); input_dbg(true, &info->client->dev, "%s - mode[%02X]\n", __func__, mode); wbuf[0] = MIP_R0_CTRL; wbuf[1] = MIP_R1_CTRL_POWER_STATE; wbuf[2] = mode; if (mip4_tk_i2c_write(info, wbuf, 3)) { input_err(true, &info->client->dev, "%s [ERROR] mip4_tk_i2c_write\n", __func__); goto ERROR; } input_dbg(true, &info->client->dev, "%s [DONE]\n", __func__); return 0; ERROR: input_err(true, &info->client->dev, "%s [ERROR]\n", __func__); return 1; } /* * Disable ESD alert */ int mip4_tk_disable_esd_alert(struct mip4_tk_info *info) { u8 wbuf[4]; u8 rbuf[4]; input_dbg(true, &info->client->dev, "%s [START]\n", __func__); wbuf[0] = MIP_R0_CTRL; wbuf[1] = MIP_R1_CTRL_DISABLE_ESD_ALERT; wbuf[2] = 1; if (mip4_tk_i2c_write(info, wbuf, 3)) { input_err(true, &info->client->dev, "%s [ERROR] mip4_tk_i2c_write\n", __func__); goto ERROR; } if (mip4_tk_i2c_read(info, wbuf, 2, rbuf, 1)) { input_err(true, &info->client->dev, "%s [ERROR] mip4_tk_i2c_read\n", __func__); goto ERROR; } if (rbuf[0] != 1) { input_dbg(true, &info->client->dev, "%s [ERROR] failed\n", __func__); goto ERROR; } input_info(true, &info->client->dev, "%s [DONE]\n", __func__); return 0; ERROR: input_err(true, &info->client->dev, "%s [ERROR]\n", __func__); return 1; } /* * Alert event handler - ESD */ static int mip4_tk_alert_handler_esd(struct mip4_tk_info *info, u8 *rbuf) { u8 frame_cnt = rbuf[1]; input_info(true, &info->client->dev, "%s - frame_cnt[%d]\n", __func__, frame_cnt); if (frame_cnt == 0) { /* sensor crack, not ESD */ info->esd_cnt++; input_info(true, &info->client->dev, "%s - esd_cnt[%d]\n", __func__, info->esd_cnt); if (info->disable_esd == true) { mip4_tk_disable_esd_alert(info); info->esd_cnt = 0; } else if (info->esd_cnt > ESD_COUNT_FOR_DISABLE) { /* Disable ESD alert */ if (mip4_tk_disable_esd_alert(info)) { } else { info->disable_esd = true; info->esd_cnt = 0; } } else { /* Reset chip */ mip4_tk_reboot(info); } } else { /* ESD detected */ /* Reset chip */ mip4_tk_reboot(info); info->esd_cnt = 0; } input_info(true, &info->client->dev, "%s [DONE]\n", __func__); return 0; } /* * Alert event handler - Input type */ static int mip4_tk_alert_handler_inputtype(struct mip4_tk_info *info, u8 *rbuf) { u8 input_type = rbuf[1]; switch (input_type) { case 0: input_dbg(true, &info->client->dev, "%s - Input type : Finger\n", __func__); break; case 1: input_dbg(true, &info->client->dev, "%s - Input type : Glove\n", __func__); break; default: input_err(true, &info->client->dev, "%s - Input type : Unknown [%d]\n", __func__, input_type); goto ERROR; break; } input_info(true, &info->client->dev, "%s [DONE]\n", __func__); return 0; ERROR: input_err(true, &info->client->dev, "%s [ERROR]\n", __func__); return 1; } /* * Interrupt handler */ static irqreturn_t mip4_tk_interrupt(int irq, void *dev_id) { struct mip4_tk_info *info = dev_id; struct i2c_client *client = info->client; u8 wbuf[8]; u8 rbuf[256]; unsigned int size = 0; u8 category = 0; u8 alert_type = 0; if (info->init == false) { input_err(true, &info->client->dev, "%s: Touchkey is not probed\n", __func__); return IRQ_HANDLED; } /* Read packet info */ wbuf[0] = MIP_R0_EVENT; wbuf[1] = MIP_R1_EVENT_PACKET_INFO; if (mip4_tk_i2c_read(info, wbuf, 2, rbuf, 1)) { input_err(true, &client->dev, "%s [ERROR] Read packet info\n", __func__); goto ERROR; } size = (rbuf[0] & 0x7F); category = ((rbuf[0] >> 7) & 0x1); input_dbg(true, &client->dev, "%s - packet info : size[%d] category[%d]\n", __func__, size, category); /* Check size */ if (size <= 0) { input_err(true, &client->dev, "%s [ERROR] Packet size [%d]\n", __func__, size); goto EXIT; } /* Read packet data */ wbuf[0] = MIP_R0_EVENT; wbuf[1] = MIP_R1_EVENT_PACKET_DATA; if (mip4_tk_i2c_read(info, wbuf, 2, rbuf, size)) { input_err(true, &client->dev, "%s [ERROR] Read packet data\n", __func__); goto ERROR; } /* Event handler */ if (category == 0) { /* Touch event*/ info->esd_cnt = 0; mip4_tk_input_event_handler(info, size, rbuf); } else { /* Alert event */ alert_type = rbuf[0]; input_info(true, &client->dev, "%s - alert type [%d]\n", __func__, alert_type); if (alert_type == MIP_ALERT_ESD) { /* ESD detection */ if (mip4_tk_alert_handler_esd(info, rbuf)) { goto ERROR; } } else if (alert_type == MIP_ALERT_INPUT_TYPE) { /* Input type changed */ if (mip4_tk_alert_handler_inputtype(info, rbuf)) { goto ERROR; } } else { input_err(true, &client->dev, "%s [ERROR] Unknown alert type [%d]\n", __func__, alert_type); goto ERROR; } } EXIT: return IRQ_HANDLED; ERROR: if (RESET_ON_EVENT_ERROR) { input_info(true, &client->dev, "%s - Reset on error\n", __func__); mip4_tk_reboot(info); } input_err(true, &client->dev, "%s [ERROR]\n", __func__); return IRQ_HANDLED; } /* * Update firmware from kernel built-in binary */ int mip4_tk_fw_update_from_kernel(struct mip4_tk_info *info, bool force) { const char *fw_name = info->pdata->firmware_name; const struct firmware *fw; int retires = 3; int ret = fw_err_none; input_info(true, &info->client->dev, "%s [START]\n", __func__); mutex_lock(&info->device); disable_irq(info->irq); request_firmware(&fw, fw_name, &info->client->dev); if (!fw) { input_err(true, &info->client->dev, "%s [ERROR] request_firmware\n", __func__); goto ERROR; } /* Update firmware */ do { ret = mip4_tk_flash_fw(info, fw->data, fw->size, force, true); if (ret >= fw_err_none) { break; } } while (--retires); if (!retires) { input_err(true, &info->client->dev, "%s [ERROR] mip4_tk_flash_fw failed\n", __func__); ret = fw_err_download; } ERROR: release_firmware(fw); enable_irq(info->irq); mutex_unlock(&info->device); if (ret < fw_err_none) { input_err(true, &info->client->dev, "%s [ERROR]\n", __func__); } else { /* mip4_tk_init_config(info); */ input_info(true, &info->client->dev, "%s [DONE]\n", __func__); } return ret; } /* * Update firmware from external storage */ int mip4_tk_fw_update_from_storage(struct mip4_tk_info *info, char *path, bool force) { struct file *fp; mm_segment_t old_fs; size_t fw_size, nread; int ret = fw_err_none; input_info(true, &info->client->dev, "%s [START]\n", __func__); mutex_lock(&info->device); disable_irq(info->irq); old_fs = get_fs(); set_fs(KERNEL_DS); fp = filp_open(path, O_RDONLY, S_IRUSR); if (IS_ERR(fp)) { input_err(true, &info->client->dev, "%s [ERROR] file_open - path[%s]\n", __func__, path); ret = fw_err_file_open; goto ERROR; } fw_size = fp->f_path.dentry->d_inode->i_size; if (0 < fw_size) { /* Read firmware */ unsigned char *fw_data; fw_data = kzalloc(fw_size, GFP_KERNEL); nread = vfs_read(fp, (char __user *)fw_data, fw_size, &fp->f_pos); if (nread != fw_size) { input_err(true, &info->client->dev, "%s [ERROR] vfs_read - size[%ld] read[%ld]\n", __func__, fw_size, nread); ret = fw_err_file_read; } else { /* Update firmware */ ret = mip4_tk_flash_fw(info, fw_data, fw_size, force, true); } kfree(fw_data); } else { input_err(true, &info->client->dev, "%s [ERROR] fw_size [%ld]\n", __func__, fw_size); ret = fw_err_file_read; } filp_close(fp, current->files); ERROR: set_fs(old_fs); enable_irq(info->irq); mutex_unlock(&info->device); if (ret < fw_err_none) { input_err(true, &info->client->dev, "%s [ERROR]\n", __func__); } else { mip4_tk_init_config(info); input_info(true, &info->client->dev, "%s [DONE]\n", __func__); } return ret; } /* * Sysfs firmware update */ static ssize_t mip4_tk_sys_fw_update(struct device *dev, struct device_attribute *attr, char *buf) { struct i2c_client *client = to_i2c_client(dev); struct mip4_tk_info *info = i2c_get_clientdata(client); int result = 0; u8 data[255]; int ret = 0; memset(info->print_buf, 0, PAGE_SIZE); input_info(true, &info->client->dev, "%s [START]\n", __func__); ret = mip4_tk_fw_update_from_storage(info, info->fw_path_ext, true); switch (ret) { case fw_err_none: sprintf(data, "F/W update success.\n"); break; case fw_err_uptodate: sprintf(data, "F/W is already up-to-date.\n"); break; case fw_err_download: sprintf(data, "F/W update failed : Download error\n"); break; case fw_err_file_type: sprintf(data, "F/W update failed : File type error\n"); break; case fw_err_file_open: sprintf(data, "F/W update failed : File open error [%s]\n", info->fw_path_ext); break; case fw_err_file_read: sprintf(data, "F/W update failed : File read error\n"); break; default: sprintf(data, "F/W update failed.\n"); break; } input_info(true, &info->client->dev, "%s [DONE]\n", __func__); strcat(info->print_buf, data); result = snprintf(buf, PAGE_SIZE, "%s\n", info->print_buf); return result; } static DEVICE_ATTR(fw_update, S_IRUGO, mip4_tk_sys_fw_update, NULL); /* * Sysfs attr info */ static struct attribute *mip4_tk_attrs[] = { &dev_attr_fw_update.attr, NULL, }; /* * Sysfs attr group info */ static const struct attribute_group mip4_tk_attr_group = { .attrs = mip4_tk_attrs, }; /* * Initial config */ int mip4_tk_init_config(struct mip4_tk_info *info) { u8 wbuf[2]; u8 rbuf[16]; int ret = 0; /* Product name */ wbuf[0] = MIP_R0_INFO; wbuf[1] = MIP_R1_INFO_PRODUCT_NAME; ret = mip4_tk_i2c_read(info, wbuf, 2, rbuf, 16); if (ret) { input_err(true, &info->client->dev, "%s [ERROR] mip4_tk_i2c_read\n", __func__); goto ERROR; } memcpy(info->product_name, rbuf, 16); input_info(true, &info->client->dev, "%s - product_name[%s]\n", __func__, info->product_name); /* Firmware version */ ret = mip4_tk_get_fw_version(info, rbuf); if (ret) { input_err(true, &info->client->dev, "%s [ERROR] mip4_tk_i2c_read\n", __func__); goto ERROR; } memcpy(info->fw_version, rbuf, 8); input_info(true, &info->client->dev, "%s - F/W Version : %02X.%02X/%02X.%02X/%02X.%02X/%02X.%02X\n", __func__, info->fw_version[0], info->fw_version[1], info->fw_version[2], info->fw_version[3], info->fw_version[4], info->fw_version[5], info->fw_version[6], info->fw_version[7]); /* Key */ wbuf[0] = MIP_R0_INFO; wbuf[1] = MIP_R1_INFO_KEY_NUM; ret = mip4_tk_i2c_read(info, wbuf, 2, rbuf, 1); if (ret) { input_err(true, &info->client->dev, "%s [ERROR] mip4_tk_i2c_read\n", __func__); goto ERROR; } info->key_num = rbuf[0]; info->node_key = rbuf[0]; if (info->key_num > MAX_KEY_NUM) { input_err(true, &info->client->dev, "%s [ERROR] MAX_KEY_NUM\n", __func__); goto ERROR; } /* LED */ wbuf[0] = MIP_R0_LED; wbuf[1] = MIP_R1_LED_NUM; ret = mip4_tk_i2c_read(info, wbuf, 2, rbuf, 2); if (ret) { input_err(true, &info->client->dev, "%s [ERROR] mip4_tk_i2c_read\n", __func__); goto ERROR; } info->led_num = rbuf[0]; info->led_max_brightness = rbuf[1]; if (info->led_num > MAX_LED_NUM) { input_err(true, &info->client->dev, "%s [ERROR] MAX_LED_NUM\n", __func__); goto ERROR; } input_info(true, &info->client->dev, "%s - Key : %d, LED : %d\n", __func__, info->key_num, info->led_num); /* Protocol */ wbuf[0] = MIP_R0_EVENT; wbuf[1] = MIP_R1_EVENT_FORMAT; ret = mip4_tk_i2c_read(info, wbuf, 2, rbuf, 3); if (ret) { input_err(true, &info->client->dev, "%s [ERROR] mip4_tk_i2c_read\n", __func__); goto ERROR; } info->event_format = (rbuf[0]) | (rbuf[1] << 8); info->event_size = rbuf[2]; if (info->event_size <= 0) { input_err(true, &info->client->dev, "%s [ERROR] event_size[%d]\n", __func__, info->event_size); goto ERROR; } input_info(true, &info->client->dev, "%s - event_format[%d] event_size[%d] \n", __func__, info->event_format, info->event_size); input_info(true, &info->client->dev, "%s [DONE]\n", __func__); return 0; ERROR: input_err(true, &info->client->dev, "%s [ERROR]\n", __func__); return 1; } unsigned int boot_mode; static int __init get_bootmode(char *arg) { get_option(&arg, &boot_mode); return 0; } early_param("bootmode", get_bootmode); /* * Initialize driver */ static int mip4_tk_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); struct mip4_tk_info *info; struct input_dev *input_dev; int ret = 0; #ifdef CONFIG_BATTERY_SAMSUNG if (lpcharge == 1) { input_err(true, &client->dev, "%s : Do not load driver due to : lpm %d\n", __func__, lpcharge); return -ENODEV; } #endif if (boot_mode == 2) { pr_err("%s : Do not load driver due to : device entered recovery mode %d\n", __func__, boot_mode); return -ENODEV; } if (!i2c_check_functionality(adapter, I2C_FUNC_I2C)) { input_err(true, &client->dev, "%s [ERROR] i2c_check_functionality\n", __func__); return -EIO; } /* Init info data */ info = kzalloc(sizeof(struct mip4_tk_info), GFP_KERNEL); if (!info) { input_err(true, &client->dev, "%s [ERROR] memory allocation for device\n", __func__); ret = -ENOMEM; goto err_alloc; } input_dev = devm_input_allocate_device(&client->dev); if (!input_dev) { input_err(true, &client->dev, "%s [ERROR] memory allocation for input device\n", __func__); ret = -ENOMEM; goto err_input_alloc; } info->client = client; info->input_dev = input_dev; info->irq = -1; info->init = false; info->power = -1; info->irq_enabled = false; info->fw_path_ext = kstrdup(FW_PATH_EXTERNAL, GFP_KERNEL); /* need to modify */ info->key_code_loaded = false; /* Get platform data */ #ifdef CONFIG_OF if (client->dev.of_node) { info->pdata = devm_kzalloc( &client->dev, sizeof(struct mip4_tk_platform_data), GFP_KERNEL); if (!info->pdata) { input_err(true, &client->dev, "%s [ERROR] memory allocation for pdata\n", __func__); ret = -ENOMEM; goto err_config; } ret = mip4_tk_parse_devicetree(&client->dev, info); if (ret) { input_err(true, &client->dev, "%s [ERROR] mip4_tk_parse_dt\n", __func__); ret = -EINVAL; goto err_config; } } #else info->pdata = client->dev.platform_data; if (!info->pdata) { input_err(true, &client->dev, "%s [ERROR] pdata is null\n", __func__); ret = -EINVAL; goto err_config; } #endif /* Init input device */ info->input_dev->name = "sec_touchkey"; snprintf(info->phys, sizeof(info->phys), "%s/input0", info->input_dev->name); info->input_dev->phys = info->phys; info->input_dev->id.bustype = BUS_I2C; info->input_dev->dev.parent = &client->dev; #if MIP_USE_INPUT_OPEN_CLOSE info->input_dev->open = mip4_tk_input_open; info->input_dev->close = mip4_tk_input_close; #endif /* Set info data */ input_set_drvdata(input_dev, info); i2c_set_clientdata(client, info); info->pinctrl = devm_pinctrl_get(&client->dev); if (IS_ERR(info->pinctrl)) { if (PTR_ERR(info->pinctrl) == -EPROBE_DEFER) input_err(true, &client->dev, "%s: Target does not use pinctrl\n", __func__); info->pinctrl = NULL; } if (info->pinctrl) { info->pins_default = pinctrl_lookup_state(info->pinctrl, "on_state"); if (IS_ERR(info->pins_default)) input_err(true, &client->dev, "could not get default pinstate\n"); info->pins_sleep = pinctrl_lookup_state(info->pinctrl, "off_state"); if (IS_ERR(info->pins_sleep)) input_err(true, &client->dev, "could not get sleep pinstate\n"); } mutex_init(&info->lock); mutex_init(&info->device); /* Power on */ mip4_tk_power_on(info); if (!info->boot_on_ldo) msleep(100); info->enabled = true; /* Firmware update */ #if MIP_USE_AUTO_FW_UPDATE ret = mip4_tk_fw_update_from_kernel(info, false); if (ret < 0) { input_err(true, &client->dev, "%s [ERROR] mip4_tk_fw_update_from_kernel\n", __func__); goto err_reg_input_dev; } #endif /* Initial config */ ret = mip4_tk_init_config(info); if (ret) { input_err(true, &client->dev, "%s [ERROR] mip4_tk_init_config\n", __func__); goto err_reg_input_dev; } /* Config input interface */ mip4_tk_config_input(info); #ifdef OPEN_CLOSE_WORK INIT_DELAYED_WORK(&info->resume_work, mip4_tk_resume_work); #endif /* Register input device */ ret = input_register_device(input_dev); if (ret) { input_err(true, &client->dev, "%s [ERROR] input_register_device\n", __func__); goto err_reg_input_dev; } #if MIP_USE_CALLBACK /* Config callback functions */ mip4_tk_config_callback(info); #endif /* Set interrupt handler */ info->irq = client->irq; ret = request_threaded_irq(client->irq, NULL, mip4_tk_interrupt, IRQF_TRIGGER_LOW | IRQF_ONESHOT, MIP_DEV_NAME, info); if (ret) { input_err(true, &client->dev, "%s [ERROR] request_threaded_irq\n", __func__); goto err_req_irq; } #ifdef CONFIG_HAS_EARLYSUSPEND info->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN +1; info->early_suspend.suspend = mip4_tk_early_suspend; info->early_suspend.resume = mip4_tk_late_resume; register_early_suspend(&info->early_suspend); #endif /* Enable device */ mip4_tk_enable(info); #if MIP_USE_DEV /* Create dev node (optional) */ if (mip4_tk_dev_create(info)) { input_err(true, &client->dev, "%s [ERROR] mip4_tk_dev_create\n", __func__); ret = -EAGAIN; goto err_dev_create; } /* Create dev */ info->class = class_create(THIS_MODULE, MIP_DEV_NAME); device_create(info->class, NULL, info->mip4_tk_dev, NULL, MIP_DEV_NAME); #endif #if MIP_USE_SYS /* Create sysfs for test mode (optional) */ if (mip4_tk_sysfs_create(info)) { input_err(true, &client->dev, "%s [ERROR] mip4_tk_sysfs_create\n", __func__); } #endif #if MIP_USE_CMD /* Create sysfs for command mode (optional) */ if (mip4_tk_sysfs_cmd_create(info)) { input_err(true, &client->dev, "%s [ERROR] mip4_tk_sysfs_cmd_create\n", __func__); } #endif /* Create sysfs */ if (sysfs_create_group(&client->dev.kobj, &mip4_tk_attr_group)) { input_err(true, &client->dev, "%s [ERROR] sysfs_create_group\n", __func__); } if (sysfs_create_link(&info->key_dev->kobj, &info->input_dev->dev.kobj, "input")) { input_err(true, &client->dev, "%s [ERROR] sysfs_create_link\n", __func__); } info->init = true; input_info(true, &client->dev, "MELFAS " CHIP_NAME " Touchkey is initialized successfully\n"); return 0; err_dev_create: disable_irq(info->irq); err_req_irq: input_unregister_device(input_dev); info->input_dev = NULL; err_reg_input_dev: mutex_destroy(&info->lock); mutex_destroy(&info->device); mip4_tk_power_off(info); err_config: if (info->input_dev) input_free_device(input_dev); err_input_alloc: kfree(info); err_alloc: input_err(true, &client->dev, "MELFAS " CHIP_NAME " Touchkey initialization failed\n"); return ret; } /* * Remove driver */ static int mip4_tk_remove(struct i2c_client *client) { struct mip4_tk_info *info = i2c_get_clientdata(client); if (info->irq >= 0) { free_irq(info->irq, info); } #if MIP_USE_CMD mip4_tk_sysfs_cmd_remove(info); #endif #if MIP_USE_SYS mip4_tk_sysfs_remove(info); #endif sysfs_remove_group(&info->client->dev.kobj, &mip4_tk_attr_group); sysfs_remove_link(NULL, MIP_DEV_NAME); #if MIP_USE_DEV device_destroy(info->class, info->mip4_tk_dev); class_destroy(info->class); #endif #ifdef CONFIG_HAS_EARLYSUSPEND unregister_early_suspend(&info->early_suspend); #endif input_unregister_device(info->input_dev); info->input_dev = NULL; kfree(info); return 0; } #if defined(CONFIG_PM) || defined(CONFIG_HAS_EARLYSUSPEND) /* * Device suspend event handler */ int mip4_tk_suspend(struct device *dev) { struct i2c_client *client = to_i2c_client(dev); struct mip4_tk_info *info = i2c_get_clientdata(client); input_info(true, &client->dev, "%s [START]\n", __func__); mip4_tk_disable(info); return 0; } /* * Device resume event handler */ int mip4_tk_resume(struct device *dev) { struct i2c_client *client = to_i2c_client(dev); struct mip4_tk_info *info = i2c_get_clientdata(client); int ret = 0; input_info(true, &client->dev, "%s [START]\n", __func__); mip4_tk_enable(info); return ret; } #endif #ifdef CONFIG_HAS_EARLYSUSPEND /* * Early suspend handler */ void mip4_tk_early_suspend(struct early_suspend *h) { struct mip4_tk_info *info = container_of(h, struct mip4_tk_info, early_suspend); input_dbg(true, &info->client->dev, "%s [START]\n", __func__); mip4_tk_suspend(&info->client->dev); input_dbg(true, &info->client->dev, "%s [DONE]\n", __func__); } /* * Late resume handler */ void mip4_tk_late_resume(struct early_suspend *h) { struct mip4_tk_info *info = container_of(h, struct mip4_tk_info, early_suspend); input_dbg(true, &info->client->dev, "%s [START]\n", __func__); mip4_tk_resume(&info->client->dev); input_dbg(true, &info->client->dev, "%s [DONE]\n", __func__); } #endif void mip4_tk_shutdown(struct i2c_client *client) { struct mip4_tk_info *info = i2c_get_clientdata(client); if (info->init == false) return; input_err(true, &info->client->dev, "%s\n", __func__); #ifdef OPEN_CLOSE_WORK cancel_delayed_work(&info->resume_work); #endif mip4_tk_power_off(info); free_irq(info->irq, info); input_unregister_device(info->input_dev); info->input_dev = NULL; mutex_destroy(&info->lock); mutex_destroy(&info->device); kfree(info); } #if defined(CONFIG_PM) /* * PM info */ static const struct dev_pm_ops mip4_tk_pm_ops = { #if !defined(CONFIG_HAS_EARLYSUSPEND) && !defined(MIP_USE_INPUT_OPEN_CLOSE) .suspend = mip4_tk_suspend, .resume = mip4_tk_resume, #endif }; #endif #ifdef CONFIG_OF /* * Device tree match table */ static const struct of_device_id mip4_tk_match_table[] = { { .compatible = "melfas," MIP_DEV_NAME, }, {}, }; MODULE_DEVICE_TABLE(of, mip4_tk_match_table); #else #define mip4_tk_match_table NULL #endif /* * I2C Device ID */ static const struct i2c_device_id mip4_tk_id[] = { {"melfas_"MIP_DEV_NAME, 0}, {}, }; MODULE_DEVICE_TABLE(i2c, mip4_tk_id); /* * I2C driver info */ static struct i2c_driver mip4_tk_driver = { .id_table = mip4_tk_id, .probe = mip4_tk_probe, .remove = mip4_tk_remove, .shutdown = &mip4_tk_shutdown, .driver = { .name = MIP_DEV_NAME, .owner = THIS_MODULE, .of_match_table = mip4_tk_match_table, #ifdef CONFIG_PM .pm = &mip4_tk_pm_ops, #endif }, }; /* * Init driver */ static int __init mip4_tk_init(void) { return i2c_add_driver(&mip4_tk_driver); } /* * Exit driver */ static void __exit mip4_tk_exit(void) { i2c_del_driver(&mip4_tk_driver); } module_init(mip4_tk_init); module_exit(mip4_tk_exit); MODULE_DESCRIPTION("MELFAS MIP4 Touchkey"); MODULE_VERSION("2016.03.15"); MODULE_AUTHOR("Sangwon Jee "); MODULE_LICENSE("GPL");