android_kernel_samsung_hero.../drivers/sensors/sx9310.c

1764 lines
44 KiB
C
Raw Normal View History

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