kernel_samsung_a53x/drivers/sensors/sx938x.c
2024-06-15 16:02:09 -03:00

2333 lines
59 KiB
C
Executable file

/*
* 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 <linux/module.h>
#include <linux/slab.h>
#include <linux/i2c.h>
#include <linux/delay.h>
#include <linux/input.h>
#include <linux/fs.h>
#include <linux/interrupt.h>
#include <linux/pm_wakeup.h>
#include <linux/uaccess.h>
#include <linux/gpio.h>
#include <linux/of_gpio.h>
#include <linux/regulator/consumer.h>
#include <linux/power_supply.h>
#include "sx938x.h"
#if IS_ENABLED(CONFIG_SENSORS_CORE_AP)
#include <linux/sensor/sensors_core.h>
#endif
#if IS_ENABLED(CONFIG_PDIC_NOTIFIER)
#include <linux/usb/typec/common/pdic_notifier.h>
#endif
#if IS_ENABLED(CONFIG_USB_TYPEC_MANAGER_NOTIFIER)
#include <linux/usb/typec/manager/usb_typec_manager_notifier.h>
#endif
#if IS_ENABLED(CONFIG_HALL_NOTIFIER)
#include <linux/hall/hall_ic_notifier.h>
#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 = &reg_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, &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", &reg_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", &regist) != 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");