1939 lines
50 KiB
C
1939 lines
50 KiB
C
|
/*
|
||
|
* Copyright (C) 2010,Imagis Technology Co. Ltd. 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 as published by
|
||
|
* the Free Software Foundation; either version 2 of the License, or
|
||
|
* (at your option) any later version.
|
||
|
*
|
||
|
* 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.
|
||
|
*
|
||
|
*/
|
||
|
|
||
|
#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/power_supply.h>
|
||
|
#include <linux/vmalloc.h>
|
||
|
#if defined(CONFIG_MUIC_NOTIFIER)
|
||
|
#include <linux/muic/muic.h>
|
||
|
#include <linux/muic/muic_notifier.h>
|
||
|
#endif
|
||
|
#if defined(CONFIG_CCIC_NOTIFIER) || defined(CONFIG_PDIC_NOTIFIER)
|
||
|
#include <linux/usb/typec/pdic_notifier.h>
|
||
|
#endif
|
||
|
#if defined(CONFIG_USB_TYPEC_MANAGER_NOTIFIER)
|
||
|
#include <linux/usb/typec/usb_typec_manager_notifier.h>
|
||
|
#endif
|
||
|
|
||
|
#include "isg5320a_reg.h"
|
||
|
|
||
|
#define CHIP_ID 0x32
|
||
|
#define DEVICE_NAME "ISG5320A"
|
||
|
#define VENDOR_NAME "IMAGIS"
|
||
|
#define MODULE_NAME "grip_sensor"
|
||
|
|
||
|
#define ISG5320A_MODE_SLEEP 0
|
||
|
#define ISG5320A_MODE_NORMAL 1
|
||
|
#define ISG5320A_DIFF_AVR_CNT 10
|
||
|
#define ISG5320A_DISPLAY_TIME 30
|
||
|
#define ISG5320A_TAG "[ISG5320A]"
|
||
|
|
||
|
#if IS_ENABLED(CONFIG_HALL_NEW_NODE)
|
||
|
#define HALLIC_PATH "/sys/class/sec/hall_ic/hall_detect"
|
||
|
#define HALLIC_CERT_PATH "/sys/class/sec/hall_ic/certify_hall_detect"
|
||
|
#else
|
||
|
#define HALLIC_PATH "/sys/class/sec/sec_key/hall_detect"
|
||
|
#define HALLIC_CERT_PATH "/sys/class/sec/sec_key/certify_hall_detect"
|
||
|
#endif
|
||
|
|
||
|
#define ISG5320A_INIT_DELAYEDWORK
|
||
|
#define GRIP_LOG_TIME 40 /* 20 sec */
|
||
|
|
||
|
#pragma pack(1)
|
||
|
typedef struct {
|
||
|
char cmd;
|
||
|
u8 addr;
|
||
|
u8 val;
|
||
|
} direct_info;
|
||
|
#pragma pack()
|
||
|
|
||
|
struct isg5320a_data {
|
||
|
struct i2c_client *client;
|
||
|
struct input_dev *input_dev;
|
||
|
struct device *dev;
|
||
|
struct delayed_work debug_work;
|
||
|
struct delayed_work cal_work;
|
||
|
struct work_struct force_cal_work;
|
||
|
#ifdef ISG5320A_INIT_DELAYEDWORK
|
||
|
struct delayed_work init_work;
|
||
|
#endif
|
||
|
struct wake_lock grip_wake_lock;
|
||
|
struct mutex lock;
|
||
|
#ifdef CONFIG_MUIC_NOTIFIER
|
||
|
struct notifier_block cpuidle_muic_nb;
|
||
|
#endif
|
||
|
#if defined(CONFIG_CCIC_NOTIFIER) || defined(CONFIG_PDIC_NOTIFIER)
|
||
|
struct notifier_block cpuidle_ccic_nb;
|
||
|
#endif
|
||
|
int gpio_int;
|
||
|
|
||
|
int enable;
|
||
|
int state;
|
||
|
|
||
|
bool skip_data;
|
||
|
int initialized;
|
||
|
|
||
|
u16 normal_th;
|
||
|
|
||
|
u32 cdc;
|
||
|
u32 base;
|
||
|
s32 diff;
|
||
|
s32 max_diff;
|
||
|
s32 max_normal_diff;
|
||
|
|
||
|
int diff_cnt;
|
||
|
int diff_sum;
|
||
|
int diff_avg;
|
||
|
int cdc_sum;
|
||
|
int cdc_avg;
|
||
|
|
||
|
u32 debug_cdc[3];
|
||
|
s32 debug_diff[3];
|
||
|
u32 debug_base[2];
|
||
|
|
||
|
int irq_count;
|
||
|
int abnormal_mode;
|
||
|
|
||
|
u16 fine_coarse;
|
||
|
u32 cfcal_th;
|
||
|
bool bfcal_chk_ready;
|
||
|
bool bfcal_chk_start;
|
||
|
u32 bfcal_chk_count;
|
||
|
u32 bfcal_chk_cdc;
|
||
|
s32 bfcal_chk_diff;
|
||
|
|
||
|
int debug_cnt;
|
||
|
|
||
|
u8 intr_debug_addr;
|
||
|
int intr_debug_size;
|
||
|
direct_info direct;
|
||
|
|
||
|
int hall_flag;
|
||
|
int hall_cert_flag;
|
||
|
int hallic_detect;
|
||
|
int hallic_cert_detect;
|
||
|
unsigned char hall_ic[6];
|
||
|
};
|
||
|
|
||
|
static int check_hallic_state(char *file_path, unsigned char hall_ic_status[])
|
||
|
{
|
||
|
int ret = 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)) {
|
||
|
ret = PTR_ERR(filep);
|
||
|
set_fs(old_fs);
|
||
|
goto exit;
|
||
|
}
|
||
|
|
||
|
ret = vfs_read(filep, hall_sysfs, sizeof(hall_sysfs),
|
||
|
&filep->f_pos);
|
||
|
|
||
|
if (ret != sizeof(hall_sysfs))
|
||
|
ret = -EIO;
|
||
|
else
|
||
|
strncpy(hall_ic_status, hall_sysfs, sizeof(hall_sysfs));
|
||
|
|
||
|
filp_close(filep, current->files);
|
||
|
set_fs(old_fs);
|
||
|
|
||
|
exit:
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
static int isg5320a_i2c_write(struct isg5320a_data *data, u8 cmd, u8 *val,
|
||
|
int len)
|
||
|
{
|
||
|
int ret;
|
||
|
u8 buf[sizeof(cmd) + len];
|
||
|
struct i2c_msg msg;
|
||
|
|
||
|
buf[0] = cmd;
|
||
|
memcpy(buf + sizeof(cmd), val, len);
|
||
|
|
||
|
msg.addr = data->client->addr;
|
||
|
msg.flags = 0; /*I2C_M_WR*/
|
||
|
msg.len = sizeof(cmd) + len;
|
||
|
msg.buf = buf;
|
||
|
|
||
|
ret = i2c_transfer(data->client->adapter, &msg, 1);
|
||
|
if (ret < 0)
|
||
|
pr_err("%s %s: i2c_transfer failed(%d)\n", ISG5320A_TAG, __func__, ret);
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
static int isg5320a_i2c_write_one(struct isg5320a_data *data, u8 cmd, u8 val)
|
||
|
{
|
||
|
int ret;
|
||
|
u8 buf[2];
|
||
|
struct i2c_msg msg;
|
||
|
|
||
|
buf[0] = cmd;
|
||
|
buf[1] = val;
|
||
|
|
||
|
msg.addr = data->client->addr;
|
||
|
msg.flags = 0; /*I2C_M_WR*/
|
||
|
msg.len = 2;
|
||
|
msg.buf = buf;
|
||
|
|
||
|
ret = i2c_transfer(data->client->adapter, &msg, 1);
|
||
|
if (ret < 0)
|
||
|
pr_err("%s %s: i2c_transfer failed(%d)\n", ISG5320A_TAG, __func__, ret);
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
static int isg5320a_i2c_read(struct isg5320a_data *data, u8 cmd, u8 *val,
|
||
|
int len)
|
||
|
{
|
||
|
int ret;
|
||
|
struct i2c_msg msgs[2] = {
|
||
|
{
|
||
|
.addr = data->client->addr,
|
||
|
.flags = 0,
|
||
|
.len = sizeof(cmd),
|
||
|
.buf = &cmd,
|
||
|
},
|
||
|
{
|
||
|
.addr = data->client->addr,
|
||
|
.flags = I2C_M_RD,
|
||
|
.len = len,
|
||
|
.buf = val,
|
||
|
},
|
||
|
};
|
||
|
|
||
|
ret = i2c_transfer(data->client->adapter, msgs, 2);
|
||
|
if (ret < 0)
|
||
|
pr_err("%s %s: i2c_transfer failed(%d)\n", ISG5320A_TAG, __func__, ret);
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
static int isg5320a_reset(struct isg5320a_data *data)
|
||
|
{
|
||
|
int ret = 0;
|
||
|
int cnt = 0;
|
||
|
u8 val;
|
||
|
|
||
|
pr_info("%s %s\n", ISG5320A_TAG, __func__);
|
||
|
|
||
|
if (data->initialized == OFF)
|
||
|
usleep_range(5000, 5100);
|
||
|
|
||
|
ret = isg5320a_i2c_read(data, ISG5320A_IRQSRC_REG, &val, 1);
|
||
|
if (ret < 0)
|
||
|
pr_err("%s irq to high failed(%d)\n", ISG5320A_TAG, ret);
|
||
|
|
||
|
while (gpio_get_value_cansleep(data->gpio_int) == 0 && cnt++ < 10)
|
||
|
usleep_range(5000, 5100);
|
||
|
|
||
|
if (cnt >= 10)
|
||
|
pr_err("%s wait irq to high failed\n", ISG5320A_TAG);
|
||
|
|
||
|
ret = isg5320a_i2c_write_one(data, ISG5320A_PROTECT_REG,
|
||
|
ISG5320A_PRT_VALUE);
|
||
|
if (ret < 0)
|
||
|
pr_err("%s unlock protect failed(%d)\n", ISG5320A_TAG, ret);
|
||
|
|
||
|
ret = isg5320a_i2c_write_one(data, ISG5320A_SOFTRESET_REG,
|
||
|
ISG5320A_RST_VALUE);
|
||
|
if (ret < 0)
|
||
|
pr_err("%s soft reset failed(%d)\n", ISG5320A_TAG, ret);
|
||
|
|
||
|
usleep_range(1000, 1100);
|
||
|
|
||
|
cnt = 0;
|
||
|
while (gpio_get_value_cansleep(data->gpio_int) != 0 && cnt++ < 10)
|
||
|
usleep_range(5000, 5100);
|
||
|
|
||
|
if (cnt >= 10) {
|
||
|
pr_err("%s wait soft reset failed\n", ISG5320A_TAG);
|
||
|
return -EIO;
|
||
|
} else {
|
||
|
pr_info("%s wait cnt:%d\n", ISG5320A_TAG, cnt);
|
||
|
}
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
static void isg5320a_force_calibration(struct isg5320a_data *data,
|
||
|
bool only_bfcal)
|
||
|
|
||
|
{
|
||
|
mutex_lock(&data->lock);
|
||
|
|
||
|
pr_info("%s %s(%d)\n", ISG5320A_TAG, __func__, only_bfcal ? 1 : 0);
|
||
|
|
||
|
if (!only_bfcal) {
|
||
|
isg5320a_i2c_write_one(data, ISG5320A_SCANCTRL1_REG,
|
||
|
ISG5320A_SCAN_STOP);
|
||
|
|
||
|
isg5320a_i2c_write_one(data, ISG5320A_BS_ON_WD_REG, ISG5320A_BS_WD_OFF);
|
||
|
isg5320a_i2c_write_one(data, ISG5320A_SCANCTRL2_REG,
|
||
|
ISG5320A_SCAN2_CLEAR);
|
||
|
msleep(100);
|
||
|
isg5320a_i2c_write_one(data, ISG5320A_OSCCON_REG, ISG5320A_OSC_NOMAL);
|
||
|
|
||
|
isg5320a_i2c_write_one(data, ISG5320A_BS_ON_WD_REG, ISG5320A_BS_WD_ON);
|
||
|
isg5320a_i2c_write_one(data, ISG5320A_SCANCTRL2_REG,
|
||
|
ISG5320A_SCAN2_RESET);
|
||
|
|
||
|
isg5320a_i2c_write_one(data, ISG5320A_SCANCTRL1_REG,
|
||
|
ISG5320A_CFCAL_START);
|
||
|
msleep(300);
|
||
|
}
|
||
|
|
||
|
isg5320a_i2c_write_one(data, ISG5320A_SCANCTRL2_REG, ISG5320A_BFCAL_START);
|
||
|
|
||
|
msleep(100);
|
||
|
|
||
|
mutex_unlock(&data->lock);
|
||
|
}
|
||
|
|
||
|
static inline unsigned char str2int(unsigned char c)
|
||
|
{
|
||
|
if (c >= '0' && c <= '9')
|
||
|
return c - '0';
|
||
|
|
||
|
if (c >= 'A' && c <= 'F')
|
||
|
return c - 'A' + 10;
|
||
|
|
||
|
if (c >= 'a' && c <= 'f')
|
||
|
return c - 'a' + 10;
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int isg5320a_setup_reg(struct isg5320a_data *data)
|
||
|
{
|
||
|
int ret;
|
||
|
int i;
|
||
|
long file_size = 0;
|
||
|
uint8_t *file_data;
|
||
|
struct file *fp = NULL;
|
||
|
loff_t pos;
|
||
|
mm_segment_t old_fs = { 0 };
|
||
|
u8 addr = 0;
|
||
|
u8 val = 0;
|
||
|
|
||
|
old_fs = get_fs();
|
||
|
set_fs(get_ds());
|
||
|
|
||
|
fp = filp_open("/sdcard/isg5320a.reg", O_RDONLY, 0);
|
||
|
if (IS_ERR(fp)) {
|
||
|
pr_err("%s %s - file not exist:%ld\n", ISG5320A_TAG, __func__,
|
||
|
PTR_ERR(fp));
|
||
|
set_fs(old_fs);
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
file_size = fp->f_path.dentry->d_inode->i_size;
|
||
|
pr_info("%s %s - size of the file : %ld(bytes)\n", ISG5320A_TAG,
|
||
|
__func__, file_size);
|
||
|
file_data = vzalloc(file_size);
|
||
|
pos = 0;
|
||
|
ret = vfs_read(fp, (char __user *)file_data, file_size, &pos);
|
||
|
if (ret != file_size) {
|
||
|
pr_err("%s %s - failed to read file (ret = %d)\n", ISG5320A_TAG,
|
||
|
__func__, ret);
|
||
|
vfree(file_data);
|
||
|
filp_close(fp, current->files);
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
filp_close(fp, current->files);
|
||
|
set_fs(old_fs);
|
||
|
|
||
|
for (i = 0; i < file_size; i++) {
|
||
|
if (file_data[i] == 'W') {
|
||
|
if ((i + 6) >= file_size)
|
||
|
break;
|
||
|
addr = (u8)((str2int(file_data[i + 2]) * 16) +
|
||
|
str2int(file_data[i + 3]));
|
||
|
val = (u8)((str2int(file_data[i + 5]) * 16) +
|
||
|
str2int(file_data[i + 6]));
|
||
|
isg5320a_i2c_write_one(data, addr, val);
|
||
|
pr_info("%s W %02X %02X\n", ISG5320A_TAG, addr, val);
|
||
|
isg5320a_i2c_read(data, addr, &val, 1);
|
||
|
pr_info("%s %02X %02X\n", ISG5320A_TAG, addr, val);
|
||
|
} else if (file_data[i] == 'R') {
|
||
|
if ((i + 3) >= file_size)
|
||
|
break;
|
||
|
addr = (u8)((str2int(file_data[i + 2]) * 16) +
|
||
|
str2int(file_data[i + 3]));
|
||
|
isg5320a_i2c_read(data, addr, &val, 1);
|
||
|
pr_info("%s R %02X %02X\n", ISG5320A_TAG, addr, val);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
vfree(file_data);
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static void isg5320a_get_raw_data(struct isg5320a_data *data, bool log_print)
|
||
|
{
|
||
|
int ret = 0;
|
||
|
u8 buf[4];
|
||
|
u16 cpbuf;
|
||
|
u32 temp;
|
||
|
|
||
|
mutex_lock(&data->lock);
|
||
|
ret = isg5320a_i2c_read(data, ISG5320A_CDC16_T_H_REG, buf, sizeof(buf));
|
||
|
|
||
|
if (ret < 0) {
|
||
|
pr_info("%s fail to get data\n", ISG5320A_TAG);
|
||
|
} else {
|
||
|
temp = ((u32)buf[0] << 8) | (u32)buf[1];
|
||
|
if ((temp != 0) && (temp != 0xFFFF))
|
||
|
data->cdc = temp;
|
||
|
|
||
|
temp = ((u32)buf[2] << 8) | (u32)buf[3];
|
||
|
if ((temp != 0) && (temp != 0xFFFF))
|
||
|
data->base = temp;
|
||
|
data->diff = (s32)data->cdc - (s32)data->base;
|
||
|
}
|
||
|
|
||
|
ret = isg5320a_i2c_read(data, ISG5320A_COARSE_OUT_B_REG, (u8 *)&cpbuf, 2);
|
||
|
if (ret < 0)
|
||
|
pr_info("%s fail to get capMain\n", ISG5320A_TAG);
|
||
|
else
|
||
|
data->fine_coarse = cpbuf;
|
||
|
|
||
|
mutex_unlock(&data->lock);
|
||
|
|
||
|
if (log_print) {
|
||
|
pr_info("%s capMain: %d%02d, cdc: %d, baseline:%d, diff:%d, skip_data:%d\n",
|
||
|
ISG5320A_TAG, (data->fine_coarse & 0xFF),
|
||
|
((data->fine_coarse >> 8) & 0x3F), data->cdc, data->base,
|
||
|
data->diff, data->skip_data);
|
||
|
} else {
|
||
|
if (data->debug_cnt >= GRIP_LOG_TIME) {
|
||
|
pr_info("%s capMain: %d%02d, cdc: %d, baseline:%d, diff:%d, skip_data:%d\n",
|
||
|
ISG5320A_TAG, (data->fine_coarse & 0xFF),
|
||
|
((data->fine_coarse >> 8) & 0x3F), data->cdc, data->base,
|
||
|
data->diff, data->skip_data);
|
||
|
data->debug_cnt = 0;
|
||
|
} else {
|
||
|
data->debug_cnt++;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void force_far_grip(struct isg5320a_data *data)
|
||
|
{
|
||
|
if (data->state == CLOSE) {
|
||
|
pr_info("%s %s\n", ISG5320A_TAG, __func__);
|
||
|
|
||
|
if (data->skip_data == true)
|
||
|
return;
|
||
|
|
||
|
input_report_rel(data->input_dev, REL_MISC, 2);
|
||
|
input_sync(data->input_dev);
|
||
|
data->state = FAR;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void report_event_data(struct isg5320a_data *data, u8 intr_msg)
|
||
|
{
|
||
|
int state;
|
||
|
|
||
|
if (data->skip_data == true) {
|
||
|
pr_info("%s - skip grip event\n", ISG5320A_TAG);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
state = (intr_msg & (1 << ISG5320A_PROX_STATE)) ? CLOSE : FAR;
|
||
|
|
||
|
if (data->abnormal_mode) {
|
||
|
if (state == CLOSE) {
|
||
|
if (data->max_diff < data->diff)
|
||
|
data->max_diff = data->diff;
|
||
|
data->irq_count++;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (state == CLOSE) {
|
||
|
if (data->state == FAR) {
|
||
|
pr_info("%s CLOSE\n", ISG5320A_TAG);
|
||
|
data->state = CLOSE;
|
||
|
data->bfcal_chk_start = true;
|
||
|
data->bfcal_chk_ready = false;
|
||
|
data->bfcal_chk_count = 0;
|
||
|
} else {
|
||
|
pr_info("%s still CLOSE\n", ISG5320A_TAG);
|
||
|
}
|
||
|
} else {
|
||
|
if (data->state == CLOSE) {
|
||
|
pr_info("%s FAR\n", ISG5320A_TAG);
|
||
|
data->state = FAR;
|
||
|
data->bfcal_chk_start = false;
|
||
|
data->bfcal_chk_ready = false;
|
||
|
data->bfcal_chk_count = 0;
|
||
|
} else {
|
||
|
pr_info("%s already FAR\n", ISG5320A_TAG);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (data->state == CLOSE)
|
||
|
input_report_rel(data->input_dev, REL_MISC, 1);
|
||
|
else
|
||
|
input_report_rel(data->input_dev, REL_MISC, 2);
|
||
|
|
||
|
input_sync(data->input_dev);
|
||
|
}
|
||
|
|
||
|
static irqreturn_t isg5320a_irq_thread(int irq, void *ptr)
|
||
|
{
|
||
|
int ret;
|
||
|
int i;
|
||
|
u8 intr_msg = 0;
|
||
|
u8 *buf8;
|
||
|
struct isg5320a_data *data = (struct isg5320a_data *)ptr;
|
||
|
|
||
|
if (data->initialized == OFF)
|
||
|
return IRQ_HANDLED;
|
||
|
|
||
|
wake_lock(&data->grip_wake_lock);
|
||
|
|
||
|
isg5320a_get_raw_data(data, true);
|
||
|
|
||
|
isg5320a_i2c_read(data, ISG5320A_IRQSRC_REG, &intr_msg, 1);
|
||
|
|
||
|
if (data->intr_debug_size > 0) {
|
||
|
buf8 = kzalloc(data->intr_debug_size, GFP_KERNEL);
|
||
|
if (buf8) {
|
||
|
pr_info("%s Intr_debug1 (0x%02X)\n", ISG5320A_TAG,
|
||
|
data->intr_debug_addr);
|
||
|
isg5320a_i2c_read(data, data->intr_debug_addr, buf8,
|
||
|
data->intr_debug_size);
|
||
|
for (i = 0; i < data->intr_debug_size; i++)
|
||
|
pr_info("%s \t%02X\n", ISG5320A_TAG, buf8[i]);
|
||
|
kfree(buf8);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
ret = isg5320a_i2c_read(data, ISG5320A_IRQSTS_REG, &intr_msg, 1);
|
||
|
if (ret < 0) {
|
||
|
pr_err("%s fail to read state(%d)\n", ISG5320A_TAG, ret);
|
||
|
goto irq_end;
|
||
|
}
|
||
|
|
||
|
pr_info("%s intr msg: 0x%02X\n", ISG5320A_TAG, intr_msg);
|
||
|
|
||
|
report_event_data(data, intr_msg);
|
||
|
|
||
|
irq_end:
|
||
|
wake_unlock(&data->grip_wake_lock);
|
||
|
|
||
|
return IRQ_HANDLED;
|
||
|
}
|
||
|
|
||
|
static void isg5320a_initialize(struct isg5320a_data *data)
|
||
|
{
|
||
|
int ret;
|
||
|
int i;
|
||
|
u8 val;
|
||
|
u8 buf[2];
|
||
|
|
||
|
pr_info("%s %s\n", ISG5320A_TAG, __func__);
|
||
|
|
||
|
force_far_grip(data);
|
||
|
|
||
|
isg5320a_i2c_read(data, ISG5320A_IRQSRC_REG, &val, 1);
|
||
|
isg5320a_i2c_write_one(data, ISG5320A_SCANCTRL1_REG, ISG5320A_SCAN_STOP);
|
||
|
msleep(30);
|
||
|
|
||
|
ret = isg5320a_setup_reg(data);
|
||
|
if (ret < 0) {
|
||
|
for (i = 0; i < (sizeof(setup_reg) >> 1); i++) {
|
||
|
isg5320a_i2c_write_one(data, setup_reg[i].addr, setup_reg[i].val);
|
||
|
pr_info("%s W %02X %02X\n", ISG5320A_TAG, setup_reg[i].addr,
|
||
|
setup_reg[i].val);
|
||
|
isg5320a_i2c_read(data, setup_reg[i].addr, &val, 1);
|
||
|
pr_info("%s %02X %02X\n", ISG5320A_TAG, setup_reg[i].addr, val);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (data->normal_th > 0) {
|
||
|
buf[0] = (data->normal_th >> 8) & 0xFF;
|
||
|
buf[1] = data->normal_th & 0xFF;
|
||
|
|
||
|
isg5320a_i2c_write(data, ISG5320A_B_PROXCTL3_REG, buf, 2);
|
||
|
}
|
||
|
ret = isg5320a_i2c_read(data, ISG5320A_DIGITAL_ACC_REG, &val, 1);
|
||
|
if (ret < 0)
|
||
|
pr_err("%s fail to read DIGITAL ACC(%d)\n", ISG5320A_TAG, ret);
|
||
|
else
|
||
|
data->cfcal_th = ISG5320A_RESET_CONDITION * val / 8;
|
||
|
|
||
|
data->initialized = ON;
|
||
|
}
|
||
|
|
||
|
static void isg5320a_set_debug_work(struct isg5320a_data *data, bool enable,
|
||
|
unsigned int delay_ms)
|
||
|
{
|
||
|
if (enable == ON) {
|
||
|
data->debug_cnt = 0;
|
||
|
schedule_delayed_work(&data->debug_work, msecs_to_jiffies(delay_ms));
|
||
|
schedule_delayed_work(&data->cal_work, msecs_to_jiffies(delay_ms));
|
||
|
} else {
|
||
|
cancel_delayed_work_sync(&data->debug_work);
|
||
|
cancel_delayed_work_sync(&data->cal_work);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void isg5320a_set_enable(struct isg5320a_data *data, int enable)
|
||
|
{
|
||
|
u8 state;
|
||
|
int ret = 0;
|
||
|
|
||
|
pr_info("%s %s\n", ISG5320A_TAG, __func__);
|
||
|
|
||
|
if (data->enable == enable) {
|
||
|
pr_info("%s - already enabled\n", ISG5320A_TAG);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (enable == ON) {
|
||
|
pr_info("%s %s - enable\n", ISG5320A_TAG, __func__);
|
||
|
|
||
|
data->diff_avg = 0;
|
||
|
data->diff_cnt = 0;
|
||
|
data->cdc_avg = 0;
|
||
|
|
||
|
ret = isg5320a_i2c_read(data, ISG5320A_IRQSTS_REG, &state, 1);
|
||
|
if (ret < 0)
|
||
|
return;
|
||
|
|
||
|
isg5320a_get_raw_data(data, true);
|
||
|
|
||
|
if (data->skip_data == true) {
|
||
|
input_report_rel(data->input_dev, REL_MISC, 2);
|
||
|
} else if (state & (1 << ISG5320A_PROX_STATE)) {
|
||
|
data->state = CLOSE;
|
||
|
input_report_rel(data->input_dev, REL_MISC, 1);
|
||
|
} else {
|
||
|
data->state = FAR;
|
||
|
input_report_rel(data->input_dev, REL_MISC, 2);
|
||
|
}
|
||
|
input_sync(data->input_dev);
|
||
|
|
||
|
isg5320a_i2c_read(data, ISG5320A_IRQSRC_REG, &state, 1);
|
||
|
isg5320a_i2c_write_one(data, ISG5320A_IRQFUNC_REG, ISG5320A_IRQ_ENABLE);
|
||
|
|
||
|
enable_irq(data->client->irq);
|
||
|
enable_irq_wake(data->client->irq);
|
||
|
} else {
|
||
|
pr_info("%s %s - disable\n", ISG5320A_TAG, __func__);
|
||
|
|
||
|
isg5320a_i2c_write_one(data, ISG5320A_IRQFUNC_REG, ISG5320A_IRQ_DISABLE);
|
||
|
|
||
|
disable_irq(data->client->irq);
|
||
|
disable_irq_wake(data->client->irq);
|
||
|
}
|
||
|
|
||
|
data->enable = enable;
|
||
|
}
|
||
|
|
||
|
static int isg5320a_set_mode(struct isg5320a_data *data, unsigned char mode)
|
||
|
{
|
||
|
int ret = -EINVAL;
|
||
|
u8 state;
|
||
|
|
||
|
isg5320a_i2c_read(data, ISG5320A_IRQSRC_REG, &state, 1);
|
||
|
|
||
|
if (mode == ISG5320A_MODE_SLEEP) {
|
||
|
isg5320a_i2c_write_one(data, ISG5320A_SCANCTRL1_REG,
|
||
|
ISG5320A_SCAN_STOP);
|
||
|
isg5320a_i2c_write_one(data, ISG5320A_OSCCON_REG, ISG5320A_OSC_SLEEP);
|
||
|
isg5320a_i2c_write_one(data, ISG5320A_BS_ON_WD_REG, ISG5320A_BS_WD_OFF);
|
||
|
} else if (mode == ISG5320A_MODE_NORMAL) {
|
||
|
isg5320a_i2c_write_one(data, ISG5320A_BS_ON_WD_REG, ISG5320A_BS_WD_ON);
|
||
|
isg5320a_force_calibration(data, false);
|
||
|
}
|
||
|
|
||
|
pr_info("%s %s - change the mode : %u\n", ISG5320A_TAG, __func__, mode);
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
static ssize_t isg5320a_name_show(struct device *dev,
|
||
|
struct device_attribute *attr, char *buf)
|
||
|
{
|
||
|
pr_info("%s %s%s\n", ISG5320A_TAG, __func__, DEVICE_NAME);
|
||
|
|
||
|
return sprintf(buf, "%s\n", DEVICE_NAME);
|
||
|
}
|
||
|
|
||
|
static ssize_t isg5320a_vendor_show(struct device *dev,
|
||
|
struct device_attribute *attr, char *buf)
|
||
|
{
|
||
|
pr_info("%s %s%s\n", ISG5320A_TAG, __func__, VENDOR_NAME);
|
||
|
|
||
|
return sprintf(buf, "%s\n", VENDOR_NAME);
|
||
|
}
|
||
|
|
||
|
static ssize_t isg5320a_mode_show(struct device *dev,
|
||
|
struct device_attribute *attr, char *buf)
|
||
|
{
|
||
|
return sprintf(buf, "1\n");
|
||
|
}
|
||
|
|
||
|
static ssize_t isg5320a_acal_show(struct device *dev,
|
||
|
struct device_attribute *attr, char *buf)
|
||
|
{
|
||
|
return sprintf(buf, "2,0,0\n");
|
||
|
}
|
||
|
|
||
|
static ssize_t isg5320a_manual_acal_show(struct device *dev,
|
||
|
struct device_attribute *attr, char *buf)
|
||
|
{
|
||
|
|
||
|
|
||
|
return sprintf(buf, "OK\n");
|
||
|
}
|
||
|
|
||
|
static ssize_t isg5320a_onoff_store(struct device *dev,
|
||
|
struct device_attribute *attr, const char *buf, size_t count)
|
||
|
{
|
||
|
u8 val;
|
||
|
int ret;
|
||
|
struct isg5320a_data *data = dev_get_drvdata(dev);
|
||
|
|
||
|
ret = kstrtou8(buf, 2, &val);
|
||
|
if (ret) {
|
||
|
pr_err("%s invalid argument\n", ISG5320A_TAG);
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
if (val == 0) {
|
||
|
data->skip_data = true;
|
||
|
if (data->enable == ON) {
|
||
|
data->state = FAR;
|
||
|
input_report_rel(data->input_dev, REL_MISC, 2);
|
||
|
input_sync(data->input_dev);
|
||
|
}
|
||
|
} else {
|
||
|
data->skip_data = false;
|
||
|
}
|
||
|
|
||
|
pr_info("%s %d\n", ISG5320A_TAG, (int)val);
|
||
|
|
||
|
return count;
|
||
|
}
|
||
|
|
||
|
static ssize_t isg5320a_onoff_show(struct device *dev,
|
||
|
struct device_attribute *attr, char *buf)
|
||
|
{
|
||
|
struct isg5320a_data *data = dev_get_drvdata(dev);
|
||
|
|
||
|
return sprintf(buf, "%u\n", !data->skip_data);
|
||
|
}
|
||
|
|
||
|
static ssize_t isg5320a_sw_reset_show(struct device *dev,
|
||
|
struct device_attribute *attr, char *buf)
|
||
|
{
|
||
|
struct isg5320a_data *data = dev_get_drvdata(dev);
|
||
|
|
||
|
pr_info("%s %s\n", ISG5320A_TAG, __func__);
|
||
|
isg5320a_force_calibration(data, true);
|
||
|
msleep(300);
|
||
|
isg5320a_get_raw_data(data, true);
|
||
|
|
||
|
return sprintf(buf, "%d\n", 0);
|
||
|
}
|
||
|
|
||
|
static ssize_t isg5320a_normal_threshold_store(struct device *dev,
|
||
|
struct device_attribute *attr, const char *buf, size_t size)
|
||
|
{
|
||
|
int val = 0;
|
||
|
u8 buf8[2];
|
||
|
struct isg5320a_data *data = dev_get_drvdata(dev);
|
||
|
|
||
|
sscanf(buf, "%d", &val);
|
||
|
|
||
|
if (val < 0) {
|
||
|
pr_err("%s invalid argument\n", ISG5320A_TAG);
|
||
|
return size;
|
||
|
}
|
||
|
|
||
|
pr_info("%s change threshold(%d->%d)\n", ISG5320A_TAG, data->normal_th,
|
||
|
val);
|
||
|
|
||
|
data->normal_th = val;
|
||
|
|
||
|
buf8[0] = (data->normal_th >> 8) & 0xFF;
|
||
|
buf8[1] = data->normal_th & 0xFF;
|
||
|
|
||
|
isg5320a_i2c_write(data, ISG5320A_B_PROXCTL3_REG, buf8, 2);
|
||
|
|
||
|
return size;
|
||
|
}
|
||
|
|
||
|
static ssize_t isg5320a_normal_threshold_show(struct device *dev,
|
||
|
struct device_attribute *attr, char *buf)
|
||
|
{
|
||
|
u32 threshold = 0;
|
||
|
u32 close_hyst = 0;
|
||
|
u32 far_hyst = 0;
|
||
|
u8 buf8[6];
|
||
|
struct isg5320a_data *data = dev_get_drvdata(dev);
|
||
|
|
||
|
isg5320a_i2c_read(data, ISG5320A_B_PROXCTL3_REG, buf8, sizeof(buf8));
|
||
|
|
||
|
threshold = ((u32)buf8[0] << 8) | (u32)buf8[1];
|
||
|
close_hyst = ((u32)buf8[2] << 8) | (u32)buf8[3];
|
||
|
far_hyst = ((u32)buf8[4] << 8) | (u32)buf8[5];
|
||
|
|
||
|
return sprintf(buf, "%d,%d\n", threshold + close_hyst,
|
||
|
threshold - far_hyst);
|
||
|
}
|
||
|
|
||
|
|
||
|
static ssize_t isg5320a_raw_data_show(struct device *dev,
|
||
|
struct device_attribute *attr, char *buf)
|
||
|
{
|
||
|
struct isg5320a_data *data = dev_get_drvdata(dev);
|
||
|
|
||
|
isg5320a_get_raw_data(data, true);
|
||
|
if (data->diff_cnt == 0) {
|
||
|
data->diff_sum = data->diff;
|
||
|
data->cdc_sum = data->cdc;
|
||
|
} else {
|
||
|
data->diff_sum += data->diff;
|
||
|
data->cdc_sum += data->cdc;
|
||
|
}
|
||
|
|
||
|
if (++data->diff_cnt >= ISG5320A_DIFF_AVR_CNT) {
|
||
|
data->diff_avg = data->diff_sum / ISG5320A_DIFF_AVR_CNT;
|
||
|
data->cdc_avg = data->cdc_sum / ISG5320A_DIFF_AVR_CNT;
|
||
|
data->diff_cnt = 0;
|
||
|
}
|
||
|
|
||
|
return sprintf(buf, "%d%02d,%d,%d,%d,%d\n", (data->fine_coarse & 0xFF),
|
||
|
((data->fine_coarse >> 8) & 0x3F), data->cdc,
|
||
|
data->fine_coarse, data->diff, data->base);
|
||
|
}
|
||
|
|
||
|
static ssize_t isg5320a_debug_raw_data_show(struct device *dev,
|
||
|
struct device_attribute *attr, char *buf)
|
||
|
{
|
||
|
int ret = 0;
|
||
|
u8 buff[10];
|
||
|
u16 temp;
|
||
|
struct isg5320a_data *data = dev_get_drvdata(dev);
|
||
|
|
||
|
mutex_lock(&data->lock);
|
||
|
ret = isg5320a_i2c_read(data, ISG5320A_CDC16_A_H_REG, buff, sizeof(buff));
|
||
|
mutex_unlock(&data->lock);
|
||
|
if (ret < 0) {
|
||
|
pr_info("%s fail to get data\n", ISG5320A_TAG);
|
||
|
} else {
|
||
|
temp = ((u32)buff[0] << 8) | (u32)buff[1];
|
||
|
if ((temp != 0) && (temp != 0xFFFF))
|
||
|
data->debug_cdc[0] = temp;
|
||
|
|
||
|
temp = ((u32)buff[2] << 8) | (u32)buff[3];
|
||
|
if ((temp != 0) && (temp != 0xFFFF))
|
||
|
data->debug_base[0] = temp;
|
||
|
data->debug_diff[0] =
|
||
|
(s32)data->debug_cdc[0] - (s32)data->debug_base[0];
|
||
|
|
||
|
temp = ((u32)buff[6] << 8) | (u32)buff[7];
|
||
|
if ((temp != 0) && (temp != 0xFFFF))
|
||
|
data->debug_cdc[1] = temp;
|
||
|
|
||
|
temp = ((u32)buff[8] << 8) | (u32)buff[9];
|
||
|
if ((temp != 0) && (temp != 0xFFFF))
|
||
|
data->debug_base[1] = temp;
|
||
|
data->debug_diff[1] =
|
||
|
(s32)data->debug_cdc[1] - (s32)data->debug_base[1];
|
||
|
|
||
|
temp = ((u32)buff[4] << 8) | (u32)buff[5];
|
||
|
if ((temp != 0) && (temp != 0xFFFF))
|
||
|
data->debug_cdc[2] = temp;
|
||
|
data->debug_diff[2] =
|
||
|
(s32)data->debug_cdc[2] - (s32)data->debug_base[1];
|
||
|
}
|
||
|
|
||
|
return sprintf(buf, "%d,%d,%d,%d,%d,%d,%d,%d,%d\n", data->debug_cdc[0],
|
||
|
data->debug_diff[0], data->debug_base[0], data->debug_cdc[1],
|
||
|
data->debug_diff[1], data->debug_base[1], data->debug_cdc[2],
|
||
|
data->debug_diff[2], data->debug_base[1]);
|
||
|
}
|
||
|
|
||
|
static ssize_t isg5320a_debug_data_show(struct device *dev,
|
||
|
struct device_attribute *attr, char *buf)
|
||
|
{
|
||
|
struct isg5320a_data *data = dev_get_drvdata(dev);
|
||
|
|
||
|
return sprintf(buf, "%d,%d,%d\n", data->cdc, data->base, data->diff);
|
||
|
}
|
||
|
|
||
|
static ssize_t isg5320a_diff_avg_show(struct device *dev,
|
||
|
struct device_attribute *attr, char *buf)
|
||
|
{
|
||
|
struct isg5320a_data *data = dev_get_drvdata(dev);
|
||
|
|
||
|
return sprintf(buf, "%d\n", data->diff_avg);
|
||
|
}
|
||
|
|
||
|
static ssize_t isg5320a_cdc_avg_show(struct device *dev,
|
||
|
struct device_attribute *attr, char *buf)
|
||
|
{
|
||
|
struct isg5320a_data *data = dev_get_drvdata(dev);
|
||
|
|
||
|
return snprintf(buf, PAGE_SIZE, "%d\n", data->cdc_avg);
|
||
|
}
|
||
|
|
||
|
static ssize_t isg5320a_ch_state_show(struct device *dev,
|
||
|
struct device_attribute *attr, char *buf)
|
||
|
{
|
||
|
int count;
|
||
|
struct isg5320a_data *data = dev_get_drvdata(dev);
|
||
|
|
||
|
if (data->skip_data == true)
|
||
|
count = snprintf(buf, PAGE_SIZE, "%d,%d\n", NONE_ENABLE, NONE_ENABLE);
|
||
|
else if (data->enable == ON)
|
||
|
count = snprintf(buf, PAGE_SIZE, "%d,%d\n", data->state, NONE_ENABLE);
|
||
|
else
|
||
|
count = snprintf(buf, PAGE_SIZE, "%d,%d\n", NONE_ENABLE, NONE_ENABLE);
|
||
|
|
||
|
return count;
|
||
|
}
|
||
|
|
||
|
static ssize_t isg5320a_hysteresis_show(struct device *dev,
|
||
|
struct device_attribute *attr, char *buf)
|
||
|
{
|
||
|
u32 far_hyst = 0;
|
||
|
u8 buf8[6];
|
||
|
struct isg5320a_data *data = dev_get_drvdata(dev);
|
||
|
|
||
|
isg5320a_i2c_read(data, ISG5320A_B_PROXCTL3_REG, buf8, sizeof(buf8));
|
||
|
|
||
|
far_hyst = ((u32)buf8[4] << 8) | (u32)buf8[5];
|
||
|
|
||
|
return sprintf(buf, "%d\n", far_hyst);
|
||
|
}
|
||
|
|
||
|
static ssize_t isg5320a_reg_update_show(struct device *dev,
|
||
|
struct device_attribute *attr, char *buf)
|
||
|
{
|
||
|
int enable_backup;
|
||
|
struct isg5320a_data *data = dev_get_drvdata(dev);
|
||
|
|
||
|
enable_backup = data->enable;
|
||
|
|
||
|
isg5320a_reset(data);
|
||
|
if (enable_backup)
|
||
|
isg5320a_set_enable(data, OFF);
|
||
|
isg5320a_set_mode(data, ISG5320A_MODE_SLEEP);
|
||
|
isg5320a_initialize(data);
|
||
|
if (enable_backup)
|
||
|
isg5320a_set_enable(data, ON);
|
||
|
isg5320a_set_mode(data, ISG5320A_MODE_NORMAL);
|
||
|
|
||
|
return sprintf(buf, "OK\n");
|
||
|
}
|
||
|
|
||
|
#define DIRECT_CMD_WRITE 'w'
|
||
|
#define DIRECT_CMD_READ 'r'
|
||
|
#define DIRECT_BUF_COUNT 16
|
||
|
static ssize_t isg5320a_direct_store(struct device *dev,
|
||
|
struct device_attribute *attr, const char *buf, size_t size)
|
||
|
{
|
||
|
int ret = -EPERM;
|
||
|
u32 tmp1, tmp2;
|
||
|
struct isg5320a_data *data = dev_get_drvdata(dev);
|
||
|
direct_info *direct = (direct_info *)&data->direct;
|
||
|
|
||
|
sscanf(buf, "%c %x %x", &direct->cmd, &tmp1, &tmp2);
|
||
|
|
||
|
direct->addr = tmp1;
|
||
|
direct->val = tmp2;
|
||
|
|
||
|
pr_info("%s direct cmd: %c, addr: %x, val: %x\n", ISG5320A_TAG, direct->cmd,
|
||
|
direct->addr, direct->val);
|
||
|
|
||
|
if ((direct->cmd != DIRECT_CMD_WRITE) && (direct->cmd != DIRECT_CMD_READ)) {
|
||
|
pr_err("%s direct cmd is not correct!\n", ISG5320A_TAG);
|
||
|
return size;
|
||
|
}
|
||
|
|
||
|
if (direct->cmd == DIRECT_CMD_WRITE) {
|
||
|
ret = isg5320a_i2c_write_one(data, direct->addr, direct->val);
|
||
|
if (ret < 0)
|
||
|
pr_err("%s direct write fail\n", ISG5320A_TAG);
|
||
|
else
|
||
|
pr_info("%s direct write addr: %x, val: %x\n", ISG5320A_TAG,
|
||
|
direct->addr, direct->val);
|
||
|
}
|
||
|
|
||
|
return size;
|
||
|
}
|
||
|
|
||
|
static ssize_t isg5320a_direct_show(struct device *dev,
|
||
|
struct device_attribute *attr, char *buf)
|
||
|
{
|
||
|
int i, count = 0;
|
||
|
int ret = 0;
|
||
|
int len;
|
||
|
u8 addr;
|
||
|
const int msg_len = 256;
|
||
|
char msg[msg_len];
|
||
|
struct isg5320a_data *data = dev_get_drvdata(dev);
|
||
|
direct_info *direct = (direct_info *)&data->direct;
|
||
|
u8 buf8[DIRECT_BUF_COUNT] = {0,};
|
||
|
int max_len = DIRECT_BUF_COUNT;
|
||
|
|
||
|
if (direct->cmd != DIRECT_CMD_READ)
|
||
|
return sprintf(buf, "ex) echo r addr len size(display) > direct\n");
|
||
|
|
||
|
len = direct->val;
|
||
|
addr = direct->addr;
|
||
|
|
||
|
while (len > 0) {
|
||
|
if (len < max_len)
|
||
|
max_len = len;
|
||
|
|
||
|
ret = isg5320a_i2c_read(data, addr, buf8, max_len);
|
||
|
if (ret < 0) {
|
||
|
count = sprintf(buf, "i2c read fail\n");
|
||
|
break;
|
||
|
}
|
||
|
addr += max_len;
|
||
|
|
||
|
for (i = 0; i < max_len; i++) {
|
||
|
count += snprintf(msg, msg_len, "0x%02X ", buf8[i]);
|
||
|
strncat(buf, msg, msg_len);
|
||
|
}
|
||
|
count += snprintf(msg, msg_len, "\n");
|
||
|
strncat(buf, msg, msg_len);
|
||
|
|
||
|
len -= max_len;
|
||
|
}
|
||
|
|
||
|
return count;
|
||
|
}
|
||
|
|
||
|
static ssize_t isg5320a_intr_debug_store(struct device *dev,
|
||
|
struct device_attribute *attr, const char *buf, size_t size)
|
||
|
{
|
||
|
u32 tmp1;
|
||
|
struct isg5320a_data *data = dev_get_drvdata(dev);
|
||
|
|
||
|
sscanf(buf, "%x %d", &tmp1, &data->intr_debug_size);
|
||
|
|
||
|
data->intr_debug_addr = tmp1;
|
||
|
|
||
|
pr_info("%s intr debug addr: 0x%x, count: %d\n", ISG5320A_TAG,
|
||
|
data->intr_debug_addr, data->intr_debug_size);
|
||
|
|
||
|
return size;
|
||
|
}
|
||
|
|
||
|
static ssize_t isg5320a_intr_debug_show(struct device *dev,
|
||
|
struct device_attribute *attr, char *buf)
|
||
|
{
|
||
|
struct isg5320a_data *data = dev_get_drvdata(dev);
|
||
|
|
||
|
pr_info("%s intr debug addr: 0x%x, count: %d\n", ISG5320A_TAG,
|
||
|
data->intr_debug_addr, data->intr_debug_size);
|
||
|
|
||
|
return sprintf(buf, "intr debug addr: 0x%x, count: %d\n",
|
||
|
data->intr_debug_addr, data->intr_debug_size);
|
||
|
}
|
||
|
|
||
|
static ssize_t isg5320a_cp_show(struct device *dev,
|
||
|
struct device_attribute *attr, char *buf)
|
||
|
{
|
||
|
int ret;
|
||
|
u16 buff;
|
||
|
struct isg5320a_data *data = dev_get_drvdata(dev);
|
||
|
|
||
|
ret = isg5320a_i2c_read(data, ISG5320A_COARSE_B_REG, (u8 *)&buff, 2);
|
||
|
if (ret < 0) {
|
||
|
pr_info("%s fail to get cp\n", ISG5320A_TAG);
|
||
|
} else {
|
||
|
data->fine_coarse = buff;
|
||
|
pr_info("%s coarse B:%04X\n", ISG5320A_TAG, data->fine_coarse);
|
||
|
}
|
||
|
|
||
|
return sprintf(buf, "%d%02d,0\n", (data->fine_coarse & 0xFF),
|
||
|
(data->fine_coarse >> 8) & 0x3F);
|
||
|
}
|
||
|
|
||
|
#define SCAN_INT 0x12
|
||
|
#define FAR_CLOSE_INT 0x0C
|
||
|
static ssize_t isg5320a_scan_int_show(struct device *dev,
|
||
|
struct device_attribute *attr, char *buf)
|
||
|
{
|
||
|
int ret;
|
||
|
u8 irq_con = SCAN_INT;
|
||
|
struct isg5320a_data *data = dev_get_drvdata(dev);
|
||
|
|
||
|
ret = isg5320a_i2c_write(data, ISG5320A_IRQCON_REG, &irq_con, 1);
|
||
|
if (ret < 0) {
|
||
|
pr_err("%s fail to set scan done int\n", ISG5320A_TAG);
|
||
|
return sprintf(buf, "FAIL\n");
|
||
|
} else {
|
||
|
pr_info("%s set scan done int\n", ISG5320A_TAG);
|
||
|
return sprintf(buf, "OK\n");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static ssize_t isg5320a_far_close_int_show(struct device *dev,
|
||
|
struct device_attribute *attr, char *buf)
|
||
|
{
|
||
|
int ret;
|
||
|
u8 irq_con = FAR_CLOSE_INT;
|
||
|
struct isg5320a_data *data = dev_get_drvdata(dev);
|
||
|
|
||
|
ret = isg5320a_i2c_write(data, ISG5320A_IRQCON_REG, &irq_con, 1);
|
||
|
if (ret < 0) {
|
||
|
pr_err("%s fail to set normal int\n", ISG5320A_TAG);
|
||
|
return sprintf(buf, "FAIL\n");
|
||
|
} else {
|
||
|
pr_info("%s set normal int\n", ISG5320A_TAG);
|
||
|
return sprintf(buf, "OK\n");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static ssize_t isg5320a_toggle_enable_show(struct device *dev,
|
||
|
struct device_attribute *attr, char *buf)
|
||
|
{
|
||
|
int enable;
|
||
|
struct isg5320a_data *data = dev_get_drvdata(dev);
|
||
|
|
||
|
enable = (data->enable == OFF) ? ON : OFF;
|
||
|
isg5320a_set_enable(data, (int)enable);
|
||
|
|
||
|
return sprintf(buf, "%d\n", data->enable);
|
||
|
}
|
||
|
|
||
|
static ssize_t isg5320a_enable_store(struct device *dev,
|
||
|
struct device_attribute *attr, const char *buf, size_t size)
|
||
|
{
|
||
|
int ret;
|
||
|
u8 enable;
|
||
|
struct isg5320a_data *data = dev_get_drvdata(dev);
|
||
|
int pre_enable = data->enable;
|
||
|
|
||
|
ret = kstrtou8(buf, 2, &enable);
|
||
|
if (ret) {
|
||
|
pr_err("%s invalid argument\n", ISG5320A_TAG);
|
||
|
return size;
|
||
|
}
|
||
|
|
||
|
pr_info("%s new_value=%d old_value=%d\n", ISG5320A_TAG, (int)enable,
|
||
|
pre_enable);
|
||
|
|
||
|
if (pre_enable == enable)
|
||
|
return size;
|
||
|
|
||
|
isg5320a_set_enable(data, (int)enable);
|
||
|
|
||
|
return size;
|
||
|
}
|
||
|
|
||
|
static ssize_t isg5320a_enable_show(struct device *dev,
|
||
|
struct device_attribute *attr, char *buf)
|
||
|
{
|
||
|
struct isg5320a_data *data = dev_get_drvdata(dev);
|
||
|
|
||
|
return sprintf(buf, "%d\n", data->enable);
|
||
|
}
|
||
|
|
||
|
static ssize_t isg5320a_sampling_freq_show(struct device *dev,
|
||
|
struct device_attribute *attr, char *buf)
|
||
|
{
|
||
|
struct isg5320a_data *data = dev_get_drvdata(dev);
|
||
|
u8 buff;
|
||
|
int sampling_freq;
|
||
|
|
||
|
isg5320a_i2c_read(data, ISG5320A_NUM_OF_CLK, &buff, 1);
|
||
|
sampling_freq = (int)(4000 / ((int)buff + 1));
|
||
|
|
||
|
return snprintf(buf, PAGE_SIZE, "%dkHz\n", sampling_freq);
|
||
|
}
|
||
|
|
||
|
static ssize_t isg5320a_isum_show(struct device *dev,
|
||
|
struct device_attribute *attr, char *buf)
|
||
|
{
|
||
|
struct isg5320a_data *data = dev_get_drvdata(dev);
|
||
|
const char *table[16] = {
|
||
|
"0", "0", "0", "0", "0", "0", "0", "0", "0",
|
||
|
"20", "24", "28", "32", "40", "48", "64"};
|
||
|
u8 buff = 0;
|
||
|
|
||
|
isg5320a_i2c_read(data, ISG5320A_CHB_LSUM_TYPE_REG, &buff, 1);
|
||
|
buff = (buff & 0xf0) >> 4;
|
||
|
|
||
|
return snprintf(buf, PAGE_SIZE, "%s\n", table[buff]);
|
||
|
}
|
||
|
|
||
|
static ssize_t isg5320a_scan_period_show(struct device *dev,
|
||
|
struct device_attribute *attr, char *buf)
|
||
|
{
|
||
|
struct isg5320a_data *data = dev_get_drvdata(dev);
|
||
|
u8 buff[2];
|
||
|
int scan_period;
|
||
|
|
||
|
isg5320a_i2c_read(data, ISG5320A_WUTDATA_REG, (u8 *)&buff, sizeof(buff));
|
||
|
|
||
|
scan_period = (int)(((u16)buff[1] & 0xff) | (((u16)buff[0] & 0x3f) << 8));
|
||
|
if (!scan_period)
|
||
|
return snprintf(buf, PAGE_SIZE, "%d\n", scan_period);
|
||
|
|
||
|
scan_period = (int)(4000 / scan_period);
|
||
|
|
||
|
return snprintf(buf, PAGE_SIZE, "%d\n", scan_period);
|
||
|
}
|
||
|
|
||
|
static ssize_t isg5320a_again_show(struct device *dev,
|
||
|
struct device_attribute *attr, char *buf)
|
||
|
{
|
||
|
struct isg5320a_data *data = dev_get_drvdata(dev);
|
||
|
u8 buff;
|
||
|
u8 temp1, temp2;
|
||
|
|
||
|
isg5320a_i2c_read(data, ISG5320A_ANALOG_GAIN, &buff, 1);
|
||
|
temp1 = (buff & 0x38) >> 3;
|
||
|
temp2 = (buff & 0x07);
|
||
|
|
||
|
return snprintf(buf, PAGE_SIZE, "%d/%d\n", (int)temp2 + 1, (int)temp1 + 1);
|
||
|
}
|
||
|
|
||
|
static ssize_t isg5320a_cdc_up_coef_show(struct device *dev,
|
||
|
struct device_attribute *attr, char *buf)
|
||
|
{
|
||
|
struct isg5320a_data *data = dev_get_drvdata(dev);
|
||
|
u8 buff;
|
||
|
int coef;
|
||
|
|
||
|
isg5320a_i2c_read(data, ISG5320A_CHB_CDC_UP_COEF_REG, &buff, 1);
|
||
|
coef = (int)buff;
|
||
|
|
||
|
return snprintf(buf, PAGE_SIZE, "%x, %d\n", buff, coef);
|
||
|
}
|
||
|
|
||
|
static ssize_t isg5320a_cdc_down_coef_show(struct device *dev,
|
||
|
struct device_attribute *attr, char *buf)
|
||
|
{
|
||
|
struct isg5320a_data *data = dev_get_drvdata(dev);
|
||
|
u8 buff;
|
||
|
int coef;
|
||
|
|
||
|
isg5320a_i2c_read(data, ISG5320A_CHB_CDC_DN_COEF_REG, &buff, 1);
|
||
|
coef = (int)buff;
|
||
|
|
||
|
return snprintf(buf, PAGE_SIZE, "%x, %d\n", buff, coef);
|
||
|
}
|
||
|
|
||
|
static ssize_t isg5320a_temp_enable_show(struct device *dev,
|
||
|
struct device_attribute *attr, char *buf)
|
||
|
{
|
||
|
struct isg5320a_data *data = dev_get_drvdata(dev);
|
||
|
u8 buff;
|
||
|
|
||
|
isg5320a_i2c_read(data, ISG5320A_TEMPERATURE_ENABLE_REG, &buff, 1);
|
||
|
|
||
|
return snprintf(buf, PAGE_SIZE, "%d\n", ((buff & 0x40) >> 6));
|
||
|
}
|
||
|
|
||
|
|
||
|
static ssize_t isg5320a_cml_show(struct device *dev,
|
||
|
struct device_attribute *attr, char *buf)
|
||
|
{
|
||
|
struct isg5320a_data *data = dev_get_drvdata(dev);
|
||
|
u8 buff;
|
||
|
|
||
|
isg5320a_i2c_read(data, ISG5320A_CML_BIAS_REG, &buff, 1);
|
||
|
|
||
|
return snprintf(buf, PAGE_SIZE, "%d\n", (buff & 0x07));
|
||
|
}
|
||
|
|
||
|
static ssize_t isg5320a_irq_count_show(struct device *dev,
|
||
|
struct device_attribute *attr, char *buf)
|
||
|
{
|
||
|
struct isg5320a_data *data = dev_get_drvdata(dev);
|
||
|
|
||
|
int ret = 0;
|
||
|
s16 max_diff_val = 0;
|
||
|
|
||
|
if (data->irq_count) {
|
||
|
ret = -1;
|
||
|
max_diff_val = data->max_diff;
|
||
|
} else {
|
||
|
max_diff_val = data->max_normal_diff;
|
||
|
}
|
||
|
|
||
|
pr_info("%s %s - called\n", ISG5320A_TAG, __func__);
|
||
|
|
||
|
return snprintf(buf, PAGE_SIZE, "%d,%d,%d\n", ret, data->irq_count,
|
||
|
max_diff_val);
|
||
|
}
|
||
|
|
||
|
static ssize_t isg5320a_irq_count_store(struct device *dev,
|
||
|
struct device_attribute *attr, const char *buf, size_t count)
|
||
|
{
|
||
|
struct isg5320a_data *data = dev_get_drvdata(dev);
|
||
|
|
||
|
u8 onoff;
|
||
|
int ret;
|
||
|
|
||
|
ret = kstrtou8(buf, 10, &onoff);
|
||
|
if (ret < 0) {
|
||
|
pr_err("%s invalid argument\n", ISG5320A_TAG);
|
||
|
return count;
|
||
|
}
|
||
|
|
||
|
mutex_lock(&data->lock);
|
||
|
|
||
|
if (onoff == 0) {
|
||
|
data->abnormal_mode = OFF;
|
||
|
} else if (onoff == 1) {
|
||
|
data->abnormal_mode = ON;
|
||
|
data->irq_count = 0;
|
||
|
data->max_diff = 0;
|
||
|
data->max_normal_diff = 0;
|
||
|
} else {
|
||
|
pr_err("%s invalid value %d\n", ISG5320A_TAG, onoff);
|
||
|
}
|
||
|
|
||
|
mutex_unlock(&data->lock);
|
||
|
|
||
|
pr_info("%s %s - %d\n", ISG5320A_TAG, __func__, onoff);
|
||
|
|
||
|
return count;
|
||
|
}
|
||
|
|
||
|
static DEVICE_ATTR(name, 0444, isg5320a_name_show, NULL);
|
||
|
static DEVICE_ATTR(vendor, 0444, isg5320a_vendor_show, NULL);
|
||
|
static DEVICE_ATTR(mode, 0444, isg5320a_mode_show, NULL);
|
||
|
static DEVICE_ATTR(manual_acal, 0444, isg5320a_manual_acal_show, NULL);
|
||
|
static DEVICE_ATTR(calibration, 0444, isg5320a_acal_show, NULL);
|
||
|
static DEVICE_ATTR(onoff, 0664, isg5320a_onoff_show, isg5320a_onoff_store);
|
||
|
static DEVICE_ATTR(reset, 0444, isg5320a_sw_reset_show, NULL);
|
||
|
static DEVICE_ATTR(normal_threshold, 0664, isg5320a_normal_threshold_show,
|
||
|
isg5320a_normal_threshold_store);
|
||
|
static DEVICE_ATTR(raw_data, 0444, isg5320a_raw_data_show, NULL);
|
||
|
static DEVICE_ATTR(debug_raw_data, 0444, isg5320a_debug_raw_data_show, NULL);
|
||
|
static DEVICE_ATTR(debug_data, 0444, isg5320a_debug_data_show, NULL);
|
||
|
static DEVICE_ATTR(diff_avg, 0444, isg5320a_diff_avg_show, NULL);
|
||
|
static DEVICE_ATTR(cdc_avg, 0444, isg5320a_cdc_avg_show, NULL);
|
||
|
static DEVICE_ATTR(useful_avg, 0444, isg5320a_cdc_avg_show, NULL);
|
||
|
static DEVICE_ATTR(ch_state, 0444, isg5320a_ch_state_show, NULL);
|
||
|
static DEVICE_ATTR(hysteresis, 0444, isg5320a_hysteresis_show, NULL);
|
||
|
static DEVICE_ATTR(reg_update, 0444, isg5320a_reg_update_show, NULL);
|
||
|
static DEVICE_ATTR(direct, 0664, isg5320a_direct_show, isg5320a_direct_store);
|
||
|
static DEVICE_ATTR(intr_debug, 0664, isg5320a_intr_debug_show, isg5320a_intr_debug_store);
|
||
|
static DEVICE_ATTR(cp, 0444, isg5320a_cp_show, NULL);
|
||
|
static DEVICE_ATTR(scan_int, 0444, isg5320a_scan_int_show, NULL);
|
||
|
static DEVICE_ATTR(far_close_int, 0444, isg5320a_far_close_int_show, NULL);
|
||
|
static DEVICE_ATTR(toggle_enable, 0444, isg5320a_toggle_enable_show, NULL);
|
||
|
static DEVICE_ATTR(sampling_freq, 0444, isg5320a_sampling_freq_show, NULL);
|
||
|
static DEVICE_ATTR(isum, 0444, isg5320a_isum_show, NULL);
|
||
|
static DEVICE_ATTR(scan_period, 0444, isg5320a_scan_period_show, NULL);
|
||
|
static DEVICE_ATTR(analog_gain, 0444, isg5320a_again_show, NULL);
|
||
|
static DEVICE_ATTR(cdc_up, 0444, isg5320a_cdc_down_coef_show, NULL);
|
||
|
static DEVICE_ATTR(cdc_down, 0444, isg5320a_cdc_up_coef_show, NULL);
|
||
|
static DEVICE_ATTR(temp_enable, 0444, isg5320a_temp_enable_show, NULL);
|
||
|
static DEVICE_ATTR(cml, 0444, isg5320a_cml_show, NULL);
|
||
|
static DEVICE_ATTR(irq_count, 0664, isg5320a_irq_count_show,
|
||
|
isg5320a_irq_count_store);
|
||
|
|
||
|
static struct device_attribute *sensor_attrs[] = {
|
||
|
&dev_attr_name,
|
||
|
&dev_attr_vendor,
|
||
|
&dev_attr_mode,
|
||
|
&dev_attr_manual_acal,
|
||
|
&dev_attr_calibration,
|
||
|
&dev_attr_onoff,
|
||
|
&dev_attr_reset,
|
||
|
&dev_attr_normal_threshold,
|
||
|
&dev_attr_raw_data,
|
||
|
&dev_attr_debug_raw_data,
|
||
|
&dev_attr_debug_data,
|
||
|
&dev_attr_diff_avg,
|
||
|
&dev_attr_useful_avg,
|
||
|
&dev_attr_cdc_avg,
|
||
|
&dev_attr_ch_state,
|
||
|
&dev_attr_hysteresis,
|
||
|
&dev_attr_reg_update,
|
||
|
&dev_attr_direct,
|
||
|
&dev_attr_intr_debug,
|
||
|
&dev_attr_cp,
|
||
|
&dev_attr_scan_int,
|
||
|
&dev_attr_far_close_int,
|
||
|
&dev_attr_toggle_enable,
|
||
|
&dev_attr_sampling_freq,
|
||
|
&dev_attr_isum,
|
||
|
&dev_attr_scan_period,
|
||
|
&dev_attr_analog_gain,
|
||
|
&dev_attr_cdc_up,
|
||
|
&dev_attr_cdc_down,
|
||
|
&dev_attr_temp_enable,
|
||
|
&dev_attr_cml,
|
||
|
&dev_attr_irq_count,
|
||
|
NULL,
|
||
|
};
|
||
|
|
||
|
static DEVICE_ATTR(enable, 0664, isg5320a_enable_show, isg5320a_enable_store);
|
||
|
|
||
|
static struct attribute *isg5320a_attributes[] = {
|
||
|
&dev_attr_enable.attr,
|
||
|
NULL,
|
||
|
};
|
||
|
|
||
|
static struct attribute_group isg5320a_attribute_group = {
|
||
|
.attrs = isg5320a_attributes,
|
||
|
};
|
||
|
|
||
|
#ifdef ISG5320A_INIT_DELAYEDWORK
|
||
|
static void init_work_func(struct work_struct *work)
|
||
|
{
|
||
|
struct delayed_work *delayed_work = to_delayed_work(work);
|
||
|
struct isg5320a_data *data = container_of(delayed_work,
|
||
|
struct isg5320a_data, init_work);
|
||
|
|
||
|
isg5320a_initialize(data);
|
||
|
isg5320a_set_mode(data, ISG5320A_MODE_NORMAL);
|
||
|
isg5320a_set_debug_work(data, ON, 2000);
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
static void cal_work_func(struct work_struct *work)
|
||
|
{
|
||
|
|
||
|
struct delayed_work *delayed_work = to_delayed_work(work);
|
||
|
struct isg5320a_data *data = container_of(delayed_work,
|
||
|
struct isg5320a_data, cal_work);
|
||
|
bool force_cal = false;
|
||
|
|
||
|
isg5320a_get_raw_data(data, false);
|
||
|
// check cfcal
|
||
|
if (data->cdc < data->cfcal_th) {
|
||
|
pr_info("%s cdc : %d, cfcal_th %d\n", ISG5320A_TAG, data->cdc,
|
||
|
data->cfcal_th);
|
||
|
isg5320a_force_calibration(data, false);
|
||
|
force_cal = true;
|
||
|
}
|
||
|
|
||
|
// check bfcal
|
||
|
if (data->bfcal_chk_start) {
|
||
|
data->bfcal_chk_count++;
|
||
|
if (data->bfcal_chk_count == ISG5320A_BFCAL_CHK_RDY_TIME) {
|
||
|
data->bfcal_chk_ready = true;
|
||
|
data->bfcal_chk_cdc = data->cdc;
|
||
|
data->bfcal_chk_diff =
|
||
|
data->diff / ISG5320A_BFCAL_CHK_DIFF_RATIO;
|
||
|
} else if (data->bfcal_chk_count > ISG5320A_BFCAL_CHK_RDY_TIME) {
|
||
|
if (((data->bfcal_chk_count - ISG5320A_BFCAL_CHK_RDY_TIME) %
|
||
|
ISG5320A_BFCAL_CHK_CYCLE_TIME) == 0) {
|
||
|
if (((s32)data->bfcal_chk_cdc - (s32)data->cdc) >=
|
||
|
data->bfcal_chk_diff) {
|
||
|
isg5320a_force_calibration(data, true);
|
||
|
force_cal = true;
|
||
|
data->bfcal_chk_start = false;
|
||
|
data->bfcal_chk_ready = false;
|
||
|
data->bfcal_chk_count = 0;
|
||
|
} else {
|
||
|
data->bfcal_chk_cdc = data->cdc;
|
||
|
data->bfcal_chk_diff =
|
||
|
data->diff / ISG5320A_BFCAL_CHK_DIFF_RATIO;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (force_cal)
|
||
|
schedule_delayed_work(&data->cal_work, msecs_to_jiffies(1000));
|
||
|
else
|
||
|
schedule_delayed_work(&data->cal_work, msecs_to_jiffies(500));
|
||
|
}
|
||
|
|
||
|
static void force_cal_work_func(struct work_struct *work)
|
||
|
{
|
||
|
struct isg5320a_data *data = container_of(work,
|
||
|
struct isg5320a_data, force_cal_work);
|
||
|
isg5320a_force_calibration(data, true);
|
||
|
}
|
||
|
|
||
|
static void debug_work_func(struct work_struct *work)
|
||
|
{
|
||
|
int ret;
|
||
|
struct delayed_work *delayed_work = to_delayed_work(work);
|
||
|
struct isg5320a_data *data = container_of(delayed_work,
|
||
|
struct isg5320a_data, debug_work);
|
||
|
|
||
|
if (data->hallic_detect) {
|
||
|
ret = check_hallic_state(HALLIC_PATH, data->hall_ic);
|
||
|
if (ret < 0)
|
||
|
pr_err("%s hallic detect fail = %d\n", ISG5320A_TAG, ret);
|
||
|
|
||
|
if (strcmp(data->hall_ic, "CLOSE") == 0) {
|
||
|
if (data->hall_flag) {
|
||
|
pr_info("%s hall IC is closed\n", ISG5320A_TAG);
|
||
|
isg5320a_force_calibration(data, true);
|
||
|
data->hall_flag = 0;
|
||
|
}
|
||
|
} else {
|
||
|
data->hall_flag = 1;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (data->hallic_cert_detect) {
|
||
|
ret = check_hallic_state(HALLIC_CERT_PATH, data->hall_ic);
|
||
|
if (ret < 0)
|
||
|
pr_err("%s Cert hall IC detect fail = %d\n", ISG5320A_TAG, ret);
|
||
|
|
||
|
if (strcmp(data->hall_ic, "CLOSE") == 0) {
|
||
|
if (data->hall_cert_flag) {
|
||
|
pr_info("%s Cert hall IC is closed\n", ISG5320A_TAG);
|
||
|
isg5320a_force_calibration(data, true);
|
||
|
data->hall_cert_flag = 0;
|
||
|
}
|
||
|
} else {
|
||
|
data->hall_cert_flag = 1;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (data->enable == ON) {
|
||
|
if (data->abnormal_mode) {
|
||
|
isg5320a_get_raw_data(data, true);
|
||
|
if (data->max_normal_diff < data->diff)
|
||
|
data->max_normal_diff = data->diff;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
schedule_delayed_work(&data->debug_work, msecs_to_jiffies(2000));
|
||
|
}
|
||
|
|
||
|
#if (defined(CONFIG_CCIC_NOTIFIER) || defined(CONFIG_PDIC_NOTIFIER)) && defined(CONFIG_USB_TYPEC_MANAGER_NOTIFIER)
|
||
|
static int isg5320a_ccic_handle_notification(struct notifier_block *nb,
|
||
|
unsigned long action, void *data)
|
||
|
{
|
||
|
#if defined(CONFIG_PDIC_NOTIFIER)
|
||
|
PD_NOTI_USB_STATUS_TYPEDEF usb_status = *(PD_NOTI_USB_STATUS_TYPEDEF *)data;
|
||
|
#else
|
||
|
CC_NOTI_USB_STATUS_TYPEDEF usb_status = *(CC_NOTI_USB_STATUS_TYPEDEF *)data;
|
||
|
#endif
|
||
|
struct isg5320a_data *pdata = container_of(nb, struct isg5320a_data,
|
||
|
cpuidle_ccic_nb);
|
||
|
static int pre_attach;
|
||
|
|
||
|
if (pre_attach == usb_status.attach)
|
||
|
return 0;
|
||
|
/*
|
||
|
* USB_STATUS_NOTIFY_DETACH = 0,
|
||
|
* USB_STATUS_NOTIFY_ATTACH_DFP = 1, // Host
|
||
|
* USB_STATUS_NOTIFY_ATTACH_UFP = 2, // Device
|
||
|
* USB_STATUS_NOTIFY_ATTACH_DRP = 3, // Dual role
|
||
|
*/
|
||
|
|
||
|
if (pdata->initialized == ON) {
|
||
|
switch (usb_status.drp) {
|
||
|
case USB_STATUS_NOTIFY_ATTACH_UFP:
|
||
|
case USB_STATUS_NOTIFY_ATTACH_DFP:
|
||
|
case USB_STATUS_NOTIFY_DETACH:
|
||
|
pr_info("%s - drp = %d attat = %d\n", ISG5320A_TAG, usb_status.drp,
|
||
|
usb_status.attach);
|
||
|
schedule_work(&pdata->force_cal_work);
|
||
|
break;
|
||
|
default:
|
||
|
pr_info("%s - DRP type : %d\n", ISG5320A_TAG, usb_status.drp);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
pre_attach = usb_status.attach;
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
#elif defined(CONFIG_MUIC_NOTIFIER)
|
||
|
static int isg5320a_cpuidle_muic_notifier(struct notifier_block *nb,
|
||
|
unsigned long action, void *muic_data)
|
||
|
{
|
||
|
|
||
|
muic_attached_dev_t attached_dev = *(muic_attached_dev_t *)muic_data;
|
||
|
|
||
|
struct isg5320a_data *data = container_of(nb, struct isg5320a_data,
|
||
|
cpuidle_muic_nb);
|
||
|
|
||
|
switch (attached_dev) {
|
||
|
case ATTACHED_DEV_OTG_MUIC:
|
||
|
case ATTACHED_DEV_USB_MUIC:
|
||
|
case ATTACHED_DEV_TA_MUIC:
|
||
|
case ATTACHED_DEV_AFC_CHARGER_PREPARE_MUIC:
|
||
|
case ATTACHED_DEV_AFC_CHARGER_9V_MUIC:
|
||
|
if (action == MUIC_NOTIFY_CMD_ATTACH)
|
||
|
pr_info("%s TA/USB is inserted\n", ISG5320A_TAG);
|
||
|
else if (action == MUIC_NOTIFY_CMD_DETACH)
|
||
|
pr_info("%s TA/USB is removed\n", ISG5320A_TAG);
|
||
|
|
||
|
if (data->initialized == ON)
|
||
|
schedule_work(&pdata->force_cal_work);
|
||
|
else
|
||
|
pr_info("%s not initialized\n", ISG5320A_TAG);
|
||
|
|
||
|
break;
|
||
|
default:
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
pr_info("%s dev=%d, action=%lu\n", ISG5320A_TAG, attached_dev, action);
|
||
|
|
||
|
return NOTIFY_DONE;
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
static int isg5320a_parse_dt(struct isg5320a_data *data, struct device *dev)
|
||
|
{
|
||
|
struct device_node *node = dev->of_node;
|
||
|
enum of_gpio_flags flags;
|
||
|
int ret;
|
||
|
|
||
|
data->gpio_int = of_get_named_gpio_flags(node, "isg5320a,irq-gpio", 0,
|
||
|
&flags);
|
||
|
if (data->gpio_int < 0) {
|
||
|
pr_err("%s get gpio_int error\n", ISG5320A_TAG);
|
||
|
return -ENODEV;
|
||
|
}
|
||
|
|
||
|
pr_info("%s gpio_int:%d\n", ISG5320A_TAG, data->gpio_int);
|
||
|
|
||
|
ret = of_property_read_u32(node, "isg5320a,hallic_detect",
|
||
|
&data->hallic_detect);
|
||
|
if (ret < 0)
|
||
|
data->hallic_detect = 0;
|
||
|
ret = of_property_read_u32(node, "isg5320a,hallic_cert_detect",
|
||
|
&data->hallic_cert_detect);
|
||
|
if (ret < 0)
|
||
|
data->hallic_cert_detect = 0;
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int isg5320a_gpio_init(struct isg5320a_data *data)
|
||
|
{
|
||
|
int ret = 0;
|
||
|
|
||
|
ret = gpio_request(data->gpio_int, "isg5320a_irq");
|
||
|
if (ret < 0) {
|
||
|
pr_err("%s gpio %d request failed\n", ISG5320A_TAG, data->gpio_int);
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
ret = gpio_direction_input(data->gpio_int);
|
||
|
if (ret < 0) {
|
||
|
pr_err("%s failed to set gpio %d(%d)\n", ISG5320A_TAG, data->gpio_int,
|
||
|
ret);
|
||
|
gpio_free(data->gpio_int);
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
static int isg5320a_probe(struct i2c_client *client,
|
||
|
const struct i2c_device_id *id)
|
||
|
{
|
||
|
int ret = -ENODEV;
|
||
|
struct isg5320a_data *data;
|
||
|
struct input_dev *input_dev;
|
||
|
|
||
|
pr_info("%s ### %s probe ###\n", ISG5320A_TAG, DEVICE_NAME);
|
||
|
|
||
|
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
|
||
|
pr_err("%s i2c_check_functionality error\n", ISG5320A_TAG);
|
||
|
goto err;
|
||
|
}
|
||
|
|
||
|
data = kzalloc(sizeof(struct isg5320a_data), GFP_KERNEL);
|
||
|
if (!data) {
|
||
|
pr_err("%s failed to allocate memory\n", ISG5320A_TAG);
|
||
|
goto err_kzalloc;
|
||
|
}
|
||
|
|
||
|
ret = isg5320a_parse_dt(data, &client->dev);
|
||
|
if (ret) {
|
||
|
pr_err("%s failed to parse dt\n", ISG5320A_TAG);
|
||
|
goto err_parse_dt;
|
||
|
}
|
||
|
|
||
|
ret = isg5320a_gpio_init(data);
|
||
|
if (ret) {
|
||
|
pr_err("%s failed to init sys\n", ISG5320A_TAG);
|
||
|
goto err_gpio_init;
|
||
|
}
|
||
|
|
||
|
data->client = client;
|
||
|
i2c_set_clientdata(client, data);
|
||
|
|
||
|
input_dev = input_allocate_device();
|
||
|
if (!input_dev) {
|
||
|
pr_err("%s input_allocate_device failed\n", ISG5320A_TAG);
|
||
|
goto err_input_alloc;
|
||
|
}
|
||
|
|
||
|
data->dev = &client->dev;
|
||
|
data->input_dev = input_dev;
|
||
|
|
||
|
input_dev->name = MODULE_NAME;
|
||
|
input_dev->id.bustype = BUS_I2C;
|
||
|
|
||
|
input_set_capability(input_dev, EV_REL, REL_MISC);
|
||
|
input_set_capability(input_dev, EV_REL, REL_MAX);
|
||
|
input_set_drvdata(input_dev, data);
|
||
|
|
||
|
ret = isg5320a_reset(data);
|
||
|
if (ret < 0) {
|
||
|
pr_err("%s IMAGIS reset failed\n", ISG5320A_TAG);
|
||
|
goto err_soft_reset;
|
||
|
}
|
||
|
|
||
|
data->skip_data = false;
|
||
|
data->state = FAR;
|
||
|
data->enable = OFF;
|
||
|
data->initialized = OFF;
|
||
|
data->debug_cnt = 0;
|
||
|
data->normal_th = 0;
|
||
|
data->fine_coarse = 0;
|
||
|
data->cfcal_th = ISG5320A_RESET_CONDITION;
|
||
|
data->bfcal_chk_ready = false;
|
||
|
data->bfcal_chk_start = false;
|
||
|
data->bfcal_chk_count = 0;
|
||
|
data->hall_flag = 1;
|
||
|
data->hall_cert_flag = 1;
|
||
|
data->debug_cdc[0] = 0;
|
||
|
data->debug_cdc[1] = 0;
|
||
|
data->debug_cdc[2] = 0;
|
||
|
data->debug_base[0] = 0;
|
||
|
data->debug_base[1] = 0;
|
||
|
data->debug_diff[0] = 0;
|
||
|
data->debug_diff[1] = 0;
|
||
|
|
||
|
client->irq = gpio_to_irq(data->gpio_int);
|
||
|
ret = request_threaded_irq(client->irq, NULL, isg5320a_irq_thread,
|
||
|
IRQF_TRIGGER_FALLING | IRQF_ONESHOT, DEVICE_NAME, data);
|
||
|
if (ret < 0) {
|
||
|
pr_err("%s failed to register interrupt\n", ISG5320A_TAG);
|
||
|
goto err_irq;
|
||
|
}
|
||
|
disable_irq(client->irq);
|
||
|
mutex_init(&data->lock);
|
||
|
|
||
|
ret = input_register_device(input_dev);
|
||
|
if (ret) {
|
||
|
input_free_device(input_dev);
|
||
|
pr_err("%s failed to register input dev (%d)\n", ISG5320A_TAG, ret);
|
||
|
goto err_register_input_dev;
|
||
|
}
|
||
|
|
||
|
ret = sensors_create_symlink(input_dev);
|
||
|
if (ret < 0) {
|
||
|
pr_err("%s failed to create symlink (%d)\n", ISG5320A_TAG, ret);
|
||
|
goto err_create_symlink;
|
||
|
}
|
||
|
|
||
|
ret = sysfs_create_group(&input_dev->dev.kobj, &isg5320a_attribute_group);
|
||
|
if (ret < 0) {
|
||
|
pr_err("%s failed to create sysfs group (%d)\n", ISG5320A_TAG, ret);
|
||
|
goto err_sysfs_create_group;
|
||
|
}
|
||
|
|
||
|
ret = sensors_register(data->dev, data, sensor_attrs, MODULE_NAME);
|
||
|
if (ret) {
|
||
|
pr_err("%s could not register sensor(%d).\n", ISG5320A_TAG, ret);
|
||
|
goto err_sensor_register;
|
||
|
}
|
||
|
|
||
|
wake_lock_init(&data->grip_wake_lock, WAKE_LOCK_SUSPEND, "grip_wake_lock");
|
||
|
INIT_DELAYED_WORK(&data->debug_work, debug_work_func);
|
||
|
INIT_DELAYED_WORK(&data->cal_work, cal_work_func);
|
||
|
INIT_WORK(&data->force_cal_work, force_cal_work_func);
|
||
|
#ifdef ISG5320A_INIT_DELAYEDWORK
|
||
|
INIT_DELAYED_WORK(&data->init_work, init_work_func);
|
||
|
schedule_delayed_work(&data->init_work, msecs_to_jiffies(300));
|
||
|
#else
|
||
|
isg5320a_initialize(data);
|
||
|
isg5320a_set_mode(data, ISG5320A_MODE_NORMAL);
|
||
|
isg5320a_set_debug_work(data, ON, 2000);
|
||
|
#endif
|
||
|
|
||
|
#if defined(CONFIG_PDIC_NOTIFIER) && defined(CONFIG_USB_TYPEC_MANAGER_NOTIFIER)
|
||
|
manager_notifier_register(&data->cpuidle_ccic_nb,
|
||
|
isg5320a_ccic_handle_notification,
|
||
|
MANAGER_NOTIFY_PDIC_SENSORHUB);
|
||
|
#elif defined(CONFIG_CCIC_NOTIFIER) && defined(CONFIG_USB_TYPEC_MANAGER_NOTIFIER)
|
||
|
manager_notifier_register(&data->cpuidle_ccic_nb,
|
||
|
isg5320a_ccic_handle_notification,
|
||
|
MANAGER_NOTIFY_CCIC_SENSORHUB);
|
||
|
#elif defined(CONFIG_MUIC_NOTIFIER)
|
||
|
muic_notifier_register(&data->cpuidle_muic_nb,
|
||
|
isg5320a_cpuidle_muic_notifier, MUIC_NOTIFY_DEV_CPUIDLE);
|
||
|
#endif
|
||
|
|
||
|
pr_info("%s ### IMAGIS probe done ###\n", ISG5320A_TAG);
|
||
|
|
||
|
return 0;
|
||
|
|
||
|
err_sensor_register:
|
||
|
sysfs_remove_group(&input_dev->dev.kobj, &isg5320a_attribute_group);
|
||
|
err_sysfs_create_group:
|
||
|
sensors_remove_symlink(input_dev);
|
||
|
err_create_symlink:
|
||
|
input_unregister_device(input_dev);
|
||
|
err_register_input_dev:
|
||
|
mutex_destroy(&data->lock);
|
||
|
free_irq(client->irq, data);
|
||
|
err_irq:
|
||
|
err_soft_reset:
|
||
|
err_input_alloc:
|
||
|
gpio_free(data->gpio_int);
|
||
|
err_gpio_init:
|
||
|
err_parse_dt:
|
||
|
kfree(data);
|
||
|
err_kzalloc:
|
||
|
err:
|
||
|
pr_info("%s ### IMAGIS probe failed ###\n", ISG5320A_TAG);
|
||
|
|
||
|
return -ENODEV;
|
||
|
}
|
||
|
|
||
|
static int isg5320a_remove(struct i2c_client *client)
|
||
|
{
|
||
|
struct isg5320a_data *data = i2c_get_clientdata(client);
|
||
|
|
||
|
pr_info("%s %s\n", ISG5320A_TAG, __func__);
|
||
|
|
||
|
isg5320a_set_debug_work(data, OFF, 0);
|
||
|
if (data->enable == ON)
|
||
|
isg5320a_set_enable(data, OFF);
|
||
|
isg5320a_set_mode(data, ISG5320A_MODE_SLEEP);
|
||
|
|
||
|
free_irq(client->irq, data);
|
||
|
gpio_free(data->gpio_int);
|
||
|
|
||
|
wake_lock_destroy(&data->grip_wake_lock);
|
||
|
sensors_unregister(data->dev, sensor_attrs);
|
||
|
sensors_remove_symlink(data->input_dev);
|
||
|
sysfs_remove_group(&data->input_dev->dev.kobj, &isg5320a_attribute_group);
|
||
|
input_unregister_device(data->input_dev);
|
||
|
mutex_destroy(&data->lock);
|
||
|
|
||
|
kfree(data);
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int isg5320a_suspend(struct device *dev)
|
||
|
{
|
||
|
struct isg5320a_data *data = dev_get_drvdata(dev);
|
||
|
|
||
|
pr_info("%s %s\n", ISG5320A_TAG, __func__);
|
||
|
isg5320a_set_debug_work(data, OFF, 0);
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int isg5320a_resume(struct device *dev)
|
||
|
{
|
||
|
struct isg5320a_data *data = dev_get_drvdata(dev);
|
||
|
|
||
|
pr_info("%s %s\n", ISG5320A_TAG, __func__);
|
||
|
isg5320a_set_debug_work(data, ON, 1000);
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static void isg5320a_shutdown(struct i2c_client *client)
|
||
|
{
|
||
|
struct isg5320a_data *data = i2c_get_clientdata(client);
|
||
|
|
||
|
pr_info("%s %s\n", ISG5320A_TAG, __func__);
|
||
|
|
||
|
cancel_work_sync(&data->force_cal_work);
|
||
|
isg5320a_set_debug_work(data, OFF, 0);
|
||
|
if (data->enable == ON)
|
||
|
isg5320a_set_enable(data, OFF);
|
||
|
isg5320a_set_mode(data, ISG5320A_MODE_SLEEP);
|
||
|
}
|
||
|
|
||
|
static const struct of_device_id isg5320a_match_table[] = {
|
||
|
{ .compatible = "isg5320a", },
|
||
|
{ },
|
||
|
};
|
||
|
|
||
|
static struct i2c_device_id isg5320a_id_table[] = {
|
||
|
{ DEVICE_NAME, 0 },
|
||
|
{ },
|
||
|
};
|
||
|
MODULE_DEVICE_TABLE(i2c, isg5320a_id_table);
|
||
|
|
||
|
static const struct dev_pm_ops isg5320a_pm_ops = {
|
||
|
.suspend = isg5320a_suspend,
|
||
|
.resume = isg5320a_resume,
|
||
|
};
|
||
|
|
||
|
static struct i2c_driver isg5320a_driver = {
|
||
|
.driver = {
|
||
|
.name = DEVICE_NAME,
|
||
|
.owner = THIS_MODULE,
|
||
|
.of_match_table = isg5320a_match_table,
|
||
|
.pm = &isg5320a_pm_ops,
|
||
|
},
|
||
|
.id_table = isg5320a_id_table,
|
||
|
.probe = isg5320a_probe,
|
||
|
.remove = isg5320a_remove,
|
||
|
.shutdown = isg5320a_shutdown,
|
||
|
};
|
||
|
|
||
|
static int __init isg5320a_init(void)
|
||
|
{
|
||
|
return i2c_add_driver(&isg5320a_driver);
|
||
|
}
|
||
|
|
||
|
static void __exit isg5320a_exit(void)
|
||
|
{
|
||
|
i2c_del_driver(&isg5320a_driver);
|
||
|
}
|
||
|
|
||
|
module_init(isg5320a_init);
|
||
|
module_exit(isg5320a_exit);
|
||
|
|
||
|
MODULE_DESCRIPTION("Imagis Grip Sensor driver");
|
||
|
MODULE_LICENSE("GPL");
|