/* * Copyright (C) 2013 Samsung Electronics. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 2 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA */ #include #include #include #include #include #include #include #include #include #include #include #include //#include #include #include "sx9310_reg.h" #include #include "../pinctrl/core.h" #if defined(CONFIG_MUIC_NOTIFIER) #include #include #endif #define VENDOR_NAME "SEMTECH" #define MODEL_NAME "SX9310" #define MODULE_NAME "grip_sensor" #define I2C_M_WR 0 /* for i2c Write */ #define I2c_M_RD 1 /* for i2c Read */ #define IDLE 0 #define ACTIVE 1 #define SX9310_MODE_SLEEP 0 #define SX9310_MODE_NORMAL 1 #define MAIN_SENSOR 1 #ifdef CONFIG_SENSORS_SX9310_USE_2ND_CH #define CH2_SENSOR 2 #endif #define DIFF_READ_NUM 10 #define GRIP_LOG_TIME 30 /* sec */ #define CSX_STATUS_REG SX9310_TCHCMPSTAT_TCHSTAT0_FLAG #define BODY_STATUS_REG SX9310_BODYCMPSTAT_FLAG #define RAW_DATA_BLOCK_SIZE (SX9310_REGOFFSETLSB - SX9310_REGUSEMSB + 1) /* CS0, CS1, CS2, CS3 */ #ifdef CONFIG_SENSORS_SX9310_USE_2ND_CH #define ENABLE_CSX ((1 << MAIN_SENSOR)| (1 << CH2_SENSOR)) #else #define ENABLE_CSX (1 << MAIN_SENSOR) #endif #define IRQ_PROCESS_CONDITION (SX9310_IRQSTAT_TOUCH_FLAG \ | SX9310_IRQSTAT_RELEASE_FLAG) #define NONE_ENABLE -1 #define IDLE_STATE 0 #define TOUCH_STATE 1 #define BODY_STATE 2 #define HALLIC1_PATH "/sys/class/sec/sec_key/certify_hall_detect" #define HALLIC2_PATH "/sys/class/sec/sec_key/hall_detect" struct sx9310_p { struct i2c_client *client; struct input_dev *input; struct device *factory_device; struct delayed_work init_work; struct delayed_work irq_work; struct delayed_work debug_work; struct wake_lock grip_wake_lock; struct mutex read_mutex; struct pinctrl *p; struct pinctrl_state *pins_active; struct pinctrl_state *pins_suspend; struct regulator *grip_vdd_3p0; #if defined(CONFIG_MUIC_NOTIFIER) struct notifier_block cpuidle_muic_nb; #endif bool skip_data; bool check_usb; u8 normal_th; u8 normal_th_buf; int irq; int gpio_nirq; int gpio_nirq_en; int state; int debug_count; int diff_avg; int diff_cnt; s32 capmain; s16 useful; s16 avg; s16 diff; u16 offset; u16 freq; #ifdef CONFIG_SENSORS_SX9310_USE_2ND_CH u8 normal_th_ch2; u8 normal_th_buf_ch2; int diff_avg_ch2; int diff_cnt_ch2; s32 capmain_ch2; s16 useful_ch2; s16 avg_ch2; s16 diff_ch2; u16 offset_ch2; u16 freq_ch2; #endif int ch1_state; int ch2_state; atomic_t enable; unsigned char hall_ic1[5]; unsigned char hall_ic2[5]; }; static int check_hallic_state(char *file_path, unsigned char hall_ic_status[]) { int iRet = 0; mm_segment_t old_fs; struct file *filep; u8 hall_sysfs[5]; old_fs = get_fs(); set_fs(KERNEL_DS); filep = filp_open(file_path, O_RDONLY, 0666); if (IS_ERR(filep)) { iRet = PTR_ERR(filep); if (iRet != -ENOENT) pr_err("[SX9310]: %s - file open fail [%s] - %d\n", __func__, file_path, iRet); set_fs(old_fs); goto exit; } iRet = filep->f_op->read(filep, hall_sysfs, (int)sizeof(u8) * 4, &filep->f_pos); if (iRet == (int)sizeof(u8) * 4) { strncpy(hall_ic_status, hall_sysfs, sizeof(hall_sysfs)); } else { pr_err("[SX9310]: %s - failed : iRet(%d), size(%d), hall= %s\n", __func__, iRet, (int)sizeof(hall_sysfs), hall_sysfs); iRet = -EIO; } filp_close(filep, current->files); set_fs(old_fs); exit: return iRet; } static int sx9310_get_nirq_state(struct sx9310_p *data) { return gpio_get_value_cansleep(data->gpio_nirq); } static int sx9310_i2c_write(struct sx9310_p *data, u8 reg_addr, u8 buf) { int ret; struct i2c_msg msg; unsigned char w_buf[2]; w_buf[0] = reg_addr; w_buf[1] = buf; msg.addr = data->client->addr; msg.flags = I2C_M_WR; msg.len = 2; msg.buf = (char *)w_buf; ret = i2c_transfer(data->client->adapter, &msg, 1); if (ret < 0) pr_err("[SX9310]: %s - i2c write error %d\n", __func__, ret); return ret; } static int sx9310_i2c_read(struct sx9310_p *data, u8 reg_addr, u8 *buf) { int ret; struct i2c_msg msg[2]; msg[0].addr = data->client->addr; msg[0].flags = I2C_M_WR; msg[0].len = 1; msg[0].buf = ®_addr; msg[1].addr = data->client->addr; msg[1].flags = I2C_M_RD; msg[1].len = 1; msg[1].buf = buf; ret = i2c_transfer(data->client->adapter, msg, 2); if (ret < 0) pr_err("[SX9310]: %s - i2c read error %d\n", __func__, ret); return ret; } static int sx9310_i2c_read_block(struct sx9310_p *data, u8 reg_addr, u8 *buf, u8 buf_size) { int ret; struct i2c_msg msg[2]; msg[0].addr = data->client->addr; msg[0].flags = I2C_M_WR; msg[0].len = 1; msg[0].buf = ®_addr; msg[1].addr = data->client->addr; msg[1].flags = I2C_M_RD; msg[1].len = buf_size; msg[1].buf = buf; ret = i2c_transfer(data->client->adapter, msg, 2); if (ret < 0) pr_err("[SX9310]: %s - i2c read error %d\n", __func__, ret); return ret; } void sx9310_pin_control(struct sx9310_p *data, bool pin_set) { int status = 0; data->p->state = NULL; if (pin_set) { if (!IS_ERR(data->pins_active)) { status = pinctrl_select_state(data->p, data->pins_active); if (status) pr_err("%s: can't set pin active state\n", __func__); pr_debug("%s active\n", __func__); } } else { if (!IS_ERR(data->pins_suspend)) { status = pinctrl_select_state(data->p, data->pins_suspend); if (status) pr_err("%s: can't set pin suspend state\n", __func__); pr_debug("%s suspend\n", __func__); } } } static u8 sx9310_read_irqstate(struct sx9310_p *data) { u8 val = 0; if (sx9310_i2c_read(data, SX9310_IRQSTAT_REG, &val) >= 0) return val; return 0; } static void sx9310_initialize_register(struct sx9310_p *data) { u8 val = 0; int idx; for (idx = 0; idx < (sizeof(setup_reg) >> 1); idx++) { sx9310_i2c_write(data, setup_reg[idx].reg, setup_reg[idx].val); pr_info("[SX9310]: %s - Write Reg: 0x%x Value: 0x%x\n", __func__, setup_reg[idx].reg, setup_reg[idx].val); sx9310_i2c_read(data, setup_reg[idx].reg, &val); pr_info("[SX9310]: %s - Read Reg: 0x%x Value: 0x%x\n\n", __func__, setup_reg[idx].reg, val); } } static void sx9310_initialize_chip(struct sx9310_p *data) { int cnt = 0; while ((sx9310_get_nirq_state(data) == 0) && (cnt++ < 10)) { sx9310_read_irqstate(data); msleep(20); } if (cnt >= 10) pr_err("[SX9310]: %s - s/w reset fail(%d)\n", __func__, cnt); sx9310_initialize_register(data); } static int sx9310_set_offset_calibration(struct sx9310_p *data) { int ret = 0; ret = sx9310_i2c_write(data, SX9310_IRQSTAT_REG, 0xFF); return ret; } static void send_event(struct sx9310_p *data, u8 state) { data->normal_th = data->normal_th_buf; if (state == ACTIVE) { data->state = ACTIVE; #if (MAIN_SENSOR == 1) sx9310_i2c_write(data, SX9310_CPS_CTRL9_REG, data->normal_th); #ifdef CONFIG_SENSORS_SX9310_USE_2ND_CH sx9310_i2c_write(data, SX9310_CPS_CTRL8_REG, data->normal_th_ch2); #endif #else sx9310_i2c_write(data, SX9310_CPS_CTRL8_REG, data->normal_th); #endif pr_info("[SX9310]: %s - button touched\n", __func__); } else { data->state = IDLE; #if (MAIN_SENSOR == 1) sx9310_i2c_write(data, SX9310_CPS_CTRL9_REG, data->normal_th); #ifdef CONFIG_SENSORS_SX9310_USE_2ND_CH sx9310_i2c_write(data, SX9310_CPS_CTRL8_REG, data->normal_th_ch2); #endif #else sx9310_i2c_write(data, SX9310_CPS_CTRL8_REG, data->normal_th); #endif pr_info("[SX9310]: %s - button released\n", __func__); } if (data->skip_data == true) return; if (state == ACTIVE) input_report_rel(data->input, REL_MISC, 1); else input_report_rel(data->input, REL_MISC, 2); input_sync(data->input); } static void sx9310_display_data_reg(struct sx9310_p *data) { u8 val, reg; sx9310_i2c_write(data, SX9310_REGSENSORSELECT, MAIN_SENSOR); for (reg = SX9310_REGUSEMSB; reg <= SX9310_REGOFFSETLSB; reg++) { sx9310_i2c_read(data, reg, &val); pr_info("[SX9310]: %s - Register(0x%2x) data(0x%2x)\n", __func__, reg, val); } #ifdef CONFIG_SENSORS_SX9310_USE_2ND_CH sx9310_i2c_write(data, SX9310_REGSENSORSELECT, CH2_SENSOR); for (reg = SX9310_REGUSEMSB; reg <= SX9310_REGOFFSETLSB; reg++) { sx9310_i2c_read(data, reg, &val); pr_info("[SX9310]: %s 2ch - Register(0x%2x) data(0x%2x)\n", __func__, reg, val); } #endif } static void sx9310_get_data(struct sx9310_p *data) { u8 ms_byte = 0; u8 is_byte = 0; u8 ls_byte = 0; u8 buf[RAW_DATA_BLOCK_SIZE]; #if (MAIN_SENSOR == 0) s32 gain = 1 << ((setup_reg[5].val >> 2) & 0x03); #else s32 gain = 1 << (setup_reg[5].val & 0x03); #ifdef CONFIG_SENSORS_SX9310_USE_2ND_CH s32 gain2= 1 << ((setup_reg[5].val >> 2) & 0x03); #endif #endif mutex_lock(&data->read_mutex); sx9310_i2c_write(data, SX9310_REGSENSORSELECT, MAIN_SENSOR); sx9310_i2c_read_block(data, SX9310_REGUSEMSB, &buf[0], RAW_DATA_BLOCK_SIZE); data->useful = (s16)((s32)buf[0] << 8) | ((s32)buf[1]); data->avg = (s16)((s32)buf[2] << 8) | ((s32)buf[3]); data->offset = ((u16)buf[6] << 8) | ((u16)buf[7]); data->diff = (data->useful - data->avg) >> 4; ms_byte = (u8)(data->offset >> 13); is_byte = (u8)((data->offset & 0x1fc0) >> 6); ls_byte = (u8)(data->offset & 0x3f); data->capmain = (((s32)ms_byte * 234000) + ((s32)is_byte * 9000) + ((s32)ls_byte * 450)) + (((s32)data->useful * 50000) / (gain * 65536)); #ifdef CONFIG_SENSORS_SX9310_USE_2ND_CH sx9310_i2c_write(data, SX9310_REGSENSORSELECT, CH2_SENSOR); sx9310_i2c_read_block(data, SX9310_REGUSEMSB, &buf[0], RAW_DATA_BLOCK_SIZE); data->useful_ch2 = (s16)((s32)buf[0] << 8) | ((s32)buf[1]); data->avg_ch2 = (s16)((s32)buf[2] << 8) | ((s32)buf[3]); data->offset_ch2 = ((u16)buf[6] << 8) | ((u16)buf[7]); data->diff_ch2 = (data->useful_ch2 - data->avg_ch2) >> 4; ms_byte = (u8)(data->offset_ch2 >> 13); is_byte = (u8)((data->offset_ch2 & 0x1fc0) >> 6); ls_byte = (u8)(data->offset_ch2 & 0x3f); data->capmain_ch2 = (((s32)ms_byte * 234000) + ((s32)is_byte * 9000) + ((s32)ls_byte * 450)) + (((s32)data->useful_ch2 * 50000) / (gain2 * 65536)); #endif mutex_unlock(&data->read_mutex); pr_info("[SX9310]: %s - Capmain: %d, Useful: %d, avg: %d, diff: %d, Offset: %u\n", __func__, data->capmain, data->useful, data->avg, data->diff, data->offset); #ifdef CONFIG_SENSORS_SX9310_USE_2ND_CH pr_info("[SX9310]: %s - Capmain[2ch]: %d, Useful: %d, avg: %d, diff: %d, Offset: %u\n", __func__, data->capmain_ch2, data->useful_ch2, data->avg_ch2, data->diff_ch2, data->offset_ch2); #endif } static int sx9310_set_mode(struct sx9310_p *data, unsigned char mode) { int ret = -EINVAL; if (mode == SX9310_MODE_SLEEP) { ret = sx9310_i2c_write(data, SX9310_CPS_CTRL0_REG, setup_reg[2].val); } else if (mode == SX9310_MODE_NORMAL) { ret = sx9310_i2c_write(data, SX9310_CPS_CTRL0_REG, setup_reg[2].val | ENABLE_CSX); msleep(20); sx9310_set_offset_calibration(data); msleep(400); } pr_info("[SX9310]: %s - change the mode : %u\n", __func__, mode); return ret; } static void sx9310_set_enable(struct sx9310_p *data, int enable) { u8 status = 0; pr_info("[SX9310]: %s\n", __func__); if (enable == ON) { sx9310_pin_control(data, true); sx9310_i2c_read(data, SX9310_STAT0_REG, &status); pr_info("[SX9310]: %s(status : 0x%x)\n", __func__, status); data->diff_avg = 0; data->diff_cnt = 0; #ifdef CONFIG_SENSORS_SX9310_USE_2ND_CH data->diff_avg_ch2 = 0; data->diff_cnt_ch2 = 0; #endif sx9310_get_data(data); if (data->skip_data == true) { input_report_rel(data->input, REL_MISC, 2); input_sync(data->input); } else if (status & (CSX_STATUS_REG << MAIN_SENSOR)) { send_event(data, ACTIVE); } else { send_event(data, IDLE); } sx9310_i2c_write(data, SX9310_CPS_CTRL0_REG, setup_reg[2].val | ENABLE_CSX); msleep(20); /* make sure no interrupts are pending since enabling irq * will only work on next falling edge */ sx9310_read_irqstate(data); enable_irq(data->irq); enable_irq_wake(data->irq); } else { pr_info("[SX9310]: %s - disable\n", __func__); sx9310_pin_control(data, false); /* disable the chip interrupt to maintain the INT pin state */ sx9310_i2c_write(data, SX9310_CPS_CTRL0_REG, setup_reg[2].val); disable_irq(data->irq); disable_irq_wake(data->irq); } } static void sx9310_set_debug_work(struct sx9310_p *data, u8 enable, unsigned int time_ms) { if (enable == ON) { data->debug_count = 0; schedule_delayed_work(&data->debug_work, msecs_to_jiffies(time_ms)); } else { cancel_delayed_work_sync(&data->debug_work); } } static ssize_t sx9310_get_offset_calibration_show(struct device *dev, struct device_attribute *attr, char *buf) { u8 val = 0; struct sx9310_p *data = dev_get_drvdata(dev); sx9310_i2c_read(data, SX9310_IRQSTAT_REG, &val); return snprintf(buf, PAGE_SIZE, "%d\n", val); } static ssize_t sx9310_set_offset_calibration_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { unsigned long val; struct sx9310_p *data = dev_get_drvdata(dev); if (kstrtoul(buf, 10, &val)) { pr_err("[SX9310]: %s - Invalid Argument\n", __func__); return -EINVAL; } if (val) sx9310_set_offset_calibration(data); return count; } static ssize_t sx9310_register_write_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { int regist = 0, val = 0; struct sx9310_p *data = dev_get_drvdata(dev); if (sscanf(buf, "%d,%d", ®ist, &val) != 2) { pr_err("[SX9310]: %s - The number of data are wrong\n", __func__); return -EINVAL; } sx9310_i2c_write(data, (unsigned char)regist, (unsigned char)val); pr_info("[SX9310]: %s - Register(0x%2x) data(0x%2x)\n", __func__, regist, val); return count; } static ssize_t sx9310_register_read_show(struct device *dev, struct device_attribute *attr, char *buf) { unsigned char val[10], i; struct sx9310_p *data = dev_get_drvdata(dev); for (i = 0; i < 10; i++) { sx9310_i2c_read(data, i + 0x10, &val[i]); pr_info("[SX9310]: %s - Register(0x%2x) data(0x%2x)\n", __func__, i + 0x10, val[i]); } return snprintf(buf, PAGE_SIZE, "0x%x,0x%x,0x%x,0x%x,0x%x,0x%x,0x%x,0x%x,0x%x,0x%x\n", val[0], val[1], val[2], val[3], val[4], val[5], val[6], val[7], val[8], val[9]); } static ssize_t sx9310_read_data_show(struct device *dev, struct device_attribute *attr, char *buf) { struct sx9310_p *data = dev_get_drvdata(dev); sx9310_display_data_reg(data); return snprintf(buf, PAGE_SIZE, "%d\n", 0); } static ssize_t sx9310_sw_reset_show(struct device *dev, struct device_attribute *attr, char *buf) { struct sx9310_p *data = dev_get_drvdata(dev); pr_info("[SX9310]: %s\n", __func__); sx9310_set_offset_calibration(data); msleep(400); sx9310_get_data(data); return snprintf(buf, PAGE_SIZE, "%d\n", 0); } static ssize_t sx9310_freq_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { unsigned long val; struct sx9310_p *data = dev_get_drvdata(dev); if (kstrtoul(buf, 10, &val)) { pr_err("[SX9310]: %s - Invalid Argument\n", __func__); return count; } data->freq = (u16)val; val = ((val << 3) | (setup_reg[6].val & 0x07)) & 0xff; sx9310_i2c_write(data, SX9310_CPS_CTRL4_REG, (u8)val); pr_info("[SX9310]: %s - Freq : 0x%x\n", __func__, data->freq); return count; } static ssize_t sx9310_freq_show(struct device *dev, struct device_attribute *attr, char *buf) { struct sx9310_p *data = dev_get_drvdata(dev); pr_info("[SX9310]: %s - Freq : 0x%x\n", __func__, data->freq); return snprintf(buf, PAGE_SIZE, "%u\n", data->freq); } static ssize_t sx9310_vendor_show(struct device *dev, struct device_attribute *attr, char *buf) { return snprintf(buf, PAGE_SIZE, "%s\n", VENDOR_NAME); } static ssize_t sx9310_name_show(struct device *dev, struct device_attribute *attr, char *buf) { return snprintf(buf, PAGE_SIZE, "%s\n", MODEL_NAME); } static ssize_t sx9310_touch_mode_show(struct device *dev, struct device_attribute *attr, char *buf) { return snprintf(buf, PAGE_SIZE, "1\n"); } static ssize_t sx9310_raw_data_show(struct device *dev, struct device_attribute *attr, char *buf) { static int sum; struct sx9310_p *data = dev_get_drvdata(dev); sx9310_get_data(data); if (data->diff_cnt == 0) sum = data->diff; else sum += data->diff; if (++data->diff_cnt >= DIFF_READ_NUM) { data->diff_avg = sum / DIFF_READ_NUM; data->diff_cnt = 0; } return snprintf(buf, PAGE_SIZE, "%d,%d,%u,%d,%d\n", data->capmain, data->useful, data->offset, data->diff, data->avg); } static ssize_t sx9310_threshold_show(struct device *dev, struct device_attribute *attr, char *buf) { /* It's for init touch */ return snprintf(buf, PAGE_SIZE, "0\n"); } static ssize_t sx9310_threshold_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { return count; } static ssize_t sx9310_normal_threshold_show(struct device *dev, struct device_attribute *attr, char *buf) { struct sx9310_p *data = dev_get_drvdata(dev); u16 thresh_temp = 0, hysteresis = 0; u16 thresh_table[32] = {2, 4, 6, 8, 12, 16, 20, 24, 28, 32, 40, 48, 56, 64, 72, 80, 88, 96, 112, 128, 144, 160, 192, 224, 256, 320, 384, 512, 640, 768, 1024, 1536}; thresh_temp = (data->normal_th >> 3) & 0x1f; thresh_temp = thresh_table[thresh_temp]; /* CTRL10 */ hysteresis = (setup_reg[12].val >> 4) & 0x3; switch (hysteresis) { case 0x01: /* 6% */ hysteresis = thresh_temp >> 4; break; case 0x02: /* 12% */ hysteresis = thresh_temp >> 3; break; case 0x03: /* 25% */ hysteresis = thresh_temp >> 2; break; default: /* None */ break; } return snprintf(buf, PAGE_SIZE, "%d,%d\n", thresh_temp + hysteresis, thresh_temp - hysteresis); } static ssize_t sx9310_normal_threshold_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { unsigned long val; struct sx9310_p *data = dev_get_drvdata(dev); /* It's for normal touch */ if (kstrtoul(buf, 10, &val)) { pr_err("[SX9310]: %s - Invalid Argument\n", __func__); return -EINVAL; } data->normal_th &= 0x07; data->normal_th |= val; pr_info("[SX9310]: %s - normal threshold %lu\n", __func__, val); data->normal_th_buf = data->normal_th = (u8)(val); return count; } static ssize_t sx9310_onoff_show(struct device *dev, struct device_attribute *attr, char *buf) { struct sx9310_p *data = dev_get_drvdata(dev); return snprintf(buf, PAGE_SIZE, "%u\n", !data->skip_data); } static ssize_t sx9310_onoff_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { u8 val; int ret; struct sx9310_p *data = dev_get_drvdata(dev); ret = kstrtou8(buf, 2, &val); if (ret) { pr_err("[SX9310]: %s - Invalid Argument\n", __func__); return ret; } if (val == 0) { data->skip_data = true; if (atomic_read(&data->enable) == ON) { data->state = IDLE; input_report_rel(data->input, REL_MISC, 2); input_sync(data->input); } } else { data->skip_data = false; } pr_info("[SX9310]: %s -%u\n", __func__, val); return count; } static ssize_t sx9310_calibration_show(struct device *dev, struct device_attribute *attr, char *buf) { return snprintf(buf, PAGE_SIZE, "2,0,0\n"); } static ssize_t sx9310_calibration_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { return count; } static ssize_t sx9310_gain_show(struct device *dev, struct device_attribute *attr, char *buf) { int ret; #if (MAIN_SENSOR == 0) u8 gain = (setup_reg[5].val >> 2) & 0x03; #else u8 gain = setup_reg[5].val & 0x03; #endif switch (gain) { case 0x00: ret = snprintf(buf, PAGE_SIZE, "x1\n"); break; case 0x01: ret = snprintf(buf, PAGE_SIZE, "x2\n"); break; case 0x02: ret = snprintf(buf, PAGE_SIZE, "x4\n"); break; default: ret = snprintf(buf, PAGE_SIZE, "x8\n"); break; } return ret; } static ssize_t sx9310_range_show(struct device *dev, struct device_attribute *attr, char *buf) { int ret; switch ((setup_reg[7].val >> 6) & 0x03) { case 0x00: ret = snprintf(buf, PAGE_SIZE, "Large\n"); break; case 0x01: ret = snprintf(buf, PAGE_SIZE, "Medium\n"); break; case 0x02: ret = snprintf(buf, PAGE_SIZE, "Medium Small\n"); break; default: ret = snprintf(buf, PAGE_SIZE, "Small\n"); break; } return ret; } static ssize_t sx9310_diff_avg_show(struct device *dev, struct device_attribute *attr, char *buf) { struct sx9310_p *data = dev_get_drvdata(dev); return snprintf(buf, PAGE_SIZE, "%d\n", data->diff_avg); } #ifdef CONFIG_SENSORS_SX9310_USE_2ND_CH static ssize_t sx9310_ch2_normal_threshold_show(struct device *dev, struct device_attribute *attr, char *buf) { struct sx9310_p *data = dev_get_drvdata(dev); u16 thresh_temp = 0, hysteresis = 0; u16 thresh_table[32] = {2, 4, 6, 8, 12, 16, 20, 24, 28, 32, 40, 48, 56, 64, 72, 80, 88, 96, 112, 128, 144, 160, 192, 224, 256, 320, 384, 512, 640, 768, 1024, 1536}; thresh_temp = (data->normal_th_ch2 >> 3) & 0x1f; thresh_temp = thresh_table[thresh_temp]; /* CTRL10 */ hysteresis = (setup_reg[12].val >> 4) & 0x3; switch (hysteresis) { case 0x01: /* 6% */ hysteresis = thresh_temp >> 4; break; case 0x02: /* 12% */ hysteresis = thresh_temp >> 3; break; case 0x03: /* 25% */ hysteresis = thresh_temp >> 2; break; default: /* None */ break; } return snprintf(buf, PAGE_SIZE, "%d,%d\n", thresh_temp + hysteresis, thresh_temp - hysteresis); } static ssize_t sx9310_ch2_normal_threshold_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { unsigned long val; struct sx9310_p *data = dev_get_drvdata(dev); /* It's for normal touch */ if (kstrtoul(buf, 10, &val)) { pr_err("[SX9310]: %s - Invalid Argument\n", __func__); return -EINVAL; } pr_info("[SX9310]: %s - normal threshold %lu\n", __func__, val); data->normal_th_buf_ch2 = data->normal_th_ch2 = (u8)(val); return count; } static ssize_t sx9310_raw_data_ch2_show(struct device *dev, struct device_attribute *attr, char *buf) { static int sum; struct sx9310_p *data = dev_get_drvdata(dev); // sx9310_get_data(data); if (data->diff_cnt_ch2 == 0) sum = data->diff_ch2; else sum += data->diff_ch2; if (++data->diff_cnt_ch2 >= DIFF_READ_NUM) { data->diff_avg_ch2 = sum / DIFF_READ_NUM; data->diff_cnt_ch2 = 0; } return snprintf(buf, PAGE_SIZE, "%d,%d,%u,%d,%d\n", data->capmain_ch2, data->useful_ch2, data->offset_ch2, data->diff_ch2, data->avg_ch2); } static ssize_t sx9310_gain_ch2_show(struct device *dev, struct device_attribute *attr, char *buf) { int ret; u8 gain = (setup_reg[5].val >> 2) & 0x03; switch (gain) { case 0x00: ret = snprintf(buf, PAGE_SIZE, "x1\n"); break; case 0x01: ret = snprintf(buf, PAGE_SIZE, "x2\n"); break; case 0x02: ret = snprintf(buf, PAGE_SIZE, "x4\n"); break; default: ret = snprintf(buf, PAGE_SIZE, "x8\n"); break; } return ret; } static ssize_t sx9310_diff_avg_ch2_show(struct device *dev, struct device_attribute *attr, char *buf) { struct sx9310_p *data = dev_get_drvdata(dev); return snprintf(buf, PAGE_SIZE, "%d\n", data->diff_avg_ch2); } #endif static ssize_t sx9310_ch_state_show(struct device *dev, struct device_attribute *attr, char *buf) { int ret; struct sx9310_p *data = dev_get_drvdata(dev); if (atomic_read(&data->enable) == ON) { ret = snprintf(buf, PAGE_SIZE, "%d,%d\n", data->ch1_state, data->ch2_state); } else { ret = snprintf(buf, PAGE_SIZE, "%d,%d\n", NONE_ENABLE, NONE_ENABLE); } return ret; } static ssize_t sx9310_body_threshold_show(struct device *dev, struct device_attribute *attr, char *buf) { struct sx9310_p *data = dev_get_drvdata(dev); u16 thresh_temp = 0, hysteresis = 0; u16 thresh_table[7] = {300, 600, 900, 1200, 1500, 1800, 30000}; thresh_temp = (data->normal_th) & 0x07; thresh_temp = thresh_table[thresh_temp]; /* CTRL10 */ hysteresis = (setup_reg[12].val >> 4) & 0x3; switch (hysteresis) { case 0x01: /* 6% */ hysteresis = thresh_temp >> 4; break; case 0x02: /* 12% */ hysteresis = thresh_temp >> 3; break; case 0x03: /* 25% */ hysteresis = thresh_temp >> 2; break; default: /* None */ break; } return snprintf(buf, PAGE_SIZE, "%d,%d\n", thresh_temp + hysteresis, thresh_temp - hysteresis); } static ssize_t sx9310_body_threshold_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { unsigned long val; struct sx9310_p *data = dev_get_drvdata(dev); if (kstrtoul(buf, 10, &val)) { pr_err("[SX9310]: %s - Invalid Argument\n", __func__); return -EINVAL; } data->normal_th &= 0xf8; data->normal_th |= val; pr_info("[SX9310]: %s - body threshold %lu\n", __func__, val); data->normal_th_buf = data->normal_th = (u8)(val); return count; } static ssize_t sx9310_grip_flush_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { struct sx9310_p *data = dev_get_drvdata(dev); int ret = 0; u8 handle = 0; ret = kstrtou8(buf, 10, &handle); if (ret < 0) { pr_err("%s - kstrtou8 failed.(%d)\n", __func__, ret); return ret; } pr_info("[SX9310]: %s - handle = %d\n", __func__, handle); input_report_rel(data->input, REL_MAX, handle); input_sync(data->input); return size; } static DEVICE_ATTR(menual_calibrate, S_IRUGO | S_IWUSR | S_IWGRP, sx9310_get_offset_calibration_show, sx9310_set_offset_calibration_store); static DEVICE_ATTR(register_write, S_IWUSR | S_IWGRP, NULL, sx9310_register_write_store); static DEVICE_ATTR(register_read, S_IRUGO, sx9310_register_read_show, NULL); static DEVICE_ATTR(readback, S_IRUGO, sx9310_read_data_show, NULL); static DEVICE_ATTR(reset, S_IRUGO, sx9310_sw_reset_show, NULL); static DEVICE_ATTR(name, S_IRUGO, sx9310_name_show, NULL); static DEVICE_ATTR(vendor, S_IRUGO, sx9310_vendor_show, NULL); static DEVICE_ATTR(gain, S_IRUGO, sx9310_gain_show, NULL); static DEVICE_ATTR(range, S_IRUGO, sx9310_range_show, NULL); static DEVICE_ATTR(mode, S_IRUGO, sx9310_touch_mode_show, NULL); static DEVICE_ATTR(raw_data, S_IRUGO, sx9310_raw_data_show, NULL); static DEVICE_ATTR(diff_avg, S_IRUGO, sx9310_diff_avg_show, NULL); static DEVICE_ATTR(calibration, S_IRUGO | S_IWUSR | S_IWGRP, sx9310_calibration_show, sx9310_calibration_store); static DEVICE_ATTR(onoff, S_IRUGO | S_IWUSR | S_IWGRP, sx9310_onoff_show, sx9310_onoff_store); static DEVICE_ATTR(threshold, S_IRUGO | S_IWUSR | S_IWGRP, sx9310_threshold_show, sx9310_threshold_store); static DEVICE_ATTR(normal_threshold, S_IRUGO | S_IWUSR | S_IWGRP, sx9310_normal_threshold_show, sx9310_normal_threshold_store); static DEVICE_ATTR(freq, S_IRUGO | S_IWUSR | S_IWGRP, sx9310_freq_show, sx9310_freq_store); static DEVICE_ATTR(ch_state, S_IRUGO, sx9310_ch_state_show, NULL); static DEVICE_ATTR(body_threshold, S_IRUGO | S_IWUSR | S_IWGRP, sx9310_body_threshold_show, sx9310_body_threshold_store); #ifdef CONFIG_SENSORS_SX9310_USE_2ND_CH static DEVICE_ATTR(normal_threshold_ch2, S_IRUGO | S_IWUSR | S_IWGRP, sx9310_ch2_normal_threshold_show, sx9310_ch2_normal_threshold_store); static DEVICE_ATTR(gain_ch2, S_IRUGO, sx9310_gain_ch2_show, NULL); static DEVICE_ATTR(raw_data_ch2, S_IRUGO, sx9310_raw_data_ch2_show, NULL); static DEVICE_ATTR(diff_avg_ch2, S_IRUGO, sx9310_diff_avg_ch2_show, NULL); #endif static DEVICE_ATTR(grip_flush, S_IWUSR | S_IWGRP, NULL, sx9310_grip_flush_store); static struct device_attribute *sensor_attrs[] = { &dev_attr_menual_calibrate, &dev_attr_register_write, &dev_attr_register_read, &dev_attr_readback, &dev_attr_reset, &dev_attr_name, &dev_attr_vendor, &dev_attr_gain, &dev_attr_range, &dev_attr_mode, &dev_attr_diff_avg, &dev_attr_raw_data, &dev_attr_threshold, &dev_attr_normal_threshold, &dev_attr_onoff, &dev_attr_calibration, &dev_attr_freq, #ifdef CONFIG_SENSORS_SX9310_USE_2ND_CH &dev_attr_normal_threshold_ch2, &dev_attr_gain_ch2, &dev_attr_raw_data_ch2, &dev_attr_diff_avg_ch2, #endif &dev_attr_ch_state, &dev_attr_body_threshold, &dev_attr_grip_flush, NULL, }; static ssize_t sx9310_enable_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { u8 enable; int ret; struct sx9310_p *data = dev_get_drvdata(dev); int pre_enable = atomic_read(&data->enable); ret = kstrtou8(buf, 2, &enable); if (ret) { pr_err("[SX9310]: %s - Invalid Argument\n", __func__); return ret; } pr_info("[SX9310]: %s - new_value = %u old_value = %d\n", __func__, enable, pre_enable); if (pre_enable == enable) return size; atomic_set(&data->enable, enable); sx9310_set_enable(data, enable); return size; } static ssize_t sx9310_enable_show(struct device *dev, struct device_attribute *attr, char *buf) { struct sx9310_p *data = dev_get_drvdata(dev); return snprintf(buf, PAGE_SIZE, "%d\n", atomic_read(&data->enable)); } static ssize_t sx9310_flush_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { u8 enable; int ret; struct sx9310_p *data = dev_get_drvdata(dev); ret = kstrtou8(buf, 2, &enable); if (ret) { pr_err("[SX9310]: %s - Invalid Argument\n", __func__); return ret; } if (enable == 1) { input_report_rel(data->input, REL_MAX, 1); input_sync(data->input); } return size; } static DEVICE_ATTR(enable, S_IRUGO | S_IWUSR | S_IWGRP, sx9310_enable_show, sx9310_enable_store); static DEVICE_ATTR(flush, S_IWUSR | S_IWGRP, NULL, sx9310_flush_store); static struct attribute *sx9310_attributes[] = { &dev_attr_enable.attr, &dev_attr_flush.attr, NULL }; static struct attribute_group sx9310_attribute_group = { .attrs = sx9310_attributes }; static void sx9310_ch_interrupt_read(struct sx9310_p *data, u8 status) { if (status & (CSX_STATUS_REG << MAIN_SENSOR)) { if (status & (BODY_STATUS_REG << (MAIN_SENSOR+1))) { data->ch1_state = BODY_STATE; } else { data->ch1_state = TOUCH_STATE; } } else { data->ch1_state = IDLE_STATE; } #ifdef CONFIG_SENSORS_SX9310_USE_2ND_CH if (status & (CSX_STATUS_REG << CH2_SENSOR)) data->ch2_state = TOUCH_STATE; else data->ch2_state = IDLE_STATE; #endif pr_info("[SX9310]: %s - ch1:%d, ch2:%d\n", __func__, data->ch1_state, data->ch2_state); } static void sx9310_touch_process(struct sx9310_p *data, u8 flag) { u8 status = 0; sx9310_i2c_read(data, SX9310_STAT0_REG, &status); pr_info("[SX9310]: %s - (status: 0x%x)\n", __func__, status); sx9310_get_data(data); sx9310_ch_interrupt_read(data, status); #ifdef CONFIG_SENSORS_SX9310_USE_2ND_CH if (data->state == IDLE) { if (status & (CSX_STATUS_REG << MAIN_SENSOR)) { if (status & (CSX_STATUS_REG << CH2_SENSOR)) { if (status & (BODY_STATUS_REG << (MAIN_SENSOR+1))) send_event(data, ACTIVE); else pr_info("[SX9310]: %s - main D, ch2 D, but body no\n", __func__); } else { send_event(data, ACTIVE); } } else { if (status & (CSX_STATUS_REG << CH2_SENSOR)) pr_info("[SX9310]: %s - only CH2 D, no SAR\n", __func__); } } else { if (!(status & (CSX_STATUS_REG << MAIN_SENSOR))) send_event(data, IDLE); else pr_info("[SX9310]: %s - still touched\n", __func__); } #else if (data->state == IDLE) { if (status & (CSX_STATUS_REG << MAIN_SENSOR)) send_event(data, ACTIVE); else pr_info("[SX9310]: %s - already released\n", __func__); } else { if (!(status & (CSX_STATUS_REG << MAIN_SENSOR))) send_event(data, IDLE); else pr_info("[SX9310]: %s - still touched\n", __func__); } #endif } static void sx9310_process_interrupt(struct sx9310_p *data) { u8 flag = 0; /* since we are not in an interrupt don't need to disable irq. */ flag = sx9310_read_irqstate(data); if (flag & IRQ_PROCESS_CONDITION) sx9310_touch_process(data, flag); } static void sx9310_init_work_func(struct work_struct *work) { struct sx9310_p *data = container_of((struct delayed_work *)work, struct sx9310_p, init_work); sx9310_initialize_chip(data); #if (MAIN_SENSOR == 1) sx9310_i2c_write(data, SX9310_CPS_CTRL9_REG, data->normal_th); #ifdef CONFIG_SENSORS_SX9310_USE_2ND_CH sx9310_i2c_write(data, SX9310_CPS_CTRL8_REG, data->normal_th_ch2); #endif #else sx9310_i2c_write(data, SX9310_CPS_CTRL8_REG, data->normal_th); #endif sx9310_set_mode(data, SX9310_MODE_NORMAL); /* make sure no interrupts are pending since enabling irq * will only work on next falling edge */ sx9310_read_irqstate(data); } static void sx9310_irq_work_func(struct work_struct *work) { struct sx9310_p *data = container_of((struct delayed_work *)work, struct sx9310_p, irq_work); if (sx9310_get_nirq_state(data) == 0) sx9310_process_interrupt(data); else pr_err("[SX9310]: %s - nirq read high %d\n", __func__, sx9310_get_nirq_state(data)); } static void sx9310_debug_work_func(struct work_struct *work) { struct sx9310_p *data = container_of((struct delayed_work *)work, struct sx9310_p, debug_work); int ret; static int hall_flag = 1; static int size_char = sizeof(u8) * 4; if (atomic_read(&data->enable) == ON) { if (data->debug_count >= GRIP_LOG_TIME) { sx9310_get_data(data); data->debug_count = 0; } else { data->debug_count++; } } if (hall_flag) { ret = check_hallic_state(HALLIC1_PATH, data->hall_ic1); if (ret < 0) { pr_err("[SX9310]: %s - hallic 1 detect fail = %d\n", __func__, ret); } ret = check_hallic_state(HALLIC2_PATH, data->hall_ic2); if (ret < 0) { pr_err("[SX9310]: %s - hallic 2 detect fail = %d\n", __func__, ret); } /* Both hall IC closed : offset cal (once)*/ if (strncmp(data->hall_ic2, "CLOSE", size_char) == 0) { if (strncmp(data->hall_ic1, "CLOSE", size_char) == 0) { pr_info("[SX9310]: %s - hall IC1&2 is closed\n", __func__); sx9310_set_offset_calibration(data); hall_flag = 0; } } } schedule_delayed_work(&data->debug_work, msecs_to_jiffies(1000)); } static irqreturn_t sx9310_interrupt_thread(int irq, void *pdata) { struct sx9310_p *data = pdata; if (sx9310_get_nirq_state(data) == 1) { pr_err("[SX9310]: %s - nirq read high\n", __func__); } else { wake_lock_timeout(&data->grip_wake_lock, 3 * HZ); schedule_delayed_work(&data->irq_work, msecs_to_jiffies(100)); } return IRQ_HANDLED; } static int sx9310_input_init(struct sx9310_p *data) { int ret = 0; struct input_dev *dev = NULL; /* Create the input device */ dev = input_allocate_device(); if (!dev) return -ENOMEM; dev->name = MODULE_NAME; dev->id.bustype = BUS_I2C; input_set_capability(dev, EV_REL, REL_MISC); input_set_capability(dev, EV_REL, REL_MAX); input_set_drvdata(dev, data); ret = input_register_device(dev); if (ret < 0) { input_free_device(dev); return ret; } ret = sensors_create_symlink(dev); if (ret < 0) { input_unregister_device(dev); return ret; } ret = sysfs_create_group(&dev->dev.kobj, &sx9310_attribute_group); if (ret < 0) { sensors_remove_symlink(dev); input_unregister_device(dev); return ret; } /* save the input pointer and finish initialization */ data->input = dev; return 0; } static int sx9310_setup_pin(struct sx9310_p *data) { int ret; ret = gpio_request(data->gpio_nirq, "SX9310_nIRQ"); if (ret < 0) { pr_err("[SX9310]: %s - gpio %d request failed (%d)\n", __func__, data->gpio_nirq, ret); return ret; } ret = gpio_direction_input(data->gpio_nirq); if (ret < 0) { pr_err("[SX9310]: %s - failed to set gpio %d(%d)\n", __func__, data->gpio_nirq, ret); gpio_free(data->gpio_nirq); return ret; } return 0; } static void sx9310_initialize_variable(struct sx9310_p *data) { data->state = IDLE; data->skip_data = false; data->check_usb = false; data->freq = setup_reg[6].val >> 3; data->debug_count = 0; atomic_set(&data->enable, OFF); data->normal_th = (u8)221; data->normal_th_buf = data->normal_th; data->ch1_state = IDLE; #ifdef CONFIG_SENSORS_SX9310_USE_2ND_CH data->normal_th_ch2 = (u8)246; data->normal_th_buf_ch2 = data->normal_th_ch2; data->ch2_state = IDLE; #endif pr_info("[SX9310]: %s - Normal Touch Threshold : %u\n", __func__, data->normal_th); } static int sx9310_parse_dt(struct sx9310_p *data, struct device *dev) { struct device_node *node = dev->of_node; enum of_gpio_flags flags; if (node == NULL) return -ENODEV; data->gpio_nirq = of_get_named_gpio_flags(node, "sx9310-i2c,nirq-gpio", 0, &flags); if (data->gpio_nirq < 0) { pr_err("[SX9310]: %s - get gpio_nirq error\n", __func__); return -ENODEV; } data->gpio_nirq_en = of_get_named_gpio_flags(node, "sx9310-i2c,nirq_en-gpio", 0, &flags); if (data->gpio_nirq_en < 0) { pr_err("[SX9310]: %s - get gpio_nirq_en error\n", __func__); return -ENODEV; } data->p = devm_pinctrl_get(dev); if (IS_ERR(data->p)) { pr_err("%s: failed pinctrl_get\n", __func__); return -EINVAL; } data->pins_suspend = pinctrl_lookup_state(data->p, "grip_suspend"); if(IS_ERR(data->pins_suspend)) { pr_err("%s : could not get pins suspend_state (%li)\n", __func__, PTR_ERR(data->pins_suspend)); pinctrl_put(data->p); return -EINVAL; } data->pins_active = pinctrl_lookup_state(data->p, "grip_active"); if(IS_ERR(data->pins_active)) { pr_err("%s : could not get pins active_state (%li)\n", __func__, PTR_ERR(data->pins_active)); pinctrl_put(data->p); return -EINVAL; } return 0; } #if defined(CONFIG_MUIC_NOTIFIER) static int sx9310_cpuidle_muic_notifier(struct notifier_block *nb, unsigned long action, void *data) { struct sx9310_p *grip_data; muic_attached_dev_t attached_dev = *(muic_attached_dev_t *)data; grip_data = container_of(nb, struct sx9310_p, cpuidle_muic_nb); switch (attached_dev) { case ATTACHED_DEV_OTG_MUIC: if (action == MUIC_NOTIFY_CMD_ATTACH) grip_data->skip_data = true; else grip_data->skip_data = false; break; case ATTACHED_DEV_USB_MUIC: case ATTACHED_DEV_TA_MUIC: case ATTACHED_DEV_AFC_CHARGER_PREPARE_MUIC: if (action == MUIC_NOTIFY_CMD_ATTACH) grip_data->check_usb = true; else { if (grip_data->check_usb == true) { grip_data->check_usb = false; sx9310_set_offset_calibration(grip_data); pr_info("[SX9310]:%s - TA/USB is removed\n", __func__); } } break; default: break; } pr_info("%s: dev=%d, action=%lu\n", __func__, attached_dev, action); return NOTIFY_DONE; } #endif static int sx9310_probe(struct i2c_client *client, const struct i2c_device_id *id) { int ret = -ENODEV; struct sx9310_p *data = NULL; pr_info("[SX9310]: %s - Probe Start!\n", __func__); if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { pr_err("[SX9310]: %s - i2c_check_functionality error\n", __func__); goto exit; } /* create memory for main struct */ data = kzalloc(sizeof(struct sx9310_p), GFP_KERNEL); if (data == NULL) { pr_err("[SX9310]: %s - kzalloc error\n", __func__); ret = -ENOMEM; goto exit_kzalloc; } i2c_set_clientdata(client, data); data->client = client; data->factory_device = &client->dev; ret = sx9310_input_init(data); if (ret < 0) goto exit_input_init; wake_lock_init(&data->grip_wake_lock, WAKE_LOCK_SUSPEND, "grip_wake_lock"); mutex_init(&data->read_mutex); ret = sx9310_parse_dt(data, &client->dev); if (ret < 0) { pr_err("[SX9310]: %s - of_node error\n", __func__); ret = -ENODEV; goto exit_of_node; } ret = sx9310_setup_pin(data); if (ret) { pr_err("[SX9310]: %s - could not setup pin\n", __func__); goto exit_setup_pin; } /* read chip id */ ret = sx9310_i2c_write(data, SX9310_SOFTRESET_REG, SX9310_SOFTRESET); if (ret < 0) { pr_err("[SX9310]: %s - reset failed %d\n", __func__, ret); goto exit_chip_reset; } sx9310_initialize_variable(data); INIT_DELAYED_WORK(&data->init_work, sx9310_init_work_func); INIT_DELAYED_WORK(&data->irq_work, sx9310_irq_work_func); INIT_DELAYED_WORK(&data->debug_work, sx9310_debug_work_func); data->irq = gpio_to_irq(data->gpio_nirq); ret = request_threaded_irq(data->irq, NULL, sx9310_interrupt_thread, IRQF_TRIGGER_FALLING | IRQF_ONESHOT, "sx9310_irq", data); if (ret < 0) { pr_err("[SX9310]: %s - failed to set request irq %d(%d)\n", __func__, data->irq, ret); goto exit_request_threaded_irq; } disable_irq(data->irq); ret = sensors_register(&data->factory_device, data, sensor_attrs, MODULE_NAME); if (ret) { pr_err("[SX9310] %s - cound not register sensor(%d).\n", __func__, ret); goto grip_sensor_register_failed; } schedule_delayed_work(&data->init_work, msecs_to_jiffies(300)); sx9310_set_debug_work(data, ON, 20000); #if defined(CONFIG_MUIC_NOTIFIER) muic_notifier_register(&data->cpuidle_muic_nb, sx9310_cpuidle_muic_notifier, MUIC_NOTIFY_DEV_CPUIDLE); #endif pr_info("[SX9310]: %s - Probe done!\n", __func__); return 0; grip_sensor_register_failed: free_irq(data->irq, data); exit_request_threaded_irq: exit_chip_reset: gpio_free(data->gpio_nirq); exit_setup_pin: exit_of_node: mutex_destroy(&data->read_mutex); wake_lock_destroy(&data->grip_wake_lock); sysfs_remove_group(&data->input->dev.kobj, &sx9310_attribute_group); sensors_remove_symlink(data->input); input_unregister_device(data->input); exit_input_init: kfree(data); exit_kzalloc: exit: pr_err("[SX9310]: %s - Probe fail!\n", __func__); return ret; } static int sx9310_remove(struct i2c_client *client) { struct sx9310_p *data = (struct sx9310_p *)i2c_get_clientdata(client); if (atomic_read(&data->enable) == ON) sx9310_set_enable(data, OFF); sx9310_set_mode(data, SX9310_MODE_SLEEP); cancel_delayed_work_sync(&data->init_work); cancel_delayed_work_sync(&data->irq_work); cancel_delayed_work_sync(&data->debug_work); free_irq(data->irq, data); gpio_free(data->gpio_nirq); wake_lock_destroy(&data->grip_wake_lock); sensors_unregister(data->factory_device, sensor_attrs); sensors_remove_symlink(data->input); sysfs_remove_group(&data->input->dev.kobj, &sx9310_attribute_group); input_unregister_device(data->input); mutex_destroy(&data->read_mutex); kfree(data); return 0; } static int sx9310_suspend(struct device *dev) { struct sx9310_p *data = dev_get_drvdata(dev); int cnt = 0; pr_info("[SX9310]: %s\n", __func__); /* before go to sleep, make the interrupt pin as high*/ while ((sx9310_get_nirq_state(data) == 0) && (cnt++ < 3)) { sx9310_read_irqstate(data); msleep(10); } if (cnt >= 3) pr_err("[SX9310]: %s - s/w reset fail(%d)\n", __func__, cnt); sx9310_pin_control(data, false); sx9310_set_debug_work(data, OFF, 1000); return 0; } static int sx9310_resume(struct device *dev) { struct sx9310_p *data = dev_get_drvdata(dev); pr_info("[SX9310]: %s\n", __func__); sx9310_set_debug_work(data, ON, 1000); return 0; } static void sx9310_shutdown(struct i2c_client *client) { struct sx9310_p *data = i2c_get_clientdata(client); pr_info("[SX9310]: %s\n", __func__); sx9310_set_debug_work(data, OFF, 1000); if (atomic_read(&data->enable) == ON) sx9310_set_enable(data, OFF); sx9310_set_mode(data, SX9310_MODE_SLEEP); } static struct of_device_id sx9310_match_table[] = { { .compatible = "sx9310-i2c",}, {}, }; static const struct i2c_device_id sx9310_id[] = { { "sx9310_match_table", 0 }, { } }; static const struct dev_pm_ops sx9310_pm_ops = { .suspend = sx9310_suspend, .resume = sx9310_resume, }; static struct i2c_driver sx9310_driver = { .driver = { .name = MODEL_NAME, .owner = THIS_MODULE, .of_match_table = sx9310_match_table, .pm = &sx9310_pm_ops }, .probe = sx9310_probe, .remove = sx9310_remove, .shutdown = sx9310_shutdown, .id_table = sx9310_id, }; static int __init sx9310_init(void) { return i2c_add_driver(&sx9310_driver); } static void __exit sx9310_exit(void) { i2c_del_driver(&sx9310_driver); } module_init(sx9310_init); module_exit(sx9310_exit); MODULE_DESCRIPTION("Semtech Corp. SX9310 Capacitive Touch Controller Driver"); MODULE_AUTHOR("Samsung Electronics"); MODULE_LICENSE("GPL");