blob: 613c0208e16b249fff0a9383ace607559240603a [file] [log] [blame]
/* abov_touchkey.c -- Linux driver for abov chip as touchkey
*
* Copyright (C) 2013 Samsung Electronics Co.Ltd
* Author: Junkyeong Kim <jk0430.kim@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/i2c/abov_touchkey_ft1804.h>
#include <linux/io.h>
#include <asm/unaligned.h>
#include <linux/regulator/consumer.h>
#include <linux/sec_sysfs.h>
#include <linux/wakelock.h>
#include <linux/pinctrl/consumer.h>
#ifdef CONFIG_OF
#include <linux/of_gpio.h>
#endif
#ifdef CONFIG_HAS_EARLYSUSPEND
#include <linux/earlysuspend.h>
#endif
#ifdef CONFIG_BATTERY_SAMSUNG
#include <linux/sec_batt.h>
#endif
#ifdef CONFIG_VBUS_NOTIFIER
#include <linux/muic/muic.h>
#include <linux/muic/muic_notifier.h>
#include <linux/vbus_notifier.h>
#endif
/* registers */
#define ABOV_BTNSTATUS 0x00
#define ABOV_FW_VER 0x01
//#define ABOV_PCB_VER 0x02
//#define ABOV_COMMAND 0x03
#define ABOV_THRESHOLD 0x02
//#define ABOV_SENS 0x05
//#define ABOV_SETIDAC 0x06
#define ABOV_BTNSTATUS_NEW 0x07
#define ABOV_LED_RECENT 0x08 //LED Dimming (0x01~0x1F)
#define ABOV_LED_BACK 0x09 //LED Dimming (0x01~0x1F)
#define ABOV_DIFFDATA 0x0A
#define ABOV_RAWDATA 0x0E
#define ABOV_VENDORID 0x12
#define ABOV_TSPTA 0x13
#define ABOV_GLOVE 0x13
#define ABOV_KEYBOARD 0x13
#define ABOV_MODEL_NUMBER 0x14 //Model No.
#define ABOV_FLIP 0x15
#define ABOV_SW_RESET 0x1A
/* command */
#define CMD_LED_ON 0x10
#define CMD_LED_OFF 0x20
#define CMD_SAR_TOTALCAP 0x16
#define CMD_SAR_MODE 0x17
#define CMD_SAR_TOTALCAP_READ 0x18
#define CMD_SAR_ENABLE 0x24
#define CMD_SAR_SENSING 0x25
#define CMD_SAR_NOISE_THRESHOLD 0x26
#define CMD_SAR_BASELINE 0x28
#define CMD_SAR_DIFFDATA 0x2A
#define CMD_SAR_RAWDATA 0x2E
#define CMD_SAR_THRESHOLD 0x32
#define CMD_DATA_UPDATE 0x40
#define CMD_MODE_CHECK 0x41
#define CMD_LED_CTRL_ON 0x60
#define CMD_LED_CTRL_OFF 0x70
#define CMD_STOP_MODE 0x80
#define CMD_GLOVE_OFF 0x10
#define CMD_GLOVE_ON 0x20
#define CMD_MOBILE_KBD_OFF 0x10
#define CMD_MOBILE_KBD_ON 0x20
#define CMD_FLIP_OFF 0x10
#define CMD_FLIP_ON 0x20
#define CMD_OFF 0x10
#define CMD_ON 0x20
#define ABOV_BOOT_DELAY 45
#define ABOV_RESET_DELAY 150
#ifdef CONFIG_KEYBOARD_ABOV_TOUCH_T316
#define ABOV_FLASH_MODE 0x31
#else
#define ABOV_FLASH_MODE 0x18
#endif
//static struct device *sec_touchkey;
#ifdef LED_TWINKLE_BOOTING
static void led_twinkle_work(struct work_struct *work);
#endif
#define TK_FW_PATH_BIN "abov/abov_noble.fw"
#define TK_FW_PATH_SDCARD "/sdcard/abov_fw.bin"
#define I2C_M_WR 0 /* for i2c */
enum {
BUILT_IN = 0,
SDCARD,
};
#define ABOV_ISP_FIRMUP_ROUTINE 0
extern unsigned int system_rev;
extern struct class *sec_class;
#ifdef CONFIG_VBUS_NOTIFIER
static bool g_ta_connected =0;
#endif
static int touchkey_keycode[] = { 0,
KEY_RECENT, KEY_BACK
#ifdef CONFIG_TOUCHKEY_GRIP
, KEY_CP_GRIP
#endif
};
struct abov_tk_info {
struct i2c_client *client;
struct input_dev *input_dev;
struct device *dev;
struct abov_touchkey_platform_data *pdata;
#ifdef CONFIG_HAS_EARLYSUSPEND
struct early_suspend early_suspend;
#endif
struct mutex lock;
struct pinctrl *pinctrl;
struct pinctrl *pinctrl_det;
struct pinctrl_state *pins_default;
const struct firmware *firm_data_bin;
const u8 *firm_data_ums;
char phys[32];
long firm_size;
int irq;
u16 menu_s;
u16 back_s;
u16 menu_raw;
u16 back_raw;
#ifdef CONFIG_TOUCHKEY_GRIP
struct wake_lock touckey_wake_lock;
u16 grip_p_thd;
u16 grip_r_thd;
u16 grip_n_thd;
u16 grip_s1;
u16 grip_s2;
u16 grip_baseline;
u16 grip_raw1;
u16 grip_raw2;
u16 grip_event;
bool sar_mode;
bool sar_enable;
bool sar_enable_off;
bool sar_sensing;
#endif
int (*power) (bool on);
void (*input_event)(void *data);
int touchkey_count;
u8 fw_update_state;
u8 fw_ver;
u8 fw_ver_bin;
u8 fw_model_number;
u8 checksum_h;
u8 checksum_h_bin;
u8 checksum_l;
u8 checksum_l_bin;
bool enabled;
bool glovemode;
bool keyboard_mode;
bool flip_mode;
#ifdef LED_TWINKLE_BOOTING
struct delayed_work led_twinkle_work;
bool led_twinkle_check;
#endif
#ifdef CONFIG_VBUS_NOTIFIER
struct notifier_block vbus_nb;
#endif
#ifdef CONFIG_TOUCHKEY_LIGHT_EFS
struct delayed_work efs_open_work;
int light_version_efs;
char light_version_full_efs[LIGHT_VERSION_LEN];
char light_version_full_bin[LIGHT_VERSION_LEN];
int light_table_crc;
u8 light_reg;
#endif
};
#ifdef CONFIG_HAS_EARLYSUSPEND
static void abov_tk_early_suspend(struct early_suspend *h);
static void abov_tk_late_resume(struct early_suspend *h);
#endif
#if 1//def CONFIG_INPUT_ENABLED
static int abov_tk_input_open(struct input_dev *dev);
static void abov_tk_input_close(struct input_dev *dev);
#endif
static int abov_tk_i2c_read_checksum(struct abov_tk_info *info);
static void abov_tk_reset(struct abov_tk_info *info);
#ifdef CONFIG_VBUS_NOTIFIER
static void abov_set_ta_status(struct abov_tk_info *info);
#endif
static int abov_touchkey_led_status;
static int abov_touchled_cmd_reserved;
void abov_delay(unsigned int ms)
{
if (ms < 20)
usleep_range(ms * 1000, ms * 1000);
else
msleep(ms);
}
static int abov_mode_enable(struct i2c_client *client,u8 cmd_reg, u8 cmd)
{
return i2c_smbus_write_byte_data(client, cmd_reg, cmd);
}
#if ABOV_ISP_FIRMUP_ROUTINE
static void abov_config_gpio_i2c(struct abov_tk_info *info, int onoff)
{
struct device *i2c_dev = info->client->dev.parent->parent;
struct pinctrl *pinctrl_i2c;
if (onoff) {
pinctrl_i2c = devm_pinctrl_get_select(i2c_dev, "on_i2c");
if (IS_ERR(pinctrl_i2c))
input_err(true, &info->client->dev, "%s: Failed to configure i2c pin\n", __func__);
} else {
pinctrl_i2c = devm_pinctrl_get_select(i2c_dev, "off_i2c");
if (IS_ERR(pinctrl_i2c))
input_err(true, &info->client->dev, "%s: Failed to configure i2c pin\n", __func__);
}
}
#endif
static int abov_tk_i2c_read(struct i2c_client *client,
u8 reg, u8 *val, unsigned int len)
{
struct abov_tk_info *info = i2c_get_clientdata(client);
struct i2c_msg msg;
int ret;
int retry = 3;
mutex_lock(&info->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;
input_err(true, &client->dev, "%s fail(address set)(%d)\n",
__func__, retry);
abov_delay(10);
}
if (ret < 0) {
mutex_unlock(&info->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(&info->lock);
return 0;
}
input_err(true, &client->dev, "%s fail(data read)(%d)\n",
__func__, retry);
abov_delay(10);
}
mutex_unlock(&info->lock);
return ret;
}
static int abov_tk_i2c_read_data(struct i2c_client *client, u8 *val, unsigned int len)
{
struct abov_tk_info *info = i2c_get_clientdata(client);
struct i2c_msg msg;
int ret;
int retry = 3;
mutex_lock(&info->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(&info->lock);
return 0;
}
input_err(true, &client->dev, "%s fail(data read)(%d)\n",
__func__, retry);
abov_delay(10);
}
mutex_unlock(&info->lock);
return ret;
}
static int abov_tk_i2c_write(struct i2c_client *client,
u8 reg, u8 *val, unsigned int len)
{
struct abov_tk_info *info = i2c_get_clientdata(client);
struct i2c_msg msg[1];
unsigned char data[2];
int ret;
int retry = 3;
mutex_lock(&info->lock);
data[0] = reg;
data[1] = *val;
msg->addr = client->addr;
msg->flags = I2C_M_WR;
msg->len = 2;
msg->buf = data;
while (retry--) {
ret = i2c_transfer(client->adapter, msg, 1);
if (ret >= 0) {
mutex_unlock(&info->lock);
return 0;
}
input_err(true, &client->dev, "%s fail(%d)\n",
__func__, retry);
abov_delay(10);
}
mutex_unlock(&info->lock);
return ret;
}
#ifdef CONFIG_TOUCHKEY_LIGHT_EFS
static int efs_read_light_table_version(struct abov_tk_info *info);
static void change_touch_key_led_brightness(struct device *dev, int led_reg)
{
struct abov_tk_info *info = dev_get_drvdata(dev);
int ret;
input_info(true, dev, "%s: 0x%02x\n", __func__, led_reg);
info->light_reg = led_reg;
/*led dimming */
ret = abov_tk_i2c_write(info->client, ABOV_LED_BACK, &info->light_reg, 1);
if (ret < 0) {
input_err(true, &info->client->dev, "%s led dimming back key write fail(%d)\n", __func__, ret);
}
ret = abov_tk_i2c_write(info->client, ABOV_LED_RECENT, &info->light_reg, 1);
if (ret < 0) {
input_err(true, &info->client->dev, "%s led dimming recent key write fail(%d)\n", __func__, ret);
}
}
static int read_window_type(void)
{
struct file *type_filp = NULL;
mm_segment_t old_fs;
int ret = 0;
char window_type[2] = {0, };
old_fs = get_fs();
set_fs(KERNEL_DS);
type_filp = filp_open("/sys/class/lcd/panel/window_type", O_RDONLY, 0440);
if (IS_ERR(type_filp)) {
set_fs(old_fs);
ret = PTR_ERR(type_filp);
return ret;
}
ret = type_filp->f_op->read(type_filp, window_type,
sizeof(window_type), &type_filp->f_pos);
if (ret != 2 * sizeof(char)) {
pr_err("%s touchkey %s: fd read fail\n", SECLOG, __func__);
ret = -EIO;
return ret;
}
filp_close(type_filp, current->files);
set_fs(old_fs);
if (window_type[1] < '0' || window_type[1] >= 'f')
return -EAGAIN;
ret = (window_type[1] - '0') & 0x0f;
pr_info("%s touchkey %s: %d\n", SECLOG, __func__, ret);
return ret;
}
static int efs_calculate_crc (struct abov_tk_info *info)
{
struct file *temp_file = NULL;
int crc = info->light_version_efs;
mm_segment_t old_fs;
char predefine_value_path[LIGHT_TABLE_PATH_LEN];
int ret = 0, i;
char temp_vol[LIGHT_CRC_SIZE] = {0, };
int table_size;
efs_read_light_table_version(info);
table_size = (int)strlen(info->light_version_full_efs) - 8;
for (i = 0; i < table_size; i++) {
char octa_temp = info->light_version_full_efs[8 + i];
int octa_temp_i;
if (octa_temp >= 'A')
octa_temp_i = octa_temp - 'A' + 0x0A;
else
octa_temp_i = octa_temp - '0';
input_info(true, &info->client->dev, "%s: octa %d\n", __func__, octa_temp_i);
snprintf(predefine_value_path, LIGHT_TABLE_PATH_LEN, "%s%d",
LIGHT_TABLE_PATH, octa_temp_i);
old_fs = get_fs();
set_fs(KERNEL_DS);
temp_file = filp_open(predefine_value_path, O_RDONLY, 0440);
if (!IS_ERR(temp_file)) {
temp_file->f_op->read(temp_file, temp_vol,
sizeof(temp_vol), &temp_file->f_pos);
filp_close(temp_file, current->files);
if (kstrtoint(temp_vol, 0, &ret) < 0) {
ret = -EIO;
} else {
crc += octa_temp_i;
crc += ret;
ret = 0;
}
}
set_fs(old_fs);
}
if (!ret)
ret = crc;
return ret;
}
static int efs_read_crc(struct abov_tk_info *info)
{
struct file *temp_file = NULL;
char crc[LIGHT_CRC_SIZE] = {0, };
mm_segment_t old_fs;
int ret = 0;
old_fs = get_fs();
set_fs(KERNEL_DS);
temp_file = filp_open(LIGHT_CRC_PATH, O_RDONLY, 0440);
if (IS_ERR(temp_file)) {
ret = PTR_ERR(temp_file);
input_info(true, &info->client->dev,
"%s: failed to open efs file %d\n", __func__, ret);
} else {
temp_file->f_op->read(temp_file, crc, sizeof(crc), &temp_file->f_pos);
filp_close(temp_file, current->files);
if (kstrtoint(crc, 0, &ret) < 0)
ret = -EIO;
}
set_fs(old_fs);
return ret;
}
static bool check_light_table_crc(struct abov_tk_info *info)
{
int crc_efs = efs_read_crc(info);
if (info->light_version_efs == info->pdata->dt_light_version) {
/* compare efs crc file with binary crc*/
input_info(true, &info->client->dev,
"%s: efs:%d, bin:%d\n",
__func__, crc_efs, info->light_table_crc);
if (crc_efs != info->light_table_crc)
return false;
}
return true;
}
static int efs_write_light_table_crc(struct abov_tk_info *info, int crc_cal)
{
struct file *temp_file = NULL;
char crc[LIGHT_CRC_SIZE] = {0, };
mm_segment_t old_fs;
int ret = 0;
old_fs = get_fs();
set_fs(KERNEL_DS);
temp_file = filp_open(LIGHT_CRC_PATH, O_TRUNC | O_RDWR | O_CREAT, 0660);
if (IS_ERR(temp_file)) {
ret = PTR_ERR(temp_file);
input_info(true, &info->client->dev,
"%s: failed to open efs file %d\n", __func__, ret);
} else {
snprintf(crc, sizeof(crc), "%d", crc_cal);
temp_file->f_op->write(temp_file, crc, sizeof(crc), &temp_file->f_pos);
filp_close(temp_file, current->files);
input_info(true, &info->client->dev, "%s: %s\n", __func__, crc);
}
set_fs(old_fs);
return ret;
}
static int efs_write_light_table_version(struct abov_tk_info *info, char *full_version)
{
struct file *temp_file = NULL;
mm_segment_t old_fs;
int ret = 0;
old_fs = get_fs();
set_fs(KERNEL_DS);
temp_file = filp_open(LIGHT_VERSION_PATH, O_TRUNC | O_RDWR | O_CREAT, 0660);
if (IS_ERR(temp_file)) {
ret = -ENOENT;
} else {
temp_file->f_op->write(temp_file, full_version,
LIGHT_VERSION_LEN, &temp_file->f_pos);
filp_close(temp_file, current->files);
input_info(true, &info->client->dev, "%s: version = %s\n",
__func__, full_version);
}
set_fs(old_fs);
return ret;
}
static int efs_write_light_table(struct abov_tk_info *info, struct light_info table)
{
struct file *type_filp = NULL;
mm_segment_t old_fs;
int ret = 0;
char predefine_value_path[LIGHT_TABLE_PATH_LEN];
char led_reg[LIGHT_DATA_SIZE] = {0, };
snprintf(predefine_value_path, LIGHT_TABLE_PATH_LEN,
"%s%d", LIGHT_TABLE_PATH, table.octa_id);
snprintf(led_reg, sizeof(led_reg), "%d", table.led_reg);
input_info(true, &info->client->dev, "%s: make %s\n", __func__, predefine_value_path);
old_fs = get_fs();
set_fs(KERNEL_DS);
type_filp = filp_open(predefine_value_path, O_TRUNC | O_RDWR | O_CREAT, 0660);
if (IS_ERR(type_filp)) {
set_fs(old_fs);
ret = PTR_ERR(type_filp);
input_err(true, &info->client->dev, "%s: open fail :%d\n",
__func__, ret);
return ret;
}
type_filp->f_op->write(type_filp, led_reg, sizeof(led_reg), &type_filp->f_pos);
filp_close(type_filp, current->files);
set_fs(old_fs);
return ret;
}
static int efs_write(struct abov_tk_info *info)
{
int ret = 0;
int i, crc_cal;
ret = efs_write_light_table_version(info, info->light_version_full_bin);
if (ret < 0)
return ret;
info->light_version_efs = info->pdata->dt_light_version;
for (i = 0; i < info->pdata->dt_light_table; i++) {
ret = efs_write_light_table(info, tkey_light_reg_table[i]);
if (ret < 0)
break;
}
if (ret < 0)
return ret;
crc_cal = efs_calculate_crc(info);
if (crc_cal < 0)
return crc_cal;
ret = efs_write_light_table_crc(info, crc_cal);
if (ret < 0)
return ret;
if (!check_light_table_crc(info))
ret = -EIO;
return ret;
}
static int pick_light_table_version(char* str)
{
static char* str_addr;
char* token = NULL;
int ret = 0;
if (str != NULL)
str_addr = str;
else if (str_addr == NULL)
return 0;
token = str_addr;
while (true) {
if (!(*str_addr)) {
break;
} else if (*str_addr == 'T') {
*str_addr = '0';
} else if (*str_addr == '.') {
*str_addr = '\0';
str_addr = str_addr + 1;
break;
}
str_addr++;
}
if (kstrtoint(token + 1, 0, &ret) < 0)
return 0;
return ret;
}
static int efs_read_light_table_version(struct abov_tk_info *info)
{
struct file *temp_file = NULL;
char version[LIGHT_VERSION_LEN] = {0, };
mm_segment_t old_fs;
int ret = 0;
old_fs = get_fs();
set_fs(KERNEL_DS);
temp_file = filp_open(LIGHT_VERSION_PATH, O_RDONLY, 0440);
if (IS_ERR(temp_file)) {
ret = PTR_ERR(temp_file);
} else {
temp_file->f_op->read(temp_file, version, sizeof(version), &temp_file->f_pos);
filp_close(temp_file, current->files);
input_info(true, &info->client->dev,
"%s: table full version = %s\n", __func__, version);
snprintf(info->light_version_full_efs,
sizeof(info->light_version_full_efs), version);
info->light_version_efs = pick_light_table_version(version);
input_info(true, &info->client->dev,
"%s: table version = %d\n", __func__, info->light_version_efs);
}
set_fs(old_fs);
return ret;
}
static int efs_read_light_table(struct abov_tk_info *info, int octa_id)
{
struct file *type_filp = NULL;
mm_segment_t old_fs;
char predefine_value_path[LIGHT_TABLE_PATH_LEN];
char led_reg[LIGHT_DATA_SIZE] = {0, };
int ret;
snprintf(predefine_value_path, LIGHT_TABLE_PATH_LEN,
"%s%d", LIGHT_TABLE_PATH, octa_id);
input_info(true, &info->client->dev, "%s: %s\n", __func__, predefine_value_path);
old_fs = get_fs();
set_fs(KERNEL_DS);
type_filp = filp_open(predefine_value_path, O_RDONLY, 0440);
if (IS_ERR(type_filp)) {
set_fs(old_fs);
ret = PTR_ERR(type_filp);
input_err(true, &info->client->dev,
"%s: fail to open light data %d\n", __func__, ret);
return ret;
}
type_filp->f_op->read(type_filp, led_reg, sizeof(led_reg), &type_filp->f_pos);
filp_close(type_filp, current->files);
set_fs(old_fs);
if (kstrtoint(led_reg, 0, &ret) < 0)
return -EIO;
return ret;
}
static int efs_read_light_table_with_default(struct abov_tk_info *info, int octa_id)
{
bool set_default = false;
int ret;
retry:
if (set_default)
octa_id = WINDOW_COLOR_DEFAULT;
ret = efs_read_light_table(info, octa_id);
if (ret < 0) {
if (!set_default) {
set_default = true;
goto retry;
}
}
return ret;
}
static bool need_update_light_table(struct abov_tk_info *info)
{
/* Check version file exist*/
if (efs_read_light_table_version(info) < 0) {
return true;
}
/* Compare version */
input_info(true, &info->client->dev,
"%s: efs:%d, bin:%d\n", __func__,
info->light_version_efs, info->pdata->dt_light_version);
if (info->light_version_efs < info->pdata->dt_light_version)
return true;
/* Check CRC */
if (!check_light_table_crc(info)) {
input_info(true, &info->client->dev,
"%s: crc is diffrent\n", __func__);
return true;
}
return false;
}
static void touchkey_efs_open_work(struct work_struct *work)
{
struct abov_tk_info *info =
container_of(work, struct abov_tk_info, efs_open_work.work);
int window_type;
static int count = 0;
int led_reg;
if (need_update_light_table(info)) {
if (efs_write(info) < 0)
goto out;
}
window_type = read_window_type();
if (window_type < 0)
goto out;
led_reg = efs_read_light_table_with_default(info, window_type);
if ((led_reg >= LIGHT_REG_MIN_VAL) && (led_reg <= LIGHT_REG_MAX_VAL)) {
change_touch_key_led_brightness(&info->client->dev, led_reg);
input_info(true, &info->client->dev,
"%s: read done for window_type=%d\n", __func__, window_type);
} else {
input_err(true, &info->client->dev,
"%s: fail. key led brightness reg is %d\n", __func__, led_reg);
}
return;
out:
if (count < 50) {
schedule_delayed_work(&info->efs_open_work, msecs_to_jiffies(2000));
count++;
} else {
input_err(true, &info->client->dev,
"%s: retry %d times but can't check efs\n", __func__, count);
}
}
#endif
#ifdef CONFIG_TOUCHKEY_GRIP
static void abov_sar_only_mode(struct abov_tk_info *info, int on)
{
struct i2c_client *client = info->client;
int retry =3;
int ret;
u8 cmd;
u8 r_buf;
int mode_retry = 5;
if(info->sar_mode == on){
input_info(true, &client->dev, "[TK] %s : skip already %s\n", __func__, (on==1)? "sar only mode":"normal mode");
return;
}
if(on == 1) cmd = 0x20;
else cmd = 0x10;
input_info(true, &client->dev, "[TK] %s : %s, cmd=%x\n", __func__, (on==1)? "sar only mode":"normal mode", cmd);
sar_mode:
while(retry>0){
ret = abov_tk_i2c_write(info->client, CMD_SAR_MODE, &cmd, 1);
if (ret < 0) {
input_err(true, &info->client->dev, "%s fail(%d), retry %d\n", __func__, ret, retry);
retry--;
abov_delay(20);
continue;
}
break;
}
abov_delay(40);
ret = abov_tk_i2c_read(info->client, CMD_SAR_MODE, &r_buf, 1);
if (ret < 0) {
input_err(true, &info->client->dev, "%s fail(%d)\n", __func__, ret);
}
input_info(true, &client->dev, "%s read reg = %x\n", __func__,r_buf);
if((r_buf != cmd) && (mode_retry > 0)){
input_info(true, &info->client->dev, "%s change fail retry\n", __func__);
mode_retry--;
if(mode_retry == 0){
abov_tk_reset(info);
}
goto sar_mode;
}
if(r_buf == 0x20)
info->sar_mode = 1;
else if(r_buf == 0x10)
info->sar_mode = 0;
}
static void touchkey_sar_sensing(struct abov_tk_info *info, int on)
{
struct i2c_client *client = info->client;
int ret;
u8 cmd;
if(on==1) cmd = CMD_ON;
else cmd = CMD_OFF;
input_info(true, &client->dev, "[TK] %s : %s\n", __func__, (on)? "on":"off");
ret = abov_tk_i2c_write(info->client, CMD_SAR_SENSING, &cmd, 1);
if (ret < 0) {
input_err(true, &info->client->dev, "%s fail(%d)\n", __func__, ret);
}
info->sar_sensing = on;
}
#endif
static void release_all_fingers(struct abov_tk_info *info)
{
struct i2c_client *client = info->client;
int i;
input_info(true, &client->dev, "%s called (touchkey_count=%d)\n", __func__,info->touchkey_count);
for (i = 1; i < info->touchkey_count; i++) {
input_report_key(info->input_dev,
touchkey_keycode[i], 0);
}
input_sync(info->input_dev);
}
static int abov_tk_reset_for_bootmode(struct abov_tk_info *info)
{
int ret=0;
info->pdata->power(info, false);
abov_delay(50);
info->pdata->power(info, true);
return ret;
}
static void abov_tk_reset(struct abov_tk_info *info)
{
struct i2c_client *client = info->client;
#ifdef CONFIG_TOUCHKEY_LIGHT_EFS
int ret;
#endif
if (info->enabled == false)
return;
input_info(true,&client->dev, "%s start\n", __func__);
disable_irq_nosync(info->irq);
info->enabled = false;
release_all_fingers(info);
abov_tk_reset_for_bootmode(info);
abov_delay(ABOV_RESET_DELAY);
#ifdef CONFIG_TOUCHKEY_LIGHT_EFS
/*led dimming */
ret = abov_tk_i2c_write(info->client, ABOV_LED_BACK, &info->light_reg, 1);
if (ret < 0) {
input_err(true, &info->client->dev, "%s led dimming back key write fail(%d)\n", __func__, ret);
}
ret = abov_tk_i2c_write(info->client, ABOV_LED_RECENT, &info->light_reg, 1);
if (ret < 0) {
input_err(true, &info->client->dev, "%s led dimming recent key write fail(%d)\n", __func__, ret);
}
#endif
#ifdef CONFIG_TOUCHKEY_GRIP
if(info->sar_enable)
abov_mode_enable(client, CMD_SAR_ENABLE, CMD_ON);
if(info->sar_sensing != 1)
touchkey_sar_sensing(info, 0);
#else
if (info->pdata->ta_notifier && g_ta_connected) {
abov_set_ta_status(info);
}
if (info->flip_mode){
abov_mode_enable(client, ABOV_FLIP, CMD_FLIP_ON);
} else {
if (info->glovemode)
abov_mode_enable(client, ABOV_GLOVE, CMD_GLOVE_ON);
}
if (info->keyboard_mode)
abov_mode_enable(client, ABOV_KEYBOARD, CMD_MOBILE_KBD_ON);
#endif
info->enabled = true;
enable_irq(info->irq);
input_info(true,&client->dev, "%s end\n", __func__);
}
static irqreturn_t abov_tk_interrupt(int irq, void *dev_id)
{
struct abov_tk_info *info = dev_id;
struct i2c_client *client = info->client;
int ret, retry;
u8 buf;
#ifdef CONFIG_TOUCHKEY_GRIP
int grip_data;
u8 grip_press = 0;
wake_lock(&info->touckey_wake_lock);
#endif
ret = abov_tk_i2c_read(client, ABOV_BTNSTATUS_NEW, &buf, 1);
if (ret < 0) {
retry = 3;
while (retry--) {
input_err(true, &client->dev, "%s read fail(%d)\n",
__func__, retry);
ret = abov_tk_i2c_read(client, ABOV_BTNSTATUS_NEW, &buf, 1);
if (ret == 0)
break;
else
abov_delay(10);
}
if (retry == 0) {
abov_tk_reset(info);
#ifdef CONFIG_TOUCHKEY_GRIP
wake_unlock(&info->touckey_wake_lock);
#endif
return IRQ_HANDLED;
}
}
input_info(true, &client->dev, " %s buf = 0x%02x\n",__func__, buf);
{
int menu_data = buf & 0x03;
int back_data = (buf >> 2) & 0x03;
u8 menu_press = !(menu_data % 2);
u8 back_press = !(back_data % 2);
#ifdef CONFIG_TOUCHKEY_GRIP
grip_data = (buf >> 4) & 0x03;
grip_press = !(grip_data % 2);
#endif
if (menu_data)
input_report_key(info->input_dev,
touchkey_keycode[1], menu_press);
if (back_data)
input_report_key(info->input_dev,
touchkey_keycode[2], back_press);
#ifdef CONFIG_TOUCHKEY_GRIP
if (grip_data){
input_report_key(info->input_dev,
touchkey_keycode[3], grip_press);
info->grip_event = grip_press;
}
#endif
#ifdef CONFIG_SAMSUNG_PRODUCT_SHIP
input_info(true, &client->dev,
"keycode : %s%s ver 0x%02x\n",
menu_data ? (menu_press ? "P" : "R") : "",
back_data ? (back_press ? "P" : "R") : "",
info->fw_ver);
#else
input_info(true, &client->dev,
"keycode : %s%s%x ver 0x%02x\n",
menu_data ? (menu_press ? "menu P " : "menu R ") : "",
back_data ? (back_press ? "back P " : "back R ") : "",
buf, info->fw_ver);
#endif
#ifdef CONFIG_TOUCHKEY_GRIP
if (grip_data){
input_info(true, &client->dev, "%s%x \n",
grip_press ? "grip P " : "grip R ", buf);
}
#endif
}
input_sync(info->input_dev);
#ifdef CONFIG_TOUCHKEY_GRIP
wake_unlock(&info->touckey_wake_lock);
#endif
return IRQ_HANDLED;
}
static int touchkey_led_set(struct abov_tk_info *info, int data)
{
u8 cmd;
int ret;
if (data == 1)
cmd = CMD_LED_ON;
else
cmd = CMD_LED_OFF;
if (!info->enabled) {
abov_touchled_cmd_reserved = 1;
return 1;
}
ret = abov_tk_i2c_write(info->client, ABOV_BTNSTATUS, &cmd, 1);
if (ret < 0) {
input_err(true, &info->client->dev, "%s fail(%d)\n", __func__, ret);
abov_touchled_cmd_reserved = 1;
return 1;
}
return 0;
}
static ssize_t touchkey_led_control(struct device *dev,
struct device_attribute *attr, const char *buf,
size_t count)
{
struct abov_tk_info *info = dev_get_drvdata(dev);
int data;
u8 cmd;
int ret;
ret = sscanf(buf, "%d", &data);
if (ret != 1) {
input_err(true, &info->client->dev, "%s: cmd read err\n", __func__);
return count;
}
if (!(data == 0 || data == 1)) {
input_err(true, &info->client->dev, "%s: wrong command(%d)\n",
__func__, data);
return count;
}
if (data == 1)
cmd = CMD_LED_ON;
else
cmd = CMD_LED_OFF;
#ifdef LED_TWINKLE_BOOTING
if(info->led_twinkle_check == 1){
info->led_twinkle_check = 0;
cancel_delayed_work(&info->led_twinkle_work);
}
#endif
if(touchkey_led_set(info, data))
goto out;
abov_delay(20);
abov_touchled_cmd_reserved = 0;
input_info(true, &info->client->dev, "%s data(%d)\n",__func__,data);
out:
abov_touchkey_led_status = cmd;
return count;
}
#ifdef CONFIG_TOUCHKEY_GRIP
static ssize_t touchkey_sar_enable(struct device *dev,
struct device_attribute *attr, const char *buf,
size_t count)
{
struct abov_tk_info *info = dev_get_drvdata(dev);
struct i2c_client *client = info->client;
int data;
int ret;
u8 cmd;
ret = sscanf(buf, "%d", &data);
if (ret != 1) {
input_err(true, &client->dev, "%s: cmd read err\n", __func__);
return count;
}
if (!(data >= 0 && data <= 3)) {
input_err(true, &client->dev, "%s: wrong command(%d)\n",
__func__, data);
return count;
}
/*data 0:0ff, 1:on, 2:force off, 3:force off->on */
if(data == 3){
info->sar_enable_off = 0;
input_info(true, &info->client->dev, "%s : Power back off _ force off -> on (%d)\n", __func__, info->sar_enable);
if(info->sar_enable)
data = 1;
else
return count;
}
if(info->sar_enable_off){
if(data == 1)
info->sar_enable = true;
else
info->sar_enable = false;
input_info(true, &info->client->dev, "%s skip, Power back off _ force off mode (%d)\n", __func__, info->sar_enable);
return count;
}
if(data == 1)
cmd = 0x20;
else if(data == 2){ //test app : Power back off _ force off
cmd = 0x10;
info->sar_enable_off = 1;
}
else cmd = 0x10;
input_info(true, &info->client->dev, "%s data(%d)\n",__func__,data);
ret = abov_mode_enable(client, CMD_SAR_ENABLE, cmd);
if (ret < 0) {
input_err(true, &info->client->dev, "%s fail(%d)\n", __func__, ret);
return count;
}
if(data == 1)
info->sar_enable = true;
else{
input_report_key(info->input_dev, touchkey_keycode[3], 0);
info->grip_event = 0;
info->sar_enable = false;
}
dev_notice(&client->dev, "%s data(%d)\n",__func__,data);
return count;
}
#endif
static ssize_t touchkey_threshold_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct abov_tk_info *info = dev_get_drvdata(dev);
struct i2c_client *client = info->client;
u8 r_buf;
int ret;
ret = abov_tk_i2c_read(client, ABOV_THRESHOLD, &r_buf, 1);
if (ret < 0) {
input_err(true, &client->dev, "%s fail(%d)\n", __func__, ret);
r_buf = 0;
}
return sprintf(buf, "%d\n", r_buf);
}
static void get_diff_data(struct abov_tk_info *info)
{
struct i2c_client *client = info->client;
u8 r_buf[4];
int ret;
ret = abov_tk_i2c_read(client, ABOV_DIFFDATA, r_buf, 4);
if (ret < 0) {
input_err(true, &client->dev, "%s fail(%d)\n", __func__, ret);
info->menu_s = 0;
info->back_s = 0;
return;
}
info->menu_s = (r_buf[0] << 8) | r_buf[1];
info->back_s = (r_buf[2] << 8) | r_buf[3];
}
static ssize_t touchkey_menu_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct abov_tk_info *info = dev_get_drvdata(dev);
get_diff_data(info);
return sprintf(buf, "%d\n", info->menu_s);
}
static ssize_t touchkey_back_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct abov_tk_info *info = dev_get_drvdata(dev);
get_diff_data(info);
return sprintf(buf, "%d\n", info->back_s);
}
static void get_raw_data(struct abov_tk_info *info)
{
struct i2c_client *client = info->client;
u8 r_buf[4];
int ret;
ret = abov_tk_i2c_read(client, ABOV_RAWDATA, r_buf, 4);
if (ret < 0) {
input_err(true, &client->dev, "%s fail(%d)\n", __func__, ret);
info->menu_raw = 0;
info->back_raw = 0;
return;
}
info->menu_raw = (r_buf[0] << 8) | r_buf[1];
info->back_raw = (r_buf[2] << 8) | r_buf[3];
}
static ssize_t touchkey_menu_raw_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct abov_tk_info *info = dev_get_drvdata(dev);
get_raw_data(info);
return sprintf(buf, "%d\n", info->menu_raw);
}
static ssize_t touchkey_back_raw_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct abov_tk_info *info = dev_get_drvdata(dev);
get_raw_data(info);
return sprintf(buf, "%d\n", info->back_raw);
}
#ifdef CONFIG_TOUCHKEY_GRIP
static ssize_t touchkey_grip_threshold_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct abov_tk_info *info = dev_get_drvdata(dev);
u8 r_buf[4];
int ret;
ret = abov_tk_i2c_read(info->client, CMD_SAR_THRESHOLD, r_buf, 4);
if (ret < 0) {
input_err(true, &info->client->dev, "%s fail(%d)\n", __func__, ret);
info->grip_p_thd = 0;
info->grip_r_thd = 0;
return sprintf(buf, "%d\n", 0);
}
info->grip_p_thd = (r_buf[0] << 8) | r_buf[1];
info->grip_r_thd = (r_buf[2] << 8) | r_buf[3];
ret = abov_tk_i2c_read(info->client, CMD_SAR_NOISE_THRESHOLD, r_buf, 2);
if (ret < 0) {
input_err(true, &info->client->dev, "%s fail(%d)\n", __func__, ret);
info->grip_n_thd = 0;
return sprintf(buf, "%d\n", 0);
}
info->grip_n_thd = (r_buf[0] << 8) | r_buf[1];
return sprintf(buf, "%d,%d,%d\n", info->grip_p_thd, info->grip_r_thd, info->grip_n_thd );
}
static ssize_t touchkey_total_cap_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct abov_tk_info *info = dev_get_drvdata(dev);
u8 r_buf[2];
u8 cmd;
int ret;
int value;
cmd = 0x20;
ret = abov_tk_i2c_write(info->client, CMD_SAR_TOTALCAP, &cmd, 1);
if (ret < 0) {
input_err(true, &info->client->dev, "%s write fail(%d)\n", __func__, ret);
}
usleep_range(10, 10);
ret = abov_tk_i2c_read(info->client, CMD_SAR_TOTALCAP_READ, r_buf, 2);
if (ret < 0) {
input_err(true, &info->client->dev, "%s fail(%d)\n", __func__, ret);
return sprintf(buf, "%d\n", 0);
}
value = (r_buf[0] << 8) | r_buf[1];
return sprintf(buf, "%d\n", value/100);
}
static ssize_t touchkey_grip_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct abov_tk_info *info = dev_get_drvdata(dev);
u8 r_buf[4];
int ret;
ret = abov_tk_i2c_read(info->client, CMD_SAR_DIFFDATA, r_buf, 4);
if (ret < 0) {
input_err(true, &info->client->dev, "%s fail(%d)\n", __func__, ret);
info->grip_s1 = 0;
info->grip_s2 = 0;
return sprintf(buf, "%d\n", 0);
}
info->grip_s1 = (r_buf[0] << 8) | r_buf[1];
info->grip_s2 = (r_buf[2] << 8) | r_buf[3];
return sprintf(buf, "%d,%d\n", info->grip_s1, info->grip_s2);
}
static ssize_t touchkey_grip_baseline_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct abov_tk_info *info = dev_get_drvdata(dev);
u8 r_buf[2];
int ret;
ret = abov_tk_i2c_read(info->client, CMD_SAR_BASELINE, r_buf, 2);
if (ret < 0) {
input_err(true, &info->client->dev, "%s fail(%d)\n", __func__, ret);
info->grip_baseline = 0;
return sprintf(buf, "%d\n", 0);
}
info->grip_baseline = (r_buf[0] << 8) | r_buf[1];
return sprintf(buf, "%d\n", info->grip_baseline);
}
static ssize_t touchkey_grip_raw_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct abov_tk_info *info = dev_get_drvdata(dev);
u8 r_buf[4];
int ret;
ret = abov_tk_i2c_read(info->client, CMD_SAR_RAWDATA, r_buf, 4);
if (ret < 0) {
input_err(true, &info->client->dev, "%s fail(%d)\n", __func__, ret);
info->grip_raw1 = 0;
info->grip_raw2 = 0;
return sprintf(buf, "%d\n", 0);
}
info->grip_raw1 = (r_buf[0] << 8) | r_buf[1];
info->grip_raw2 = 0; //(r_buf[2] << 8) | r_buf[3]; NA
return sprintf(buf, "%d,%d\n", info->grip_raw1, info->grip_raw2);
}
static ssize_t touchkey_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 touchkey_grip_check_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct abov_tk_info *info = dev_get_drvdata(dev);
return sprintf(buf, "%d\n", info->grip_event);
}
static ssize_t touchkey_grip_sw_reset(struct device *dev,
struct device_attribute *attr, const char *buf,
size_t count)
{
struct abov_tk_info *info = dev_get_drvdata(dev);
struct i2c_client *client = info->client;
int data;
int ret;
u8 cmd = 0x10;
ret = sscanf(buf, "%d", &data);
if (ret != 1) {
input_err(true, &client->dev, "%s: cmd read err\n", __func__);
return count;
}
if (!(data == 1)) {
input_err(true, &client->dev, "%s: wrong command(%d)\n",
__func__, data);
return count;
}
ret = abov_tk_i2c_write(info->client, ABOV_SW_RESET, &cmd, 1);
if (ret < 0) {
input_err(true, &info->client->dev, "%s fail(%d)\n", __func__, ret);
return count;
}
input_info(true, &info->client->dev, "%s data(%d)\n",__func__,data);
return count;
}
static ssize_t touchkey_sensing_change(struct device *dev,
struct device_attribute *attr, const char *buf,
size_t count)
{
struct abov_tk_info *info = dev_get_drvdata(dev);
struct i2c_client *client = info->client;
int ret, data;
ret = sscanf(buf, "%d", &data);
if (ret != 1) {
input_err(true, &client->dev, "%s: cmd read err\n", __func__);
return count;
}
if (!(data == 0 || data == 1)) {
input_err(true, &client->dev, "%s: wrong command(%d)\n",
__func__, data);
return count;
}
if(data == 1) //EarJack inserted
touchkey_sar_sensing(info, 0);
else
touchkey_sar_sensing(info, 1);
input_info(true, &info->client->dev, "%s earjack (%d)\n",__func__,data);
return count;
}
#ifndef CONFIG_SAMSUNG_PRODUCT_SHIP
static ssize_t touchkey_sar_press_threshold_store(struct device *dev,
struct device_attribute *attr, const char *buf,
size_t count)
{
struct abov_tk_info *info = dev_get_drvdata(dev);
int ret;
int threshold;
u8 cmd[2];
ret = sscanf(buf, "%d", &threshold);
if (ret != 1) {
input_err(true, &info->client->dev, "%s: failed to read thresold, buf is %s\n", __func__,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;
}
input_info(true,&info->client->dev, "%s buf : %d, threshold : %d\n",
__func__, threshold,(cmd[0]<<8 )| cmd[1]);
ret = abov_tk_i2c_write(info->client, CMD_SAR_THRESHOLD, &cmd[0], 1);
if (ret != 0) {
input_info(true,&info->client->dev, "%s failed to write press_threhold data1", __func__);
goto press_threshold_out;
}
ret = abov_tk_i2c_write(info->client, CMD_SAR_THRESHOLD + 0x01, &cmd[1], 1);
if (ret != 0) {
input_info(true,&info->client->dev, "%s failed to write press_threhold data2", __func__);
goto press_threshold_out;
}
press_threshold_out:
return count;
}
static ssize_t touchkey_sar_release_threshold_store(struct device *dev,
struct device_attribute *attr, const char *buf,
size_t count)
{
struct abov_tk_info *info = dev_get_drvdata(dev);
int ret;
int threshold;
u8 cmd[2];
ret = sscanf(buf, "%d", &threshold);
if (ret != 1) {
input_err(true, &info->client->dev, "%s: failed to read thresold, buf is %s\n", __func__,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;
}
input_info(true,&info->client->dev, "%s buf : %d, threshold : %d\n",
__func__, threshold,(cmd[0] << 8) | cmd[1]);
ret = abov_tk_i2c_write(info->client, CMD_SAR_THRESHOLD+0x02, &cmd[0], 1);
input_info(true,&info->client->dev, "%s ret : %d\n", __func__,ret);
if (ret != 0) {
input_info(true,&info->client->dev, "%s failed to write release_threshold_data1", __func__);
goto release_threshold_out;
}
ret = abov_tk_i2c_write(info->client, CMD_SAR_THRESHOLD+0x03, &cmd[1], 1);
input_info(true,&info->client->dev, "%s ret : %d\n", __func__,ret);
if (ret != 0) {
input_info(true,&info->client->dev, "%s failed to write release_threshold_data2", __func__);
goto release_threshold_out;
}
release_threshold_out:
return count;
}
static ssize_t touchkey_mode_change(struct device *dev,
struct device_attribute *attr, const char *buf,
size_t count)
{
struct abov_tk_info *info = dev_get_drvdata(dev);
struct i2c_client *client = info->client;
int ret, data;
ret = sscanf(buf, "%d", &data);
if (ret != 1) {
input_err(true, &client->dev, "%s: cmd read err\n", __func__);
return count;
}
if (!(data == 0 || data == 1)) {
input_err(true, &client->dev, "%s: wrong command(%d)\n",
__func__, data);
return count;
}
input_info(true, &info->client->dev, "%s data(%d)\n",__func__,data);
abov_sar_only_mode(info, data);
return count;
}
#endif
#endif
static ssize_t touchkey_chip_name(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct abov_tk_info *info = dev_get_drvdata(dev);
struct i2c_client *client = info->client;
input_info(true, &client->dev, "%s\n", __func__);
#ifdef CONFIG_KEYBOARD_ABOV_TOUCH_T316
return sprintf(buf, "A96T326\n");
#else
return sprintf(buf, "FT1804\n");
#endif
}
static ssize_t bin_fw_ver(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct abov_tk_info *info = dev_get_drvdata(dev);
struct i2c_client *client = info->client;
input_info(true, &client->dev, "fw version bin : 0x%x\n", info->fw_ver_bin);
return sprintf(buf, "0x%02x\n", info->fw_ver_bin);
}
static int get_tk_fw_version(struct abov_tk_info *info, bool bootmode)
{
struct i2c_client *client = info->client;
u8 buf;
int ret;
int retry = 3;
ret = abov_tk_i2c_read(client, ABOV_FW_VER, &buf, 1);
if (ret < 0) {
while (retry--) {
input_err(true, &client->dev, "%s read fail(%d)\n",
__func__, retry);
if (!bootmode)
abov_tk_reset(info);
else
return -1;
ret = abov_tk_i2c_read(client, ABOV_FW_VER, &buf, 1);
if (ret == 0)
break;
}
if (retry <= 0)
return -1;
}
info->fw_ver = buf;
input_info(true, &client->dev, "%s : 0x%x\n", __func__, buf);
return 0;
}
static ssize_t read_fw_ver(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct abov_tk_info *info = dev_get_drvdata(dev);
struct i2c_client *client = info->client;
int ret;
ret = get_tk_fw_version(info, false);
if (ret < 0) {
input_err(true, &client->dev, "%s read fail\n", __func__);
info->fw_ver = 0;
}
return sprintf(buf, "0x%02x\n", info->fw_ver);
}
static int abov_load_fw(struct abov_tk_info *info, u8 cmd)
{
struct i2c_client *client = info->client;
struct file *fp;
mm_segment_t old_fs;
long fsize, nread;
int ret = 0;
switch(cmd) {
case BUILT_IN:
ret = request_firmware(&info->firm_data_bin,
info->pdata->fw_path, &client->dev);
if (ret) {
input_err(true, &client->dev,
"%s request_firmware fail(%d)\n", __func__, cmd);
return ret;
}
/* Header info
* 0x00 0x91 : model info,
* 0x00 0x00 : module info (Rev 0.0),
* 0x00 0xF3 : F/W
* 0x00 0x00 0x17 0x10 : checksum
* ~ 22byte 0x00 */
info->fw_model_number = info->firm_data_bin->data[1];
info->fw_ver_bin = info->firm_data_bin->data[5];
info->checksum_h_bin = info->firm_data_bin->data[8];
info->checksum_l_bin = info->firm_data_bin->data[9];
info->firm_size = info->firm_data_bin->size;
input_info(true, &client->dev, "%s, bin version:%2X,%2X,%2X crc:%2X,%2X\n", __func__, \
info->firm_data_bin->data[1], info->firm_data_bin->data[3], info->fw_ver_bin, \
info->checksum_h_bin, info->checksum_l_bin);
break;
case SDCARD:
old_fs = get_fs();
set_fs(get_ds());
fp = filp_open(TK_FW_PATH_SDCARD, O_RDONLY, S_IRUSR);
if (IS_ERR(fp)) {
input_err(true, &client->dev,
"%s %s open error\n", __func__, TK_FW_PATH_SDCARD);
ret = -ENOENT;
goto fail_sdcard_open;
}
fsize = fp->f_path.dentry->d_inode->i_size;
info->firm_data_ums = kzalloc((size_t)fsize, GFP_KERNEL);
if (!info->firm_data_ums) {
input_err(true, &client->dev,
"%s fail to kzalloc for fw\n", __func__);
ret = -ENOMEM;
goto fail_sdcard_kzalloc;
}
nread = vfs_read(fp,
(char __user *)info->firm_data_ums, fsize, &fp->f_pos);
if (nread != fsize) {
input_err(true, &client->dev,
"%s fail to vfs_read file\n", __func__);
ret = -EINVAL;
goto fail_sdcard_size;
}
filp_close(fp, current->files);
set_fs(old_fs);
info->firm_size = nread;
info->checksum_h_bin = info->firm_data_ums[8];
info->checksum_l_bin = info->firm_data_ums[9];
input_info(true, &client->dev,"%s, bin version:%2X,%2X,%2X crc:%2X,%2X\n", __func__, \
info->firm_data_ums[1], info->firm_data_ums[3], info->firm_data_ums[5], \
info->checksum_h_bin, info->checksum_l_bin);
break;
default:
ret = -1;
break;
}
input_info(true, &client->dev, "fw_size : %lu\n", info->firm_size);
input_info(true, &client->dev, "%s success\n", __func__);
return ret;
fail_sdcard_size:
kfree(&info->firm_data_ums);
fail_sdcard_kzalloc:
filp_close(fp, current->files);
fail_sdcard_open:
set_fs(old_fs);
return ret;
}
#if ABOV_ISP_FIRMUP_ROUTINE
void abov_i2c_start(int scl, int sda)
{
gpio_direction_output(sda, 1);
gpio_direction_output(scl, 1);
usleep_range(15, 17);
gpio_direction_output(sda, 0);
usleep_range(10, 12);
gpio_direction_output(scl, 0);
usleep_range(10, 12);
}
void abov_i2c_stop(int scl, int sda)
{
gpio_direction_output(scl, 0);
usleep_range(10, 12);
gpio_direction_output(sda, 0);
usleep_range(10, 12);
gpio_direction_output(scl, 1);
usleep_range(10, 12);
gpio_direction_output(sda, 1);
}
void abov_testdelay(void)
{
u8 i;
u8 delay;
/* 120nms */
for (i = 0; i < 15; i++)
delay = 0;
}
void abov_byte_send(u8 data, int scl, int sda)
{
u8 i;
for (i = 0x80; i != 0; i >>= 1) {
gpio_direction_output(scl, 0);
usleep_range(1,1);
if (data & i)
gpio_direction_output(sda, 1);
else
gpio_direction_output(sda, 0);
usleep_range(1,1);
gpio_direction_output(scl, 1);
usleep_range(1,1);
}
usleep_range(1,1);
gpio_direction_output(scl, 0);
gpio_direction_input(sda);
usleep_range(1,1);
gpio_direction_output(scl, 1);
usleep_range(1,1);
gpio_get_value(sda);
abov_testdelay();
gpio_direction_output(scl, 0);
gpio_direction_output(sda, 0);
usleep_range(20,20);
}
u8 abov_byte_read(bool type, int scl, int sda)
{
u8 i;
u8 data = 0;
u8 index = 0x7;
gpio_direction_output(scl, 0);
gpio_direction_input(sda);
usleep_range(1,1);
for (i = 0; i < 8; i++) {
gpio_direction_output(scl, 0);
usleep_range(1,1);
gpio_direction_output(scl, 1);
usleep_range(1,1);
data = data | (u8)(gpio_get_value(sda) << index);
index -= 1;
}
usleep_range(1,1);
gpio_direction_output(scl, 0);
gpio_direction_output(sda, 0);
usleep_range(1,1);
if (type) { /*ACK */
gpio_direction_output(sda, 0);
usleep_range(1,1);
gpio_direction_output(scl, 1);
usleep_range(1,1);
gpio_direction_output(scl, 0);
usleep_range(1,1);
} else { /* NAK */
gpio_direction_output(sda, 1);
usleep_range(1,1);
gpio_direction_output(scl, 1);
usleep_range(1,1);
gpio_direction_output(scl, 0);
usleep_range(1,1);
gpio_direction_output(sda, 0);
usleep_range(1,1);
}
usleep_range(20,20);
return data;
}
void abov_enter_mode(int scl, int sda)
{
abov_i2c_start(scl, sda);
abov_testdelay();
abov_byte_send(ABOV_ID, scl, sda);
abov_byte_send(0xAC, scl, sda);
abov_byte_send(0x5B, scl, sda);
abov_byte_send(0x2D, scl, sda);
abov_i2c_stop(scl, sda);
}
void abov_firm_write(const u8 *fw_data, int block, int scl, int sda)
{
int i, j;
u16 pos = 0x20;
u8 addr[2];
u8 data[32] = {0, };
addr[0] = 0x10;
addr[1] = 0x00;
for (i = 0; i < (block - 0x20); i++) {
if (i % 8 == 0) {
addr[0] = 0x10 + i/8;
addr[1] = 0;
} else
addr[1] = addr[1] + 0x20;
memcpy(data, fw_data + pos, 32);
abov_i2c_start(scl, sda);
abov_testdelay();
abov_byte_send(ABOV_ID, scl, sda);
abov_byte_send(0xAC, scl, sda);
abov_byte_send(0x7A, scl, sda);
abov_byte_send(addr[0], scl, sda);
abov_byte_send(addr[1], scl, sda);
for (j = 0; j < 32; j++)
abov_byte_send(data[j], scl, sda);
abov_i2c_stop(scl, sda);
pos += 0x20;
abov_delay(3);
}
}
void abov_read_address_set(int scl, int sda)
{
abov_i2c_start(scl, sda);
abov_testdelay();
abov_byte_send(ABOV_ID, scl, sda);
abov_byte_send(0xAC, scl, sda);
abov_byte_send(0x9E, scl, sda);
abov_byte_send(0x10, scl, sda); /* start addr H */
abov_byte_send(0x00, scl, sda); /* start addr L */
abov_byte_send(0x3F, scl, sda); /* end addr H */
abov_byte_send(0xFF, scl, sda); /* end addr L */
abov_i2c_stop(scl, sda);
}
void abov_checksum(struct abov_tk_info *info, int scl, int sda)
{
struct i2c_client *client = info->client;
u8 status;
u8 bootver;
u8 firmver;
u8 checksumh;
u8 checksuml;
abov_read_address_set(scl, sda);
abov_delay(5);
abov_i2c_start(scl, sda);
abov_testdelay();
abov_byte_send(ABOV_ID, scl, sda);
abov_byte_send(0x00, scl, sda);
abov_i2c_start(scl, sda); /* restart */
abov_testdelay();
abov_byte_send(ABOV_ID + 1, scl, sda);
status = abov_byte_read(true, scl, sda);
bootver = abov_byte_read(true, scl, sda);
firmver = abov_byte_read(true, scl, sda);
checksumh = abov_byte_read(true, scl, sda);
checksuml = abov_byte_read(false, scl, sda);
abov_i2c_stop(scl, sda);
abov_delay(3);
info->checksum_h = checksumh;
info->checksum_l = checksuml;
input_info(true, &client->dev,
"%s status(0x%x), boot(0x%x), firm(0x%x), cs_h(0x%x), cs_l(0x%x)\n",
__func__, status, bootver, firmver, checksumh, checksuml);
}
void abov_exit_mode(int scl, int sda)
{
abov_i2c_start(scl, sda);
abov_testdelay();
abov_byte_send(ABOV_ID, scl, sda);
abov_byte_send(0xAC, scl, sda);
abov_byte_send(0x5B, scl, sda);
abov_byte_send(0xE1, scl, sda);
abov_i2c_stop(scl, sda);
}
static int abov_fw_update(struct abov_tk_info *info,
const u8 *fw_data, int block, int scl, int sda)
{
input_info(true, &info->client->dev, "%s start (%d)\n",__func__,__LINE__);
abov_config_gpio_i2c(info, 0);
abov_delay(ABOV_BOOT_DELAY);
abov_enter_mode(scl, sda);
abov_delay(1100);
abov_firm_write(fw_data, block, scl, sda);
abov_checksum(info, scl, sda);
abov_config_gpio_i2c(info, 1);
input_info(true, &info->client->dev, "%s end (%d)\n",__func__,__LINE__);
return 0;
}
#endif
static int abov_tk_check_busy(struct abov_tk_info *info)
{
int ret, count = 0;
unsigned char val = 0x00;
do {
ret = i2c_master_recv(info->client, &val, sizeof(val));
if (val & 0x01) {
count++;
if (count > 1000)
input_err(true, &info->client->dev,
"%s: busy %d\n", __func__, count);
return ret;
} else {
break;
}
} while(1);
return ret;
}
static int abov_tk_i2c_read_checksum(struct abov_tk_info *info)
{
unsigned char data[6] = {0xAC, 0x9E, 0x10, 0x00, 0x3F, 0xFF};
unsigned char data2[1] = {0x00};
unsigned char checksum[6] = {0, };
int ret;
i2c_master_send(info->client, data, 6);
abov_delay(5);
i2c_master_send(info->client, data2, 1);
abov_delay(5);
ret = abov_tk_i2c_read_data(info->client, checksum, 6);
input_info(true, &info->client->dev, "%s: ret:%d [%X][%X][%X][%X][%X]\n",
__func__, ret, checksum[0], checksum[1], checksum[2]
, checksum[4], checksum[5]);
info->checksum_h = checksum[4];
info->checksum_l = checksum[5];
return 0;
}
static int abov_tk_fw_write(struct abov_tk_info *info, unsigned char *addrH,
unsigned char *addrL, unsigned char *val)
{
int length = 36, ret = 0;
unsigned char data[36];
data[0] = 0xAC;
data[1] = 0x7A;
memcpy(&data[2], addrH, 1);
memcpy(&data[3], addrL, 1);
memcpy(&data[4], val, 32);
ret = i2c_master_send(info->client, data, length);
if (ret != length) {
input_err(true, &info->client->dev,
"%s: write fail[%x%x], %d\n", __func__, *addrH, *addrL, ret);
return ret;
}
abov_delay(3);
abov_tk_check_busy(info);
return 0;
}
static int abov_tk_fw_mode_enter(struct abov_tk_info *info)
{
unsigned char data[2] = {0xAC, 0x5B};
int ret = 0;
ret = i2c_master_send(info->client, data, 2);
if (ret != 2) {
input_err(true, &info->client->dev, "%s: write fail\n", __func__);
return -1;
}
return 0;
}
static int abov_tk_fw_mode_check(struct abov_tk_info *info)
{
unsigned char buf[1] = {0};
int ret;
ret = abov_tk_i2c_read_data(info->client, buf, 1);
if (ret < 0){
input_err(true, &info->client->dev, "%s: write fail\n", __func__);
return 0;
}
input_info(true, &info->client->dev, "%s: ret:%02X\n",__func__, buf[0]);
#ifdef CONFIG_KEYBOARD_ABOV_TOUCH_T316
if ((buf[0] == ABOV_FLASH_MODE) || (buf[0] == 0x32)) /* support T316, T326 */
return 1;
#else
if (buf[0] == ABOV_FLASH_MODE)
return 1;
#endif
input_err(true, &info->client->dev, "%s: flash mode does not match, %X, %X\n",
__func__, ABOV_FLASH_MODE, buf[0]);
return 0;
}
static int abov_tk_flash_erase(struct abov_tk_info *info)
{
unsigned char data[2] = {0xAC, 0x2D};
int ret = 0;
ret = i2c_master_send(info->client, data, 2);
if (ret != 2) {
input_err(true, &info->client->dev, "%s: write fail\n", __func__);
return -1;
}
return 0;
}
static int abov_tk_fw_mode_exit(struct abov_tk_info *info)
{
unsigned char data[2] = {0xAC, 0xE1};
int ret = 0;
ret = i2c_master_send(info->client, data, 2);
if (ret != 2) {
input_err(true, &info->client->dev, "%s: write fail\n", __func__);
return -1;
}
return 0;
}
static int abov_tk_fw_update(struct abov_tk_info *info, u8 cmd)
{
int ret, ii = 0;
int count;
unsigned short address;
unsigned char addrH, addrL;
unsigned char data[32] = {0, };
input_info(true, &info->client->dev, "%s start\n", __func__);
count = info->firm_size / 32;
address = 0x800;
input_info(true, &info->client->dev, "%s reset\n", __func__);
abov_tk_reset_for_bootmode(info);
abov_delay(ABOV_BOOT_DELAY);
ret = abov_tk_fw_mode_enter(info);
if(ret<0){
input_err(true, &info->client->dev,
"%s:abov_tk_fw_mode_enter fail\n", __func__);
return ret;
}
abov_delay(5);
input_info(true, &info->client->dev, "%s fw_mode_cmd sended\n", __func__);
if (abov_tk_fw_mode_check(info) != 1) {
input_err(true, &info->client->dev, "%s: err, flash mode is not: %d\n", __func__, ret);
return 0;
}
ret = abov_tk_flash_erase(info);
abov_delay(1400);
input_info(true, &info->client->dev, "%s fw_write start\n", __func__);
for (ii = 1; ii < count; ii++) {
/* first 32byte is header */
addrH = (unsigned char)((address >> 8) & 0xFF);
addrL = (unsigned char)(address & 0xFF);
if (cmd == BUILT_IN)
memcpy(data, &info->firm_data_bin->data[ii * 32], 32);
else if (cmd == SDCARD)
memcpy(data, &info->firm_data_ums[ii * 32], 32);
ret = abov_tk_fw_write(info, &addrH, &addrL, data);
if (ret < 0) {
input_err(true, &info->client->dev,
"%s: err, no device : %d\n", __func__, ret);
return ret;
}
address += 0x20;
memset(data, 0, 32);
}
ret = abov_tk_i2c_read_checksum(info);
input_info(true, &info->client->dev, "%s checksum readed\n", __func__);
ret = abov_tk_fw_mode_exit(info);
input_info(true, &info->client->dev, "%s fw_write end\n", __func__);
return ret;
}
static void abov_release_fw(struct abov_tk_info *info, u8 cmd)
{
switch(cmd) {
case BUILT_IN:
release_firmware(info->firm_data_bin);
break;
case SDCARD:
kfree(info->firm_data_ums);
break;
default:
break;
}
}
static int abov_flash_fw(struct abov_tk_info *info, bool probe, u8 cmd)
{
struct i2c_client *client = info->client;
int retry = 2;
int ret;
int block_count;
const u8 *fw_data;
switch(cmd) {
case BUILT_IN:
fw_data = info->firm_data_bin->data;
break;
case SDCARD:
fw_data = info->firm_data_ums;
break;
default:
return -1;
break;
}
block_count = (int)(info->firm_size / 32);
while (retry--) {
ret = abov_tk_fw_update(info, cmd);
if (ret < 0)
break;
#if ABOV_ISP_FIRMUP_ROUTINE
abov_tk_reset_for_bootmode(info);
abov_fw_update(info, fw_data, block_count,
info->pdata->gpio_scl, info->pdata->gpio_sda);
#endif
if ((info->checksum_h != info->checksum_h_bin) ||
(info->checksum_l != info->checksum_l_bin)) {
input_err(true, &client->dev,
"%s checksum fail.(0x%x,0x%x),(0x%x,0x%x) retry:%d\n",
__func__, info->checksum_h, info->checksum_l,
info->checksum_h_bin, info->checksum_l_bin, retry);
ret = -1;
continue;
} else {
input_info(true, &client->dev,"%s checksum successed.\n",__func__);
}
abov_tk_reset_for_bootmode(info);
abov_delay(ABOV_RESET_DELAY);
ret = get_tk_fw_version(info, true);
if (ret) {
input_err(true, &client->dev, "%s fw version read fail\n", __func__);
ret = -1;
continue;
}
if (info->fw_ver == 0) {
input_err(true, &client->dev, "%s fw version fail (0x%x)\n",
__func__, info->fw_ver);
ret = -1;
continue;
}
if ((cmd == BUILT_IN) && (info->fw_ver != info->fw_ver_bin)){
input_err(true, &client->dev, "%s fw version fail 0x%x, 0x%x\n",
__func__, info->fw_ver, info->fw_ver_bin);
ret = -1;
continue;
}
ret = 0;
break;
}
return ret;
}
static ssize_t touchkey_fw_update(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
struct abov_tk_info *info = dev_get_drvdata(dev);
struct i2c_client *client = info->client;
int ret;
u8 cmd;
switch(*buf) {
case 's':
case 'S':
cmd = BUILT_IN;
break;
#ifndef CONFIG_SAMSUNG_PRODUCT_SHIP
case 'i':
case 'I':
cmd = SDCARD;
break;
#endif
default:
info->fw_update_state = 2;
goto touchkey_fw_update_out;
}
ret = abov_load_fw(info, cmd);
if (ret) {
input_err(true, &client->dev,
"%s fw load fail\n", __func__);
info->fw_update_state = 2;
goto touchkey_fw_update_out;
}
info->fw_update_state = 1;
disable_irq(info->irq);
info->enabled = false;
ret = abov_flash_fw(info, false, cmd);
if (info->flip_mode){
abov_mode_enable(client, ABOV_FLIP, CMD_FLIP_ON);
} else {
if (info->glovemode)
abov_mode_enable(client, ABOV_GLOVE, CMD_GLOVE_ON);
}
if (info->keyboard_mode)
abov_mode_enable(client, ABOV_KEYBOARD, CMD_MOBILE_KBD_ON);
info->enabled = true;
enable_irq(info->irq);
if (ret) {
input_err(true, &client->dev, "%s fail\n", __func__);
// info->fw_update_state = 2;
info->fw_update_state = 0;
} else {
input_info(true, &client->dev, "%s success\n", __func__);
info->fw_update_state = 0;
}
abov_release_fw(info, cmd);
touchkey_fw_update_out:
input_info(true, &client->dev, "%s : %d\n", __func__, info->fw_update_state);
return count;
}
static ssize_t touchkey_fw_update_status(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct abov_tk_info *info = dev_get_drvdata(dev);
struct i2c_client *client = info->client;
int count = 0;
input_info(true, &client->dev, "%s : %d\n", __func__, info->fw_update_state);
if (info->fw_update_state == 0)
count = sprintf(buf, "PASS\n");
else if (info->fw_update_state == 1)
count = sprintf(buf, "Downloading\n");
else if (info->fw_update_state == 2)
count = sprintf(buf, "Fail\n");
return count;
}
static ssize_t abov_glove_mode(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
struct abov_tk_info *info = dev_get_drvdata(dev);
struct i2c_client *client = info->client;
int scan_buffer;
int ret;
u8 cmd;
ret = sscanf(buf, "%d", &scan_buffer);
input_info(true, &client->dev, "%s : %d\n", __func__, scan_buffer);
if (ret != 1) {
input_err(true, &client->dev, "%s: cmd read err\n", __func__);
return count;
}
if (!(scan_buffer == 0 || scan_buffer == 1)) {
input_err(true, &client->dev, "%s: wrong command(%d)\n",
__func__, scan_buffer);
return count;
}
if (!info->enabled)
return count;
if (info->glovemode == scan_buffer) {
input_info(true, &client->dev, "%s same command(%d)\n",
__func__, scan_buffer);
return count;
}
if (scan_buffer == 1) {
cmd = CMD_GLOVE_ON;
} else {
cmd = CMD_GLOVE_OFF;
}
ret = abov_mode_enable(client, ABOV_GLOVE, cmd);
if (ret < 0) {
input_err(true, &client->dev, "%s fail(%d)\n", __func__, ret);
return count;
}
info->glovemode = scan_buffer;
return count;
}
static ssize_t abov_glove_mode_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct abov_tk_info *info = dev_get_drvdata(dev);
return sprintf(buf, "%d\n", info->glovemode);
}
static ssize_t keyboard_cover_mode_enable(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct abov_tk_info *info = dev_get_drvdata(dev);
struct i2c_client *client = info->client;
int keyboard_mode_on;
int ret;
u8 cmd;
input_info(true, &client->dev, "%s : Mobile KBD sysfs node called\n",__func__);
sscanf(buf, "%d", &keyboard_mode_on);
input_info(true, &client->dev, "%s : %d\n",
__func__, keyboard_mode_on);
if (!(keyboard_mode_on == 0 || keyboard_mode_on == 1)) {
input_err(true, &client->dev, "%s: wrong command(%d)\n",
__func__, keyboard_mode_on);
return count;
}
if (!info->enabled)
goto out;
if (info->keyboard_mode == keyboard_mode_on) {
input_info(true, &client->dev, "%s same command(%d)\n",
__func__, keyboard_mode_on);
goto out;
}
if (keyboard_mode_on == 1) {
cmd = CMD_MOBILE_KBD_ON;
} else {
cmd = CMD_MOBILE_KBD_OFF;
}
/* mobile keyboard use same register with glove mode */
ret = abov_mode_enable(client, ABOV_KEYBOARD, cmd);
if (ret < 0) {
input_err(true, &client->dev, "%s fail(%d)\n", __func__, ret);
goto out;
}
out:
info->keyboard_mode = keyboard_mode_on;
return count;
}
static ssize_t flip_cover_mode_enable(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct abov_tk_info *info = dev_get_drvdata(dev);
struct i2c_client *client = info->client;
int flip_mode_on;
int ret;
u8 cmd;
sscanf(buf, "%d\n", &flip_mode_on);
input_info(true, &client->dev, "%s : %d\n", __func__, flip_mode_on);
if (!info->enabled)
goto out;
#ifdef CONFIG_TOUCHKEY_GRIP
if (flip_mode_on) {
cmd = 0x10;
ret = abov_tk_i2c_write(info->client, ABOV_SW_RESET, &cmd, 1);
if (ret < 0) {
input_err(true, &client->dev, "%s sw_reset fail(%d)\n", __func__, ret);
}
abov_sar_only_mode(info, 1);
} else {
abov_sar_only_mode(info, 0);
}
#else
/* glove mode control */
if (flip_mode_on) {
cmd = CMD_FLIP_ON;
} else {
if (info->glovemode)
cmd = CMD_GLOVE_ON;
cmd = CMD_FLIP_OFF;
}
if (info->glovemode){
ret = abov_mode_enable(client, ABOV_GLOVE, cmd);
if (ret < 0) {
input_err(true, &client->dev, "%s glove mode fail(%d)\n", __func__, ret);
goto out;
}
} else{
ret = abov_mode_enable(client, ABOV_FLIP, cmd);
if (ret < 0) {
input_err(true, &client->dev, "%s fail(%d)\n", __func__, ret);
goto out;
}
}
#endif
out:
info->flip_mode = flip_mode_on;
return count;
}
#ifdef CONFIG_TOUCHKEY_LIGHT_EFS
static ssize_t touchkey_light_version_read(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct abov_tk_info *info = dev_get_drvdata(dev);
int count;
int crc_cal, crc_efs;
if (efs_read_light_table_version(info) < 0) {
count = sprintf(buf, "NG");
goto out;
} else {
if (info->light_version_efs == info->pdata->dt_light_version) {
if (!check_light_table_crc(info)) {
count = sprintf(buf, "NG_CS");
goto out;
}
} else {
crc_cal = efs_calculate_crc(info);
crc_efs = efs_read_crc(info);
input_info(true, &info->client->dev,
"CRC compare: efs[%d], bin[%d]\n",
crc_cal, crc_efs);
if (crc_cal != crc_efs) {
count = sprintf(buf, "NG_CS");
goto out;
}
}
}
count = sprintf(buf, "%s,%s",
info->light_version_full_efs,
info->light_version_full_bin);
out:
input_info(true, &info->client->dev, "%s: %s\n", __func__, buf);
return count;
}
static ssize_t touchkey_light_update(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
{
struct abov_tk_info *info = dev_get_drvdata(dev);
int ret;
int led_reg;
int window_type = read_window_type();
ret = efs_write(info);
if (ret < 0) {
input_err(true, &info->client->dev, "%s: fail %d\n", __func__, ret);
return -EIO;
}
led_reg = efs_read_light_table_with_default(info, window_type);
if ((led_reg >= LIGHT_REG_MIN_VAL) && (led_reg <= LIGHT_REG_MAX_VAL)) {
change_touch_key_led_brightness(&info->client->dev, led_reg);
input_info(true, &info->client->dev,
"%s: read done for %d\n", __func__, window_type);
} else {
input_err(true, &info->client->dev,
"%s: fail. key led brightness reg is %d\n", __func__, led_reg);
}
return size;
}
static ssize_t touchkey_light_id_compare(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct abov_tk_info *info = dev_get_drvdata(dev);
int count, ret;
int window_type = read_window_type();
if (window_type < 0) {
input_err(true, &info->client->dev,
"%s: window_type:%d, NG\n", __func__, window_type);
return sprintf(buf, "NG");
}
ret = efs_read_light_table(info, window_type);
if (ret < 0) {
count = sprintf(buf, "NG");
} else {
count = sprintf(buf, "OK");
}
input_info(true, &info->client->dev,
"%s: window_type:%d, %s\n", __func__, window_type, buf);
return count;
}
static char* tokenizer(char* str, char delim)
{
static char* str_addr;
char* token = NULL;
if (str != NULL)
str_addr = str;
else if (str_addr == NULL)
return 0;
token = str_addr;
while (true) {
if (!(*str_addr)) {
break;
} else if (*str_addr == delim) {
*str_addr = '\0';
str_addr = str_addr + 1;
break;
}
str_addr++;
}
return token;
}
static ssize_t touchkey_light_table_write(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
{
struct abov_tk_info *info = dev_get_drvdata(dev);
struct light_info table[16];
int ret;
int led_reg;
int window_type;
char *full_version;
char data[150] = {0, };
int i, crc, crc_cal;
char *octa_id;
int table_size = 0;
snprintf(data, sizeof(data), buf);
input_info(true, &info->client->dev, "%s: %s\n",
__func__, data);
full_version = tokenizer(data, ',');
if (!full_version)
return -EIO;
table_size = (int)strlen(full_version) - 8;
input_info(true, &info->client->dev, "%s: version:%s size:%d\n",
__func__, full_version, table_size);
if (table_size < 0 || table_size > 16) {
input_err(true, &info->client->dev, "%s: table_size is unavailable\n", __func__);
return -EIO;
}
if (kstrtoint(tokenizer(NULL, ','), 0, &crc))
return -EIO;
input_info(true, &info->client->dev, "%s: crc:%d\n",
__func__, crc);
if (!crc)
return -EIO;
for (i = 0; i < table_size; i++) {
octa_id = tokenizer(NULL, '_');
if (!octa_id)
break;
if (octa_id[0] >= 'A')
table[i].octa_id = octa_id[0] - 'A' + 0x0A;
else
table[i].octa_id = octa_id[0] - '0';
if (table[i].octa_id < 0 || table[i].octa_id > 0x0F)
break;
if (kstrtoint(tokenizer(NULL, ','), 0, &table[i].led_reg))
break;
}
if (!table_size) {
input_err(true, &info->client->dev, "%s: no data in table\n", __func__);
return -ENODATA;
}
for (i = 0; i < table_size; i++) {
input_info(true, &info->client->dev, "%s: [%d] %X - %x\n",
__func__, i, table[i].octa_id, table[i].led_reg);
}
/* write efs */
ret = efs_write_light_table_version(info, full_version);
if (ret < 0) {
input_err(true, &info->client->dev,
"%s: failed to write table ver %s. %d\n",
__func__, full_version, ret);
return ret;
}
info->light_version_efs = pick_light_table_version(full_version);
for (i = 0; i < table_size; i++) {
ret = efs_write_light_table(info, table[i]);
if (ret < 0)
break;
}
if (ret < 0) {
input_err(true, &info->client->dev,
"%s: failed to write table%d. %d\n",
__func__, i, ret);
return ret;
}
ret = efs_write_light_table_crc(info, crc);
if (ret < 0) {
input_err(true, &info->client->dev,
"%s: failed to write table crc. %d\n",
__func__, ret);
return ret;
}
crc_cal = efs_calculate_crc(info);
input_info(true, &info->client->dev,
"%s: efs crc:%d, caldulated crc:%d\n",
__func__, crc, crc_cal);
if (crc_cal != crc)
return -EIO;
window_type = read_window_type();
led_reg = efs_read_light_table_with_default(info, window_type);
if ((led_reg >= LIGHT_REG_MIN_VAL) && (led_reg <= LIGHT_REG_MAX_VAL)) {
change_touch_key_led_brightness(&info->client->dev, led_reg);
input_info(true, &info->client->dev,
"%s: read done for %d\n", __func__, window_type);
} else {
input_err(true, &info->client->dev,
"%s: fail. key led brightness reg is %d\n", __func__, led_reg);
}
return size;
}
#endif
static DEVICE_ATTR(touchkey_threshold, S_IRUGO, touchkey_threshold_show, NULL);
static DEVICE_ATTR(brightness, S_IRUGO | S_IWUSR | S_IWGRP, NULL,
touchkey_led_control);
#ifdef CONFIG_TOUCHKEY_GRIP
static DEVICE_ATTR(touchkey_grip_threshold, S_IRUGO, touchkey_grip_threshold_show, NULL);
static DEVICE_ATTR(touchkey_total_cap, S_IRUGO, touchkey_total_cap_show, NULL);
static DEVICE_ATTR(sar_enable, S_IRUGO | S_IWUSR | S_IWGRP, NULL, touchkey_sar_enable);
static DEVICE_ATTR(sw_reset, S_IRUGO | S_IWUSR | S_IWGRP, NULL, touchkey_grip_sw_reset);
static DEVICE_ATTR(touchkey_earjack, S_IRUGO | S_IWUSR | S_IWGRP, NULL, touchkey_sensing_change);
static DEVICE_ATTR(touchkey_grip, S_IRUGO, touchkey_grip_show, NULL);
static DEVICE_ATTR(touchkey_grip_baseline, S_IRUGO, touchkey_grip_baseline_show, NULL);
static DEVICE_ATTR(touchkey_grip_raw, S_IRUGO, touchkey_grip_raw_show, NULL);
static DEVICE_ATTR(touchkey_grip_gain, S_IRUGO, touchkey_grip_gain_show, NULL);
static DEVICE_ATTR(touchkey_grip_check, S_IRUGO, touchkey_grip_check_show, NULL);
#ifndef CONFIG_SAMSUNG_PRODUCT_SHIP
static DEVICE_ATTR(touchkey_sar_only_mode, S_IRUGO | S_IWUSR | S_IWGRP ,
NULL, touchkey_mode_change);
static DEVICE_ATTR(touchkey_sar_press_threshold, S_IRUGO | S_IWUSR | S_IWGRP ,
NULL, touchkey_sar_press_threshold_store);
static DEVICE_ATTR(touchkey_sar_release_threshold, S_IRUGO | S_IWUSR | S_IWGRP ,
NULL, touchkey_sar_release_threshold_store);
#endif
#endif
static DEVICE_ATTR(touchkey_recent, S_IRUGO, touchkey_menu_show, NULL);
static DEVICE_ATTR(touchkey_back, S_IRUGO, touchkey_back_show, NULL);
static DEVICE_ATTR(touchkey_recent_raw, S_IRUGO, touchkey_menu_raw_show, NULL);
static DEVICE_ATTR(touchkey_back_raw, S_IRUGO, touchkey_back_raw_show, NULL);
static DEVICE_ATTR(touchkey_chip_name, S_IRUGO, touchkey_chip_name, NULL);
static DEVICE_ATTR(touchkey_firm_version_phone, S_IRUGO, bin_fw_ver, NULL);
static DEVICE_ATTR(touchkey_firm_version_panel, S_IRUGO, read_fw_ver, NULL);
static DEVICE_ATTR(touchkey_firm_update, S_IRUGO | S_IWUSR | S_IWGRP, NULL,
touchkey_fw_update);
static DEVICE_ATTR(touchkey_firm_update_status, S_IRUGO | S_IWUSR | S_IWGRP,
touchkey_fw_update_status, NULL);
static DEVICE_ATTR(glove_mode, S_IRUGO | S_IWUSR | S_IWGRP,
abov_glove_mode_show, abov_glove_mode);
static DEVICE_ATTR(keyboard_mode, S_IRUGO | S_IWUSR | S_IWGRP, NULL,
keyboard_cover_mode_enable);
static DEVICE_ATTR(flip_mode, S_IRUGO | S_IWUSR | S_IWGRP, NULL,
flip_cover_mode_enable);
#ifdef CONFIG_TOUCHKEY_LIGHT_EFS
static DEVICE_ATTR(touchkey_light_version, S_IRUGO, touchkey_light_version_read, NULL);
static DEVICE_ATTR(touchkey_light_update, S_IWUSR | S_IWGRP, NULL, touchkey_light_update);
static DEVICE_ATTR(touchkey_light_id_compare, S_IRUGO, touchkey_light_id_compare, NULL);
static DEVICE_ATTR(touchkey_light_table_write, S_IWUSR | S_IWGRP, NULL, touchkey_light_table_write);
#endif
static struct attribute *sec_touchkey_attributes[] = {
&dev_attr_touchkey_threshold.attr,
&dev_attr_brightness.attr,
#ifdef CONFIG_TOUCHKEY_GRIP
&dev_attr_touchkey_grip_threshold.attr,
&dev_attr_touchkey_total_cap.attr,
&dev_attr_sar_enable.attr,
&dev_attr_sw_reset.attr,
&dev_attr_touchkey_earjack.attr,
&dev_attr_touchkey_grip.attr,
&dev_attr_touchkey_grip_baseline.attr,
&dev_attr_touchkey_grip_raw.attr,
&dev_attr_touchkey_grip_gain.attr,
&dev_attr_touchkey_grip_check.attr,
#ifndef CONFIG_SAMSUNG_PRODUCT_SHIP
&dev_attr_touchkey_sar_only_mode.attr,
&dev_attr_touchkey_sar_press_threshold.attr,
&dev_attr_touchkey_sar_release_threshold.attr,
#endif
#endif
&dev_attr_touchkey_recent.attr,
&dev_attr_touchkey_back.attr,
&dev_attr_touchkey_recent_raw.attr,
&dev_attr_touchkey_back_raw.attr,
&dev_attr_touchkey_chip_name.attr,
&dev_attr_touchkey_firm_version_phone.attr,
&dev_attr_touchkey_firm_version_panel.attr,
&dev_attr_touchkey_firm_update.attr,
&dev_attr_touchkey_firm_update_status.attr,
&dev_attr_glove_mode.attr,
&dev_attr_keyboard_mode.attr,
&dev_attr_flip_mode.attr,
#ifdef CONFIG_TOUCHKEY_LIGHT_EFS
&dev_attr_touchkey_light_version.attr,
&dev_attr_touchkey_light_update.attr,
&dev_attr_touchkey_light_id_compare.attr,
&dev_attr_touchkey_light_table_write.attr,
#endif
NULL,
};
static struct attribute_group sec_touchkey_attr_group = {
.attrs = sec_touchkey_attributes,
};
extern int get_samsung_lcd_attached(void);
static int abov_tk_fw_check(struct abov_tk_info *info)
{
struct i2c_client *client = info->client;
int ret, fw_update=0;
u8 buf;
if (info->pdata->bringup) {
input_info(true, &client->dev, "%s: firmware update skip, bring up\n", __func__);
return 0;
}
ret = abov_load_fw(info, BUILT_IN);
if (ret) {
input_err(true, &client->dev,
"%s fw load fail\n", __func__);
return ret;
}
ret = get_tk_fw_version(info, true);
#ifdef LED_TWINKLE_BOOTING
if(ret)
input_err(true, &client->dev,
"%s: i2c fail...[%d], addr[%d]\n",
__func__, ret, info->client->addr);
input_err(true, &client->dev,
"%s: touchkey driver unload\n", __func__);
if (get_samsung_lcd_attached() == 0) {
input_err(true, &client->dev, "%s : get_samsung_lcd_attached()=0 \n", __func__);
abov_release_fw(info, BUILT_IN);
return ret;
}
#endif
//Check Model No.
ret = abov_tk_i2c_read(client, ABOV_MODEL_NUMBER, &buf, 1);
if (ret < 0) {
input_err(true, &client->dev, "%s fail(%d)\n", __func__, ret);
}
if(info->fw_model_number != buf){
input_info(true, &client->dev, "fw model number = %x ic model number = %x \n", info->fw_model_number, buf);
fw_update = 1;
goto flash_fw;
}
if ((info->fw_ver == 0) || info->fw_ver < info->fw_ver_bin){
input_info(true, &client->dev, "excute tk firmware update (0x%x -> 0x%x\n",
info->fw_ver, info->fw_ver_bin);
fw_update = 1;
}
#ifdef CONFIG_SAMSUNG_PRODUCT_SHIP
if(info->fw_ver >= 0xd0){ //test firmware
input_info(true, &client->dev, "excute tk firmware update (0x%x -> 0x%x\n",
info->fw_ver, info->fw_ver_bin);
fw_update = 1;
}
#endif
flash_fw:
if(fw_update){
ret = abov_flash_fw(info, true, BUILT_IN);
if (ret) {
input_err(true, &client->dev,
"failed to abov_flash_fw (%d)\n", ret);
} else {
input_info(true, &client->dev,
"fw update success\n");
}
}
abov_release_fw(info, BUILT_IN);
return ret;
}
static int abov_led_power(void *data, bool on)
{
struct abov_tk_info *info = (struct abov_tk_info *)data;
struct i2c_client *client = info->client;
int ret = 0;
info->pdata->avdd_vreg = regulator_get(NULL, "vtouch_3.3v");
if (IS_ERR(info->pdata->avdd_vreg)) {
info->pdata->avdd_vreg = NULL;
return ret;
}
if(regulator_is_enabled(info->pdata->avdd_vreg)==on){
input_info(true, &client->dev, "%s %s skip\n", __func__, on ? "on" : "off");
return ret;
}
if (on) {
if (info->pdata->avdd_vreg) {
ret = regulator_enable(info->pdata->avdd_vreg);
if(ret){
input_err(true, &client->dev, "%s : avdd reg enable fail\n", __func__);
return ret;
}
}
} else {
if (info->pdata->avdd_vreg) {
input_info(true, &client->dev, "%s 1\n", __func__);
ret = regulator_disable(info->pdata->avdd_vreg);
if(ret){
input_err(true, &client->dev, "%s : avdd reg disable fail\n", __func__);
return ret;
}
}
}
regulator_put(info->pdata->avdd_vreg);
input_info(true, &client->dev, "%s %s\n", __func__, on ? "on" : "off");
return ret;
}
static int abov_power(void *data, bool on)
{
struct abov_tk_info *info = (struct abov_tk_info *)data;
struct i2c_client *client = info->client;
int ret = 0;
if (gpio_is_valid(info->pdata->gpio_ldo_en)) {
if (on) {
ret = gpio_direction_output(info->pdata->gpio_ldo_en, 1);
} else {
ret = gpio_direction_output(info->pdata->gpio_ldo_en, 0);
}
} else {
info->pdata->dvdd_vreg = regulator_get(NULL, "vtouch_2.8v");
if (IS_ERR(info->pdata->dvdd_vreg)) {
info->pdata->dvdd_vreg = NULL;
input_err(true, &client->dev, "%s : dvdd_vreg get error, ignoring\n",__func__);
}
if (on) {
if (info->pdata->dvdd_vreg) {
ret = regulator_enable(info->pdata->dvdd_vreg);
if(ret){
input_err(true, &client->dev, "%s : dvdd reg enable fail\n", __func__);
return ret;
}
}
} else {
if (info->pdata->dvdd_vreg) {
ret = regulator_disable(info->pdata->dvdd_vreg);
if(ret){
input_err(true, &client->dev, "%s : dvdd reg disable fail\n", __func__);
return ret;
}
}
}
regulator_put(info->pdata->dvdd_vreg);
abov_led_power(info, on);
}
input_info(true, &client->dev, "%s %s\n", __func__, on ? "on" : "off");
return ret;
}
#ifdef CONFIG_VBUS_NOTIFIER
static void abov_set_ta_status(struct abov_tk_info *info)
{
u8 cmd_data = 0x10;
u8 cmd_ta;
int ret = 0;
input_info(true, &info->client->dev, "%s g_ta_connected %d\n", __func__, g_ta_connected);
if (info->enabled == false)
{
input_info(true, &info->client->dev, "%s status of ic is off\n", __func__);
return;
}
if (g_ta_connected) {
cmd_ta = 0x10;
}
else {
cmd_ta = 0x20;
}
ret = abov_tk_i2c_write(info->client, ABOV_TSPTA, &cmd_ta, 1);
if (ret < 0) {
input_err(true, &info->client->dev, "%s fail(%d)\n", __func__, ret);
}
ret = abov_tk_i2c_write(info->client, ABOV_SW_RESET, &cmd_data, 1);
if (ret < 0) {
input_err(true, &info->client->dev, "%s sw reset fail(%d)\n", __func__, ret);
}
}
int abov_touchkey_vbus_notification(struct notifier_block *nb,
unsigned long cmd, void *data)
{
struct abov_tk_info *info = container_of(nb, struct abov_tk_info, vbus_nb);
vbus_status_t vbus_type = *(vbus_status_t *)data;
input_info(true, &info->client->dev, "%s cmd=%lu, vbus_type=%d\n", __func__, cmd, vbus_type);
switch (vbus_type) {
case STATUS_VBUS_HIGH:
input_info(true, &info->client->dev, "%s : attach\n",__func__);
g_ta_connected = true;
break;
case STATUS_VBUS_LOW:
input_info(true, &info->client->dev, "%s : detach\n",__func__);
g_ta_connected = false;
break;
default:
break;
}
abov_set_ta_status(info);
return 0;
}
#endif
#if 1
static int abov_pinctrl_configure(struct abov_tk_info *info,
bool active)
{
struct pinctrl_state *set_state;
int retval;
if (active) {
set_state =
pinctrl_lookup_state(info->pinctrl,
"on_irq");
if (IS_ERR(set_state)) {
input_err(true, &info->client->dev,
"%s: cannot get ts pinctrl active state\n", __func__);
return PTR_ERR(set_state);
}
} else {
set_state =
pinctrl_lookup_state(info->pinctrl,
"off_irq");
if (IS_ERR(set_state)) {
input_err(true, &info->client->dev,
"%s: cannot get gpiokey pinctrl sleep state\n", __func__);
return PTR_ERR(set_state);
}
}
retval = pinctrl_select_state(info->pinctrl, set_state);
if (retval) {
input_err(true, &info->client->dev,
"%s: cannot set ts pinctrl active state\n", __func__);
return retval;
}
return 0;
}
#endif
static int abov_gpio_reg_init(struct device *dev,
struct abov_touchkey_platform_data *pdata)
{
int ret = 0;
ret = gpio_request(pdata->gpio_int, "tkey_gpio_int");
if(ret < 0){
input_err(true, dev,
"unable to request gpio_int\n");
return ret;
}
pdata->power = abov_power;
return ret;
}
#ifdef CONFIG_OF
static int abov_parse_dt(struct device *dev,
struct abov_touchkey_platform_data *pdata)
{
struct device_node *np = dev->of_node;
int ret;
#ifdef CONFIG_TOUCHKEY_LIGHT_EFS
int i;
u32 tmp[LIGHT_TABLE_MAX] = {0, };
#endif
pdata->gpio_int = of_get_named_gpio(np, "abov,irq-gpio", 0);
if(pdata->gpio_int < 0){
input_err(true, dev, "unable to get gpio_int\n");
return pdata->gpio_int;
}
pdata->gpio_scl = of_get_named_gpio(np, "abov,scl-gpio", 0);
if(pdata->gpio_scl < 0){
input_err(true, dev, "unable to get gpio_scl\n");
return pdata->gpio_scl;
}
pdata->gpio_sda = of_get_named_gpio(np, "abov,sda-gpio", 0);
if(pdata->gpio_sda < 0){
input_err(true, dev, "unable to get gpio_sda\n");
return pdata->gpio_sda;
}
pdata->sub_det = of_get_named_gpio(np, "abov,sub-det",0);
if(pdata->sub_det < 0){
input_info(true, dev, "unable to get sub_det\n");
}else{
input_info(true, dev, "%s: sub_det:%d\n",__func__,pdata->sub_det);
}
if (of_property_read_bool(np, "abov,use_gpio_ldo")) {
input_info(true, dev, "%s: use use_gpio_ldo\n", __func__);
pdata->gpio_ldo_en = of_get_named_gpio(np, "abov,gpio_ldo_en", 0);
if (gpio_is_valid(pdata->gpio_ldo_en)) {
gpio_request_one(pdata->gpio_ldo_en, GPIOF_OUT_INIT_LOW, "TOUCHKEY_GPIO_OUTPUT_LOW");
} else {
input_err(true, dev, "failed to get tsp_ldo_en\n");
return -EINVAL;
}
}
ret = of_property_read_string(np, "abov,fw_path", (const char **)&pdata->fw_path);
if (ret) {
input_err(true, dev, "touchkey:failed to read fw_path %d\n", ret);
pdata->fw_path = TK_FW_PATH_BIN;
}
input_info(true, dev, "%s: fw path %s\n", __func__, pdata->fw_path);
pdata->boot_on_ldo = of_property_read_bool(np, "abov,boot-on-ldo");
pdata->bringup = of_property_read_bool(np, "abov,bringup");
pdata->ta_notifier = of_property_read_bool(np, "abov,ta-notifier");
input_info(true, dev, "%s: gpio_int:%d, gpio_scl:%d, gpio_sda:%d\n",
__func__, pdata->gpio_int, pdata->gpio_scl,
pdata->gpio_sda);
#ifdef CONFIG_TOUCHKEY_LIGHT_EFS
ret = of_property_read_u32_array(np, "abov,light_version", tmp, 2);
if (ret) {
input_err(true, dev, "touchkey:failed to read light_version %d\n", ret);
}
pdata->dt_light_version = tmp[0];
pdata->dt_light_table = tmp[1];
input_info(true, dev, "%s: light_version:%d, light_table:%d\n",
__func__, pdata->dt_light_version, pdata->dt_light_table);
if(pdata->dt_light_table > 0){
ret = of_property_read_u32_array(np, "abov,octa_id", tmp, pdata->dt_light_table);
if (ret) {
input_err(true, dev, "touchkey:failed to read light_version %d\n", ret);
}
for(i = 0 ; i < pdata->dt_light_table ; i++){
tkey_light_reg_table[i].octa_id = tmp[i];
}
ret = of_property_read_u32_array(np, "abov,light_reg", tmp, pdata->dt_light_table);
if (ret) {
input_err(true, dev, "touchkey:failed to read light_version %d\n", ret);
}
for(i = 0 ; i < pdata->dt_light_table ; i++){
tkey_light_reg_table[i].led_reg = tmp[i];
}
for(i = 0 ; i < pdata->dt_light_table ; i++){
input_info(true, dev, "%s: tkey_light_reg_table: %d 0x%02x\n",
__func__, tkey_light_reg_table[i].octa_id, tkey_light_reg_table[i].led_reg);
}
}
#endif
return 0;
}
#else
static int abov_parse_dt(struct device *dev,
struct abov_touchkey_platform_data *pdata)
{
return -ENODEV;
}
#endif
static int abov_tk_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct abov_tk_info *info;
struct input_dev *input_dev;
int ret = 0;
#ifdef CONFIG_TOUCHKEY_LIGHT_EFS
int i;
char tmp[2] = {0, };
#endif
#ifdef LED_TWINKLE_BOOTING
if (get_samsung_lcd_attached() == 0) {
input_err(true, &client->dev, "%s : get_samsung_lcd_attached()=0 \n", __func__);
return -EIO;
}
#endif
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
input_err(true, &client->dev,
"i2c_check_functionality fail\n");
return -EIO;
}
info = kzalloc(sizeof(struct abov_tk_info), GFP_KERNEL);
if (!info) {
input_err(true, &client->dev, "Failed to allocate memory\n");
ret = -ENOMEM;
goto err_alloc;
}
input_dev = input_allocate_device();
if (!input_dev) {
input_err(true, &client->dev,
"Failed to allocate memory for input device\n");
ret = -ENOMEM;
goto err_input_alloc;
}
info->client = client;
info->input_dev = input_dev;
#ifdef CONFIG_TOUCHKEY_GRIP
wake_lock_init(&info->touckey_wake_lock, WAKE_LOCK_SUSPEND, "touchkey wake lock");
#endif
if (client->dev.of_node) {
struct abov_touchkey_platform_data *pdata;
pdata = devm_kzalloc(&client->dev,
sizeof(struct abov_touchkey_platform_data), GFP_KERNEL);
if (!pdata) {
input_err(true, &client->dev, "Failed to allocate memory\n");
ret = -ENOMEM;
goto err_config;
}
ret = abov_parse_dt(&client->dev, pdata);
if (ret){
input_err(true, &client->dev, "failed to abov_parse_dt\n");
ret = -ENOMEM;
goto err_config;
}
info->pdata = pdata;
} else
info->pdata = client->dev.platform_data;
if (info->pdata == NULL) {
input_err(true, &client->dev, "failed to get platform data\n");
goto err_config;
}
#if 1
/* Get pinctrl if target uses pinctrl */
info->pinctrl = devm_pinctrl_get(&client->dev);
if (IS_ERR(info->pinctrl)) {
if (PTR_ERR(info->pinctrl) == -EPROBE_DEFER)
goto err_config;
input_err(true, &client->dev, "%s: Target does not use pinctrl\n", __func__);
info->pinctrl = NULL;
}
if (info->pinctrl) {
ret = abov_pinctrl_configure(info, true);
if (ret)
input_err(true, &client->dev,
"%s: cannot set ts pinctrl active state\n", __func__);
}
/* sub-det pinctrl */
if (gpio_is_valid(info->pdata->sub_det)) {
info->pinctrl_det = devm_pinctrl_get(&client->dev);
if (IS_ERR(info->pinctrl_det)) {
input_err(true, &client->dev, "%s: Failed to get pinctrl\n", __func__);
goto err_config;
}
info->pins_default = pinctrl_lookup_state(info->pinctrl_det, "sub_det");
if (IS_ERR(info->pins_default)) {
input_err(true, &client->dev, "%s: Failed to get pinctrl state\n", __func__);
devm_pinctrl_put(info->pinctrl_det);
goto err_config;
}
ret = pinctrl_select_state(info->pinctrl_det, info->pins_default);
if (ret < 0)
input_err(true, &client->dev, "%s: Failed to configure sub_det pin\n", __func__);
}
#endif
ret = abov_gpio_reg_init(&client->dev, info->pdata);
if(ret){
input_err(true, &client->dev, "failed to init reg\n");
goto pwr_config;
}
if (info->pdata->power)
info->pdata->power(info, true);
if(!info->pdata->boot_on_ldo)
abov_delay(ABOV_RESET_DELAY);
if (gpio_is_valid(info->pdata->sub_det)) {
ret = gpio_get_value(info->pdata->sub_det);
if (ret) {
input_err(true, &client->dev, "Device wasn't connected to board \n");
ret = -ENODEV;
goto err_i2c_check;
}
}
info->enabled = true;
info->irq = -1;
client->irq = gpio_to_irq(info->pdata->gpio_int);
mutex_init(&info->lock);
info->input_event = info->pdata->input_event;
info->touchkey_count = sizeof(touchkey_keycode) / sizeof(int);
i2c_set_clientdata(client, info);
ret = abov_tk_fw_check(info);
if (ret) {
input_err(true, &client->dev,
"failed to firmware check (%d)\n", ret);
goto err_reg_input_dev;
}
ret = get_tk_fw_version(info, false);
if (ret < 0) {
input_err(true, &client->dev, "%s read fail\n", __func__);
goto err_reg_input_dev;
}
snprintf(info->phys, sizeof(info->phys),
"%s/input0", dev_name(&client->dev));
input_dev->name = "sec_touchkey";
input_dev->phys = info->phys;
input_dev->id.bustype = BUS_HOST;
input_dev->dev.parent = &client->dev;
#if 1 //def CONFIG_INPUT_ENABLED
input_dev->open = abov_tk_input_open;
input_dev->close = abov_tk_input_close;
#endif
set_bit(EV_KEY, input_dev->evbit);
set_bit(KEY_RECENT, input_dev->keybit);
set_bit(KEY_BACK, input_dev->keybit);
set_bit(KEY_CP_GRIP, input_dev->keybit);
set_bit(EV_LED, input_dev->evbit);
set_bit(LED_MISC, input_dev->ledbit);
input_set_drvdata(input_dev, info);
ret = input_register_device(input_dev);
if (ret) {
input_err(true, &client->dev, "failed to register input dev (%d)\n",
ret);
goto err_reg_input_dev;
}
if (!info->pdata->irq_flag) {
input_err(true, &client->dev, "no irq_flag\n");
ret = request_threaded_irq(client->irq, NULL, abov_tk_interrupt,
IRQF_TRIGGER_FALLING | IRQF_ONESHOT, ABOV_TK_NAME, info);
} else {
ret = request_threaded_irq(client->irq, NULL, abov_tk_interrupt,
info->pdata->irq_flag, ABOV_TK_NAME, info);
}
if (ret < 0) {
input_err(true, &client->dev, "Failed to register interrupt\n");
goto err_req_irq;
}
info->irq = client->irq;
#ifdef CONFIG_HAS_EARLYSUSPEND
info->early_suspend.level = EARLY_SUSPEND_LEVEL_STOP_DRAWING;
info->early_suspend.suspend = abov_tk_early_suspend;
info->early_suspend.resume = abov_tk_late_resume;
register_early_suspend(&info->early_suspend);
#endif
info->dev = sec_device_create(info, "sec_touchkey");
if (IS_ERR(info->dev))
input_err(true, &client->dev,
"Failed to create device for the touchkey sysfs\n");
ret = sysfs_create_group(&info->dev ->kobj,
&sec_touchkey_attr_group);
if (ret)
input_err(true, &client->dev, "Failed to create sysfs group\n");
ret = sysfs_create_link(&info->dev ->kobj,
&info->input_dev->dev.kobj, "input");
if (ret < 0) {
input_err(true, &client->dev,
"%s: Failed to create input symbolic link\n",
__func__);
}
#ifdef LED_TWINKLE_BOOTING
if (get_samsung_lcd_attached() == 0) {
input_err(true, &client->dev,
"%s : get_samsung_lcd_attached()=0, so start LED twinkle \n", __func__);
INIT_DELAYED_WORK(&info->led_twinkle_work, led_twinkle_work);
info->led_twinkle_check = 1;
schedule_delayed_work(&info->led_twinkle_work, msecs_to_jiffies(400));
}
#endif
#ifdef CONFIG_TOUCHKEY_GRIP
info->sar_sensing = 1;
device_init_wakeup(&client->dev, true);
#endif
input_err(true, &client->dev, "%s done\n", __func__);
#ifdef CONFIG_TOUCHKEY_GRIP
if (lpcharge == 1) {
disable_irq(info->irq);
input_err(true, &client->dev, "%s disable_irq\n", __func__);
abov_sar_only_mode(info, 1);
}
#endif
#ifdef CONFIG_VBUS_NOTIFIER
if (info->pdata->ta_notifier) {
vbus_notifier_register(&info->vbus_nb, abov_touchkey_vbus_notification,
VBUS_NOTIFY_DEV_CHARGER);
}
#endif
#ifdef CONFIG_TOUCHKEY_LIGHT_EFS
INIT_DELAYED_WORK(&info->efs_open_work, touchkey_efs_open_work);
info->light_table_crc = info->pdata->dt_light_version;
sprintf(info->light_version_full_bin, "T%d.", info->pdata->dt_light_version);
for (i = 0; i < info->pdata->dt_light_table; i++) {
info->light_table_crc += tkey_light_reg_table[i].octa_id;
info->light_table_crc += tkey_light_reg_table[i].led_reg;
snprintf(tmp, 2, "%X", tkey_light_reg_table[i].octa_id);
strncat(info->light_version_full_bin, tmp, 1);
}
input_info(true, &client->dev, "%s: light version of kernel : %s\n",
__func__, info->light_version_full_bin);
schedule_delayed_work(&info->efs_open_work, msecs_to_jiffies(2000));
#endif
return 0;
err_req_irq:
input_unregister_device(input_dev);
err_reg_input_dev:
mutex_destroy(&info->lock);
gpio_free(info->pdata->gpio_int);
err_i2c_check:
if (info->pdata->power)
info->pdata->power(info, false);
pwr_config:
err_config:
#ifdef CONFIG_TOUCHKEY_GRIP
wake_lock_destroy(&info->touckey_wake_lock);
#endif
input_free_device(input_dev);
err_input_alloc:
kfree(info);
err_alloc:
input_err(true, &client->dev, "%s fail\n",__func__);
return ret;
}
#ifdef LED_TWINKLE_BOOTING
static void led_twinkle_work(struct work_struct *work)
{
struct abov_tk_info *info = container_of(work, struct abov_tk_info,
led_twinkle_work.work);
static bool led_on = 1;
static int count = 0;
input_info(true, &info->client->dev, "%s, on=%d, c=%d\n",__func__, led_on, count++ );
if(info->led_twinkle_check == 1){
touchkey_led_set(info,led_on);
if(led_on) led_on = 0;
else led_on = 1;
schedule_delayed_work(&info->led_twinkle_work, msecs_to_jiffies(400));
}else{
if(led_on == 0)
touchkey_led_set(info, 0);
}
}
#endif
static int abov_tk_remove(struct i2c_client *client)
{
struct abov_tk_info *info = i2c_get_clientdata(client);
/* if (info->enabled)
info->pdata->power(0);
*/
info->enabled = false;
#ifdef CONFIG_TOUCHKEY_GRIP
device_init_wakeup(&client->dev, false);
wake_lock_destroy(&info->touckey_wake_lock);
#endif
#ifdef CONFIG_HAS_EARLYSUSPEND
unregister_early_suspend(&info->early_suspend);
#endif
if (info->irq >= 0)
free_irq(info->irq, info);
input_unregister_device(info->input_dev);
input_free_device(info->input_dev);
kfree(info);
return 0;
}
static void abov_tk_shutdown(struct i2c_client *client)
{
struct abov_tk_info *info = i2c_get_clientdata(client);
u8 cmd = CMD_LED_OFF;
input_info(true, &client->dev, "Inside abov_tk_shutdown \n");
if (info->enabled){
disable_irq(info->irq);
abov_tk_i2c_write(client, ABOV_BTNSTATUS, &cmd, 1);
info->pdata->power(info, false);
}
info->enabled = false;
#ifdef CONFIG_TOUCHKEY_LIGHT_EFS
cancel_delayed_work(&info->efs_open_work);
#endif
// just power off.
// if (info->irq >= 0)
// free_irq(info->irq, info);
// kfree(info);
}
#if defined(CONFIG_PM) && !defined(CONFIG_TOUCHKEY_GRIP)
static int abov_tk_suspend(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct abov_tk_info *info = i2c_get_clientdata(client);
if (!info->enabled) {
input_info(true, &client->dev, "%s: already power off\n", __func__);
return 0;
}
input_info(true, &client->dev, "%s: users=%d\n", __func__,
info->input_dev->users);
disable_irq(info->irq);
info->enabled = false;
release_all_fingers(info);
if (info->pdata->power)
info->pdata->power(info, false);
return 0;
}
static int abov_tk_resume(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct abov_tk_info *info = i2c_get_clientdata(client);
u8 led_data;
if (info->enabled) {
input_info(true, &client->dev, "%s: already power on\n", __func__);
return 0;
}
input_info(true, &info->client->dev, "%s: users=%d\n", __func__,
info->input_dev->users);
if (info->pdata->power) {
info->pdata->power(info, true);
abov_delay(ABOV_RESET_DELAY);
} else /* touchkey on by i2c */
get_tk_fw_version(info, true);
info->enabled = true;
if (abov_touchled_cmd_reserved && \
abov_touchkey_led_status == CMD_LED_ON) {
abov_touchled_cmd_reserved = 0;
led_data=abov_touchkey_led_status;
abov_tk_i2c_write(client, ABOV_BTNSTATUS, &led_data, 1);
input_info(true, &info->client->dev, "%s: LED reserved on\n", __func__);
}
enable_irq(info->irq);
return 0;
}
#endif
#ifdef CONFIG_HAS_EARLYSUSPEND
static void abov_tk_early_suspend(struct early_suspend *h)
{
struct abov_tk_info *info;
info = container_of(h, struct abov_tk_info, early_suspend);
abov_tk_suspend(&info->client->dev);
}
static void abov_tk_late_resume(struct early_suspend *h)
{
struct abov_tk_info *info;
info = container_of(h, struct abov_tk_info, early_suspend);
abov_tk_resume(&info->client->dev);
}
#endif
#if 1//def CONFIG_INPUT_ENABLED
static int abov_tk_input_open(struct input_dev *dev)
{
struct abov_tk_info *info = input_get_drvdata(dev);
input_info(true, &info->client->dev, "%s: users=%d, v:0x%02x, g(%d), f(%d), k(%d)\n", __func__,
info->input_dev->users, info->fw_ver, info->flip_mode, info->glovemode, info->keyboard_mode);
#ifdef CONFIG_TOUCHKEY_GRIP
if (lpcharge == 1) {
input_info(true, &info->client->dev, "%s(lpcharge): sar_enable(%d)\n", __func__, info->sar_enable);
return 0;
}
input_info(true, &info->client->dev, "%s: sar_enable(%d)\n", __func__, info->sar_enable);
/* abov_led_power(info, true); */
if (info->flip_mode)
abov_sar_only_mode(info, 1);
else
abov_sar_only_mode(info, 0);
if (device_may_wakeup(&info->client->dev))
disable_irq_wake(info->irq );
#else
abov_tk_resume(&info->client->dev);
if (info->pinctrl)
abov_pinctrl_configure(info, true);
#ifdef CONFIG_VBUS_NOTIFIER
if (info->pdata->ta_notifier && g_ta_connected) {
abov_set_ta_status(info);
}
#endif
if (info->flip_mode){
abov_mode_enable(info->client, ABOV_FLIP, CMD_FLIP_ON);
} else {
if (info->glovemode)
abov_mode_enable(info->client, ABOV_GLOVE, CMD_GLOVE_ON);
}
if (info->keyboard_mode)
abov_mode_enable(info->client, ABOV_KEYBOARD, CMD_MOBILE_KBD_ON);
#endif
return 0;
}
static void abov_tk_input_close(struct input_dev *dev)
{
struct abov_tk_info *info = input_get_drvdata(dev);
input_info(true, &info->client->dev, "%s: users=%d\n", __func__,
info->input_dev->users);
#ifdef CONFIG_TOUCHKEY_GRIP
input_info(true, &info->client->dev, "%s: sar_enable(%d)\n", __func__, info->sar_enable);
abov_sar_only_mode(info, 1);
if (device_may_wakeup(&info->client->dev))
enable_irq_wake(info->irq );
/* abov_led_power(info, false); */
#else
abov_tk_suspend(&info->client->dev);
if (info->pinctrl)
abov_pinctrl_configure(info, false);
#endif
#ifdef LED_TWINKLE_BOOTING
info->led_twinkle_check = 0;
#endif
}
#endif
#if 0//defined(CONFIG_PM) && !defined(CONFIG_HAS_EARLYSUSPEND) &&!defined(CONFIG_INPUT_ENABLED)
static const struct dev_pm_ops abov_tk_pm_ops = {
.suspend = abov_tk_suspend,
.resume = abov_tk_resume,
};
#endif
static const struct i2c_device_id abov_tk_id[] = {
{ABOV_TK_NAME, 0},
{}
};
MODULE_DEVICE_TABLE(i2c, abov_tk_id);
#ifdef CONFIG_OF
static struct of_device_id abov_match_table[] = {
{ .compatible = "abov,mc96ft18xx",},
{ },
};
#else
#define abov_match_table NULL
#endif
static struct i2c_driver abov_tk_driver = {
.probe = abov_tk_probe,
.remove = abov_tk_remove,
.shutdown = abov_tk_shutdown,
.driver = {
.name = ABOV_TK_NAME,
.of_match_table = abov_match_table,
#if 0//defined(CONFIG_PM) && !defined(CONFIG_HAS_EARLYSUSPEND) &&!defined(CONFIG_INPUT_ENABLED)
.pm = &abov_tk_pm_ops,
#endif
},
.id_table = abov_tk_id,
};
static int __init touchkey_init(void)
{
#if defined(CONFIG_BATTERY_SAMSUNG) && !defined(CONFIG_TOUCHKEY_GRIP)
if (lpcharge == 1) {
pr_notice("%s : Do not load driver due to : lpm %d\n",
__func__, lpcharge);
return 0;
}
#endif
return i2c_add_driver(&abov_tk_driver);
}
static void __exit touchkey_exit(void)
{
i2c_del_driver(&abov_tk_driver);
}
module_init(touchkey_init);
module_exit(touchkey_exit);
/* Module information */
MODULE_AUTHOR("Samsung Electronics");
MODULE_DESCRIPTION("Touchkey driver for Abov MF18xx chip");
MODULE_LICENSE("GPL");