/* * Copyright (C) 2018 Semtech Corporation. All rights reserved. * * 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 "sx9360_reg.h" #ifdef CONFIG_CCIC_NOTIFIER #include #endif #ifdef CONFIG_USB_TYPEC_MANAGER_NOTIFIER #include #endif #ifdef CONFIG_VBUS_NOTIFIER #include #endif #define VENDOR_NAME "SEMTECH" #define MODEL_NAME "SX9360" #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 SX9360_MODE_SLEEP 0 #define SX9360_MODE_NORMAL 1 #define DIFF_READ_NUM 10 #define GRIP_LOG_TIME 15 /* 30 sec */ /* CS Main */ #define REFERENCE_DISABLE 0x02 #define ENABLE_CSX 0x03 #define CSX_STATUS_REG SX9360_STAT_PROXSTAT_FLAG #define IRQ_PROCESS_CONDITION (SX9360_IRQSTAT_TOUCH_FLAG \ | SX9360_IRQSTAT_RELEASE_FLAG \ | SX9360_IRQSTAT_COMPDONE_FLAG) #if defined(CONFIG_FOLDER_HALL) #define HALLIC_PATH "/sys/class/sec/sec_flip/flipStatus" #else #if IS_ENABLED(CONFIG_HALL_NEW_NODE) #define HALLIC_PATH "/sys/class/sec/hall_ic/hall_detect" #else #define HALLIC_PATH "/sys/class/sec/sec_key/hall_detect" #endif #endif #if IS_ENABLED(CONFIG_HALL_NEW_NODE) #define HALLIC_CERT_PATH "/sys/class/sec/hall_ic/certify_hall_detect" #else #define HALLIC_CERT_PATH "/sys/class/sec/sec_key/certify_hall_detect" #endif struct sx9360_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 mode_mutex; struct mutex read_mutex; #ifdef CONFIG_CCIC_NOTIFIER struct notifier_block ccic_nb; #endif #ifdef CONFIG_VBUS_NOTIFIER struct notifier_block vbus_nb; #endif bool skip_data; int irq; int gpio_nirq; int state; int init_done; int hallic_detect; int hallic_cert_detect; atomic_t enable; int again_m; int dgain_m; u16 detect_threshold; u16 offset; s32 capMain; s32 useful; s16 avg; s16 diff; s16 diff_avg; int diff_cnt; s32 useful_avg; int irq_count; int abnormal_mode; s16 max_diff; s16 max_normal_diff; int debug_count; char hall_ic[6]; }; static int sx9360_check_hallic_state(char *file_path, char hall_ic_status[]) { int iRet = 0; mm_segment_t old_fs; struct file *filep; char hall_sysfs[5]; old_fs = get_fs(); set_fs(KERNEL_DS); filep = filp_open(file_path, O_RDONLY, 0440); if (IS_ERR(filep)) { iRet = PTR_ERR(filep); pr_err("[SX9360]: %s - file open fail %d\n", __func__, iRet); set_fs(old_fs); return iRet; } iRet = filep->f_op->read(filep, hall_sysfs, sizeof(hall_sysfs), &filep->f_pos); if (iRet <= 0) { pr_err("[SX9360]: %s - file read fail %d\n", __func__, iRet); filp_close(filep, current->files); set_fs(old_fs); return -EIO; } else { strncpy(hall_ic_status, hall_sysfs, sizeof(hall_sysfs)); } filp_close(filep, current->files); set_fs(old_fs); return iRet; } static int sx9360_get_nirq_state(struct sx9360_p *data) { return gpio_get_value_cansleep(data->gpio_nirq); } static int sx9360_i2c_write(struct sx9360_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("[SX9360]: %s - i2c write error %d\n", __func__, ret); return 0; } static int sx9360_i2c_read(struct sx9360_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("[SX9360]: %s - i2c read error %d\n", __func__, ret); return ret; } static u8 sx9360_read_irqstate(struct sx9360_p *data) { u8 val = 0; if (sx9360_i2c_read(data, SX9360_IRQSTAT_REG, &val) >= 0) return (val & 0xFF); return 0; } static void sx9360_initialize_register(struct sx9360_p *data) { u8 val = 0; unsigned int idx; for (idx = 0; idx < (sizeof(setup_reg) >> 1); idx++) { sx9360_i2c_write(data, setup_reg[idx].reg, setup_reg[idx].val); pr_info("[SX9360]: %s - Write Reg: 0x%x Value: 0x%x\n", __func__, setup_reg[idx].reg, setup_reg[idx].val); sx9360_i2c_read(data, setup_reg[idx].reg, &val); pr_info("[SX9360]: %s - Read Reg: 0x%x Value: 0x%x\n\n", __func__, setup_reg[idx].reg, val); } sx9360_i2c_read(data, SX9360_PROXCTRL5_REG, &val); data->detect_threshold = (u16)val * (u16)val / 2; sx9360_i2c_read(data, SX9360_PROXCTRL4_REG, &val); val = (val & 0x30) >> 4; if(val) data->detect_threshold += data->detect_threshold >> (5 - val); pr_info("[SX9360]: %s - detect threshold: %u\n", __func__, data->detect_threshold); data->init_done = ON; } static void sx9360_initialize_chip(struct sx9360_p *data) { int cnt = 0; while((sx9360_get_nirq_state(data) == 0) && (cnt++ < 10)) { sx9360_read_irqstate(data); msleep(20); } if (cnt >= 10) pr_err("[SX9360]: %s - s/w reset fail(%d)\n", __func__, cnt); sx9360_initialize_register(data); } static int sx9360_set_offset_calibration(struct sx9360_p *data) { int ret = 0; pr_info("[SX9360]: %s\n", __func__); ret = sx9360_i2c_write(data, SX9360_STAT_REG, SX9360_STAT_COMPSTAT_ALL_FLAG); return ret; } static void sx9360_send_event(struct sx9360_p *data, u8 state) { if (data->skip_data == true) { pr_info("[SX9360]: %s - skip grip event\n", __func__); return; } if (state == ACTIVE) { data->state = ACTIVE; pr_info("[SX9360]: %s - touched\n", __func__); } else { data->state = IDLE; pr_info("[SX9360]: %s - released\n", __func__); } 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 sx9360_display_data_reg(struct sx9360_p *data) { u8 val, reg; pr_info("[SX9360]: ############# %d reference #############\n", 0); for (reg = SX9360_REGUSEMSBPHR; reg <= SX9360_REGOFFSETLSBPHR; reg++) { sx9360_i2c_read(data, reg, &val); pr_info("[SX9360]: %s - Register(0x%2x) data(0x%2x)\n", __func__, reg, val); } pr_info("[SX9360]: ############# %d Main #############\n", 0); for (reg = SX9360_REGUSEMSBPHM; reg <= SX9360_REGOFFSETLSBPHM; reg++) { sx9360_i2c_read(data, reg, &val); pr_info("[SX9360]: %s - Register(0x%2x) data(0x%2x)\n", __func__, reg, val); } } static void sx9360_get_gain(struct sx9360_p *data) { u8 msByte; static const int again_phm[]={7500,22500,37500,52500,60000,75000,90000,105000}; sx9360_i2c_read(data, SX9360_AFEPARAM1PHM_REG, &msByte); msByte=(msByte>>4) & 0x07; data->again_m = again_phm[msByte]; sx9360_i2c_read(data, SX9360_PROXCTRL0PHM_REG, &msByte); msByte=(msByte>>3) & 0x07; if(msByte) data->dgain_m = 1 << (msByte-1); else data->dgain_m = 1; } static void sx9360_get_data(struct sx9360_p *data) { u8 msByte = 0; u8 lsByte = 0; u16 offset = 0; s32 capMain = 0, useful = 0; s16 avg = 0, diff = 0; s16 retry = 0; u8 convstat = 0; mutex_lock(&data->read_mutex); sx9360_get_gain(data); while(1) { sx9360_i2c_read(data, SX9360_STAT_REG, &convstat); convstat &= 0x01; if(++retry > 5 || convstat == 0) break; usleep_range(10000, 11000); } pr_info("[SX9360]: %s retry : %d, CONVSTAT : %u\n", __func__, retry, convstat); /* diff read */ sx9360_i2c_read(data, SX9360_REGDIFFMSBPHM, &msByte); sx9360_i2c_read(data, SX9360_REGDIFFLSBPHM, &lsByte); diff = (s16)msByte; diff = (diff << 8) | ((s16)lsByte); /* Calculate out the Main Cap information */ sx9360_i2c_read(data, SX9360_REGUSEMSBPHM, &msByte); sx9360_i2c_read(data, SX9360_REGUSELSBPHM, &lsByte); useful = (s32)msByte; useful = (useful << 8) | ((s32)lsByte); if (useful > 32767) useful -= 65536; sx9360_i2c_read(data, SX9360_REGOFFSETMSBPHM, &msByte); sx9360_i2c_read(data, SX9360_REGOFFSETLSBPHM, &lsByte); offset = (u16)msByte; offset = (offset << 8) | ((u16)lsByte); msByte = (u8)((offset >> 7) & 0x7F); lsByte = (u8)((offset) & 0x7F); capMain = (((s32)msByte * 30000) + ((s32)lsByte * 500)) + (s32)(((s64)useful * data->again_m) / (data->dgain_m * 32768)); /* avg read */ sx9360_i2c_read(data, SX9360_REGAVGMSBPHM, &msByte); sx9360_i2c_read(data, SX9360_REGAVGLSBPHM, &lsByte); avg = (s16)msByte; avg = (avg << 8) | ((s16)lsByte); data->useful = useful; data->offset = offset; data->capMain = capMain; data->avg = avg; data->diff = diff; mutex_unlock(&data->read_mutex); pr_info("[SX9360]: %s - capMain: %ld, useful: %ld, avg: %d, diff: %d, Offset: %u\n", __func__, (long int)capMain, (long int)useful, avg, diff, offset); } static int sx9360_set_mode(struct sx9360_p *data, unsigned char mode) { int ret = -EINVAL; pr_info("[SX9360]: %s %u\n", __func__, mode); mutex_lock(&data->mode_mutex); if (mode == SX9360_MODE_SLEEP) { ret = sx9360_i2c_write(data, SX9360_GNRLCTRL0_REG, SX9360_GNRLCTRL0_VAL_PHOFF); } else if (mode == SX9360_MODE_NORMAL) { ret = sx9360_i2c_write(data, SX9360_GNRLCTRL0_REG, SX9360_GNRLCTRL0_VAL_PHOFF | REFERENCE_DISABLE); msleep(20); sx9360_set_offset_calibration(data); msleep(450); } pr_info("[SX9360]: %s - change the mode : %u\n", __func__, mode); mutex_unlock(&data->mode_mutex); return ret; } static void sx9360_check_status(struct sx9360_p *data) { u8 status = 0; sx9360_i2c_read(data, SX9360_STAT_REG, &status); pr_info("[SX9360]: %s - (status: 0x%x)\n", __func__, status); if (data->skip_data == true) { input_report_rel(data->input, REL_MISC, 2); input_sync(data->input); return; } if ((status & CSX_STATUS_REG) && (data->diff > data->detect_threshold)) { sx9360_send_event(data, ACTIVE); } else { sx9360_send_event(data, IDLE); } } static void sx9360_set_enable(struct sx9360_p *data, int enable) { int pre_enable = atomic_read(&data->enable); pr_info("[SX9360]: %s %d\n", __func__, enable); if (enable) { if (pre_enable == OFF) { data->diff_avg = 0; data->diff_cnt = 0; data->useful_avg = 0; sx9360_get_data(data); sx9360_check_status(data); msleep(20); /* make sure no interrupts are pending since enabling irq * will only work on next falling edge */ sx9360_read_irqstate(data); /* enable interrupt */ sx9360_i2c_write(data, SX9360_IRQ_ENABLE_REG, 0x0E); enable_irq(data->irq); enable_irq_wake(data->irq); atomic_set(&data->enable, ON); } } else { if (pre_enable == ON) { /* disable interrupt */ sx9360_i2c_write(data, SX9360_IRQ_ENABLE_REG, 0x00); disable_irq(data->irq); disable_irq_wake(data->irq); atomic_set(&data->enable, OFF); } } } static void sx9360_set_debug_work(struct sx9360_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 sx9360_get_offset_calibration_show(struct device *dev, struct device_attribute *attr, char *buf) { u8 val = 0; struct sx9360_p *data = dev_get_drvdata(dev); sx9360_i2c_read(data, SX9360_IRQSTAT_REG, &val); return snprintf(buf, PAGE_SIZE, "%d\n", val); } static ssize_t sx9360_set_offset_calibration_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { unsigned long val; struct sx9360_p *data = dev_get_drvdata(dev); if (kstrtoul(buf, 10, &val)) { pr_err("[SX9360]: %s - Invalid Argument\n", __func__); return -EINVAL; } if (val) sx9360_set_offset_calibration(data); return count; } static ssize_t sx9360_register_write_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { int regist = 0, val = 0; struct sx9360_p *data = dev_get_drvdata(dev); if (sscanf(buf, "%2x,%2x", ®ist, &val) != 2) { pr_err("[SX9360]: %s - The number of data are wrong\n", __func__); return -EINVAL; } sx9360_i2c_write(data, (unsigned char)regist, (unsigned char)val); pr_info("[SX9360]: %s - Register(0x%2x) data(0x%2x)\n", __func__, regist, val); return count; } static ssize_t sx9360_register_read_show(struct device *dev, struct device_attribute *attr, char *buf) { u8 val = 0; int offset = 0, idx = 0; struct sx9360_p *data = dev_get_drvdata(dev); for (idx = 0; idx < (int)(ARRAY_SIZE(setup_reg)); idx++) { sx9360_i2c_read(data, setup_reg[idx].reg, &val); pr_info("[SX9360]: %s - Read Reg: 0x%x Value: 0x%x\n\n", __func__, setup_reg[idx].reg, val); offset += snprintf(buf + offset, PAGE_SIZE - offset, "Reg: 0x%x Value: 0x%08x\n", setup_reg[idx].reg, val); } return offset; } static ssize_t sx9360_read_data_show(struct device *dev, struct device_attribute *attr, char *buf) { struct sx9360_p *data = dev_get_drvdata(dev); sx9360_display_data_reg(data); return snprintf(buf, PAGE_SIZE, "%d\n", 0); } static ssize_t sx9360_sw_reset_show(struct device *dev, struct device_attribute *attr, char *buf) { struct sx9360_p *data = dev_get_drvdata(dev); pr_info("[SX9360]: %s\n", __func__); sx9360_set_offset_calibration(data); msleep(450); sx9360_get_data(data); return snprintf(buf, PAGE_SIZE, "%d\n", 0); } static ssize_t sx9360_vendor_show(struct device *dev, struct device_attribute *attr, char *buf) { return snprintf(buf, PAGE_SIZE, "%s\n", VENDOR_NAME); } static ssize_t sx9360_name_show(struct device *dev, struct device_attribute *attr, char *buf) { return snprintf(buf, PAGE_SIZE, "%s\n", MODEL_NAME); } static ssize_t sx9360_touch_mode_show(struct device *dev, struct device_attribute *attr, char *buf) { return snprintf(buf, PAGE_SIZE, "1\n"); } static ssize_t sx9360_raw_data_show(struct device *dev, struct device_attribute *attr, char *buf) { static s32 sum_diff, sum_useful; struct sx9360_p *data = dev_get_drvdata(dev); sx9360_get_data(data); if (data->diff_cnt == 0) { sum_diff = (s32)data->diff; sum_useful = data->useful; } else { sum_diff += (s32)data->diff; sum_useful += data->useful; } if (++data->diff_cnt >= DIFF_READ_NUM) { data->diff_avg = (s16)(sum_diff / DIFF_READ_NUM); data->useful_avg = sum_useful / DIFF_READ_NUM; data->diff_cnt = 0; } return snprintf(buf, PAGE_SIZE, "%ld,%ld,%u,%d,%d\n", (long int)data->capMain, (long int)data->useful, data->offset, data->diff, data->avg); } static ssize_t sx9360_diff_avg_show(struct device *dev, struct device_attribute *attr, char *buf) { struct sx9360_p *data = dev_get_drvdata(dev); return snprintf(buf, PAGE_SIZE, "%d\n", data->diff_avg); } static ssize_t sx9360_useful_avg_show(struct device *dev, struct device_attribute *attr, char *buf) { struct sx9360_p *data = dev_get_drvdata(dev); return snprintf(buf, PAGE_SIZE, "%ld\n", (long int)data->useful_avg); } static ssize_t sx9360_avgnegfilt_show(struct device *dev, struct device_attribute *attr, char *buf) { struct sx9360_p *data = dev_get_drvdata(dev); u8 avgnegfilt = 0; sx9360_i2c_read(data, SX9360_PROXCTRL3_REG, &avgnegfilt); avgnegfilt = (avgnegfilt & 0x38) >> 3; if (avgnegfilt == 7) return snprintf(buf, PAGE_SIZE, "1\n"); else if (avgnegfilt > 0 && avgnegfilt < 7) return snprintf(buf, PAGE_SIZE, "1-1/%d\n", 1 << avgnegfilt); else if (avgnegfilt == 0) return snprintf(buf, PAGE_SIZE, "0\n"); return snprintf(buf, PAGE_SIZE, "not set\n"); } static ssize_t sx9360_avgposfilt_show(struct device *dev, struct device_attribute *attr, char *buf) { struct sx9360_p *data = dev_get_drvdata(dev); u8 avgposfilt = 0; sx9360_i2c_read(data, SX9360_PROXCTRL3_REG, &avgposfilt); avgposfilt = avgposfilt & 0x07; if (avgposfilt == 7) return snprintf(buf, PAGE_SIZE, "1\n"); else if (avgposfilt > 1 && avgposfilt < 7) return snprintf(buf, PAGE_SIZE, "1-1/%d\n", 16 << avgposfilt); else if (avgposfilt == 1) return snprintf(buf, PAGE_SIZE, "1-1/16\n"); return snprintf(buf, PAGE_SIZE, "0\n"); } static ssize_t sx9360_gain_show(struct device *dev, struct device_attribute *attr, char *buf) { struct sx9360_p *data = dev_get_drvdata(dev); u8 gain = 0; sx9360_i2c_read(data, SX9360_PROXCTRL0PHM_REG, &gain); gain = (gain & 0x38) >> 3; if (gain > 0 && gain < 5) return snprintf(buf, PAGE_SIZE, "x%u\n", 1 << (gain - 1)); return snprintf(buf, PAGE_SIZE, "Reserved\n"); } static ssize_t sx9360_range_show(struct device *dev, struct device_attribute *attr, char *buf) { return snprintf(buf, PAGE_SIZE, "None\n"); } static ssize_t sx9360_avgthresh_show(struct device *dev, struct device_attribute *attr, char *buf) { struct sx9360_p *data = dev_get_drvdata(dev); u8 avgthresh = 0; sx9360_i2c_read(data, SX9360_PROXCTRL1_REG, &avgthresh); avgthresh = avgthresh & 0x3F; return snprintf(buf, PAGE_SIZE, "%ld\n", 512 * (long int)avgthresh); } static ssize_t sx9360_rawfilt_show(struct device *dev, struct device_attribute *attr, char *buf) { struct sx9360_p *data = dev_get_drvdata(dev); u8 rawfilt = 0; sx9360_i2c_read(data, SX9360_PROXCTRL0PHM_REG, &rawfilt); rawfilt = rawfilt & 0x07; if (rawfilt > 0 && rawfilt < 8) return snprintf(buf, PAGE_SIZE, "1-1/%d\n", 1 << rawfilt); return snprintf(buf, PAGE_SIZE, "0\n"); } static ssize_t sx9360_sampling_freq_show(struct device *dev, struct device_attribute *attr, char *buf) { struct sx9360_p *data = dev_get_drvdata(dev); u8 sampling_freq = 0; const char *table[16] = { "250", "200", "166.67", "142.86", "125", "100", "83.33", "71.43", "62.50", "50", "41.67", "35.71", "27.78", "20.83", "15.62", "7.81"}; sx9360_i2c_read(data, SX9360_AFEPARAM1PHM_REG, &sampling_freq); sampling_freq = sampling_freq & 0x0F; return snprintf(buf, PAGE_SIZE, "%skHz\n", table[sampling_freq]); } static ssize_t sx9360_scan_period_show(struct device *dev, struct device_attribute *attr, char *buf) { struct sx9360_p *data = dev_get_drvdata(dev); u8 scan_period = 0; sx9360_i2c_read(data, SX9360_GNRLCTRL2_REG, &scan_period); return snprintf(buf, PAGE_SIZE, "%ld\n", (long int)(((long int)scan_period << 11) / 1000)); } static ssize_t sx9360_again_show(struct device *dev, struct device_attribute *attr, char *buf) { struct sx9360_p *data = dev_get_drvdata(dev); const char *table[8] = { "+/-0.75", "+/-2.25", "+/-3.75", "+/-5.25", "+/-6", "+/-7.5", "+/-9", "+/-10.5"}; u8 again = 0; sx9360_i2c_read(data, SX9360_AFEPARAM1PHM_REG, &again); again = (again & 0x70) >> 4; return snprintf(buf, PAGE_SIZE, "%spF\n", table[again]); } static ssize_t sx9360_phase_show(struct device *dev, struct device_attribute *attr, char *buf) { return snprintf(buf, PAGE_SIZE, "1\n"); } static ssize_t sx9360_hysteresis_show(struct device *dev, struct device_attribute *attr, char *buf) { struct sx9360_p *data = dev_get_drvdata(dev); const char *table[4] = {"None", "+/-6%", "+/-12%", "+/-25%"}; u8 hyst = 0; sx9360_i2c_read(data, SX9360_PROXCTRL4_REG, &hyst); hyst = (hyst & 0x30) >> 4; return snprintf(buf, PAGE_SIZE, "%s\n", table[hyst]); } static ssize_t sx9360_resolution_show(struct device *dev, struct device_attribute *attr, char *buf) { struct sx9360_p *data = dev_get_drvdata(dev); u8 resolution = 0; sx9360_i2c_read(data, SX9360_AFEPARAM0PHM_REG, &resolution); resolution = resolution & 0x7; return snprintf(buf, PAGE_SIZE, "%u\n", 1 << (resolution + 3)); } static ssize_t sx9360_adc_filt_show(struct device *dev, struct device_attribute *attr, char *buf) { return snprintf(buf, PAGE_SIZE, "None\n"); } static ssize_t sx9360_useful_filt_show(struct device *dev, struct device_attribute *attr, char *buf) { struct sx9360_p *data = dev_get_drvdata(dev); u8 useful_filt = 0; sx9360_i2c_read(data, SX9360_USEFILTER4_REG, &useful_filt); useful_filt = useful_filt & 0x01; return snprintf(buf, PAGE_SIZE, "%s\n", useful_filt ? "on" : "off"); } static ssize_t sx9360_irq_count_show(struct device *dev, struct device_attribute *attr, char *buf) { struct sx9360_p *data = dev_get_drvdata(dev); int ret = 0; s16 max_diff_val = 0; if (data->irq_count) { ret = -1; max_diff_val = data->max_diff; } else { max_diff_val = data->max_normal_diff; } pr_info("[SX9360]: %s - called\n", __func__); return snprintf(buf, PAGE_SIZE, "%d,%d,%d\n", ret, data->irq_count, max_diff_val); } static ssize_t sx9360_irq_count_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct sx9360_p *data = dev_get_drvdata(dev); u8 onoff; int ret; ret = kstrtou8(buf, 10, &onoff); if (ret < 0) { pr_err("[SX9360]: %s - kstrtou8 failed.(%d)\n", __func__, ret); return count; } mutex_lock(&data->read_mutex); if (onoff == 0) { data->abnormal_mode = OFF; } else if (onoff == 1) { data->abnormal_mode = ON; data->irq_count = 0; data->max_diff = 0; data->max_normal_diff = 0; } else { pr_err("[SX9360]: %s - unknown value %d\n", __func__, onoff); } mutex_unlock(&data->read_mutex); pr_info("[SX9360]: %s - %d\n", __func__, onoff); return count; } static ssize_t sx9360_normal_threshold_show(struct device *dev, struct device_attribute *attr, char *buf) { struct sx9360_p *data = dev_get_drvdata(dev); u8 th_buf = 0, hyst = 0; u32 threshold = 0; sx9360_i2c_read(data, SX9360_PROXCTRL5_REG, &th_buf); threshold = (u32)th_buf * (u32)th_buf / 2; sx9360_i2c_read(data, SX9360_PROXCTRL4_REG, &hyst); hyst = (hyst & 0x30) >> 4; switch (hyst) { case 0x01: /* 6% */ hyst = threshold >> 4; break; case 0x02: /* 12% */ hyst = threshold >> 3; break; case 0x03: /* 25% */ hyst = threshold >> 2; break; default: /* None */ break; } return snprintf(buf, PAGE_SIZE, "%lu,%lu\n", (u32)threshold + (u32)hyst, (u32)threshold - (u32)hyst); } static ssize_t sx9360_onoff_show(struct device *dev, struct device_attribute *attr, char *buf) { struct sx9360_p *data = dev_get_drvdata(dev); return snprintf(buf, PAGE_SIZE, "%u\n", !data->skip_data); } static ssize_t sx9360_onoff_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { u8 val; int ret; struct sx9360_p *data = dev_get_drvdata(dev); ret = kstrtou8(buf, 2, &val); if (ret) { pr_err("[SX9360]: %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("[SX9360]: %s -%u\n", __func__, val); return count; } static DEVICE_ATTR(menual_calibrate, S_IRUGO | S_IWUSR | S_IWGRP, sx9360_get_offset_calibration_show, sx9360_set_offset_calibration_store); static DEVICE_ATTR(register_write, S_IWUSR | S_IWGRP, NULL, sx9360_register_write_store); static DEVICE_ATTR(register_read, S_IRUGO, sx9360_register_read_show, NULL); static DEVICE_ATTR(readback, S_IRUGO, sx9360_read_data_show, NULL); static DEVICE_ATTR(reset, S_IRUGO, sx9360_sw_reset_show, NULL); static DEVICE_ATTR(name, S_IRUGO, sx9360_name_show, NULL); static DEVICE_ATTR(vendor, S_IRUGO, sx9360_vendor_show, NULL); static DEVICE_ATTR(mode, S_IRUGO, sx9360_touch_mode_show, NULL); static DEVICE_ATTR(raw_data, S_IRUGO, sx9360_raw_data_show, NULL); static DEVICE_ATTR(diff_avg, S_IRUGO, sx9360_diff_avg_show, NULL); static DEVICE_ATTR(useful_avg, S_IRUGO, sx9360_useful_avg_show, NULL); static DEVICE_ATTR(onoff, S_IRUGO | S_IWUSR | S_IWGRP, sx9360_onoff_show, sx9360_onoff_store); static DEVICE_ATTR(normal_threshold, S_IRUGO, sx9360_normal_threshold_show, NULL); static DEVICE_ATTR(avg_negfilt, S_IRUGO, sx9360_avgnegfilt_show, NULL); static DEVICE_ATTR(avg_posfilt, S_IRUGO, sx9360_avgposfilt_show, NULL); static DEVICE_ATTR(avg_thresh, S_IRUGO, sx9360_avgthresh_show, NULL); static DEVICE_ATTR(rawfilt, S_IRUGO, sx9360_rawfilt_show, NULL); static DEVICE_ATTR(sampling_freq, S_IRUGO, sx9360_sampling_freq_show, NULL); static DEVICE_ATTR(scan_period, S_IRUGO, sx9360_scan_period_show, NULL); static DEVICE_ATTR(gain, S_IRUGO, sx9360_gain_show, NULL); static DEVICE_ATTR(range, S_IRUGO, sx9360_range_show, NULL); static DEVICE_ATTR(analog_gain, S_IRUGO, sx9360_again_show, NULL); static DEVICE_ATTR(phase, S_IRUGO, sx9360_phase_show, NULL); static DEVICE_ATTR(hysteresis, S_IRUGO, sx9360_hysteresis_show, NULL); static DEVICE_ATTR(irq_count, S_IRUGO | S_IWUSR | S_IWGRP, sx9360_irq_count_show, sx9360_irq_count_store); static DEVICE_ATTR(resolution, S_IRUGO, sx9360_resolution_show, NULL); static DEVICE_ATTR(adc_filt, S_IRUGO, sx9360_adc_filt_show, NULL); static DEVICE_ATTR(useful_filt, S_IRUGO, sx9360_useful_filt_show, NULL); 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_mode, &dev_attr_raw_data, &dev_attr_diff_avg, &dev_attr_useful_avg, &dev_attr_onoff, &dev_attr_normal_threshold, &dev_attr_avg_negfilt, &dev_attr_avg_posfilt, &dev_attr_avg_thresh, &dev_attr_rawfilt, &dev_attr_sampling_freq, &dev_attr_scan_period, &dev_attr_gain, &dev_attr_range, &dev_attr_analog_gain, &dev_attr_phase, &dev_attr_hysteresis, &dev_attr_irq_count, &dev_attr_resolution, &dev_attr_adc_filt, &dev_attr_useful_filt, NULL, }; /*****************************************************************************/ static ssize_t sx9360_enable_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { u8 enable; int ret; struct sx9360_p *data = dev_get_drvdata(dev); ret = kstrtou8(buf, 2, &enable); if (ret) { pr_err("[SX9360]: %s - Invalid Argument\n", __func__); return ret; } pr_info("[SX9360]: %s - new_value = %u\n", __func__, enable); if ((enable == 0) || (enable == 1)) sx9360_set_enable(data, (int)enable); return size; } static ssize_t sx9360_enable_show(struct device *dev, struct device_attribute *attr, char *buf) { struct sx9360_p *data = dev_get_drvdata(dev); return snprintf(buf, PAGE_SIZE, "%d\n", atomic_read(&data->enable)); } static DEVICE_ATTR(enable, S_IRUGO | S_IWUSR | S_IWGRP, sx9360_enable_show, sx9360_enable_store); static struct attribute *sx9360_attributes[] = { &dev_attr_enable.attr, NULL }; static struct attribute_group sx9360_attribute_group = { .attrs = sx9360_attributes }; static void sx9360_touch_process(struct sx9360_p *data) { u8 status = 0; sx9360_i2c_read(data, SX9360_STAT_REG, &status); pr_info("[SX9360]: %s - 0x%x\n", __func__, status); sx9360_get_data(data); if (data->abnormal_mode) { if (status & CSX_STATUS_REG) { if (data->max_diff < data->diff) data->max_diff = data->diff; data->irq_count++; } } if (data->state == IDLE) { if (status & CSX_STATUS_REG) sx9360_send_event(data, ACTIVE); else pr_info("[SX9360]: %s - 0x%x already released.\n", __func__, status); } else { /* User released button */ if (!(status & CSX_STATUS_REG)) { sx9360_send_event(data, IDLE); } else { pr_info("[SX9360]: %s - 0x%x still touched\n", __func__, status); } } } static void sx9360_process_interrupt(struct sx9360_p *data) { u8 status = 0; /* since we are not in an interrupt don't need to disable irq. */ status = sx9360_read_irqstate(data); pr_info("[SX9360]: %s - status %d\n", __func__, status); if (status & IRQ_PROCESS_CONDITION) sx9360_touch_process(data); } static void sx9360_init_work_func(struct work_struct *work) { struct sx9360_p *data = container_of((struct delayed_work *)work, struct sx9360_p, init_work); sx9360_initialize_chip(data); sx9360_set_mode(data, SX9360_MODE_NORMAL); /* make sure no interrupts are pending since enabling irq * will only work on next falling edge */ sx9360_read_irqstate(data); } static void sx9360_irq_work_func(struct work_struct *work) { struct sx9360_p *data = container_of((struct delayed_work *)work, struct sx9360_p, irq_work); if (sx9360_get_nirq_state(data) == 0) sx9360_process_interrupt(data); else pr_err("[SX9360]: %s - nirq read high %d\n", __func__, sx9360_get_nirq_state(data)); } static void sx9360_debug_work_func(struct work_struct *work) { struct sx9360_p *data = container_of((struct delayed_work *)work, struct sx9360_p, debug_work); static int hall_flag = 1; static int hall_cert_flag = 1; #if defined(CONFIG_FOLDER_HALL) char str[2] = "0"; #else char str[6] = "CLOSE"; #endif if (data->hallic_detect) { sx9360_check_hallic_state(HALLIC_PATH, data->hall_ic); data->hall_ic[sizeof(str)-1] = '\0'; if (strcmp(data->hall_ic, str) == 0) { if (hall_flag) { pr_info("[SX9360]: %s - hall IC is closed\n", __func__); sx9360_set_offset_calibration(data); hall_flag = 0; } } else { hall_flag = 1; } } if (data->hallic_cert_detect) { sx9360_check_hallic_state(HALLIC_CERT_PATH, data->hall_ic); data->hall_ic[sizeof(str)-1] = '\0'; if (strcmp(data->hall_ic, str) == 0) { if (hall_cert_flag) { pr_info("[SX9360]: %s - cert hall IC is closed\n", __func__); sx9360_set_offset_calibration(data); hall_cert_flag = 0; } } else { hall_cert_flag = 1; } } if (atomic_read(&data->enable) == ON) { if (data->abnormal_mode) { sx9360_get_data(data); if (data->max_normal_diff < data->diff) data->max_normal_diff = data->diff; } else { if (data->debug_count >= GRIP_LOG_TIME) { sx9360_get_data(data); data->debug_count = 0; } else { data->debug_count++; } } } schedule_delayed_work(&data->debug_work, msecs_to_jiffies(2000)); } static irqreturn_t sx9360_interrupt_thread(int irq, void *pdata) { struct sx9360_p *data = pdata; wake_lock_timeout(&data->grip_wake_lock, 3 * HZ); schedule_delayed_work(&data->irq_work, msecs_to_jiffies(100)); return IRQ_HANDLED; } static int sx9360_input_init(struct sx9360_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_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, &sx9360_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 sx9360_setup_pin(struct sx9360_p *data) { int ret; ret = gpio_request(data->gpio_nirq, "SX9360_nIRQ"); if (ret < 0) { pr_err("[SX9360]: %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("[SX9360]: %s - failed to set gpio %d as input (%d)\n", __func__, data->gpio_nirq, ret); gpio_free(data->gpio_nirq); return ret; } return 0; } static void sx9360_initialize_variable(struct sx9360_p *data) { data->init_done = OFF; data->skip_data = false; data->state = IDLE; atomic_set(&data->enable, OFF); } static int sx9360_read_setupreg(struct device_node *dnode, char *str, u32 *val) { u32 temp_val; int ret; ret = of_property_read_u32(dnode, str, &temp_val); if (!ret) *val = temp_val; else pr_err("[SX9360]: %s - %s: property read err 0x%2x (%d)\n", __func__, str, temp_val, ret); return ret; } static int sx9360_parse_dt(struct sx9360_p *data, struct device *dev) { struct device_node *dNode = dev->of_node; enum of_gpio_flags flags; u32 val = 0; int ret; if (dNode == NULL) return -ENODEV; data->gpio_nirq = of_get_named_gpio_flags(dNode, "sx9360,nirq-gpio", 0, &flags); if (data->gpio_nirq < 0) { pr_err("[SX9360]: %s - get gpio_nirq error\n", __func__); return -ENODEV; } if (!sx9360_read_setupreg(dNode, SX9360_REGGNRLCTL2, &val)) setup_reg[SX9360_REGGNRLCTL2_REG_IDX].val = (u8)val; if (!sx9360_read_setupreg(dNode, SX9360_REGPROXCTRL3, &val)) setup_reg[SX9360_REGPROXCTRL3_REG_IDX].val = (u8)val; if (!sx9360_read_setupreg(dNode, SX9360_REFRESOLUTION, &val)) setup_reg[SX9360_REFRESOLUTION_REG_IDX].val = (u8)val; if (!sx9360_read_setupreg(dNode, SX9360_REFAGAINFREQ, &val)) setup_reg[SX9360_REFAGAINFREQ_REG_IDX].val = (u8)val; if (!sx9360_read_setupreg(dNode, SX9360_RESOLUTION, &val)) setup_reg[SX9360_RESOLUTION_REG_IDX].val = (u8)val; if (!sx9360_read_setupreg(dNode, SX9360_AGAINFREQ, &val)) setup_reg[SX9360_AGAINFREQ_REG_IDX].val = (u8)val; if (!sx9360_read_setupreg(dNode, SX9360_REFGAINRAWFILT, &val)) setup_reg[SX9360_REFGAINRAWFILT_REG_IDX].val = (u8)val; if (!sx9360_read_setupreg(dNode, SX9360_GAINRAWFILT, &val)) setup_reg[SX9360_GAINRAWFILT_REG_IDX].val = (u8)val; if (!sx9360_read_setupreg(dNode, SX9360_HYST, &val)) setup_reg[SX9360_HYST_REG_IDX].val = (u8)val; if (!sx9360_read_setupreg(dNode, SX9360_PROXTHRESH, &val)) setup_reg[SX9360_PROXTHRESH_REG_IDX].val = (u8)val; ret = of_property_read_u32(dNode, "sx9360,hallic_detect", &data->hallic_detect); if (ret < 0) data->hallic_detect = 0; ret = of_property_read_u32(dNode, "sx9360,hallic_cert_detect", &data->hallic_cert_detect); if (ret < 0) data->hallic_cert_detect = 0; pr_info("[SX9360]: %s - grip_int:%d\n", __func__,data->gpio_nirq); return 0; } #if defined(CONFIG_CCIC_NOTIFIER) && defined(CONFIG_USB_TYPEC_MANAGER_NOTIFIER) static int sx9360_ccic_handle_notification(struct notifier_block *nb, unsigned long action, void *data) { CC_NOTI_USB_STATUS_TYPEDEF usb_status = *(CC_NOTI_USB_STATUS_TYPEDEF *) data; struct sx9360_p *pdata = container_of(nb, struct sx9360_p, ccic_nb); static int pre_attach; if ((usb_status.drp != USB_STATUS_NOTIFY_ATTACH_DFP) && (usb_status.drp != USB_STATUS_NOTIFY_DETACH)) return 0; if (pre_attach == usb_status.drp) return 0; if (pdata->init_done == ON) { switch (usb_status.drp) { case USB_STATUS_NOTIFY_ATTACH_DFP: case USB_STATUS_NOTIFY_DETACH: pr_info("[SX9360]: %s accept attach = %d\n", __func__, usb_status.drp); sx9360_set_offset_calibration(pdata); break; default: pr_info("[SX9360]: %s skip attach = %d\n", __func__, usb_status.drp); break; } } pre_attach = usb_status.drp; return 0; } #endif #ifdef CONFIG_VBUS_NOTIFIER static int sx9360_vbus_handle_notification(struct notifier_block *nb, unsigned long action, void *data) { vbus_status_t vbus_type = *(vbus_status_t *) data; struct sx9360_p *pdata = container_of(nb, struct sx9360_p, vbus_nb); static int pre_attach; if (pre_attach == vbus_type) return 0; if (pdata->init_done == ON) { switch (vbus_type) { case STATUS_VBUS_HIGH: case STATUS_VBUS_LOW: pr_info("[SX9360]: %s accept attach = %d\n", __func__, vbus_type); sx9360_set_offset_calibration(pdata); break; default: pr_info("[SX9360]: %s skip attach = %d\n", __func__, vbus_type); break; } } pre_attach = vbus_type; return 0; } #endif static int sx9360_check_chip_id(struct sx9360_p *data) { int ret; u8 value = 0; ret = sx9360_i2c_read(data, SX9360_WHOAMI_REG, &value); if (ret < 0) { pr_err("[SX9360]: whoami[0x%x] read failed %d\n", value, ret); return ret; } if(value != WHO_AM_I) { pr_err("[SX9360]: invalid whoami(%x)\n", value); return -1; } return 0; } static int sx9360_probe(struct i2c_client *client, const struct i2c_device_id *id) { int ret = -ENODEV; struct sx9360_p *data = NULL; pr_info("[SX9360]: %s - Probe Start!\n", __func__); if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { pr_err("[SX9360]: %s - i2c_check_functionality error\n", __func__); goto exit; } /* create memory for main struct */ data = kzalloc(sizeof(struct sx9360_p), GFP_KERNEL); if (data == NULL) { pr_err("[SX9360]: %s - kzalloc error\n", __func__); ret = -ENOMEM; goto exit_kzalloc; } i2c_set_clientdata(client, data); data->client = client; data->factory_device = &client->dev; ret = sx9360_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->mode_mutex); mutex_init(&data->read_mutex); ret = sx9360_parse_dt(data, &client->dev); if (ret < 0) { pr_err("[SX9360]: %s - of_node error\n", __func__); ret = -ENODEV; goto exit_of_node; } ret = sx9360_setup_pin(data); if (ret) { pr_err("[SX9360]: %s - could not setup pin\n", __func__); goto exit_setup_pin; } /* read chip id */ ret = sx9360_check_chip_id(data); if (ret < 0) { pr_err("[SX9360]: %s - chip id check failed %d\n", __func__, ret); goto exit_chip_reset; } ret = sx9360_i2c_write(data, SX9360_SOFTRESET_REG, SX9360_SOFTRESET); if (ret < 0) { pr_err("[SX9360]: %s - chip reset failed %d\n", __func__, ret); goto exit_chip_reset; } sx9360_initialize_variable(data); INIT_DELAYED_WORK(&data->init_work, sx9360_init_work_func); INIT_DELAYED_WORK(&data->irq_work, sx9360_irq_work_func); INIT_DELAYED_WORK(&data->debug_work, sx9360_debug_work_func); data->irq = gpio_to_irq(data->gpio_nirq); /* initailize interrupt reporting */ ret = request_threaded_irq(data->irq, NULL, sx9360_interrupt_thread, IRQF_TRIGGER_FALLING | IRQF_ONESHOT, "sx9360_irq", data); if (ret < 0) { pr_err("[SX9360]: %s - failed to set request_threaded_irq %d" " as returning (%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("[SX9360] %s - cound not register sensor(%d).\n", __func__, ret); goto exit_register_failed; } schedule_delayed_work(&data->init_work, msecs_to_jiffies(300)); sx9360_set_debug_work(data, ON, 20000); #if defined(CONFIG_CCIC_NOTIFIER) && defined(CONFIG_USB_TYPEC_MANAGER_NOTIFIER) pr_info("[SX9360]: %s - register ccic notifier\n", __func__); manager_notifier_register(&data->ccic_nb, sx9360_ccic_handle_notification, MANAGER_NOTIFY_CCIC_USB); #endif #ifdef CONFIG_VBUS_NOTIFIER pr_info("[SX9360]: %s - register vbus notifier\n", __func__); vbus_notifier_register(&data->vbus_nb, sx9360_vbus_handle_notification, VBUS_NOTIFY_DEV_CHARGER); #endif pr_info("[SX9360]: %s - Probe done!\n", __func__); return 0; exit_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->mode_mutex); mutex_destroy(&data->read_mutex); wake_lock_destroy(&data->grip_wake_lock); sysfs_remove_group(&data->input->dev.kobj, &sx9360_attribute_group); sensors_remove_symlink(data->input); input_unregister_device(data->input); exit_input_init: kfree(data); exit_kzalloc: exit: pr_err("[SX9360]: %s - Probe fail!\n", __func__); return ret; } static int sx9360_remove(struct i2c_client *client) { struct sx9360_p *data = (struct sx9360_p *)i2c_get_clientdata(client); if (atomic_read(&data->enable) == ON) sx9360_set_enable(data, OFF); sx9360_set_mode(data, SX9360_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, &sx9360_attribute_group); input_unregister_device(data->input); mutex_destroy(&data->mode_mutex); mutex_destroy(&data->read_mutex); kfree(data); return 0; } static int sx9360_suspend(struct device *dev) { struct sx9360_p *data = dev_get_drvdata(dev); int cnt = 0; pr_info("[SX9360]: %s\n", __func__); /* before go to sleep, make the interrupt pin as high*/ while ((sx9360_get_nirq_state(data) == 0) && (cnt++ < 3)) { sx9360_read_irqstate(data); msleep(20); } if (cnt >= 3) pr_err("[SX9360]: %s - s/w reset fail(%d)\n", __func__, cnt); sx9360_set_debug_work(data, OFF, 1000); return 0; } static int sx9360_resume(struct device *dev) { struct sx9360_p *data = dev_get_drvdata(dev); pr_info("[SX9360]: %s\n", __func__); sx9360_set_debug_work(data, ON, 1000); return 0; } static void sx9360_shutdown(struct i2c_client *client) { struct sx9360_p *data = i2c_get_clientdata(client); pr_info("[SX9360]: %s\n", __func__); sx9360_set_debug_work(data, OFF, 1000); if (atomic_read(&data->enable) == ON) sx9360_set_enable(data, OFF); sx9360_set_mode(data, SX9360_MODE_SLEEP); } static struct of_device_id sx9360_match_table[] = { { .compatible = "sx9360",}, {}, }; static const struct i2c_device_id sx9360_id[] = { { "sx9360_match_table", 0 }, { } }; static const struct dev_pm_ops sx9360_pm_ops = { .suspend = sx9360_suspend, .resume = sx9360_resume, }; static struct i2c_driver sx9360_driver = { .driver = { .name = MODEL_NAME, .owner = THIS_MODULE, .of_match_table = sx9360_match_table, .pm = &sx9360_pm_ops }, .probe = sx9360_probe, .remove = sx9360_remove, .shutdown = sx9360_shutdown, .id_table = sx9360_id, }; static int __init sx9360_init(void) { return i2c_add_driver(&sx9360_driver); } static void __exit sx9360_exit(void) { i2c_del_driver(&sx9360_driver); } module_init(sx9360_init); module_exit(sx9360_exit); MODULE_DESCRIPTION("Semtech Corp. SX9360 Capacitive Touch Controller Driver"); MODULE_AUTHOR("Samsung Electronics"); MODULE_LICENSE("GPL");