/* * Driver for the SX938x * Copyright (c) 2011 Semtech Corp * * 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. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "sx938x.h" #if IS_ENABLED(CONFIG_SENSORS_CORE_AP) #include #endif #if IS_ENABLED(CONFIG_PDIC_NOTIFIER) #include #endif #if IS_ENABLED(CONFIG_USB_TYPEC_MANAGER_NOTIFIER) #include #endif #if IS_ENABLED(CONFIG_HALL_NOTIFIER) #include #define HALL_NAME "hall" #define HALL_CERT_NAME "certify_hall" #define HALL_FLIP_NAME "flip" #define HALL_ATTACH 1 #define HALL_DETACH 0 #endif #define I2C_M_WR 0 /* for i2c Write */ //#define I2C_M_RD 1 /* for i2c Read */ #define IDLE 0 #define ACTIVE 1 #define DIFF_READ_NUM 10 #define GRIP_LOG_TIME 15 /* 30 sec */ #define REF_OFF_MAIN_ON 0x02 #define REF_OFF_MAIN_OFF 0x00 #define IRQ_PROCESS_CONDITION (SX938x_IRQSTAT_TOUCH_FLAG \ | SX938x_IRQSTAT_RELEASE_FLAG \ | SX938x_IRQSTAT_COMPDONE_FLAG) #define SX938x_MODE_SLEEP 0 #define SX938x_MODE_NORMAL 1 #define IDLE 0 #define ACTIVE 1 /* Failure Index */ #define SX938x_ID_ERROR (-1) #define SX938x_NIRQ_ERROR (-2) #define SX938x_CONN_ERROR (-3) #define SX938x_I2C_ERROR (-4) #define SX938x_REG_ERROR (-5) #define GRIP_WORKING 1 #define GRIP_RELEASE 2 #define INTERRUPT_HIGH 1 #define INTERRUPT_LOW 0 #define TYPE_USB 1 #define TYPE_HALL 2 #define TYPE_BOOT 3 #define TYPE_FORCE 4 #define UNKNOWN_ON 1 #define UNKNOWN_OFF 2 struct sx938x_p { struct i2c_client *client; struct input_dev *input; struct input_dev *noti_input_dev; struct device *factory_device; struct delayed_work init_work; struct delayed_work irq_work; struct delayed_work debug_work; struct wakeup_source *grip_ws; struct mutex mode_mutex; struct mutex read_mutex; #if IS_ENABLED(CONFIG_PDIC_NOTIFIER) struct notifier_block pdic_nb; #endif #if IS_ENABLED(CONFIG_HALL_NOTIFIER) struct notifier_block hall_nb; #endif struct regulator *dvdd_vreg; /* regulator */ const char *dvdd_vreg_name; /* regulator name */ #if IS_ENABLED(CONFIG_SENSORS_COMMON_VDD_SUB) int gpio_nirq_sub; #endif int pre_attach; int debug_count; int debug_zero_count; int fail_status_code; int is_unknown_mode; int motion; int irq_count; int abnormal_mode; int ldo_en; int irq; int gpio_nirq; int state; int init_done; int noti_enable; int again_m; int dgain_m; int diff_cnt; atomic_t enable; u32 unknown_sel; s32 useful_avg; s32 capMain; s32 useful; u16 detect_threshold; u16 offset; s16 avg; s16 diff; s16 diff_avg; s16 max_diff; s16 max_normal_diff; u8 ic_num; bool first_working; bool skip_data; }; static int sx938x_get_nirq_state(struct sx938x_p *data) { return gpio_get_value_cansleep(data->gpio_nirq); } static int sx938x_i2c_write(struct sx938x_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) { GRIP_ERR("err %d", ret); data->fail_status_code = SX938x_I2C_ERROR; } return 0; } static int sx938x_i2c_read(struct sx938x_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) { GRIP_ERR("err %d", ret); data->fail_status_code = SX938x_I2C_ERROR; } return ret; } static int sx938x_read_reg_stat(struct sx938x_p *data) { u8 value = 0; if (data) { if (sx938x_i2c_read(data, SX938x_IRQSTAT_REG, &value) >= 0) return (value & 0x00FF); } return 0; } static void sx938x_initialize_register(struct sx938x_p *data) { u8 val = 0; int idx; data->init_done = OFF; for (idx = 0; idx < (int)(ARRAY_SIZE(setup_reg[data->ic_num])); idx++) { sx938x_i2c_write(data, setup_reg[data->ic_num][idx].reg, setup_reg[data->ic_num][idx].val); GRIP_INFO("Write Reg 0x%x Val 0x%x\n", setup_reg[data->ic_num][idx].reg, setup_reg[data->ic_num][idx].val); sx938x_i2c_read(data, setup_reg[data->ic_num][idx].reg, &val); GRIP_INFO("Read Reg 0x%x Val 0x%x\n", setup_reg[data->ic_num][idx].reg, val); } val = 0; sx938x_i2c_read(data, SX938x_PROXCTRL5_REG, &val); data->detect_threshold = (u16)val * (u16)val / 2; val = 0; sx938x_i2c_read(data, SX938x_PROXCTRL4_REG, &val); val = (val & 0x30) >> 4; if (val) data->detect_threshold += data->detect_threshold >> (5 - val); GRIP_INFO("detect threshold %u\n", data->detect_threshold); data->init_done = ON; } static int sx938x_hardware_check(struct sx938x_p *data) { int ret; u8 whoami = 0; u8 loop = 0; //Check th IRQ Status GRIP_INFO("irq state %d", sx938x_get_nirq_state(data)); while (sx938x_get_nirq_state(data) == INTERRUPT_LOW) { sx938x_read_reg_stat(data); GRIP_INFO("irq state %d", sx938x_get_nirq_state(data)); if (++loop > 10) { data->fail_status_code = SX938x_NIRQ_ERROR; return data->fail_status_code; } usleep_range(10000, 11000); } ret = sx938x_i2c_read(data, SX938x_WHOAMI_REG, &whoami); if (ret < 0) { data->fail_status_code = SX938x_I2C_ERROR; return data->fail_status_code; } GRIP_INFO("whoami 0x%x\n", whoami); if (whoami != SX938x_WHOAMI_VALUE) { data->fail_status_code = SX938x_ID_ERROR; return data->fail_status_code; } return ret; } static int sx938x_manual_offset_calibration(struct sx938x_p *data) { s32 ret = 0; GRIP_INFO("\n"); ret = sx938x_i2c_write(data, SX938x_STAT_REG, SX938x_STAT_COMPSTAT_ALL_FLAG); return ret; } static void sx938x_send_event(struct sx938x_p *data, u8 state) { if (data->skip_data == true) { GRIP_INFO("skip grip event\n"); return; } if (state == ACTIVE) { data->state = ACTIVE; GRIP_INFO("touched\n"); } else { data->state = IDLE; GRIP_INFO("released\n"); } if (state == ACTIVE) input_report_rel(data->input, REL_MISC, GRIP_WORKING); else input_report_rel(data->input, REL_MISC, GRIP_RELEASE); if (data->unknown_sel) input_report_rel(data->input, REL_X, data->is_unknown_mode); input_sync(data->input); } static void sx938x_get_gain(struct sx938x_p *data) { u8 msByte = 0; static const int again_phm[] = {7500, 22500, 37500, 52500, 60000, 75000, 90000, 105000}; sx938x_i2c_read(data, SX938x_AFE_PARAM1_PHM_REG, &msByte); msByte = (msByte >> 4) & 0x07; data->again_m = again_phm[msByte]; msByte = 0; sx938x_i2c_read(data, SX938x_PROXCTRL0_PHM_REG, &msByte); msByte = (msByte >> 3) & 0x07; if (msByte) data->dgain_m = 1 << (msByte - 1); else data->dgain_m = 1; } static void sx938x_get_data(struct sx938x_p *data) { u8 msb = 0, lsb = 0; u8 convstat = 0; s16 useful_ref, retry = 0; u16 offset_ref = 0; int diff = 0; mutex_lock(&data->read_mutex); if (data) { /* Semtech reference code receives the DIFF data, when 'CONVERSION' is completed (IRQ falling), but Samsung receive diff data by POLLING. So we have to check the completion of 'CONVERSION'.*/ while (1) { sx938x_i2c_read(data, SX938x_STAT_REG, &convstat); convstat &= 0x01; if (++retry > 5 || convstat == 0) break; usleep_range(10000, 11000); } GRIP_INFO("retry %d, CONVSTAT %u\n", retry, convstat); //READ REF Channel data sx938x_i2c_read(data, SX938x_USEMSB_PHR, &msb); sx938x_i2c_read(data, SX938x_USELSB_PHR, &lsb); useful_ref = (s16)((msb << 8) | lsb); msb = lsb = 0; sx938x_i2c_read(data, SX938x_OFFSETMSB_PHR, &msb); sx938x_i2c_read(data, SX938x_OFFSETLSB_PHR, &lsb); offset_ref = (u16)((msb << 8) | lsb); //READ Main channel data msb = lsb = 0; sx938x_i2c_read(data, SX938x_USEMSB_PHM, &msb); sx938x_i2c_read(data, SX938x_USELSB_PHM, &lsb); data->useful = (s16)((msb << 8) | lsb); msb = lsb = 0; sx938x_i2c_read(data, SX938x_AVGMSB_PHM, &msb); sx938x_i2c_read(data, SX938x_AVGLSB_PHM, &lsb); data->avg = (s16)((msb << 8) | lsb); #if 0 sx938x_i2c_read(data, SX938x_DIFFMSB_PHM, &msb); sx938x_i2c_read(data, SX938x_DIFFLSB_PHM, &lsb); data->diff = (s32)((msb << 8) | lsb); if (data->diff > 32767) data->diff -= 65536; #endif diff = data->useful - data->avg; if (diff > 32767) data->diff = 32767; else if (diff < -32768) data->diff = -32768; else data->diff = diff; msb = lsb = 0; sx938x_i2c_read(data, SX938x_OFFSETMSB_PHM, &msb); sx938x_i2c_read(data, SX938x_OFFSETLSB_PHM, &lsb); data->capMain = (int)(msb >> 7) * 2369640 + (int)(msb & 0x7F) * 30380 + (int)(lsb * 270) + (int)(((int)data->useful * data->again_m) / (data->dgain_m * 32768)); data->offset = (u16)((msb << 8) | lsb); GRIP_INFO("[MAIN] capMain %d Useful %d Average %d DIFF %d Offset %d [REF] Useful %d Offset %d\n", data->capMain, data->useful, data->avg, data->diff, data->offset, useful_ref, offset_ref); } mutex_unlock(&data->read_mutex); } static int sx938x_set_mode(struct sx938x_p *data, unsigned char mode) { int ret = -EINVAL; u8 val = 0; GRIP_INFO("%u\n", mode); mutex_lock(&data->mode_mutex); if (mode == SX938x_MODE_SLEEP) { ret = sx938x_i2c_write(data, SX938x_GNRL_CTRL0_REG, REF_OFF_MAIN_OFF); } else if (mode == SX938x_MODE_NORMAL) { val = setup_reg[data->ic_num][SX938x_GNRL_CTRL0_REG_IDX].val; ret = sx938x_i2c_write(data, SX938x_GNRL_CTRL0_REG, val); msleep(20); sx938x_manual_offset_calibration(data); msleep(450); } GRIP_INFO("changed %u\n", mode); mutex_unlock(&data->mode_mutex); return ret; } static void sx938x_check_status(struct sx938x_p *data) { u8 status = 0; sx938x_i2c_read(data, SX938x_STAT_REG, &status); GRIP_INFO("0x%x\n", status); if (data->skip_data == true) { input_report_rel(data->input, REL_MISC, GRIP_RELEASE); if (data->unknown_sel) input_report_rel(data->input, REL_X, UNKNOWN_OFF); input_sync(data->input); return; } if ((status & SX938x_PROXSTAT_FLAG) && (data->diff > data->detect_threshold)) sx938x_send_event(data, ACTIVE); else sx938x_send_event(data, IDLE); } static void sx938x_set_enable(struct sx938x_p *data, int enable) { int pre_enable = atomic_read(&data->enable); GRIP_INFO("%d\n", enable); if (enable) { if (pre_enable == OFF) { data->diff_avg = 0; data->diff_cnt = 0; data->useful_avg = 0; sx938x_get_data(data); sx938x_check_status(data); msleep(20); sx938x_read_reg_stat(data); /* enable interrupt */ sx938x_i2c_write(data, SX938x_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 */ sx938x_i2c_write(data, SX938x_IRQ_ENABLE_REG, 0x00); disable_irq(data->irq); disable_irq_wake(data->irq); atomic_set(&data->enable, OFF); } } } static void sx938x_set_debug_work(struct sx938x_p *data, u8 enable, unsigned int time_ms) { if (enable == ON) { data->debug_count = 0; data->debug_zero_count = 0; schedule_delayed_work(&data->debug_work, msecs_to_jiffies(time_ms)); } else { cancel_delayed_work_sync(&data->debug_work); } } static void sx938x_enter_unknown_mode(struct sx938x_p *data, int type) { if (data->noti_enable && !data->skip_data && data->unknown_sel) { data->motion = 0; data->first_working = false; if (data->is_unknown_mode == UNKNOWN_OFF) { data->is_unknown_mode = UNKNOWN_ON; if (atomic_read(&data->enable) == ON) { input_report_rel(data->input, REL_X, UNKNOWN_ON); input_sync(data->input); } GRIP_INFO("UNKNOWN Re-enter\n"); } else { GRIP_INFO("already UNKNOWN\n"); } input_report_rel(data->noti_input_dev, REL_X, type); input_sync(data->noti_input_dev); } } static ssize_t sx938x_get_offset_calibration_show(struct device *dev, struct device_attribute *attr, char *buf) { u8 reg_value = 0; struct sx938x_p *data = dev_get_drvdata(dev); sx938x_i2c_read(data, SX938x_IRQSTAT_REG, ®_value); return sprintf(buf, "%d\n", reg_value); } static ssize_t sx938x_set_offset_calibration_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct sx938x_p *data = dev_get_drvdata(dev); unsigned long val; if (kstrtoul(buf, 0, &val)) return -EINVAL; if (val) sx938x_manual_offset_calibration(data); return count; } static ssize_t sx938x_sw_reset_show(struct device *dev, struct device_attribute *attr, char *buf) { struct sx938x_p *data = dev_get_drvdata(dev); u8 compstat = 0xFF; int retry = 0; GRIP_INFO("\n"); sx938x_manual_offset_calibration(data); msleep(450); sx938x_get_data(data); while (retry++ <= 3) { sx938x_i2c_read(data, SX938x_STAT_REG, &compstat); GRIP_INFO("compstat 0x%x\n", compstat); compstat &= 0x04; if (compstat == 0) break; msleep(50); } if (compstat == 0) return snprintf(buf, PAGE_SIZE, "%d\n", 0); return snprintf(buf, PAGE_SIZE, "%d\n", -1); } static ssize_t sx938x_vendor_show(struct device *dev, struct device_attribute *attr, char *buf) { return snprintf(buf, PAGE_SIZE, "%s\n", VENDOR_NAME); } static ssize_t sx938x_name_show(struct device *dev, struct device_attribute *attr, char *buf) { struct sx938x_p *data = dev_get_drvdata(dev); return snprintf(buf, PAGE_SIZE, "%s\n", device_name[data->ic_num]); } static ssize_t sx938x_touch_mode_show(struct device *dev, struct device_attribute *attr, char *buf) { return snprintf(buf, PAGE_SIZE, "1\n"); } static ssize_t sx938x_raw_data_show(struct device *dev, struct device_attribute *attr, char *buf) { static s32 sum_diff, sum_useful; struct sx938x_p *data = dev_get_drvdata(dev); sx938x_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 sx938x_diff_avg_show(struct device *dev, struct device_attribute *attr, char *buf) { struct sx938x_p *data = dev_get_drvdata(dev); return snprintf(buf, PAGE_SIZE, "%d\n", data->diff_avg); } static ssize_t sx938x_useful_avg_show(struct device *dev, struct device_attribute *attr, char *buf) { struct sx938x_p *data = dev_get_drvdata(dev); return snprintf(buf, PAGE_SIZE, "%ld\n", (long int)data->useful_avg); } static ssize_t sx938x_avgnegfilt_show(struct device *dev, struct device_attribute *attr, char *buf) { struct sx938x_p *data = dev_get_drvdata(dev); u8 avgnegfilt = 0; sx938x_i2c_read(data, SX938x_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 sx938x_avgposfilt_show(struct device *dev, struct device_attribute *attr, char *buf) { struct sx938x_p *data = dev_get_drvdata(dev); u8 avgposfilt = 0; sx938x_i2c_read(data, SX938x_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"); else return snprintf(buf, PAGE_SIZE, "0\n"); } static ssize_t sx938x_gain_show(struct device *dev, struct device_attribute *attr, char *buf) { struct sx938x_p *data = dev_get_drvdata(dev); u8 gain = 0; sx938x_i2c_read(data, SX938x_PROXCTRL0_PHM_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 sx938x_avgthresh_show(struct device *dev, struct device_attribute *attr, char *buf) { struct sx938x_p *data = dev_get_drvdata(dev); u8 avgthresh = 0; sx938x_i2c_read(data, SX938x_PROXCTRL1_REG, &avgthresh); avgthresh = avgthresh & 0x3F; return snprintf(buf, PAGE_SIZE, "%ld\n", 512 * (long int)avgthresh); } static ssize_t sx938x_rawfilt_show(struct device *dev, struct device_attribute *attr, char *buf) { struct sx938x_p *data = dev_get_drvdata(dev); u8 rawfilt = 0; sx938x_i2c_read(data, SX938x_PROXCTRL0_PHM_REG, &rawfilt); rawfilt = rawfilt & 0x07; if (rawfilt > 0 && rawfilt < 8) return snprintf(buf, PAGE_SIZE, "1-1/%d\n", 1 << rawfilt); else return snprintf(buf, PAGE_SIZE, "0\n"); } static ssize_t sx938x_sampling_freq_show(struct device *dev, struct device_attribute *attr, char *buf) { struct sx938x_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" }; sx938x_i2c_read(data, SX938x_AFE_PARAM1_PHM_REG, &sampling_freq); sampling_freq = sampling_freq & 0x0F; return snprintf(buf, PAGE_SIZE, "%skHz\n", table[sampling_freq]); } static ssize_t sx938x_scan_period_show(struct device *dev, struct device_attribute *attr, char *buf) { struct sx938x_p *data = dev_get_drvdata(dev); u8 scan_period = 0; sx938x_i2c_read(data, SX938x_GNRL_CTRL2_REG, &scan_period); return snprintf(buf, PAGE_SIZE, "%ld\n", (long int)(((long int)scan_period << 11) / 1000)); } static ssize_t sx938x_again_show(struct device *dev, struct device_attribute *attr, char *buf) { struct sx938x_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; sx938x_i2c_read(data, SX938x_AFE_PARAM1_PHM_REG, &again); again = (again & 0x70) >> 4; return snprintf(buf, PAGE_SIZE, "%spF\n", table[again]); } static ssize_t sx938x_phase_show(struct device *dev, struct device_attribute *attr, char *buf) { return snprintf(buf, PAGE_SIZE, "1\n"); } static ssize_t sx938x_hysteresis_show(struct device *dev, struct device_attribute *attr, char *buf) { struct sx938x_p *data = dev_get_drvdata(dev); const char *table[4] = {"None", "+/-6%", "+/-12%", "+/-25%"}; u8 hyst = 0; sx938x_i2c_read(data, SX938x_PROXCTRL4_REG, &hyst); hyst = (hyst & 0x30) >> 4; return snprintf(buf, PAGE_SIZE, "%s\n", table[hyst]); } static ssize_t sx938x_resolution_show(struct device *dev, struct device_attribute *attr, char *buf) { struct sx938x_p *data = dev_get_drvdata(dev); u8 resolution = 0; sx938x_i2c_read(data, SX938x_AFE_PARAM0_PHM_REG, &resolution); resolution = resolution & 0x7; return snprintf(buf, PAGE_SIZE, "%u\n", 1 << (resolution + 3)); } static ssize_t sx938x_useful_filt_show(struct device *dev, struct device_attribute *attr, char *buf) { struct sx938x_p *data = dev_get_drvdata(dev); u8 useful_filt = 0; sx938x_i2c_read(data, SX938x_USEFILTER4_REG, &useful_filt); useful_filt = useful_filt & 0x01; return snprintf(buf, PAGE_SIZE, "%s\n", useful_filt ? "on" : "off"); } static ssize_t sx938x_resistor_filter_input_show(struct device *dev, struct device_attribute *attr, char *buf) { struct sx938x_p *data = dev_get_drvdata(dev); u8 resistor_filter = 0; sx938x_i2c_read(data, SX938x_AFE_CTRL1_REG, &resistor_filter); resistor_filter = resistor_filter & 0x0F; return snprintf(buf, PAGE_SIZE, "%d kohm\n", resistor_filter * 2); } static ssize_t sx938x_closedeb_show(struct device *dev, struct device_attribute *attr, char *buf) { struct sx938x_p *data = dev_get_drvdata(dev); u8 closedeb = 0; sx938x_i2c_read(data, SX938x_PROXCTRL4_REG, &closedeb); closedeb = closedeb & 0x0C; closedeb = closedeb >> 2; if (closedeb == 0) return snprintf(buf, PAGE_SIZE, "off"); return snprintf(buf, PAGE_SIZE, "%d\n", 1 << closedeb); } static ssize_t sx938x_fardeb_show(struct device *dev, struct device_attribute *attr, char *buf) { struct sx938x_p *data = dev_get_drvdata(dev); u8 fardeb = 0; sx938x_i2c_read(data, SX938x_PROXCTRL4_REG, &fardeb); fardeb = fardeb & 0x03; if (fardeb == 0) return snprintf(buf, PAGE_SIZE, "off"); return snprintf(buf, PAGE_SIZE, "%d\n", 1 << fardeb); } static ssize_t sx938x_irq_count_show(struct device *dev, struct device_attribute *attr, char *buf) { struct sx938x_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; } GRIP_INFO("called\n"); return snprintf(buf, PAGE_SIZE, "%d,%d,%d\n", ret, data->irq_count, max_diff_val); } static ssize_t sx938x_irq_count_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct sx938x_p *data = dev_get_drvdata(dev); u8 onoff; int ret; ret = kstrtou8(buf, 10, &onoff); if (ret < 0) { GRIP_ERR("kstrtou8 fail %d\n", 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 { GRIP_ERR("unknown val %d\n", onoff); } mutex_unlock(&data->read_mutex); GRIP_INFO("%d\n", onoff); return count; } static ssize_t sx938x_normal_threshold_show(struct device *dev, struct device_attribute *attr, char *buf) { struct sx938x_p *data = dev_get_drvdata(dev); u8 th_buf = 0, hyst = 0; u32 threshold = 0; sx938x_i2c_read(data, SX938x_PROXCTRL5_REG, &th_buf); threshold = (u32)th_buf * (u32)th_buf / 2; sx938x_i2c_read(data, SX938x_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 sx938x_onoff_show(struct device *dev, struct device_attribute *attr, char *buf) { struct sx938x_p *data = dev_get_drvdata(dev); return snprintf(buf, PAGE_SIZE, "%u\n", !data->skip_data); } static ssize_t sx938x_onoff_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { u8 val; int ret; struct sx938x_p *data = dev_get_drvdata(dev); ret = kstrtou8(buf, 2, &val); if (ret) { GRIP_ERR("kstrtou8 fail %d\n", ret); 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, GRIP_RELEASE); if (data->unknown_sel) input_report_rel(data->input, REL_X, UNKNOWN_OFF); input_sync(data->input); } data->motion = 1; data->is_unknown_mode = UNKNOWN_OFF; data->first_working = false; } else { data->skip_data = false; } GRIP_INFO("%u\n", val); return count; } static ssize_t sx938x_register_show(struct device *dev, struct device_attribute *attr, char *buf) { int idx; int i = 0; u8 val; char *p = buf; struct sx938x_p *data = dev_get_drvdata(dev); for (idx = 0; idx < (int)(ARRAY_SIZE(setup_reg[data->ic_num])); idx++) { sx938x_i2c_read(data, setup_reg[data->ic_num][idx].reg, &val); i += snprintf(p + i, PAGE_SIZE - i, "(0x%02x)=0x%02x\n", setup_reg[data->ic_num][idx].reg, val); } return i; } static ssize_t sx938x_register_write_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { int reg_address = 0, val = 0; struct sx938x_p *data = dev_get_drvdata(dev); if (sscanf(buf, "%x,%x", ®_address, &val) != 2) { GRIP_ERR("sscanf err\n"); return -EINVAL; } sx938x_i2c_write(data, (unsigned char)reg_address, (unsigned char)val); GRIP_INFO("Regi 0x%x Val 0x%x\n", reg_address, val); sx938x_get_gain(data); return count; } static unsigned int register_read_store_reg; static unsigned int register_read_store_val; static ssize_t sx938x_register_read_show(struct device *dev, struct device_attribute *attr, char *buf) { return sprintf(buf, "0x%02x \n", register_read_store_val); } static ssize_t sx938x_register_read_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { u8 val = 0; int regist = 0; struct sx938x_p *data = dev_get_drvdata(dev); GRIP_INFO("\n"); if (sscanf(buf, "%x", ®ist) != 1) { GRIP_ERR("sscanf err\n"); return -EINVAL; } sx938x_i2c_read(data, regist, &val); GRIP_INFO("Regi 0x%2x Val 0x%2x\n", regist, val); register_read_store_reg = (unsigned int)regist; register_read_store_val = (unsigned int)val; return count; } /* show far near status reg data */ static ssize_t sx938x_status_regdata_show(struct device *dev, struct device_attribute *attr, char *buf) { u8 val0; struct sx938x_p *data = dev_get_drvdata(dev); sx938x_i2c_read(data, SX938x_STAT_REG, &val0); GRIP_INFO("Status 0x%2x \n", val0); return sprintf(buf, "%d \n", val0); } static ssize_t sx938x_status_show(struct device *dev, struct device_attribute *attr, char *buf) { u8 val = 0; int status = 0; struct sx938x_p *data = dev_get_drvdata(dev); sx938x_i2c_read(data, SX938x_STAT_REG, &val); if ((val & 0x08) == 0)//bit3 for proxstat status = 0; else status = 1; return sprintf(buf, "%d\n", status); } /* check if manual calibrate success or not */ static ssize_t sx938x_cal_state_show(struct device *dev, struct device_attribute *attr, char *buf) { u8 val = 0; int status = 0; struct sx938x_p *data = dev_get_drvdata(dev); sx938x_i2c_read(data, SX938x_STAT_REG, &val); GRIP_INFO("Regi 0x01 Val 0x%2x\n", val); if ((val & 0x06) == 0) status = 1; else status = 0; return sprintf(buf, "%d\n", status); } static ssize_t sx938x_motion_show(struct device *dev, struct device_attribute *attr, char *buf) { struct sx938x_p *data = dev_get_drvdata(dev); return snprintf(buf, PAGE_SIZE, "%s\n", data->motion == 1 ? "motion_detect" : "motion_non_detect"); } static ssize_t sx938x_motion_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { u8 val; int ret; struct sx938x_p *data = dev_get_drvdata(dev); ret = kstrtou8(buf, 2, &val); if (ret) { GRIP_ERR("kstrtou8 fail %d\n", ret); return ret; } data->motion = val; GRIP_INFO("%u\n", val); return count; } static ssize_t sx938x_unknown_state_show(struct device *dev, struct device_attribute *attr, char *buf) { struct sx938x_p *data = dev_get_drvdata(dev); return snprintf(buf, PAGE_SIZE, "%s\n", (data->is_unknown_mode == UNKNOWN_ON) ? "UNKNOWN" : "NORMAL"); } static ssize_t sx938x_unknown_state_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { u8 val; int ret; struct sx938x_p *data = dev_get_drvdata(dev); ret = kstrtou8(buf, 2, &val); if (ret) { GRIP_ERR("kstrtou8 fail %d\n", ret); return ret; } if (val == 1) sx938x_enter_unknown_mode(data, TYPE_FORCE); else if (val == 0) data->is_unknown_mode = UNKNOWN_OFF; else GRIP_INFO("Invalid Val %u\n", val); GRIP_INFO("%u\n", val); return count; } static ssize_t sx938x_noti_enable_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { int ret; u8 enable; struct sx938x_p *data = dev_get_drvdata(dev); ret = kstrtou8(buf, 2, &enable); if (ret) { GRIP_ERR("kstrtou8 fail %d\n", ret); return size; } GRIP_INFO("new val %d\n", (int)enable); data->noti_enable = enable; if (data->noti_enable) sx938x_enter_unknown_mode(data, TYPE_BOOT); else { data->motion = 1; data->first_working = false; data->is_unknown_mode = UNKNOWN_OFF; } return size; } static ssize_t sx938x_noti_enable_show(struct device *dev, struct device_attribute *attr, char *buf) { struct sx938x_p *data = dev_get_drvdata(dev); return sprintf(buf, "%d\n", data->noti_enable); } static DEVICE_ATTR(regproxdata, 0444, sx938x_status_regdata_show, NULL); static DEVICE_ATTR(proxstatus, 0444, sx938x_status_show, NULL); static DEVICE_ATTR(cal_state, 0444, sx938x_cal_state_show, NULL); static DEVICE_ATTR(menual_calibrate, S_IRUGO | S_IWUSR | S_IWGRP, sx938x_get_offset_calibration_show, sx938x_set_offset_calibration_store); static DEVICE_ATTR(register_write, S_IWUSR | S_IWGRP, NULL, sx938x_register_write_store); static DEVICE_ATTR(register_read_all, S_IRUGO, sx938x_register_show, NULL); static DEVICE_ATTR(register_read, S_IRUGO | S_IWUSR | S_IWGRP, sx938x_register_read_show, sx938x_register_read_store); static DEVICE_ATTR(reset, S_IRUGO, sx938x_sw_reset_show, NULL); static DEVICE_ATTR(name, S_IRUGO, sx938x_name_show, NULL); static DEVICE_ATTR(vendor, S_IRUGO, sx938x_vendor_show, NULL); static DEVICE_ATTR(mode, S_IRUGO, sx938x_touch_mode_show, NULL); static DEVICE_ATTR(raw_data, S_IRUGO, sx938x_raw_data_show, NULL); static DEVICE_ATTR(diff_avg, S_IRUGO, sx938x_diff_avg_show, NULL); static DEVICE_ATTR(useful_avg, S_IRUGO, sx938x_useful_avg_show, NULL); static DEVICE_ATTR(onoff, S_IRUGO | S_IWUSR | S_IWGRP, sx938x_onoff_show, sx938x_onoff_store); static DEVICE_ATTR(normal_threshold, S_IRUGO, sx938x_normal_threshold_show, NULL); static DEVICE_ATTR(avg_negfilt, S_IRUGO, sx938x_avgnegfilt_show, NULL); static DEVICE_ATTR(avg_posfilt, S_IRUGO, sx938x_avgposfilt_show, NULL); static DEVICE_ATTR(avg_thresh, S_IRUGO, sx938x_avgthresh_show, NULL); static DEVICE_ATTR(rawfilt, S_IRUGO, sx938x_rawfilt_show, NULL); static DEVICE_ATTR(sampling_freq, S_IRUGO, sx938x_sampling_freq_show, NULL); static DEVICE_ATTR(scan_period, S_IRUGO, sx938x_scan_period_show, NULL); static DEVICE_ATTR(gain, S_IRUGO, sx938x_gain_show, NULL); static DEVICE_ATTR(analog_gain, S_IRUGO, sx938x_again_show, NULL); static DEVICE_ATTR(phase, S_IRUGO, sx938x_phase_show, NULL); static DEVICE_ATTR(hysteresis, S_IRUGO, sx938x_hysteresis_show, NULL); static DEVICE_ATTR(irq_count, S_IRUGO | S_IWUSR | S_IWGRP, sx938x_irq_count_show, sx938x_irq_count_store); static DEVICE_ATTR(resolution, S_IRUGO, sx938x_resolution_show, NULL); static DEVICE_ATTR(useful_filt, S_IRUGO, sx938x_useful_filt_show, NULL); static DEVICE_ATTR(resistor_filter_input, S_IRUGO, sx938x_resistor_filter_input_show, NULL); static DEVICE_ATTR(closedeb, S_IRUGO, sx938x_closedeb_show, NULL); static DEVICE_ATTR(fardeb, S_IRUGO, sx938x_fardeb_show, NULL); static DEVICE_ATTR(motion, S_IRUGO | S_IWUSR | S_IWGRP, sx938x_motion_show, sx938x_motion_store); static DEVICE_ATTR(unknown_state, S_IRUGO | S_IWUSR | S_IWGRP, sx938x_unknown_state_show, sx938x_unknown_state_store); static DEVICE_ATTR(noti_enable, 0664, sx938x_noti_enable_show, sx938x_noti_enable_store); static struct device_attribute *sensor_attrs[] = { &dev_attr_regproxdata, &dev_attr_proxstatus, &dev_attr_cal_state, &dev_attr_menual_calibrate, &dev_attr_register_write, &dev_attr_register_read, &dev_attr_register_read_all, &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_analog_gain, &dev_attr_phase, &dev_attr_hysteresis, &dev_attr_irq_count, &dev_attr_resolution, &dev_attr_useful_filt, &dev_attr_resistor_filter_input, &dev_attr_closedeb, &dev_attr_fardeb, &dev_attr_motion, &dev_attr_unknown_state, &dev_attr_noti_enable, NULL, }; static ssize_t sx938x_enable_show(struct device *dev, struct device_attribute *attr, char *buf) { struct sx938x_p *data = dev_get_drvdata(dev); return snprintf(buf, PAGE_SIZE, "%d\n", atomic_read(&data->enable)); } static ssize_t sx938x_enable_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { u8 enable; int ret; struct sx938x_p *data = dev_get_drvdata(dev); ret = kstrtou8(buf, 2, &enable); if (ret) { GRIP_ERR("kstrtou8 fail %d\n", ret); return ret; } GRIP_INFO("new val %u\n", enable); if ((enable == 0) || (enable == 1)) sx938x_set_enable(data, (int)enable); return size; } static DEVICE_ATTR(enable, S_IRUGO | S_IWUSR | S_IWGRP, sx938x_enable_show, sx938x_enable_store); static struct attribute *sx938x_attributes[] = { &dev_attr_enable.attr, NULL }; static struct attribute_group sx938x_attribute_group = { .attrs = sx938x_attributes }; static void sx938x_touch_process(struct sx938x_p *data) { u8 status = 0; sx938x_i2c_read(data, SX938x_STAT_REG, &status); GRIP_INFO("0x%x\n", status); sx938x_get_data(data); if (data->abnormal_mode) { if (status & SX938x_PROXSTAT_FLAG) { if (data->max_diff < data->diff) data->max_diff = data->diff; data->irq_count++; } } if (data->state == IDLE) { if (status & SX938x_PROXSTAT_FLAG) { if (data->is_unknown_mode == UNKNOWN_ON && data->motion) data->first_working = true; sx938x_send_event(data, ACTIVE); } else { GRIP_INFO("0x%x already released.\n", status); } } else { /* User released button */ if (!(status & SX938x_PROXSTAT_FLAG)) { if (data->is_unknown_mode == UNKNOWN_ON && data->motion) { GRIP_INFO("unknown mode off\n"); data->is_unknown_mode = UNKNOWN_OFF; } sx938x_send_event(data, IDLE); } else { GRIP_INFO("0x%x still touched\n", status); } } } static void sx938x_process_interrupt(struct sx938x_p *data) { u8 status = 0; /* since we are not in an interrupt don't need to disable irq. */ status = sx938x_read_reg_stat(data); GRIP_INFO("status %d\n", status); if (status & IRQ_PROCESS_CONDITION) sx938x_touch_process(data); } static void sx938x_init_work_func(struct work_struct *work) { struct sx938x_p *data = container_of((struct delayed_work *)work, struct sx938x_p, init_work); if (data) { GRIP_INFO("initialize\n"); sx938x_initialize_register(data); msleep(100); sx938x_manual_offset_calibration(data); sx938x_set_mode(data, SX938x_MODE_NORMAL); sx938x_read_reg_stat(data); sx938x_get_gain(data); } return; } static void sx938x_irq_work_func(struct work_struct *work) { struct sx938x_p *data = container_of((struct delayed_work *)work, struct sx938x_p, irq_work); if (sx938x_get_nirq_state(data) == INTERRUPT_LOW) sx938x_process_interrupt(data); else GRIP_ERR("nirq read high %d\n", sx938x_get_nirq_state(data)); } static void sx938x_check_first_working(struct sx938x_p *data) { if (data->noti_enable && data->motion) { if (data->detect_threshold < data->diff) { data->first_working = true; GRIP_INFO("first working detected %d\n", data->diff); } else { if (data->first_working) { data->is_unknown_mode = UNKNOWN_OFF; GRIP_INFO("Release detected %d, unknown mode off\n", data->diff); } } } } static void sx938x_debug_work_func(struct work_struct *work) { struct sx938x_p *data = container_of((struct delayed_work *)work, struct sx938x_p, debug_work); if (atomic_read(&data->enable) == ON) { if (data->abnormal_mode) { sx938x_get_data(data); if (data->max_normal_diff < data->diff) data->max_normal_diff = data->diff; } } if (data->debug_count >= GRIP_LOG_TIME) { if (data->fail_status_code != 0) GRIP_ERR("err code %d", data->fail_status_code); sx938x_get_data(data); if (data->is_unknown_mode == UNKNOWN_ON && data->motion) sx938x_check_first_working(data); data->debug_count = 0; } else { if (data->is_unknown_mode == UNKNOWN_ON && data->motion) { sx938x_get_data(data); sx938x_check_first_working(data); } data->debug_count++; } schedule_delayed_work(&data->debug_work, msecs_to_jiffies(2000)); } static irqreturn_t sx938x_interrupt_thread(int irq, void *pdata) { struct sx938x_p *data = pdata; __pm_wakeup_event(data->grip_ws, jiffies_to_msecs(3 * HZ)); schedule_delayed_work(&data->irq_work, msecs_to_jiffies(100)); return IRQ_HANDLED; } static int sx938x_input_init(struct sx938x_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[data->ic_num]; dev->id.bustype = BUS_I2C; input_set_capability(dev, EV_REL, REL_MISC); input_set_capability(dev, EV_REL, REL_X); input_set_drvdata(dev, data); ret = input_register_device(dev); if (ret < 0) { input_free_device(dev); return ret; } #if IS_ENABLED(CONFIG_SENSORS_CORE_AP) ret = sensors_create_symlink(&dev->dev.kobj, dev->name); if (ret < 0) { GRIP_ERR("fail to create symlink %d\n", ret); input_unregister_device(dev); return ret; } ret = sysfs_create_group(&dev->dev.kobj, &sx938x_attribute_group); if (ret < 0) { GRIP_ERR("fail to create sysfs group %d\n", ret); sensors_remove_symlink(&data->input->dev.kobj, data->input->name); input_unregister_device(dev); return ret; } #else ret = sensors_create_symlink(dev); if (ret < 0) { GRIP_ERR("fail to create symlink %d\n", ret); input_unregister_device(dev); return ret; } ret = sysfs_create_group(&dev->dev.kobj, &sx938x_attribute_group); if (ret < 0) { sensors_remove_symlink(dev); input_unregister_device(dev); return ret; } #endif /* save the input pointer and finish initialization */ data->input = dev; return 0; } static int sx938x_noti_input_init(struct sx938x_p *data) { int ret = 0; struct input_dev *noti_input_dev = NULL; if (data->unknown_sel) { /* Create the input device */ noti_input_dev = input_allocate_device(); if (!noti_input_dev) { GRIP_ERR("input_allocate_device fail\n"); return -ENOMEM; } noti_input_dev->name = NOTI_MODULE_NAME; noti_input_dev->id.bustype = BUS_I2C; input_set_capability(noti_input_dev, EV_REL, REL_X); input_set_drvdata(noti_input_dev, data); ret = input_register_device(noti_input_dev); if (ret < 0) { GRIP_ERR("fail to regi input dev for noti %d\n", ret); input_free_device(noti_input_dev); return ret; } data->noti_input_dev = noti_input_dev; } return 0; } static int sx938x_setup_pin(struct sx938x_p *data) { int ret; ret = gpio_request(data->gpio_nirq, "SX938X_nIRQ"); if (ret < 0) { GRIP_ERR("gpio %d req fail %d\n", data->gpio_nirq, ret); return ret; } ret = gpio_direction_input(data->gpio_nirq); if (ret < 0) { GRIP_ERR("fail to set gpio %d as input %d\n", data->gpio_nirq, ret); gpio_free(data->gpio_nirq); return ret; } return 0; } /* There is an issue that a grip address depends on the sequence of power and IRQ. So, Add power control function */ static int sx938x_power_onoff(struct sx938x_p *data, bool on) { int ret = 0; int voltage = 0; int reg_enabled = 0; if (data->ldo_en) { ret = gpio_request(data->ldo_en, "sx938x_ldo_en"); if (ret < 0) { GRIP_ERR("gpio %d req fail %d\n", data->ldo_en, ret); return ret; } gpio_set_value(data->ldo_en, on); GRIP_INFO("ldo_en power %d\n", on); gpio_free(data->ldo_en); } if (data->dvdd_vreg_name) { if (data->dvdd_vreg == NULL) { data->dvdd_vreg = regulator_get(NULL, data->dvdd_vreg_name); if (IS_ERR(data->dvdd_vreg)) { data->dvdd_vreg = NULL; GRIP_ERR("fail to get dvdd_vreg %s\n", data->dvdd_vreg_name); } } } if (data->dvdd_vreg) { voltage = regulator_get_voltage(data->dvdd_vreg); reg_enabled = regulator_is_enabled(data->dvdd_vreg); GRIP_INFO("dvdd_vreg reg_enabled=%d voltage=%d\n", reg_enabled, voltage); } if (on) { if (data->dvdd_vreg) { if (reg_enabled == 0) { ret = regulator_enable(data->dvdd_vreg); if (ret) { GRIP_ERR("dvdd reg enable fail\n"); return ret; } GRIP_INFO("dvdd_vreg turn on\n"); } } } else { if (data->dvdd_vreg) { if (reg_enabled == 1) { ret = regulator_disable(data->dvdd_vreg); if (ret) { GRIP_ERR("dvdd reg disable fail\n"); return ret; } GRIP_INFO("dvdd_vreg turn off\n"); } } } GRIP_INFO("%s\n", on ? "on" : "off"); return ret; } static void sx938x_initialize_variable(struct sx938x_p *data) { data->init_done = OFF; data->skip_data = false; data->state = IDLE; data->fail_status_code = 0; data->pre_attach = -1; data->is_unknown_mode = UNKNOWN_OFF; data->motion = 1; data->first_working = false; atomic_set(&data->enable, OFF); } static int sx938x_read_setupreg(struct sx938x_p *data, struct device_node *dnode, const char *str, int idx) { u32 temp_val; int ret; ret = of_property_read_u32(dnode, str, &temp_val); if (!ret) { setup_reg[data->ic_num][idx].val = (u8)temp_val; GRIP_INFO("reg debug str %s add 0x%2x val 0x%2x", str, setup_reg[data->ic_num][idx].reg, setup_reg[data->ic_num][idx].val); } else if (ret == -22) GRIP_ERR("%s default 0x%2x %d\n", str, temp_val, ret); else { GRIP_ERR("%s property read err 0x%2x %d\n", str, temp_val, ret); data->fail_status_code = SX938x_REG_ERROR; } return ret; } static int sx938x_check_dependency(struct device *dev, int ic_num) { struct device_node *dNode = dev->of_node; struct regulator *dvdd_vreg = NULL; /* regulator */ char *dvdd_vreg_name = NULL; /* regulator name */ if (ic_num == MAIN_GRIP) { if (of_property_read_string_index(dNode, "sx938x,dvdd_vreg_name", 0, (const char **)&dvdd_vreg_name)) { dvdd_vreg_name = NULL; } } #if IS_ENABLED(CONFIG_SENSORS_SX9380_SUB) if (ic_num == SUB_GRIP) { if (of_property_read_string_index(dNode, "sx938x_sub,dvdd_vreg_name", 0, (const char **)&dvdd_vreg_name)) { dvdd_vreg_name = NULL; } } #endif #if IS_ENABLED(CONFIG_SENSORS_SX9380_SUB2) if (ic_num == SUB2_GRIP) { if (of_property_read_string_index(dNode, "sx938x_sub2,dvdd_vreg_name", 0, (const char **)&dvdd_vreg_name)) { dvdd_vreg_name = NULL; } } #endif #if IS_ENABLED(CONFIG_SENSORS_SX9380_WIFI) if (ic_num == WIFI_GRIP) { if (of_property_read_string_index(dNode, "sx938x_wifi,dvdd_vreg_name", 0, (const char **)&dvdd_vreg_name)) { dvdd_vreg_name = NULL; } } #endif pr_info("[GRIP_%d] dvdd_vreg_name %s\n", ic_num, dvdd_vreg_name); if (dvdd_vreg_name) { if (dvdd_vreg == NULL) { dvdd_vreg = regulator_get(NULL, dvdd_vreg_name); if (IS_ERR(dvdd_vreg)) { pr_info("[GRIP_%d] %s\n", ic_num, __func__); return -EPROBE_DEFER; } } } return 0; } static int sx938x_parse_dt(struct sx938x_p *data, struct device *dev) { struct device_node *dNode = dev->of_node; enum of_gpio_flags flags; int reg_size = 0, ret = 0; int i; if (dNode == NULL) return -ENODEV; if (data->ic_num == MAIN_GRIP) { #if IS_ENABLED(CONFIG_SENSORS_COMMON_VDD_SUB) data->gpio_nirq_sub = of_get_named_gpio_flags(dNode, "sx938x,nirq_gpio_sub", 0, &flags); if (data->gpio_nirq_sub < 0) GRIP_ERR("nirq_gpio_sub is null\n"); else GRIP_INFO("get nirq_gpio_sub %d\n", data->gpio_nirq_sub); #endif data->gpio_nirq = of_get_named_gpio_flags(dNode, "sx938x,nirq-gpio", 0, &flags); ret = of_property_read_u32(dNode, "sx938x,unknown_sel", &data->unknown_sel); #if IS_ENABLED(CONFIG_SENSORS_SX9380_SUB) } else if (data->ic_num == SUB_GRIP) { data->gpio_nirq = of_get_named_gpio_flags(dNode, "sx938x_sub,nirq-gpio", 0, &flags); ret = of_property_read_u32(dNode, "sx938x_sub,unknown_sel", &data->unknown_sel); #endif #if IS_ENABLED(CONFIG_SENSORS_SX9380_SUB2) } else if (data->ic_num == SUB2_GRIP) { data->gpio_nirq = of_get_named_gpio_flags(dNode, "sx938x_sub2,nirq-gpio", 0, &flags); ret = of_property_read_u32(dNode, "sx938x_sub2,unknown_sel", &data->unknown_sel); #endif #if IS_ENABLED(CONFIG_SENSORS_SX9380_WIFI) } else if (data->ic_num == WIFI_GRIP) { data->gpio_nirq = of_get_named_gpio_flags(dNode, "sx938x_wifi,nirq-gpio", 0, &flags); ret = of_property_read_u32(dNode, "sx938x_wifi,unknown_sel", &data->unknown_sel); #endif } if (ret < 0) { GRIP_ERR("unknown_sel read fail %d\n", ret); data->unknown_sel = 1; ret = 0; } GRIP_INFO("unknown_sel %d\n", data->unknown_sel); if (data->gpio_nirq < 0) { GRIP_ERR("get gpio_nirq err\n"); return -ENODEV; } else { GRIP_INFO("get gpio_nirq %d\n", data->gpio_nirq); } if (data->ic_num == MAIN_GRIP) data->ldo_en = of_get_named_gpio_flags(dNode, "sx938x,ldo_en", 0, &flags); #if IS_ENABLED(CONFIG_SENSORS_SX9380_SUB) if (data->ic_num == SUB_GRIP) data->ldo_en = of_get_named_gpio_flags(dNode, "sx938x_sub,ldo_en", 0, &flags); #endif #if IS_ENABLED(CONFIG_SENSORS_SX9380_SUB2) if (data->ic_num == SUB2_GRIP) data->ldo_en = of_get_named_gpio_flags(dNode, "sx938x_sub2,ldo_en", 0, &flags); #endif #if IS_ENABLED(CONFIG_SENSORS_SX9380_WIFI) if (data->ic_num == WIFI_GRIP) data->ldo_en = of_get_named_gpio_flags(dNode, "sx938x_wifi,ldo_en", 0, &flags); #endif if (data->ldo_en < 0) { GRIP_ERR("skip ldo_en\n"); data->ldo_en = 0; } else { if (data->ic_num == MAIN_GRIP) ret = gpio_request(data->ldo_en, "sx938x_ldo_en"); #if IS_ENABLED(CONFIG_SENSORS_SX9380_SUB) if (data->ic_num == SUB_GRIP) ret = gpio_request(data->ldo_en, "sx938x_sub_ldo_en"); #endif #if IS_ENABLED(CONFIG_SENSORS_SX9380_SUB2) if (data->ic_num == SUB2_GRIP) ret = gpio_request(data->ldo_en, "sx938x_sub2_ldo_en"); #endif #if IS_ENABLED(CONFIG_SENSORS_SX9380_WIFI) if (data->ic_num == WIFI_GRIP) ret = gpio_request(data->ldo_en, "sx938x_wifi_ldo_en"); #endif if (ret < 0) { GRIP_ERR("gpio %d req fail %d\n", data->ldo_en, ret); return ret; } gpio_direction_output(data->ldo_en, 0); gpio_free(data->ldo_en); } if (data->ic_num == MAIN_GRIP) { if (of_property_read_string_index(dNode, "sx938x,dvdd_vreg_name", 0, (const char **)&data->dvdd_vreg_name)) { data->dvdd_vreg_name = NULL; } } #if IS_ENABLED(CONFIG_SENSORS_SX9380_SUB) if (data->ic_num == SUB_GRIP) { if (of_property_read_string_index(dNode, "sx938x_sub,dvdd_vreg_name", 0, (const char **)&data->dvdd_vreg_name)) { data->dvdd_vreg_name = NULL; } } #endif #if IS_ENABLED(CONFIG_SENSORS_SX9380_SUB2) if (data->ic_num == SUB2_GRIP) { if (of_property_read_string_index(dNode, "sx938x_sub2,dvdd_vreg_name", 0, (const char **)&data->dvdd_vreg_name)) { data->dvdd_vreg_name = NULL; } } #endif #if IS_ENABLED(CONFIG_SENSORS_SX9380_WIFI) if (data->ic_num == WIFI_GRIP) { if (of_property_read_string_index(dNode, "sx938x_wifi,dvdd_vreg_name", 0, (const char **)&data->dvdd_vreg_name)) { data->dvdd_vreg_name = NULL; } } #endif GRIP_INFO("dvdd_vreg_name %s\n", data->dvdd_vreg_name); if (data->ic_num == MAIN_GRIP) reg_size = sizeof(sx938x_parse_reg) / sizeof(char *); #if IS_ENABLED(CONFIG_SENSORS_SX9380_SUB) else if (data->ic_num == SUB_GRIP) reg_size = sizeof(sx938x_sub_parse_reg) / sizeof(char *); #endif #if IS_ENABLED(CONFIG_SENSORS_SX9380_SUB2) else if (data->ic_num == SUB2_GRIP) reg_size = sizeof(sx938x_sub2_parse_reg) / sizeof(char *); #endif #if IS_ENABLED(CONFIG_SENSORS_SX9380_WIFI) else if (data->ic_num == WIFI_GRIP) reg_size = sizeof(sx938x_wifi_parse_reg) / sizeof(char *); #endif for (i = 0; i < reg_size; i++) { if (data->ic_num == MAIN_GRIP) sx938x_read_setupreg(data, dNode, sx938x_parse_reg[i], i); #if IS_ENABLED(CONFIG_SENSORS_SX9380_SUB) else if (data->ic_num == SUB_GRIP) sx938x_read_setupreg(data, dNode, sx938x_sub_parse_reg[i], i); #endif #if IS_ENABLED(CONFIG_SENSORS_SX9380_SUB2) else if (data->ic_num == SUB2_GRIP) sx938x_read_setupreg(data, dNode, sx938x_sub2_parse_reg[i], i); #endif #if IS_ENABLED(CONFIG_SENSORS_SX9380_WIFI) else if (data->ic_num == WIFI_GRIP) sx938x_read_setupreg(data, dNode, sx938x_wifi_parse_reg[i], i); #endif } return 0; } #if IS_ENABLED(CONFIG_PDIC_NOTIFIER) && IS_ENABLED(CONFIG_USB_TYPEC_MANAGER_NOTIFIER) static int sx938x_pdic_handle_notification(struct notifier_block *nb, unsigned long action, void *pdic_data) { PD_NOTI_ATTACH_TYPEDEF usb_typec_info = *(PD_NOTI_ATTACH_TYPEDEF *)pdic_data; struct sx938x_p *data = container_of(nb, struct sx938x_p, pdic_nb); if (usb_typec_info.id != PDIC_NOTIFY_ID_ATTACH) return 0; if (data->pre_attach == usb_typec_info.attach) return 0; GRIP_INFO("src %d id %d attach %d rprd %d\n", usb_typec_info.src, usb_typec_info.id, usb_typec_info.attach, usb_typec_info.rprd); if (data->init_done == ON) { sx938x_enter_unknown_mode(data, TYPE_USB); sx938x_manual_offset_calibration(data); } data->pre_attach = usb_typec_info.attach; return 0; } #endif #if IS_ENABLED(CONFIG_HALL_NOTIFIER) static int sx938x_hall_notifier(struct notifier_block *nb, unsigned long action, void *hall_data) { struct hall_notifier_context *hall_notifier; struct sx938x_p *data = container_of(nb, struct sx938x_p, hall_nb); hall_notifier = hall_data; if (action == HALL_ATTACH) { GRIP_INFO("%s attach\n", hall_notifier->name); sx938x_enter_unknown_mode(data, TYPE_HALL); sx938x_manual_offset_calibration(data); } else { sx938x_enter_unknown_mode(data, TYPE_HALL); return 0; } return 0; } #endif static int sx938x_probe(struct i2c_client *client, const struct i2c_device_id *id) { int ret = -ENODEV; struct sx938x_p *data = NULL; int ic_num = 0; if (strcmp(client->name, "sx938x") == 0) ic_num = MAIN_GRIP; #if IS_ENABLED(CONFIG_SENSORS_SX9380_SUB) else if (strcmp(client->name, "sx938x_sub") == 0) ic_num = SUB_GRIP; #endif #if IS_ENABLED(CONFIG_SENSORS_SX9380_SUB2) else if (strcmp(client->name, "sx938x_sub2") == 0) ic_num = SUB2_GRIP; #endif #if IS_ENABLED(CONFIG_SENSORS_SX9380_WIFI) else if (strcmp(client->name, "sx938x_wifi") == 0) ic_num = WIFI_GRIP; #endif else { pr_err("[GRIP] name %s can't find grip ic num", client->name); return -1; } pr_info("[GRIP_%s] %s start 0x%x\n", grip_name[ic_num], __func__, client->addr); ret = sx938x_check_dependency(&client->dev, ic_num); if (ret < 0) return ret; if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { pr_info("[GRIP_%s] i2c func err\n", grip_name[ic_num]); goto exit; } data = kzalloc(sizeof(struct sx938x_p), GFP_KERNEL); if (data == NULL) { pr_info("[GRIP_%s] Fail to mem alloc\n", grip_name[ic_num]); ret = -ENOMEM; goto exit_kzalloc; } data->ic_num = ic_num; i2c_set_clientdata(client, data); data->client = client; data->factory_device = &client->dev; ret = sx938x_input_init(data); if (ret < 0) goto exit_input_init; data->grip_ws = wakeup_source_register(&client->dev, "grip_wake_lock"); mutex_init(&data->mode_mutex); mutex_init(&data->read_mutex); ret = sx938x_parse_dt(data, &client->dev); if (ret < 0) { GRIP_ERR("of_node err\n"); ret = -ENODEV; goto exit_of_node; } ret = sx938x_noti_input_init(data); if (ret < 0) goto exit_noti_input_init; //If Host NIRQ pin has a protection diode and PULL UP (state HIGH), addr can be 0x29 or 0x2A //However, if the state of the pin is LOW, 0x28 is guaranteed. #if IS_ENABLED(CONFIG_SENSORS_COMMON_VDD_SUB) if (ic_num == SUB_GRIP) { GRIP_INFO("skip irq outmode\n"); } else { ret = gpio_direction_output(data->gpio_nirq, 0); if (ret < 0) GRIP_ERR("could not setup outmode\n"); if (ic_num == MAIN_GRIP) { ret = gpio_direction_output(data->gpio_nirq_sub, 0); if (ret < 0) GRIP_ERR("could not setup outmode(sub)\n"); } usleep_range(1000, 1100); sx938x_power_onoff(data, 1); usleep_range(6000, 6100); // Tpor > 5ms } #else ret = gpio_direction_output(data->gpio_nirq, 0); if (ret < 0) GRIP_ERR("could not setup outmode\n"); usleep_range(1000, 1100); sx938x_power_onoff(data, 1); usleep_range(6000, 6100); // Tpor > 5ms #endif ret = sx938x_setup_pin(data); if (ret) { GRIP_ERR("could not setup pin\n"); goto exit_setup_pin; } /* read chip id */ ret = sx938x_hardware_check(data); if (ret < 0) { GRIP_ERR("chip id check fail %d\n", ret); goto exit_chip_reset; } sx938x_initialize_variable(data); INIT_DELAYED_WORK(&data->init_work, sx938x_init_work_func); INIT_DELAYED_WORK(&data->irq_work, sx938x_irq_work_func); INIT_DELAYED_WORK(&data->debug_work, sx938x_debug_work_func); data->irq = gpio_to_irq(data->gpio_nirq); /* initailize interrupt reporting */ ret = request_threaded_irq(data->irq, NULL, sx938x_interrupt_thread, IRQF_TRIGGER_FALLING | IRQF_ONESHOT, device_name[data->ic_num], data); if (ret < 0) { GRIP_ERR("fail to req thread irq %d ret %d\n", data->irq, ret); goto exit_request_threaded_irq; } disable_irq(data->irq); ret = sensors_register(&data->factory_device, data, sensor_attrs, (char *)module_name[data->ic_num]); if (ret) { GRIP_ERR("could not regi sensor %d\n", ret); goto exit_register_failed; } schedule_delayed_work(&data->init_work, msecs_to_jiffies(300)); sx938x_set_debug_work(data, ON, 20000); #if IS_ENABLED(CONFIG_PDIC_NOTIFIER) && IS_ENABLED(CONFIG_USB_TYPEC_MANAGER_NOTIFIER) GRIP_INFO("regi pdic notifier\n"); manager_notifier_register(&data->pdic_nb, sx938x_pdic_handle_notification, MANAGER_NOTIFY_PDIC_SENSORHUB); #endif #if IS_ENABLED(CONFIG_HALL_NOTIFIER) GRIP_INFO("regi hall notifier\n"); data->hall_nb.priority = 1; data->hall_nb.notifier_call = sx938x_hall_notifier; hall_notifier_register(&data->hall_nb); #endif GRIP_INFO("done!\n"); return 0; exit_register_failed: sensors_unregister(data->factory_device, sensor_attrs); free_irq(data->irq, data); exit_request_threaded_irq: exit_chip_reset: gpio_free(data->gpio_nirq); exit_setup_pin: if (data->unknown_sel) input_unregister_device(data->noti_input_dev); exit_noti_input_init: exit_of_node: mutex_destroy(&data->mode_mutex); mutex_destroy(&data->read_mutex); wakeup_source_unregister(data->grip_ws); sysfs_remove_group(&data->input->dev.kobj, &sx938x_attribute_group); #if IS_ENABLED(CONFIG_SENSORS_CORE_AP) sensors_remove_symlink(&data->input->dev.kobj, data->input->name); #else sensors_remove_symlink(data->input); #endif input_unregister_device(data->input); exit_input_init: kfree(data); exit_kzalloc: exit: pr_err("[GRIP_%s] Probe fail!\n", grip_name[ic_num]); return ret; } static int sx938x_remove(struct i2c_client *client) { struct sx938x_p *data = (struct sx938x_p *)i2c_get_clientdata(client); if (atomic_read(&data->enable) == ON) sx938x_set_enable(data, OFF); sx938x_set_mode(data, SX938x_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); wakeup_source_unregister(data->grip_ws); sensors_unregister(data->factory_device, sensor_attrs); #if IS_ENABLED(CONFIG_SENSORS_CORE_AP) sensors_remove_symlink(&data->input->dev.kobj, data->input->name); #else sensors_remove_symlink(data->input); #endif sysfs_remove_group(&data->input->dev.kobj, &sx938x_attribute_group); input_unregister_device(data->input); input_unregister_device(data->noti_input_dev); mutex_destroy(&data->mode_mutex); mutex_destroy(&data->read_mutex); kfree(data); return 0; } static int sx938x_suspend(struct device *dev) { struct sx938x_p *data = dev_get_drvdata(dev); int cnt = 0; GRIP_INFO("\n"); /* before go to sleep, make the interrupt pin as high*/ while ((sx938x_get_nirq_state(data) == INTERRUPT_LOW) && (cnt++ < 3)) { sx938x_read_reg_stat(data); msleep(20); } if (cnt >= 3) GRIP_ERR("s/w reset fail(%d)\n", cnt); sx938x_set_debug_work(data, OFF, 1000); return 0; } static int sx938x_resume(struct device *dev) { struct sx938x_p *data = dev_get_drvdata(dev); GRIP_INFO("\n"); sx938x_set_debug_work(data, ON, 1000); return 0; } static void sx938x_shutdown(struct i2c_client *client) { struct sx938x_p *data = i2c_get_clientdata(client); GRIP_INFO("\n"); sx938x_set_debug_work(data, OFF, 1000); if (atomic_read(&data->enable) == ON) sx938x_set_enable(data, OFF); sx938x_set_mode(data, SX938x_MODE_SLEEP); } static const struct of_device_id sx938x_match_table[] = { { .compatible = "sx938x",}, {}, }; static const struct i2c_device_id sx938x_id[] = { { "SX938X", 0 }, { } }; static const struct dev_pm_ops sx938x_pm_ops = { .suspend = sx938x_suspend, .resume = sx938x_resume, }; static struct i2c_driver sx938x_driver = { .driver = { .name = "SX9380", .owner = THIS_MODULE, .of_match_table = sx938x_match_table, .pm = &sx938x_pm_ops }, .probe = sx938x_probe, .remove = sx938x_remove, .shutdown = sx938x_shutdown, .id_table = sx938x_id, }; #if IS_ENABLED(CONFIG_SENSORS_SX9380_SUB) static const struct of_device_id sx938x_sub_match_table[] = { { .compatible = "sx938x_sub",}, {}, }; static const struct i2c_device_id sx938x_sub_id[] = { { "SX938X_SUB", 0 }, { } }; static struct i2c_driver sx938x_sub_driver = { .driver = { .name = "SX9380_SUB", .owner = THIS_MODULE, .of_match_table = sx938x_sub_match_table, .pm = &sx938x_pm_ops }, .probe = sx938x_probe, .remove = sx938x_remove, .shutdown = sx938x_shutdown, .id_table = sx938x_sub_id, }; #endif #if IS_ENABLED(CONFIG_SENSORS_SX9380_SUB2) static const struct of_device_id sx938x_sub2_match_table[] = { { .compatible = "sx938x_sub2",}, {}, }; static const struct i2c_device_id sx938x_sub2_id[] = { { "SX938X_SUB2", 0 }, { } }; static struct i2c_driver sx938x_sub2_driver = { .driver = { .name = "SX9380_SUB2", .owner = THIS_MODULE, .of_match_table = sx938x_sub2_match_table, .pm = &sx938x_pm_ops }, .probe = sx938x_probe, .remove = sx938x_remove, .shutdown = sx938x_shutdown, .id_table = sx938x_sub2_id, }; #endif #if IS_ENABLED(CONFIG_SENSORS_SX9380_WIFI) static const struct of_device_id sx938x_wifi_match_table[] = { { .compatible = "sx938x_wifi",}, {}, }; static const struct i2c_device_id sx938x_wifi_id[] = { { "SX938X_WIFI", 0 }, { } }; static struct i2c_driver sx938x_wifi_driver = { .driver = { .name = "SX9380_WIFI", .owner = THIS_MODULE, .of_match_table = sx938x_wifi_match_table, .pm = &sx938x_pm_ops }, .probe = sx938x_probe, .remove = sx938x_remove, .shutdown = sx938x_shutdown, .id_table = sx938x_wifi_id, }; #endif static int __init sx938x_init(void) { int ret = 0; ret = i2c_add_driver(&sx938x_driver); if (ret != 0) pr_err("[GRIP] sx938x_driver probe fail\n"); #if IS_ENABLED(CONFIG_SENSORS_SX9380_SUB) ret = i2c_add_driver(&sx938x_sub_driver); if (ret != 0) pr_err("[GRIP_SUB] sx938x_sub_driver probe fail\n"); #endif #if IS_ENABLED(CONFIG_SENSORS_SX9380_SUB2) ret = i2c_add_driver(&sx938x_sub2_driver); if (ret != 0) pr_err("[GRIP_SUB] sx938x_sub2_driver probe fail\n"); #endif #if IS_ENABLED(CONFIG_SENSORS_SX9380_WIFI) ret = i2c_add_driver(&sx938x_wifi_driver); if (ret != 0) pr_err("[GRIP_WIFI] sx938x_wifi_driver probe fail\n"); #endif return ret; } static void __exit sx938x_exit(void) { i2c_del_driver(&sx938x_driver); #if IS_ENABLED(CONFIG_SENSORS_SX9380_SUB) i2c_del_driver(&sx938x_sub_driver); #endif #if IS_ENABLED(CONFIG_SENSORS_SX9380_SUB2) i2c_del_driver(&sx938x_sub2_driver); #endif #if IS_ENABLED(CONFIG_SENSORS_SX9380_WIFI) i2c_del_driver(&sx938x_wifi_driver); #endif } module_init(sx938x_init); module_exit(sx938x_exit); MODULE_DESCRIPTION("Semtech Corp. SX9380 Capacitive Touch Controller Driver"); MODULE_AUTHOR("Samsung Electronics"); MODULE_LICENSE("GPL");