lineage_kernel_xcoverpro/drivers/sensorhub/a96t3x6_wifi.c

2595 lines
60 KiB
C
Executable File

/* a96t3x6.c -- Linux driver for A96T3X6 chip as grip sensor
*
* Copyright (C) 2017 Samsung Electronics Co.Ltd
* Author: YunJae Hwang <yjz.hwang@samsung.com>
*
* 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, 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/delay.h>
#include <linux/firmware.h>
#include <linux/gpio.h>
#include <linux/i2c.h>
#include <linux/init.h>
#include <linux/input.h>
#include <linux/input/mt.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/io.h>
#include <linux/wakelock.h>
#include <asm/unaligned.h>
#include <linux/regulator/consumer.h>
#include <linux/sec_class.h>
#include <linux/pinctrl/consumer.h>
#if defined(CONFIG_MUIC_NOTIFIER)
#include <linux/muic/muic.h>
#include <linux/muic/muic_notifier.h>
#endif
#if defined(CONFIG_CCIC_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
#ifdef CONFIG_OF
#include <linux/of_gpio.h>
#endif
#include "a96t3x6_wifi.h"
struct a96t3x6_data {
struct i2c_client *client;
struct input_dev *input_dev;
struct device *dev;
struct mutex lock;
struct delayed_work debug_work;
struct delayed_work firmware_work;
const struct firmware *firm_data_bin;
const u8 *firm_data_ums;
char phys[32];
long firm_size;
int irq;
struct wake_lock grip_wake_lock;
u16 grip_p_thd;
u16 grip_r_thd;
u16 grip_n_thd;
u16 grip_baseline;
u16 grip_raw;
u16 grip_raw_d;
u16 grip_event;
u16 diff;
u16 diff_d;
bool sar_mode;
bool current_state;
bool expect_state;
bool earjack;
int earjack_noise;
#ifdef CONFIG_SEC_FACTORY
int irq_count;
int abnormal_mode;
s16 max_diff;
s16 max_normal_diff;
#endif
int irq_en_cnt;
u8 fw_update_state;
u8 fw_ver;
u8 md_ver;
u8 id_ver;
u8 fw_ver_bin;
u8 md_ver_bin;
u8 checksum_h;
u8 checksum_h_bin;
u8 checksum_l;
u8 checksum_l_bin;
bool enabled;
bool skip_event;
bool resume_called;
int ldo_en; /* ldo_en pin gpio */
int grip_int; /* irq pin gpio */
struct regulator *dvdd_vreg; /* regulator */
int (*power)(void *, bool on); /* power onoff function ptr */
const char *fw_path;
bool bringup;
bool probe_done;
int firmup_cmd;
int debug_count;
int firmware_count;
int identity_number;
bool crc_check;
#if defined(CONFIG_MUIC_NOTIFIER)
struct notifier_block cpuidle_muic_nb;
#endif
#if defined(CONFIG_CCIC_NOTIFIER)
struct notifier_block cpuidle_ccic_nb;
#endif
};
static void a96t3x6_reset(struct a96t3x6_data *data);
static void a96t3x6_diff_getdata(struct a96t3x6_data *data);
static void grip_always_active(struct a96t3x6_data *data, int on);
#ifdef CONFIG_SENSORS_FW_VENDOR
static int a96t3x6_fw_check(struct a96t3x6_data *data);
static void a96t3x6_set_firmware_work(struct a96t3x6_data *data, u8 enable,
unsigned int time_ms);
#endif
static int a96t3x6_i2c_read(struct i2c_client *client,
u8 reg, u8 *val, unsigned int len)
{
struct a96t3x6_data *data = i2c_get_clientdata(client);
struct i2c_msg msg;
int ret;
int retry = 3;
mutex_lock(&data->lock);
msg.addr = client->addr;
msg.flags = I2C_M_WR;
msg.len = 1;
msg.buf = &reg;
while (retry--) {
ret = i2c_transfer(client->adapter, &msg, 1);
if (ret >= 0)
break;
GRIP_ERR("fail(address set)(%d)(%d)\n", retry, ret);
usleep_range(10000, 11000);
}
if (ret < 0) {
mutex_unlock(&data->lock);
return ret;
}
retry = 3;
msg.flags = 1;/*I2C_M_RD*/
msg.len = len;
msg.buf = val;
while (retry--) {
ret = i2c_transfer(client->adapter, &msg, 1);
if (ret >= 0) {
mutex_unlock(&data->lock);
return 0;
}
GRIP_ERR("fail(data read)(%d)(%d)\n", retry, ret);
usleep_range(10000, 11000);
}
mutex_unlock(&data->lock);
return ret;
}
static int a96t3x6_i2c_read_data(struct i2c_client *client, u8 *val, unsigned int len)
{
struct a96t3x6_data *data = i2c_get_clientdata(client);
struct i2c_msg msg;
int ret;
int retry = 3;
mutex_lock(&data->lock);
msg.addr = client->addr;
msg.flags = 1;/*I2C_M_RD*/
msg.len = len;
msg.buf = val;
while (retry--) {
ret = i2c_transfer(client->adapter, &msg, 1);
if (ret >= 0) {
mutex_unlock(&data->lock);
return 0;
}
GRIP_ERR("fail(data read)(%d)\n", retry);
usleep_range(10000, 11000);
}
mutex_unlock(&data->lock);
return ret;
}
static int a96t3x6_i2c_write(struct i2c_client *client, u8 reg, u8 *val)
{
struct a96t3x6_data *data = i2c_get_clientdata(client);
struct i2c_msg msg[1];
unsigned char buf[2];
int ret;
int retry = 3;
mutex_lock(&data->lock);
buf[0] = reg;
buf[1] = *val;
msg->addr = client->addr;
msg->flags = I2C_M_WR;
msg->len = 2;
msg->buf = buf;
while (retry--) {
ret = i2c_transfer(client->adapter, msg, 1);
if (ret >= 0) {
mutex_unlock(&data->lock);
return 0;
}
GRIP_ERR("fail(%d)\n", retry);
usleep_range(10000, 11000);
}
mutex_unlock(&data->lock);
return ret;
}
static void a96t3x6_check_first_status(struct a96t3x6_data *data, int enable)
{
u8 r_buf[2];
u16 grip_thd;
if (data->skip_event == true) {
GRIP_INFO("skip event..\n");
return;
}
a96t3x6_i2c_read(data->client, REG_SAR_THRESHOLD, r_buf, 2);
grip_thd = (r_buf[0] << 8) | r_buf[1];
a96t3x6_diff_getdata(data);
if (grip_thd < data->diff) {
input_report_rel(data->input_dev, REL_MISC, 1);
} else {
input_report_rel(data->input_dev, REL_MISC, 2);
}
input_sync(data->input_dev);
}
/*
* @enable: turn it on or off.
* @force: if caller is grip_sensing_change(), it's true. others, it's false.
*
* This function was designed to prevent noise issue from ic for specific models.
* If earjack_noise is true, it handled enable control for it.
*/
static void a96t3x6_set_enable(struct a96t3x6_data *data, int enable, bool force)
{
u8 cmd;
int ret;
if ((data->current_state == enable) ||
((data->earjack_noise) && ((!force && data->earjack) ||
(force && ((!enable && (data->expect_state != data->current_state))
|| (enable && (data->expect_state == data->current_state))))))) {
GRIP_INFO("old = %d, new = %d, skip exception case\n",
data->current_state, enable);
return;
}
GRIP_INFO("old enable = %d, new enable = %d\n",
data->current_state, enable);
if (enable) {
cmd = CMD_ON;
ret = a96t3x6_i2c_write(data->client, REG_SAR_ENABLE, &cmd);
if (ret < 0)
GRIP_ERR("failed to enable grip irq\n");
a96t3x6_check_first_status(data, enable);
enable_irq(data->irq);
enable_irq_wake(data->irq);
data->irq_en_cnt++;
} else {
cmd = CMD_OFF;
disable_irq_wake(data->irq);
disable_irq(data->irq);
data->irq_en_cnt--;
ret = a96t3x6_i2c_write(data->client, REG_SAR_ENABLE, &cmd);
if (ret < 0)
GRIP_ERR("failed to disable grip irq\n");
}
data->current_state = enable;
}
static void a96t3x6_sar_only_mode(struct a96t3x6_data *data, int on)
{
int retry = 3;
int ret;
u8 cmd;
u8 r_buf;
int mode_retry = 5;
if (data->sar_mode == on) {
GRIP_INFO("skip already %s\n", (on == 1) ? "sar only mode" : "normal mode");
return;
}
if (on == 1)
cmd = CMD_ON;
else
cmd = CMD_OFF;
GRIP_INFO("%s, cmd=%x\n", (on == 1) ? "sar only mode" : "normal mode", cmd);
sar_mode:
while (retry > 0) {
ret = a96t3x6_i2c_write(data->client, REG_SAR_MODE, &cmd);
if (ret < 0) {
GRIP_ERR("i2c read fail(%d), retry %d\n", ret, retry);
retry--;
usleep_range(20000, 20000);
continue;
}
break;
}
usleep_range(40000, 40000);
ret = a96t3x6_i2c_read(data->client, REG_SAR_MODE, &r_buf, 1);
if (ret < 0)
GRIP_ERR("i2c read fail(%d)\n", ret);
GRIP_INFO("read reg = %x\n", r_buf);
if ((r_buf != cmd) && (mode_retry > 0)) {
GRIP_INFO("change fail retry %d\n", 6 - mode_retry--);
if (mode_retry == 0)
a96t3x6_reset(data);
goto sar_mode;
}
if (r_buf == CMD_ON)
data->sar_mode = 1;
else
data->sar_mode = 0;
}
static void a96t3x6_sar_sensing(struct a96t3x6_data *data, int on)
{
u8 cmd;
int ret;
GRIP_INFO("%s", (on) ? "on" : "off");
if (on)
cmd = CMD_ON;
else
cmd = CMD_OFF;
ret = a96t3x6_i2c_write(data->client, REG_SAR_SENSING, &cmd);
if (ret < 0)
GRIP_ERR("failed to %s grip sensing\n", (on) ? "enable" : "disable");
}
static void a96t3x6_reset_for_bootmode(struct a96t3x6_data *data)
{
GRIP_INFO("\n");
data->power(data, false);
usleep_range(50000, 50000);
data->power(data, true);
}
static void a96t3x6_reset(struct a96t3x6_data *data)
{
if (data->enabled == false)
return;
GRIP_INFO("start\n");
disable_irq_nosync(data->irq);
data->enabled = false;
a96t3x6_reset_for_bootmode(data);
usleep_range(RESET_DELAY, RESET_DELAY);
if (data->current_state)
a96t3x6_set_enable(data, 1, 0);
data->enabled = true;
GRIP_INFO("done\n");
}
static void a96t3x6_diff_getdata(struct a96t3x6_data *data)
{
int ret;
int retry = 3;
u8 r_buf[4] = {0,};
while (retry--) {
ret = a96t3x6_i2c_read(data->client, REG_SAR_DIFFDATA, r_buf, 4);
if (ret == 0)
break;
GRIP_ERR("read failed(%d)\n", retry);
usleep_range(10000, 10000);
}
data->diff = (r_buf[0] << 8) | r_buf[1];
data->diff_d = (r_buf[2] << 8) | r_buf[3];
GRIP_INFO("%u\n", data->diff);
}
static void a96t3x6_check_diff_and_cap(struct a96t3x6_data *data)
{
u8 r_buf[2] = {0,0};
u8 cmd = 0x20;
int ret;
int value = 0;
ret = a96t3x6_i2c_write(data->client, REG_SAR_TOTALCAP, &cmd);
if (ret < 0)
GRIP_ERR("write fail(%d)\n", ret);
usleep_range(20, 20);
ret = a96t3x6_i2c_read(data->client, REG_SAR_TOTALCAP_READ, r_buf, 2);
if (ret < 0)
GRIP_ERR("fail(%d)\n", ret);
value = (r_buf[0] << 8) | r_buf[1];
GRIP_INFO("Cap Read %d\n", value);
a96t3x6_diff_getdata(data);
}
static void a96t3x6_grip_sw_reset(struct a96t3x6_data *data)
{
int ret, retry = 3;
u8 cmd = CMD_SW_RESET;
GRIP_INFO("\n");
while (retry--) {
a96t3x6_check_diff_and_cap(data);
usleep_range(10000, 10000);
}
ret = a96t3x6_i2c_write(data->client, REG_SW_RESET, &cmd);
if (ret < 0)
GRIP_ERR("fail(%d)\n", ret);
else
usleep_range(35000, 35000);
}
static int a96t3x6_get_hallic_state(struct a96t3x6_data *data)
{
char hall_buf[6];
int ret = -ENODEV;
int hall_state = -1;
mm_segment_t old_fs;
struct file *filep;
memset(hall_buf, 0, sizeof(hall_buf));
old_fs = get_fs();
set_fs(KERNEL_DS);
filep = filp_open(HALL_PATH, O_RDONLY, 0666);
if (IS_ERR(filep)) {
set_fs(old_fs);
return hall_state;
}
ret = filep->f_op->read(filep, hall_buf,
sizeof(hall_buf) - 1, &filep->f_pos);
if (ret != sizeof(hall_buf) - 1)
goto exit;
if (strcmp(hall_buf, "CLOSE") == 0)
hall_state = HALL_CLOSE_STATE;
exit:
filp_close(filep, current->files);
set_fs(old_fs);
return hall_state;
}
#ifdef CONFIG_SENSORS_FW_VENDOR
static void a96t3x6_firmware_work_func(struct work_struct *work)
{
struct a96t3x6_data *data = container_of((struct delayed_work *)work,
struct a96t3x6_data, firmware_work);
int ret;
GRIP_INFO("called\n");
ret = a96t3x6_fw_check(data);
if (ret) {
if (data->firmware_count++ < FIRMWARE_VENDOR_CALL_CNT) {
GRIP_ERR("failed to load firmware (%d)\n",
data->firmware_count);
schedule_delayed_work(&data->firmware_work,
msecs_to_jiffies(1000));
return;
}
GRIP_ERR("final retry failed\n");
} else {
GRIP_INFO("fw check success\n");
}
}
#endif
static void a96t3x6_debug_work_func(struct work_struct *work)
{
struct a96t3x6_data *data = container_of((struct delayed_work *)work,
struct a96t3x6_data, debug_work);
static int hall_prev_state;
int hall_state;
if (data->resume_called == true) {
data->resume_called = false;
a96t3x6_sar_only_mode(data, 0);
schedule_delayed_work(&data->debug_work, msecs_to_jiffies(1000));
return;
}
hall_state = a96t3x6_get_hallic_state(data);
if (hall_state == HALL_CLOSE_STATE && hall_prev_state != hall_state) {
GRIP_INFO("hall is closed\n");
a96t3x6_grip_sw_reset(data);
}
hall_prev_state = hall_state;
if (data->current_state) {
#ifdef CONFIG_SEC_FACTORY
if (data->abnormal_mode) {
a96t3x6_diff_getdata(data);
if (data->max_normal_diff < data->diff)
data->max_normal_diff = data->diff;
} else {
#endif
if (data->debug_count >= GRIP_LOG_TIME) {
a96t3x6_diff_getdata(data);
data->debug_count = 0;
} else {
data->debug_count++;
}
#ifdef CONFIG_SEC_FACTORY
}
#endif
}
schedule_delayed_work(&data->debug_work, msecs_to_jiffies(2000));
}
static void a96t3x6_set_debug_work(struct a96t3x6_data *data, u8 enable,
unsigned int time_ms)
{
GRIP_INFO("\n");
if (enable == 1) {
data->debug_count = 0;
schedule_delayed_work(&data->debug_work,
msecs_to_jiffies(time_ms));
} else {
cancel_delayed_work_sync(&data->debug_work);
}
}
#ifdef CONFIG_SENSORS_FW_VENDOR
static void a96t3x6_set_firmware_work(struct a96t3x6_data *data, u8 enable,
unsigned int time_ms)
{
GRIP_INFO("%s %s\n", __func__, enable ? "enabled": "disabled");
if (enable == 1) {
data->firmware_count = 0;
schedule_delayed_work(&data->firmware_work,
msecs_to_jiffies(time_ms));
} else {
cancel_delayed_work_sync(&data->firmware_work);
}
}
#endif
static irqreturn_t a96t3x6_interrupt(int irq, void *dev_id)
{
struct a96t3x6_data *data = dev_id;
struct i2c_client *client = data->client;
int ret, retry;
u8 buf;
int grip_data;
u8 grip_press = 0;
wake_lock(&data->grip_wake_lock);
ret = a96t3x6_i2c_read(client, REG_BTNSTATUS, &buf, 1);
if (ret < 0) {
retry = 3;
while (retry--) {
GRIP_ERR("read fail(%d)\n", retry);
ret = a96t3x6_i2c_read(client, REG_BTNSTATUS, &buf, 1);
if (ret == 0)
break;
usleep_range(10000, 11000);
}
if (retry < 0) {
a96t3x6_reset(data);
wake_unlock(&data->grip_wake_lock);
return IRQ_HANDLED;
}
}
GRIP_INFO("buf = 0x%02x\n", buf);
grip_data = (buf >> 4) & 0x03;
grip_press = !(grip_data % 2);
if (grip_data) {
if (data->skip_event) {
GRIP_INFO("int was generated, but event skipped\n");
} else {
if (grip_press)
input_report_rel(data->input_dev, REL_MISC, 1);
else
input_report_rel(data->input_dev, REL_MISC, 2);
input_sync(data->input_dev);
data->grip_event = grip_press;
}
}
a96t3x6_diff_getdata(data);
#ifdef CONFIG_SEC_FACTORY
if (data->abnormal_mode) {
if (data->grip_event) {
if (data->max_diff < data->diff)
data->max_diff = data->diff;
data->irq_count++;
}
}
#endif
if (grip_data)
GRIP_INFO("%s %x\n", grip_press ? "grip P" : "grip R", buf);
wake_unlock(&data->grip_wake_lock);
return IRQ_HANDLED;
}
static int a96t3x6_get_raw_data(struct a96t3x6_data *data)
{
int ret;
u8 r_buf[4] = {0,};
ret = a96t3x6_i2c_read(data->client, REG_SAR_RAWDATA, r_buf, 4);
if (ret < 0) {
GRIP_ERR("fail(%d)\n", ret);
data->grip_raw = 0;
data->grip_raw_d = 0;
return ret;
}
data->grip_raw = (r_buf[0] << 8) | r_buf[1];
data->grip_raw_d = (r_buf[2] << 8) | r_buf[3];
GRIP_INFO("grip_raw = %d\n", data->grip_raw);
return ret;
}
static ssize_t grip_sar_enable_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct a96t3x6_data *data = dev_get_drvdata(dev);
return snprintf(buf, PAGE_SIZE, "%u\n", !data->skip_event);
}
static ssize_t grip_sar_enable_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
struct a96t3x6_data *data = dev_get_drvdata(dev);
int ret, enable;
ret = sscanf(buf, "%2d", &enable);
if (ret != 1) {
GRIP_ERR(" cmd read err\n");
return count;
}
if (!(enable >= 0 && enable <= 3)) {
GRIP_ERR(" wrong command(%d)\n", enable);
return count;
}
GRIP_INFO(" enable = %d\n", enable);
/* enable 0:off, 1:on, 2:skip event , 3:cancel skip event */
if (enable == 2) {
data->skip_event = true;
input_report_rel(data->input_dev, REL_MISC, 2);
input_sync(data->input_dev);
} else if (enable == 3) {
data->skip_event = false;
} else {
data->expect_state = enable;
a96t3x6_set_enable(data, enable, 0);
}
return count;
}
static ssize_t grip_threshold_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct a96t3x6_data *data = dev_get_drvdata(dev);
u8 r_buf[4];
int ret;
#ifdef CONFIG_SENSORS_A96T3X6_WIFI_2CH
ret = a96t3x6_i2c_read(data->client, REG_SAR_THRESHOLD, r_buf, 2);
if (ret < 0) {
GRIP_ERR("fail(%d)\n", ret);
data->grip_p_thd = 0;
return snprintf(buf, PAGE_SIZE, "%u\n", 0);
}
data->grip_p_thd = (r_buf[0] << 8) | r_buf[1];
ret = a96t3x6_i2c_read(data->client, REG_SAR_RELEASE_THRESHOLD, r_buf, 2);
if (ret < 0) {
GRIP_ERR("fail(%d)\n", ret);
data->grip_r_thd = 0;
return snprintf(buf, PAGE_SIZE, "%u\n", 0);
}
data->grip_r_thd = (r_buf[0] << 8) | r_buf[1];
#else
ret = a96t3x6_i2c_read(data->client, REG_SAR_THRESHOLD, r_buf, 4);
if (ret < 0) {
GRIP_ERR("fail(%d)\n", ret);
data->grip_p_thd = 0;
data->grip_r_thd = 0;
return snprintf(buf, PAGE_SIZE, "%u\n", 0);
}
data->grip_p_thd = (r_buf[0] << 8) | r_buf[1];
data->grip_r_thd = (r_buf[2] << 8) | r_buf[3];
#endif
ret = a96t3x6_i2c_read(data->client, REG_SAR_NOISE_THRESHOLD, r_buf, 2);
if (ret < 0) {
GRIP_ERR("fail(%d)\n", ret);
data->grip_n_thd = 0;
return snprintf(buf, PAGE_SIZE, "%u\n", 0);
}
data->grip_n_thd = (r_buf[0] << 8) | r_buf[1];
return sprintf(buf, "%u,%u,%u\n", data->grip_p_thd, data->grip_r_thd, data->grip_n_thd);
}
static ssize_t grip_total_cap_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct a96t3x6_data *data = dev_get_drvdata(dev);
u8 r_buf[2];
u8 cmd;
int ret;
int value;
cmd = 0x20;
ret = a96t3x6_i2c_write(data->client, REG_SAR_TOTALCAP, &cmd);
if (ret < 0)
GRIP_ERR("write fail(%d)\n", ret);
usleep_range(10, 20);
ret = a96t3x6_i2c_read(data->client, REG_SAR_TOTALCAP_READ, r_buf, 2);
if (ret < 0) {
GRIP_ERR("fail(%d)\n", ret);
return snprintf(buf, PAGE_SIZE, "%u\n", 0);
}
value = (r_buf[0] << 8) | r_buf[1];
return snprintf(buf, PAGE_SIZE, "%d\n", value / 100);
}
static ssize_t grip_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct a96t3x6_data *data = dev_get_drvdata(dev);
int ret;
int retry = 3;
u8 r_buf[4] = {0,};
while (retry--) {
ret = a96t3x6_i2c_read(data->client, REG_SAR_DIFFDATA, r_buf, 4);
if (ret == 0)
break;
GRIP_ERR("read failed(%d)\n", retry);
usleep_range(10000, 10000);
}
data->diff = (r_buf[0] << 8) | r_buf[1];
data->diff_d = (r_buf[2] << 8) | r_buf[3];
return sprintf(buf, "%u,%u\n", data->diff, data->diff_d);
}
static ssize_t grip_baseline_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct a96t3x6_data *data = dev_get_drvdata(dev);
u8 r_buf[2];
int ret;
ret = a96t3x6_i2c_read(data->client, REG_SAR_BASELINE, r_buf, 2);
if (ret < 0) {
GRIP_ERR("fail(%d)\n", ret);
data->grip_baseline = 0;
return snprintf(buf, PAGE_SIZE, "%d\n", 0);
}
data->grip_baseline = (r_buf[0] << 8) | r_buf[1];
return snprintf(buf, PAGE_SIZE, "%u\n", data->grip_baseline);
}
static ssize_t grip_raw_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct a96t3x6_data *data = dev_get_drvdata(dev);
int ret;
ret = a96t3x6_get_raw_data(data);
if (ret < 0)
return sprintf(buf, "%d\n", 0);
else
return sprintf(buf, "%u,%u\n", data->grip_raw,
data->grip_raw_d);
}
static ssize_t grip_gain_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
return sprintf(buf, "%d,%d,%d,%d\n", 0, 0, 0, 0);
}
static ssize_t grip_check_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct a96t3x6_data *data = dev_get_drvdata(dev);
a96t3x6_diff_getdata(data);
return snprintf(buf, PAGE_SIZE, "%d\n", data->grip_event);
}
static ssize_t grip_sw_reset_ready_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct a96t3x6_data *data = dev_get_drvdata(dev);
int ret;
int retry = 10;
u8 r_buf[1] = {0};
GRIP_INFO("Wait start\n");
/* To garuantee grip sensor sw reset delay*/
msleep(500);
while (retry--) {
ret = a96t3x6_i2c_read(data->client, REG_SW_RESET, r_buf, 1);
if (r_buf[0] == 0x20)
break;
if (ret < 0)
GRIP_ERR("failed(%d)\n", retry);
msleep(100);
}
if(r_buf[0] == 0x20) {
GRIP_INFO("reset done");
a96t3x6_check_diff_and_cap(data);
return snprintf(buf, PAGE_SIZE, "1\n");
} else {
GRIP_INFO("expect 0x20 read 0x%x\n", r_buf[0]);
return snprintf(buf, PAGE_SIZE, "0\n");
}
}
static ssize_t grip_sw_reset(struct device *dev,
struct device_attribute *attr, const char *buf,
size_t count)
{
struct a96t3x6_data *data = dev_get_drvdata(dev);
u8 cmd;
int ret;
ret = kstrtou8(buf, 2, &cmd);
if (ret) {
GRIP_ERR("cmd read err\n");
return count;
}
if (!(cmd == 1)) {
GRIP_ERR("wrong command(%d)\n", cmd);
return count;
}
data->grip_event = 0;
GRIP_INFO("cmd(%d)\n", cmd);
a96t3x6_grip_sw_reset(data);
return count;
}
static ssize_t grip_sensing_change(struct device *dev,
struct device_attribute *attr, const char *buf,
size_t count)
{
struct a96t3x6_data *data = dev_get_drvdata(dev);
int ret, earjack;
ret = sscanf(buf, "%2d", &earjack);
if (ret != 1) {
GRIP_ERR("cmd read err\n");
return count;
}
if (!(earjack == 0 || earjack == 1)) {
GRIP_ERR("wrong command(%d)\n", earjack);
return count;
}
if (!data->earjack_noise) {
if (earjack == 1)
a96t3x6_sar_only_mode(data, 1);
else
a96t3x6_sar_only_mode(data, 0);
} else {
if (earjack == 1) {
a96t3x6_set_enable(data, 0, 1);
a96t3x6_sar_sensing(data, 0);
data->grip_event = 0;
input_report_rel(data->input_dev, REL_MISC, 2);
input_sync(data->input_dev);
} else {
a96t3x6_grip_sw_reset(data);
a96t3x6_sar_sensing(data, 1);
a96t3x6_set_enable(data, 1, 1);
}
}
data->earjack = earjack;
GRIP_INFO("earjack was %s\n", (earjack) ? "inserted" : "removed");
return count;
}
#ifndef CONFIG_SAMSUNG_PRODUCT_SHIP
static ssize_t grip_sar_press_threshold_store(struct device *dev,
struct device_attribute *attr, const char *buf,
size_t count)
{
struct a96t3x6_data *data = dev_get_drvdata(dev);
int ret;
int threshold;
u8 cmd[2];
ret = sscanf(buf, "%11d", &threshold);
if (ret != 1) {
GRIP_ERR("failed to read thresold, buf is %s\n", buf);
return count;
}
if (threshold > 0xff) {
cmd[0] = (threshold >> 8) & 0xff;
cmd[1] = 0xff & threshold;
} else if (threshold < 0) {
cmd[0] = 0x0;
cmd[1] = 0x0;
} else {
cmd[0] = 0x0;
cmd[1] = (u8)threshold;
}
GRIP_INFO("buf : %d, threshold : %d\n", threshold,
(cmd[0] << 8) | cmd[1]);
ret = a96t3x6_i2c_write(data->client, REG_SAR_THRESHOLD, &cmd[0]);
if (ret != 0) {
GRIP_INFO("failed to write press_threhold data1");
goto press_threshold_out;
}
ret = a96t3x6_i2c_write(data->client, REG_SAR_THRESHOLD + 0x01, &cmd[1]);
if (ret != 0) {
GRIP_INFO("failed to write press_threhold data2");
goto press_threshold_out;
}
press_threshold_out:
return count;
}
static ssize_t grip_sar_release_threshold_store(struct device *dev,
struct device_attribute *attr, const char *buf,
size_t count)
{
struct a96t3x6_data *data = dev_get_drvdata(dev);
int ret;
int threshold;
u8 cmd[2];
ret = sscanf(buf, "%11d", &threshold);
if (ret != 1) {
GRIP_ERR("failed to read thresold, buf is %s\n", buf);
return count;
}
if (threshold > 0xff) {
cmd[0] = (threshold >> 8) & 0xff;
cmd[1] = 0xff & threshold;
} else if (threshold < 0) {
cmd[0] = 0x0;
cmd[1] = 0x0;
} else {
cmd[0] = 0x0;
cmd[1] = (u8)threshold;
}
GRIP_INFO("buf : %d, threshold : %d\n", threshold,
(cmd[0] << 8) | cmd[1]);
ret = a96t3x6_i2c_write(data->client, REG_SAR_THRESHOLD + 0x02,
&cmd[0]);
GRIP_INFO("ret : %d\n", ret);
if (ret != 0) {
GRIP_INFO("failed to write release_threshold_data1");
goto release_threshold_out;
}
ret = a96t3x6_i2c_write(data->client, REG_SAR_THRESHOLD + 0x03,
&cmd[1]);
GRIP_INFO("ret : %d\n", ret);
if (ret != 0) {
GRIP_INFO("failed to write release_threshold_data2");
goto release_threshold_out;
}
release_threshold_out:
return count;
}
static ssize_t grip_mode_change(struct device *dev,
struct device_attribute *attr, const char *buf,
size_t count)
{
struct a96t3x6_data *data = dev_get_drvdata(dev);
int ret, mode;
ret = sscanf(buf, "%2d", &mode);
if (ret != 1) {
GRIP_ERR("cmd read err\n");
return count;
}
if (!(mode == 0 || mode == 1)) {
GRIP_ERR("wrong command(%d)\n", mode);
return count;
}
GRIP_INFO("mode(%d)\n", mode);
a96t3x6_sar_only_mode(data, mode);
return count;
}
#endif
#ifdef CONFIG_SEC_FACTORY
static ssize_t a96t3x6_irq_count_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct a96t3x6_data *data = dev_get_drvdata(dev);
int result = 0;
s16 max_diff_val = 0;
if (data->irq_count) {
result = -1;
max_diff_val = data->max_diff;
} else {
max_diff_val = data->max_normal_diff;
}
return snprintf(buf, PAGE_SIZE, "%d,%d,%d\n", result,
data->irq_count, max_diff_val);
}
static ssize_t a96t3x6_irq_count_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
struct a96t3x6_data *data = dev_get_drvdata(dev);
u8 onoff;
int ret;
ret = kstrtou8(buf, 10, &onoff);
if (ret < 0) {
GRIP_ERR("kstrtou8 failed.(%d)\n", ret);
return count;
}
mutex_lock(&data->lock);
if (onoff == 0) {
data->abnormal_mode = 0;
} else if (onoff == 1) {
data->abnormal_mode = 1;
data->irq_count = 0;
data->max_diff = 0;
data->max_normal_diff = 0;
} else {
GRIP_ERR("Invalid value.(%d)\n", onoff);
}
mutex_unlock(&data->lock);
GRIP_INFO("result : %d\n", onoff);
return count;
}
#endif
static ssize_t grip_vendor_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
return snprintf(buf, PAGE_SIZE, "%s\n", VENDOR_NAME);
}
static ssize_t grip_name_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
return snprintf(buf, PAGE_SIZE, "%s\n", MODEL_NAME);
}
static ssize_t bin_fw_ver(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct a96t3x6_data *data = dev_get_drvdata(dev);
return snprintf(buf, PAGE_SIZE, "0x%02x%02x\n", data->md_ver_bin, data->fw_ver_bin);
}
static int a96t3x6_get_fw_version(struct a96t3x6_data *data, bool bootmode)
{
struct i2c_client *client = data->client;
u8 buf;
int ret;
int retry = 3;
grip_always_active(data, 1);
ret = a96t3x6_i2c_read(client, REG_FW_VER, &buf, 1);
if (ret < 0) {
while (retry--) {
GRIP_ERR("read fail(%d)\n", retry);
if (!bootmode)
a96t3x6_reset(data);
else
goto err_grip_revert_mode;
ret = a96t3x6_i2c_read(client, REG_FW_VER, &buf, 1);
if (ret == 0)
break;
}
if (retry <= 0)
goto err_grip_revert_mode;
}
data->fw_ver = buf;
retry = 3;
ret = a96t3x6_i2c_read(client, REG_MODEL_NO, &buf, 1);
if (ret < 0) {
while (retry--) {
GRIP_ERR("read fail(%d)\n", retry);
if (!bootmode)
a96t3x6_reset(data);
else
goto err_grip_revert_mode;
ret = a96t3x6_i2c_read(client, REG_MODEL_NO, &buf, 1);
if (ret == 0)
break;
}
if (retry <= 0)
goto err_grip_revert_mode;
}
data->md_ver = buf;
if (data->identity_number) {
retry = 3;
ret = a96t3x6_i2c_read(client, REG_ID_NO, &buf, 1);
if (ret < 0) {
while (retry--) {
GRIP_ERR("read fail(%d)\n", retry);
if (!bootmode)
a96t3x6_reset(data);
else
goto err_grip_revert_mode;
ret = a96t3x6_i2c_read(client, REG_ID_NO, &buf, 1);
if (ret == 0)
break;
}
if (retry <= 0)
goto err_grip_revert_mode;
}
data->id_ver = buf;
GRIP_INFO(" fw = 0x%x, md = 0x%x, id = 0x%x\n", data->fw_ver, data->md_ver, data->id_ver);
} else
GRIP_INFO(" fw = 0x%x, md = 0x%x\n", data->fw_ver, data->md_ver);
grip_always_active(data, 0);
return 0;
err_grip_revert_mode:
grip_always_active(data, 0);
return -1;
}
static ssize_t read_fw_ver(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct a96t3x6_data *data = dev_get_drvdata(dev);
int ret;
ret = a96t3x6_get_fw_version(data, false);
if (ret < 0) {
GRIP_ERR("read fail\n");
data->fw_ver = 0;
}
if (data->identity_number)
return snprintf(buf, PAGE_SIZE, "0x%02x%02x\n", data->id_ver, data->fw_ver);
else
return snprintf(buf, PAGE_SIZE, "0x%02x%02x\n", data->md_ver, data->fw_ver);
}
static int a96t3x6_load_fw_kernel(struct a96t3x6_data *data)
{
int ret = 0;
ret = request_firmware(&data->firm_data_bin,
data->fw_path, &data->client->dev);
if (ret) {
GRIP_ERR("request_firmware fail.\n");
return ret;
}
data->firm_size = data->firm_data_bin->size;
data->fw_ver_bin = data->firm_data_bin->data[5];
data->md_ver_bin = data->firm_data_bin->data[1];
GRIP_INFO("fw = 0x%x, md = 0x%x\n", data->fw_ver_bin, data->md_ver_bin);
data->checksum_h_bin = data->firm_data_bin->data[8];
data->checksum_l_bin = data->firm_data_bin->data[9];
GRIP_INFO("crc 0x%x 0x%x\n", data->checksum_h_bin, data->checksum_l_bin);
return ret;
}
static int a96t3x6_load_fw(struct a96t3x6_data *data, u8 cmd)
{
struct file *fp;
mm_segment_t old_fs;
long fsize, nread;
int ret = 0;
switch (cmd) {
case BUILT_IN:
break;
case SDCARD:
old_fs = get_fs();
set_fs(get_ds());
fp = filp_open(TK_FW_PATH_SDCARD, O_RDONLY, 0400);
if (IS_ERR(fp)) {
GRIP_ERR("%s open error (%d)\n", TK_FW_PATH_SDCARD, (int)PTR_ERR(fp));
ret = -ENOENT;
goto fail_sdcard_open;
}
fsize = fp->f_path.dentry->d_inode->i_size;
data->firm_data_ums = kzalloc((size_t)fsize, GFP_KERNEL);
if (!data->firm_data_ums) {
GRIP_ERR("fail to kzalloc for fw\n");
ret = -ENOMEM;
goto fail_sdcard_kzalloc;
}
nread = vfs_read(fp,
(char __user *)data->firm_data_ums, fsize, &fp->f_pos);
if (nread != fsize) {
GRIP_ERR("fail to vfs_read file\n");
ret = -EINVAL;
goto fail_sdcard_size;
}
filp_close(fp, current->files);
set_fs(old_fs);
data->firm_size = nread;
break;
default:
ret = -1;
break;
}
GRIP_INFO("fw_size : %lu, success\n", data->firm_size);
return ret;
fail_sdcard_size:
kfree(&data->firm_data_ums);
fail_sdcard_kzalloc:
filp_close(fp, current->files);
fail_sdcard_open:
set_fs(old_fs);
return ret;
}
static int a96t3x6_check_busy(struct a96t3x6_data *data)
{
int ret, count = 0;
unsigned char val = 0x00;
do {
ret = i2c_master_recv(data->client, &val, sizeof(val));
if (val)
count++;
else
break;
if (count > 1000)
break;
} while (1);
if (count > 1000)
GRIP_ERR("busy %d\n", count);
return ret;
}
static int a96t3x6_i2c_read_checksum(struct a96t3x6_data *data)
{
unsigned char buf[6] = {0xAC, 0x9E, 0x10, 0x00, 0x3F, 0xFF};
unsigned char buf2[1] = {0x00};
unsigned char checksum[6] = {0, };
int ret;
i2c_master_send(data->client, buf, 6);
usleep_range(5000, 6000);
i2c_master_send(data->client, buf2, 1);
usleep_range(5000, 6000);
ret = a96t3x6_i2c_read_data(data->client, checksum, 6);
GRIP_INFO("ret:%d [%X][%X][%X][%X][%X]\n", ret,
checksum[0], checksum[1], checksum[2], checksum[4], checksum[5]);
data->checksum_h = checksum[4];
data->checksum_l = checksum[5];
return 0;
}
#ifdef CONFIG_SENSORS_A96T3X6_CRC_CHECK
static int a96t3x6_crc_check(struct a96t3x6_data *data)
{
unsigned char cmd = 0xAA;
unsigned char val = 0xFF;
unsigned char retry = 2;
int ret;
/*
* abov grip fw uses active/deactive mode in each period
* To check crc check, make the mode as always active mode.
*/
grip_always_active(data, 1);
/* crc check */
ret = a96t3x6_i2c_write(data->client, REG_FW_VER, &cmd);
if (ret < 0) {
GRIP_INFO("crc checking enter failed\n");
grip_always_active(data, 0);
return ret;
}
data->crc_check = CRC_FAIL;
while (retry--) {
msleep(400);
ret = a96t3x6_i2c_read(data->client, REG_FW_VER, &val, 1);
if (ret < 0) {
GRIP_INFO("crc read failed\n");
continue;
}
ret = (int)val;
if (val == 0x00) {
GRIP_INFO("crc check fail 0x%2x\n", val);
} else {
/* only success route */
data->crc_check = CRC_PASS;
GRIP_INFO("crc check normal 0x%2x\n", val);
break;
}
}
grip_always_active(data, 0);
return ret;
}
#endif
static int a96t3x6_fw_write(struct a96t3x6_data *data, unsigned char *addrH,
unsigned char *addrL, unsigned char *val)
{
int length = 36, ret = 0;
unsigned char buf[36];
buf[0] = 0xAC;
buf[1] = 0x7A;
memcpy(&buf[2], addrH, 1);
memcpy(&buf[3], addrL, 1);
memcpy(&buf[4], val, 32);
ret = i2c_master_send(data->client, buf, length);
if (ret != length) {
GRIP_ERR("write fail[%x%x], %d\n", *addrH, *addrL, ret);
return ret;
}
usleep_range(3000, 3000);
a96t3x6_check_busy(data);
return 0;
}
static int a96t3x6_fw_mode_enter(struct a96t3x6_data *data)
{
unsigned char buf[2] = {0xAC, 0x5B};
u8 cmd = 0;
int ret = 0;
GRIP_INFO("cmd send\n");
ret = i2c_master_send(data->client, buf, 2);
if (ret != 2) {
GRIP_ERR("write fail\n");
return -1;
}
ret = i2c_master_recv(data->client, &cmd, 1);
GRIP_INFO("cmd receive %2x, %2x\n", data->firmup_cmd, cmd);
if (data->firmup_cmd != cmd) {
GRIP_ERR("cmd not matched, firmup fail (ret = %d)\n", ret);
return -2;
}
return 0;
}
static int a96t3x6_flash_erase(struct a96t3x6_data *data)
{
unsigned char buf[2] = {0xAC, 0x2D};
int ret = 0;
ret = i2c_master_send(data->client, buf, 2);
if (ret != 2) {
GRIP_ERR("write fail\n");
return -1;
}
return 0;
}
static int a96t3x6_fw_mode_exit(struct a96t3x6_data *data)
{
unsigned char buf[2] = {0xAC, 0xE1};
int ret = 0;
ret = i2c_master_send(data->client, buf, 2);
if (ret != 2) {
GRIP_ERR("write fail\n");
return -1;
}
usleep_range(RESET_DELAY, RESET_DELAY);
return 0;
}
static int a96t3x6_fw_update(struct a96t3x6_data *data, u8 cmd)
{
int ret, i = 0;
int count;
int retry = 5;
unsigned short address;
unsigned char addrH, addrL;
unsigned char buf[32] = {0, };
GRIP_INFO("start\n");
count = data->firm_size / 32;
address = 0x800;
while(retry > 0) {
a96t3x6_reset_for_bootmode(data);
usleep_range(BOOT_DELAY, BOOT_DELAY);
ret = a96t3x6_fw_mode_enter(data);
if (ret < 0)
GRIP_ERR("a96t3x6_fw_mode_enter fail, retry : %d\n", i);
else
break;
retry--;
}
if(ret < 0 && retry == 0) {
GRIP_ERR("a96t3x6_fw_mode_enter fail\n");
return ret;
}
usleep_range(5000, 5000);
GRIP_INFO("fw_mode_cmd sent\n");
ret = a96t3x6_flash_erase(data);
usleep_range(FLASH_DELAY, FLASH_DELAY);
GRIP_INFO("fw_write start\n");
for (i = 1; i < count; i++) {
/* first 32byte is header */
addrH = (unsigned char)((address >> 8) & 0xFF);
addrL = (unsigned char)(address & 0xFF);
if (cmd == BUILT_IN)
memcpy(buf, &data->firm_data_bin->data[i * 32], 32);
else if (cmd == SDCARD)
memcpy(buf, &data->firm_data_ums[i * 32], 32);
ret = a96t3x6_fw_write(data, &addrH, &addrL, buf);
if (ret < 0) {
GRIP_ERR("err, no device : %d\n", ret);
return ret;
}
address += 0x20;
memset(buf, 0, 32);
}
ret = a96t3x6_i2c_read_checksum(data);
GRIP_INFO("checksum read%d\n", ret);
ret = a96t3x6_fw_mode_exit(data);
GRIP_INFO("fw_write end\n");
return ret;
}
static void a96t3x6_release_fw(struct a96t3x6_data *data, u8 cmd)
{
switch (cmd) {
case BUILT_IN:
release_firmware(data->firm_data_bin);
break;
case SDCARD:
kfree(data->firm_data_ums);
break;
default:
break;
}
}
static int a96t3x6_flash_fw(struct a96t3x6_data *data, bool probe, u8 cmd)
{
int retry = 2;
int ret;
int block_count;
const u8 *fw_data;
ret = a96t3x6_get_fw_version(data, probe);
if (ret)
data->fw_ver = 0;
ret = a96t3x6_load_fw(data, cmd);
if (ret) {
GRIP_ERR("fw load fail\n");
return ret;
}
switch (cmd) {
case BUILT_IN:
fw_data = data->firm_data_bin->data;
break;
case SDCARD:
fw_data = data->firm_data_ums;
break;
default:
return -1;
}
block_count = (int)(data->firm_size / 32);
while (retry--) {
ret = a96t3x6_fw_update(data, cmd);
if (ret < 0)
break;
if (cmd == BUILT_IN) {
if ((data->checksum_h != data->checksum_h_bin) ||
(data->checksum_l != data->checksum_l_bin)) {
GRIP_ERR("checksum fail.(0x%x,0x%x),(0x%x,0x%x) retry:%d\n",
data->checksum_h, data->checksum_l,
data->checksum_h_bin, data->checksum_l_bin, retry);
ret = -1;
continue;
}
}
a96t3x6_reset_for_bootmode(data);
usleep_range(RESET_DELAY, RESET_DELAY);
ret = a96t3x6_get_fw_version(data, true);
if (ret) {
GRIP_ERR("fw version read fail\n");
ret = -1;
continue;
}
if (data->fw_ver == 0) {
GRIP_ERR("fw version fail (0x%x)\n", data->fw_ver);
ret = -1;
continue;
}
if ((cmd == BUILT_IN) && (data->fw_ver != data->fw_ver_bin)) {
GRIP_ERR("fw version fail 0x%x, 0x%x\n",
data->fw_ver, data->fw_ver_bin);
ret = -1;
continue;
}
ret = 0;
break;
}
a96t3x6_release_fw(data, cmd);
return ret;
}
static void grip_always_active(struct a96t3x6_data *data, int on)
{
int ret, retry = 3;
u8 cmd, r_buf;
GRIP_INFO("Grip always active mode %d\n", on);
if (on == 1)
cmd = CMD_ON;
else
cmd = CMD_OFF;
while (retry--) {
ret = a96t3x6_i2c_write(data->client, REG_GRIP_ALWAYS_ACTIVE, &cmd);
if (ret < 0) {
GRIP_INFO("i2c write fail(%d)\n", ret);
continue;
}
msleep(20);
ret = a96t3x6_i2c_read(data->client, REG_GRIP_ALWAYS_ACTIVE, &r_buf, 1);
if (ret < 0) {
GRIP_INFO("i2c read fail(%d)\n", ret);
continue;
}
if ((cmd == CMD_ON && r_buf == GRIP_ALWAYS_ACTIVE_READY) ||
(cmd == CMD_OFF && r_buf == CMD_OFF))
break;
else
GRIP_INFO("Wrong value 0x%x, retry again %d\n", r_buf, retry);
}
if (retry < 0)
GRIP_INFO("failed to change grip always active mode\n");
else
GRIP_INFO("cmd 0x%x, return 0x%x\n", cmd, r_buf);
}
static ssize_t grip_fw_update(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
struct a96t3x6_data *data = dev_get_drvdata(dev);
int ret;
u8 cmd;
switch (*buf) {
case 's':
case 'S':
cmd = BUILT_IN;
break;
case 'i':
case 'I':
cmd = SDCARD;
break;
default:
data->fw_update_state = 2;
goto fw_update_out;
}
data->fw_update_state = 1;
disable_irq(data->irq);
data->enabled = false;
if (cmd == BUILT_IN) {
ret = a96t3x6_load_fw_kernel(data);
if (ret) {
GRIP_ERR("failed to load firmware(%d)\n", ret);
goto fw_update_out;
} else {
GRIP_INFO("fw version read success (%d)\n", ret);
}
}
ret = a96t3x6_flash_fw(data, false, cmd);
data->enabled = true;
enable_irq(data->irq);
if (ret) {
GRIP_ERR("failed to flash firmware(%d)\n", ret);
data->fw_update_state = 2;
} else {
GRIP_INFO("success\n");
data->fw_update_state = 0;
}
if (data->current_state) {
cmd = CMD_ON;
ret = a96t3x6_i2c_write(data->client, REG_SAR_ENABLE, &cmd);
if (ret < 0)
GRIP_INFO("failed to enable grip irq\n");
a96t3x6_check_first_status(data, 1);
}
#if defined(CONFIG_SENSOR_A96T3X6_LDO_SHARE)
GRIP_INFO("a96t3x6 register recovery\n");
input_report_rel(data->input_dev, REL_WHEEL, 1);
input_sync(data->input_dev);
#endif
fw_update_out:
GRIP_INFO("fw_update_state = %d\n", data->fw_update_state);
return count;
}
static ssize_t grip_fw_update_status(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct a96t3x6_data *data = dev_get_drvdata(dev);
int count = 0;
GRIP_INFO("%d\n", data->fw_update_state);
if (data->fw_update_state == 0)
count = snprintf(buf, PAGE_SIZE, "PASS\n");
else if (data->fw_update_state == 1)
count = snprintf(buf, PAGE_SIZE, "Downloading\n");
else if (data->fw_update_state == 2)
count = snprintf(buf, PAGE_SIZE, "Fail\n");
return count;
}
static ssize_t grip_irq_state_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct a96t3x6_data *data = dev_get_drvdata(dev);
int status = 0;
status = gpio_get_value(data->grip_int);
GRIP_INFO("status=%d\n", status);
return snprintf(buf, PAGE_SIZE, "%d\n", status);
}
static ssize_t grip_irq_en_cnt_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct a96t3x6_data *data = dev_get_drvdata(dev);
GRIP_INFO("irq_en_cnt=%d\n", data->irq_en_cnt);
return snprintf(buf, PAGE_SIZE, "%d\n", data->irq_en_cnt);
}
static ssize_t grip_reg_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
u8 val = 0;
int offset = 0, i = 0;
struct a96t3x6_data *data = dev_get_drvdata(dev);
for (i = 0; i < 128; i++) {
a96t3x6_i2c_read(data->client, i, &val, 1);
GRIP_INFO("reg=%02X val=%02X\n", i, val);
offset += snprintf(buf + offset, PAGE_SIZE - offset,
"reg=0x%x val=0x%x\n", i, val);
}
return offset;
}
static ssize_t grip_reg_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t size)
{
int regist = 0, val = 0;
u8 cmd = 0;
struct a96t3x6_data *data = dev_get_drvdata(dev);
if (sscanf(buf, "%4x,%4x", &regist, &val) != 2) {
GRIP_ERR("The number of data are wrong\n");
return -EINVAL;
}
GRIP_INFO("reg=0x%2x value=0x%2x\n", regist, val);
cmd = (u8) val;
a96t3x6_i2c_write(data->client, (u8)regist, &cmd);
return size;
}
static ssize_t grip_crc_check_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct a96t3x6_data *data = dev_get_drvdata(dev);
#ifndef CONFIG_SENSORS_A96T3X6_CRC_CHECK
int ret;
unsigned char cmd[3] = {0x1B, 0x00, 0x10};
unsigned char checksum[2] = {0, };
i2c_master_send(data->client, cmd, 3);
usleep_range(50 * 1000, 50 * 1000);
ret = a96t3x6_i2c_read(data->client, 0x1B, checksum, 2);
if (ret < 0) {
GRIP_ERR("i2c read fail\n");
return snprintf(buf, PAGE_SIZE, "NG,0000\n");
}
GRIP_INFO("CRC:%02x%02x, BIN:%02x%02x\n", checksum[0], checksum[1],
data->checksum_h_bin, data->checksum_l_bin);
if ((checksum[0] != data->checksum_h_bin) ||
(checksum[1] != data->checksum_l_bin))
return snprintf(buf, PAGE_SIZE, "NG,%02x%02x\n",
checksum[0], checksum[1]);
else
return snprintf(buf, PAGE_SIZE, "OK,%02x%02x\n",
checksum[0], checksum[1]);
#else
{
int val;
val = a96t3x6_crc_check(data);
if (data->crc_check == CRC_PASS)
return snprintf(buf, PAGE_SIZE, "OK,%02x\n", val);
else
return snprintf(buf, PAGE_SIZE, "NG,%02x\n", val);
}
#endif
}
static ssize_t a96t3x6_enable_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct a96t3x6_data *data = dev_get_drvdata(dev);
return snprintf(buf, PAGE_SIZE, "%d\n", data->current_state);
}
#if defined(CONFIG_SENSOR_A96T3X6_LDO_SHARE)
static ssize_t grip_register_recover_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t size)
{
struct a96t3x6_data *data = dev_get_drvdata(dev);
int ret = 0;
u8 reg_value = 0;
u8 cmd = 0;
u8 check = 0;
GRIP_INFO("Register recover\n");
ret = kstrtou8(buf, 10, &check);
if(check == 1)
{
//register reset
ret = a96t3x6_i2c_read(data->client, REG_SAR_ENABLE, &reg_value, 1);
if (ret < 0) {
GRIP_ERR("fail(%d)\n", ret);
return size;
}
GRIP_INFO("reg=0x24 val=%02X\n", reg_value);
if(data->current_state) {
if(reg_value == CMD_OFF) {
GRIP_ERR("REG_SAR_ENABLE register recover after HW reset\n");
cmd = CMD_ON;
ret = a96t3x6_i2c_write(data->client, REG_SAR_ENABLE, &cmd);
if (ret < 0)
GRIP_INFO("failed to enable grip irq\n");
a96t3x6_check_first_status(data, 1);
}
}
ret = a96t3x6_i2c_read(data->client, REG_SAR_SENSING, &reg_value, 1);
if (ret < 0) {
GRIP_ERR("fail(%d)\n", ret);
return size;
}
GRIP_INFO("reg=0x25 val=%02X\n", reg_value);
if (data->earjack_noise)
{
if(!data->current_state && data->earjack)
{
if(reg_value == CMD_ON)
{
GRIP_ERR("REG_SAR_SENSING register recover after HW reset\n");
cmd = CMD_OFF;
ret = a96t3x6_i2c_write(data->client, REG_SAR_SENSING, &cmd);
if (ret < 0)
GRIP_INFO("failed to enable grip irq\n");
a96t3x6_check_first_status(data, 1);
}
}
}
}
else {
GRIP_INFO("Unsupported Command\n");
}
return size;
}
#endif
static DEVICE_ATTR(grip_threshold, 0444, grip_threshold_show, NULL);
static DEVICE_ATTR(grip_total_cap, 0444, grip_total_cap_show, NULL);
static DEVICE_ATTR(grip_sar_enable, 0664, grip_sar_enable_show,
grip_sar_enable_store);
static DEVICE_ATTR(grip_sw_reset_ready, 0444, grip_sw_reset_ready_show, NULL);
static DEVICE_ATTR(grip_sw_reset, 0220, NULL, grip_sw_reset);
static DEVICE_ATTR(grip_earjack, 0220, NULL, grip_sensing_change);
static DEVICE_ATTR(grip, 0444, grip_show, NULL);
static DEVICE_ATTR(grip_baseline, 0444, grip_baseline_show, NULL);
static DEVICE_ATTR(grip_raw, 0444, grip_raw_show, NULL);
static DEVICE_ATTR(grip_gain, 0444, grip_gain_show, NULL);
static DEVICE_ATTR(grip_check, 0444, grip_check_show, NULL);
#ifndef CONFIG_SAMSUNG_PRODUCT_SHIP
static DEVICE_ATTR(grip_sar_only_mode, 0220, NULL, grip_mode_change);
static DEVICE_ATTR(grip_sar_press_threshold, 0220,
NULL, grip_sar_press_threshold_store);
static DEVICE_ATTR(grip_sar_release_threshold, 0220,
NULL, grip_sar_release_threshold_store);
#endif
#ifdef CONFIG_SEC_FACTORY
static DEVICE_ATTR(grip_irq_count, 0664, a96t3x6_irq_count_show,
a96t3x6_irq_count_store);
#endif
static DEVICE_ATTR(name, 0444, grip_name_show, NULL);
static DEVICE_ATTR(vendor, 0444, grip_vendor_show, NULL);
static DEVICE_ATTR(grip_firm_version_phone, 0444, bin_fw_ver, NULL);
static DEVICE_ATTR(grip_firm_version_panel, 0444, read_fw_ver, NULL);
static DEVICE_ATTR(grip_firm_update, 0220, NULL, grip_fw_update);
static DEVICE_ATTR(grip_firm_update_status, 0444, grip_fw_update_status, NULL);
static DEVICE_ATTR(grip_irq_state, 0444, grip_irq_state_show, NULL);
static DEVICE_ATTR(grip_irq_en_cnt, 0444, grip_irq_en_cnt_show, NULL);
static DEVICE_ATTR(grip_reg_rw, 0664, grip_reg_show, grip_reg_store);
static DEVICE_ATTR(grip_crc_check, 0444, grip_crc_check_show, NULL);
#ifdef CONFIG_SENSOR_A96T3X6_LDO_SHARE
static DEVICE_ATTR(grip_register_recover, 0220, NULL, grip_register_recover_store);
#endif
static struct device_attribute *grip_sensor_attributes[] = {
&dev_attr_grip_threshold,
&dev_attr_grip_total_cap,
&dev_attr_grip_sar_enable,
&dev_attr_grip_sw_reset,
&dev_attr_grip_sw_reset_ready,
&dev_attr_grip_earjack,
&dev_attr_grip,
&dev_attr_grip_baseline,
&dev_attr_grip_raw,
&dev_attr_grip_gain,
&dev_attr_grip_check,
#ifndef CONFIG_SAMSUNG_PRODUCT_SHIP
&dev_attr_grip_sar_only_mode,
&dev_attr_grip_sar_press_threshold,
&dev_attr_grip_sar_release_threshold,
#endif
#ifdef CONFIG_SEC_FACTORY
&dev_attr_grip_irq_count,
#endif
&dev_attr_name,
&dev_attr_vendor,
&dev_attr_grip_firm_version_phone,
&dev_attr_grip_firm_version_panel,
&dev_attr_grip_firm_update,
&dev_attr_grip_firm_update_status,
&dev_attr_grip_irq_state,
&dev_attr_grip_irq_en_cnt,
&dev_attr_grip_reg_rw,
&dev_attr_grip_crc_check,
#ifdef CONFIG_SENSOR_A96T3X6_LDO_SHARE
&dev_attr_grip_register_recover,
#endif
NULL,
};
static DEVICE_ATTR(enable, 0664, a96t3x6_enable_show, grip_sar_enable_store);
static struct attribute *a96t3x6_attributes[] = {
&dev_attr_enable.attr,
NULL
};
static struct attribute_group a96t3x6_attribute_group = {
.attrs = a96t3x6_attributes
};
static int a96t3x6_fw_check(struct a96t3x6_data *data)
{
int ret;
bool force = false;
if (data->bringup) {
GRIP_INFO("bring up mode. skip firmware check\n");
return 0;
}
ret = a96t3x6_load_fw_kernel(data);
if (ret) {
#ifdef CONFIG_SENSORS_FW_VENDOR
GRIP_ERR("fw was not loaded yet from ueventd\n");
return ret;
#else
GRIP_ERR("failed load_fw_kernel(%d)\n", ret);
#endif
} else
GRIP_INFO("fw version read success (%d)\n", ret);
ret = a96t3x6_get_fw_version(data, true);
if (ret)
GRIP_ERR("i2c fail(%d), addr[%d]\n", ret, data->client->addr);
if (data->md_ver != data->md_ver_bin) {
GRIP_ERR("MD version is different.(IC %x, BN %x). Do force FW update\n",
data->md_ver, data->md_ver_bin);
force = true;
}
if (data->fw_ver < data->fw_ver_bin || data->fw_ver > TEST_FIRMWARE_DETECT_VER
|| force == true || data->crc_check == CRC_FAIL) {
GRIP_ERR("excute fw update (0x%x -> 0x%x)\n",
data->fw_ver, data->fw_ver_bin);
ret = a96t3x6_flash_fw(data, true, BUILT_IN);
if (ret)
GRIP_ERR("failed to a96t3x6_flash_fw (%d)\n", ret);
else
GRIP_INFO("fw update success\n");
}
return ret;
}
static int a96t3x6_power_onoff(void *pdata, bool on)
{
struct a96t3x6_data *data = (struct a96t3x6_data *)pdata;
int ret = 0;
if (data->ldo_en) {
ret = gpio_request(data->ldo_en, "a96t3x6_ldo_en");
if (ret < 0) {
GRIP_ERR("gpio %d request failed %d\n", data->ldo_en, ret);
return ret;
}
gpio_set_value(data->ldo_en, on);
GRIP_INFO("ldo_en power %d\n", on);
gpio_free(data->ldo_en);
}
data->dvdd_vreg = regulator_get(NULL, "vtouch_2.8v");
if (IS_ERR(data->dvdd_vreg)) {
data->dvdd_vreg = NULL;
GRIP_ERR("dvdd_vreg get error, ignoring\n");
}
if (on) {
if (data->dvdd_vreg) {
ret = regulator_enable(data->dvdd_vreg);
if (ret) {
GRIP_ERR("dvdd reg enable fail\n");
return ret;
}
}
} else {
if (data->dvdd_vreg) {
ret = regulator_disable(data->dvdd_vreg);
if (ret) {
GRIP_ERR("dvdd reg disable fail\n");
return ret;
}
}
}
regulator_put(data->dvdd_vreg);
GRIP_INFO("%s\n", on ? "on" : "off");
return ret;
}
static int a96t3x6_irq_init(struct device *dev,
struct a96t3x6_data *data)
{
int ret = 0;
ret = gpio_request(data->grip_int, "a96t3x6_IRQ");
if (ret < 0) {
GRIP_ERR("gpio %d request failed (%d)\n", data->grip_int, ret);
return ret;
}
ret = gpio_direction_input(data->grip_int);
if (ret < 0) {
GRIP_ERR("failed to set direction input gpio %d(%d)\n",
data->grip_int, ret);
gpio_free(data->grip_int);
return ret;
}
// assigned power function to function ptr
data->power = a96t3x6_power_onoff;
return ret;
}
static int a96t3x6_parse_dt(struct a96t3x6_data *data, struct device *dev)
{
struct device_node *np = dev->of_node;
struct pinctrl *p;
int ret;
enum of_gpio_flags flags;
data->grip_int = of_get_named_gpio(np, "a96t3x6,irq_gpio", 0);
if (data->grip_int < 0) {
GRIP_ERR("Cannot get grip_int\n");
return data->grip_int;
}
data->ldo_en = of_get_named_gpio_flags(np, "a96t3x6,ldo_en", 0, &flags);
if (data->ldo_en < 0) {
GRIP_ERR("fail to get ldo_en\n");
data->ldo_en = 0;
} else {
ret = gpio_request(data->ldo_en, "a96t3x6_ldo_en");
if (ret < 0) {
GRIP_ERR("gpio %d request failed %d\n", data->ldo_en, ret);
return ret;
}
gpio_direction_output(data->ldo_en, 1);
gpio_free(data->ldo_en);
}
ret = of_property_read_string(np, "a96t3x6,fw_path", (const char **)&data->fw_path);
if (ret < 0) {
GRIP_ERR("failed to read fw_path %d\n", ret);
data->fw_path = TK_FW_PATH_BIN;
}
GRIP_INFO("fw path %s\n", data->fw_path);
data->bringup = of_property_read_bool(np, "a96t3x6,bringup");
ret = of_property_read_u32(np, "a96t3x6,firmup_cmd", &data->firmup_cmd);
if (ret < 0)
data->firmup_cmd = 0;
ret = of_property_read_u32(np, "a96t3x6,earjack_noise", &data->earjack_noise);
if (ret < 0)
data->earjack_noise = 0;
ret = of_property_read_u32(np, "a96t3x6,identity_number", &data->identity_number);
if (ret < 0)
data->identity_number = 0;
p = pinctrl_get_select_default(dev);
if (IS_ERR(p)) {
GRIP_INFO("failed pinctrl_get\n");
}
GRIP_INFO("grip_int:%d, ldo_en:%d\n", data->grip_int, data->ldo_en);
return 0;
}
#if defined(CONFIG_CCIC_NOTIFIER) && defined(CONFIG_USB_TYPEC_MANAGER_NOTIFIER)
static int a96t3x6_ccic_handle_notification(struct notifier_block *nb,
unsigned long action, void *data)
{
CC_NOTI_ATTACH_TYPEDEF usb_typec_info = *(CC_NOTI_ATTACH_TYPEDEF *)data;
struct a96t3x6_data *grip_data =
container_of(nb, struct a96t3x6_data, cpuidle_ccic_nb);
u8 cmd = CMD_ON;
if (usb_typec_info.src != CCIC_NOTIFY_DEV_MUIC ||
usb_typec_info.dest != CCIC_NOTIFY_DEV_BATTERY)
return 0;
GRIP_INFO("(%ld) %01x, %01x, %02x, %04x, %04x, %04x\n",
action, usb_typec_info.src, usb_typec_info.dest,
usb_typec_info.id, usb_typec_info.attach, usb_typec_info.rprd,
usb_typec_info.cable_type);
if (usb_typec_info.cable_type == 0)
return 0;
switch (usb_typec_info.cable_type) {
case ATTACHED_DEV_NONE_MUIC:
case ATTACHED_DEV_JIG_UART_OFF_MUIC:
case ATTACHED_DEV_JIG_UART_OFF_VB_MUIC: /* VBUS enabled */
case ATTACHED_DEV_JIG_UART_OFF_VB_OTG_MUIC: /* for otg test */
case ATTACHED_DEV_JIG_UART_OFF_VB_FG_MUIC: /* for fuelgauge test */
case ATTACHED_DEV_JIG_UART_ON_MUIC:
case ATTACHED_DEV_JIG_UART_ON_VB_MUIC: /* VBUS enabled */
case ATTACHED_DEV_JIG_USB_OFF_MUIC:
case ATTACHED_DEV_JIG_USB_ON_MUIC:
GRIP_INFO("skip cable = %u, attach = %u\n",
usb_typec_info.cable_type, usb_typec_info.attach);
break;
default:
if (usb_typec_info.attach == MUIC_NOTIFY_CMD_ATTACH) {
cmd = CMD_OFF;
a96t3x6_i2c_write(grip_data->client, REG_TSPTA, &cmd);
GRIP_INFO("TA/USB is inserted\n");
} else if (usb_typec_info.attach == MUIC_NOTIFY_CMD_DETACH) {
cmd = CMD_ON;
a96t3x6_i2c_write(grip_data->client, REG_TSPTA, &cmd);
GRIP_INFO("TA/USB is removed\n");
}
break;
}
return 0;
}
#elif defined(CONFIG_MUIC_NOTIFIER)
static int a96t3x6_cpuidle_muic_notifier(struct notifier_block *nb,
unsigned long action, void *data)
{
struct a96t3x6_data *grip_data;
u8 cmd = CMD_ON;
muic_attached_dev_t attached_dev = *(muic_attached_dev_t *)data;
grip_data = container_of(nb, struct a96t3x6_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) {
cmd = CMD_OFF;
GRIP_INFO("TA/USB is inserted\n");
}
else if (action == MUIC_NOTIFY_CMD_DETACH) {
cmd = CMD_ON;
GRIP_INFO("TA/USB is removed\n");
}
a96t3x6_i2c_write(grip_data->client, REG_TSPTA, &cmd);
break;
default:
break;
}
GRIP_INFO("dev=%d, action=%lu\n", attached_dev, action);
return NOTIFY_DONE;
}
#endif
static int a96t3x6_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct a96t3x6_data *data;
struct input_dev *input_dev;
int ret;
GRIP_INFO("start (0x%x)\n", client->addr);
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
GRIP_ERR("i2c_check_functionality fail\n");
return -EIO;
}
data = kzalloc(sizeof(struct a96t3x6_data), GFP_KERNEL);
if (!data) {
GRIP_ERR("Failed to allocate memory\n");
ret = -ENOMEM;
goto err_alloc;
}
input_dev = input_allocate_device();
if (!input_dev) {
GRIP_ERR("Failed to allocate memory for input device\n");
ret = -ENOMEM;
goto err_input_alloc;
}
data->client = client;
data->input_dev = input_dev;
data->probe_done = false;
data->earjack = 0;
data->current_state = false;
data->expect_state = false;
data->skip_event = false;
data->sar_mode = false;
data->crc_check = CRC_PASS;
wake_lock_init(&data->grip_wake_lock, WAKE_LOCK_SUSPEND, "grip wake lock");
ret = a96t3x6_parse_dt(data, &client->dev);
if (ret) {
GRIP_ERR("failed to a96t3x6_parse_dt\n");
goto err_config;
}
ret = a96t3x6_irq_init(&client->dev, data);
if (ret) {
GRIP_ERR("failed to init reg\n");
goto err_config;
}
if (data->power) {
data->power(data, true);
usleep_range(RESET_DELAY, RESET_DELAY);
}
data->irq = -1;
client->irq = gpio_to_irq(data->grip_int);
mutex_init(&data->lock);
i2c_set_clientdata(client, data);
#ifndef CONFIG_SENSORS_FW_VENDOR
#if defined(CONFIG_SEC_FACTORY) && defined(CONFIG_SENSORS_A96T3X6_CRC_CHECK)
a96t3x6_crc_check(data);
#endif
ret = a96t3x6_fw_check(data);
if (ret) {
GRIP_ERR("failed to firmware check (%d)\n", ret);
goto err_reg_input_dev;
}
#else
{
/*
* Add probe fail routine if i2c is failed
* non fw IC returns 0 from ALL register but i2c is success.
*/
u8 buf;
ret = a96t3x6_i2c_read(client, REG_MODEL_NO, &buf, 1);
if (ret) {
GRIP_ERR("i2c is failed %d\n", ret);
goto err_reg_input_dev;
} else {
GRIP_INFO("i2c is normal, model_no = 0x%2x\n", buf);
}
}
#endif
input_dev->name = MODULE_NAME;
input_dev->id.bustype = BUS_I2C;
input_set_capability(input_dev, EV_REL, REL_MISC);
#ifdef CONFIG_SENSOR_A96T3X6_LDO_SHARE
input_set_capability(input_dev, EV_REL, REL_WHEEL);
#endif
input_set_drvdata(input_dev, data);
INIT_DELAYED_WORK(&data->debug_work, a96t3x6_debug_work_func);
#ifdef CONFIG_SENSORS_FW_VENDOR
INIT_DELAYED_WORK(&data->firmware_work, a96t3x6_firmware_work_func);
#endif
ret = input_register_device(input_dev);
if (ret) {
input_free_device(input_dev);
GRIP_ERR("failed to register input dev (%d)\n",
ret);
goto err_reg_input_dev;
}
ret = sensors_create_symlink(input_dev);
if (ret < 0) {
GRIP_ERR("Failed to create sysfs symlink\n");
goto err_sysfs_symlink;
}
ret = sysfs_create_group(&data->input_dev->dev.kobj,
&a96t3x6_attribute_group);
if (ret < 0) {
GRIP_ERR("Failed to create sysfs group\n");
goto err_sysfs_group;
}
ret = sensors_register(data->dev, data, grip_sensor_attributes,
MODULE_NAME);
if (ret) {
GRIP_ERR("could not register grip_sensor(%d)\n", ret);
goto err_sensor_register;
}
data->enabled = true;
ret = request_threaded_irq(client->irq, NULL, a96t3x6_interrupt,
IRQF_TRIGGER_LOW | IRQF_ONESHOT, MODEL_NAME, data);
disable_irq(client->irq);
if (ret < 0) {
GRIP_ERR("Failed to register interrupt\n");
goto err_req_irq;
}
data->irq = client->irq;
data->dev = &client->dev;
device_init_wakeup(&client->dev, true);
a96t3x6_set_debug_work(data, 1, 20000);
#ifdef CONFIG_SENSORS_FW_VENDOR
a96t3x6_set_firmware_work(data, 1, 1);
#endif
#if defined(CONFIG_USB_TYPEC_MANAGER_NOTIFIER) && defined(CONFIG_CCIC_NOTIFIER)
manager_notifier_register(&data->cpuidle_ccic_nb,
a96t3x6_ccic_handle_notification, MANAGER_NOTIFY_CCIC_SENSORHUB);
#elif defined(CONFIG_MUIC_NOTIFIER)
muic_notifier_register(&data->cpuidle_muic_nb,
a96t3x6_cpuidle_muic_notifier, MUIC_NOTIFY_DEV_CPUIDLE);
#endif
GRIP_INFO("done\n");
data->probe_done = true;
data->resume_called = false;
return 0;
err_req_irq:
sensors_unregister(data->dev, grip_sensor_attributes);
err_sensor_register:
sysfs_remove_group(&data->input_dev->dev.kobj,
&a96t3x6_attribute_group);
err_sysfs_group:
sensors_remove_symlink(input_dev);
err_sysfs_symlink:
input_unregister_device(input_dev);
err_reg_input_dev:
mutex_destroy(&data->lock);
gpio_free(data->grip_int);
err_config:
wake_lock_destroy(&data->grip_wake_lock);
err_input_alloc:
kfree(data);
err_alloc:
GRIP_ERR("failed\n");
return ret;
}
static int a96t3x6_remove(struct i2c_client *client)
{
struct a96t3x6_data *data = i2c_get_clientdata(client);
if (data->enabled)
data->power(data, false);
data->enabled = false;
device_init_wakeup(&client->dev, false);
wake_lock_destroy(&data->grip_wake_lock);
cancel_delayed_work_sync(&data->debug_work);
#ifdef CONFIG_SENSORS_FW_VENDOR
cancel_delayed_work_sync(&data->firmware_work);
#endif
if (data->irq >= 0)
free_irq(data->irq, data);
sensors_unregister(data->dev, grip_sensor_attributes);
sysfs_remove_group(&data->input_dev->dev.kobj,
&a96t3x6_attribute_group);
sensors_remove_symlink(data->input_dev);
input_unregister_device(data->input_dev);
kfree(data);
return 0;
}
static int a96t3x6_suspend(struct device *dev)
{
struct a96t3x6_data *data = dev_get_drvdata(dev);
data->resume_called = false;
GRIP_INFO("\n");
a96t3x6_sar_only_mode(data, 1);
a96t3x6_set_debug_work(data, 0, 1000);
return 0;
}
static int a96t3x6_resume(struct device *dev)
{
struct a96t3x6_data *data = dev_get_drvdata(dev);
GRIP_INFO("\n");
data->resume_called = true;
a96t3x6_set_debug_work(data, 1, 0);
return 0;
}
static void a96t3x6_shutdown(struct i2c_client *client)
{
struct a96t3x6_data *data = i2c_get_clientdata(client);
a96t3x6_set_debug_work(data, 0, 1000);
if (data->enabled) {
disable_irq(data->irq);
data->power(data, false);
}
data->enabled = false;
}
static const struct i2c_device_id a96t3x6_device_id[] = {
{MODULE_NAME, 0},
{}
};
MODULE_DEVICE_TABLE(i2c, a96t3x6_device_id);
#ifdef CONFIG_OF
static const struct of_device_id a96t3x6_match_table[] = {
{ .compatible = "a96t3x6_wifi",},
{ },
};
#else
#define a96t3x6_match_table NULL
#endif
static const struct dev_pm_ops a96t3x6_pm_ops = {
.suspend = a96t3x6_suspend,
.resume = a96t3x6_resume,
};
static struct i2c_driver a96t3x6_driver = {
.probe = a96t3x6_probe,
.remove = a96t3x6_remove,
.shutdown = a96t3x6_shutdown,
.id_table = a96t3x6_device_id,
.driver = {
.name = MODEL_NAME,
.owner = THIS_MODULE,
.of_match_table = a96t3x6_match_table,
.pm = &a96t3x6_pm_ops
},
};
static int __init a96t3x6_init(void)
{
return i2c_add_driver(&a96t3x6_driver);
}
static void __exit a96t3x6_exit(void)
{
i2c_del_driver(&a96t3x6_driver);
}
module_init(a96t3x6_init);
module_exit(a96t3x6_exit);
MODULE_AUTHOR("Samsung Electronics");
MODULE_DESCRIPTION("Grip sensor driver for A96T3X6 chip");
MODULE_LICENSE("GPL");