1923 lines
48 KiB
C
1923 lines
48 KiB
C
/*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
*/
|
|
|
|
#include <linux/module.h>
|
|
#include <linux/init.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/i2c.h>
|
|
#include <linux/mutex.h>
|
|
#include <linux/kdev_t.h>
|
|
#include <linux/fs.h>
|
|
#include <linux/input.h>
|
|
#include <linux/workqueue.h>
|
|
#include <linux/irq.h>
|
|
#include <linux/delay.h>
|
|
#include <linux/sched.h>
|
|
#include <linux/kthread.h>
|
|
#include <linux/errno.h>
|
|
#include <linux/wakelock.h>
|
|
#include <linux/interrupt.h>
|
|
#include <linux/gpio.h>
|
|
#include <linux/fs.h>
|
|
#include <linux/uaccess.h>
|
|
#include <linux/regulator/consumer.h>
|
|
#include <linux/of_gpio.h>
|
|
|
|
#include <linux/sensor/sensors_core.h>
|
|
#include "stk3013.h"
|
|
|
|
#define DRIVER_VERSION "3.10.0_ps_only_20150508"
|
|
|
|
/* Driver Settings */
|
|
#define STK_INT_PS_MODE 1 /* 1, 2, or 3 */
|
|
#undef STK_CHK_REG
|
|
|
|
#define PROX_READ_NUM 40
|
|
|
|
/* Define Register Map */
|
|
#define STK_STATE_REG 0x00
|
|
#define STK_PSCTRL_REG 0x01
|
|
#define STK_LEDCTRL_REG 0x03
|
|
#define STK_INT_REG 0x04
|
|
#define STK_WAIT_REG 0x05
|
|
#define STK_THDH1_PS_REG 0x06
|
|
#define STK_THDH2_PS_REG 0x07
|
|
#define STK_THDL1_PS_REG 0x08
|
|
#define STK_THDL2_PS_REG 0x09
|
|
#define STK_FLAG_REG 0x10
|
|
#define STK_DATA1_PS_REG 0x11
|
|
#define STK_DATA2_PS_REG 0x12
|
|
#define STK_DATA1_OFFSET_REG 0x15
|
|
#define STK_DATA2_OFFSET_REG 0x16
|
|
#define STK_DATA1_IR_REG 0x17
|
|
#define STK_DATA2_IR_REG 0x18
|
|
#define STK_PDT_ID_REG 0x3E
|
|
#define STK_RSRVD_REG 0x3F
|
|
#define STK_SW_RESET_REG 0x80
|
|
|
|
#define STK_GSCTRL_REG 0x1A
|
|
#define STK_FLAG2_REG 0x1C
|
|
|
|
/* Define state reg */
|
|
#define STK_STATE_EN_IRS_SHIFT 7
|
|
#define STK_STATE_EN_AK_SHIFT 6
|
|
#define STK_STATE_EN_ASO_SHIFT 5
|
|
#define STK_STATE_EN_IRO_SHIFT 4
|
|
#define STK_STATE_EN_WAIT_SHIFT 2
|
|
#define STK_STATE_EN_PS_SHIFT 0
|
|
|
|
#define STK_STATE_EN_IRS_MASK 0x80
|
|
#define STK_STATE_EN_AK_MASK 0x40
|
|
#define STK_STATE_EN_ASO_MASK 0x20
|
|
#define STK_STATE_EN_IRO_MASK 0x10
|
|
#define STK_STATE_EN_WAIT_MASK 0x04
|
|
#define STK_STATE_EN_PS_MASK 0x01
|
|
|
|
/* Define PS ctrl reg */
|
|
#define STK_PS_PRS_SHIFT 6
|
|
#define STK_PS_GAIN_SHIFT 4
|
|
#define STK_PS_IT_SHIFT 0
|
|
|
|
#define STK_PS_PRS_MASK 0xC0
|
|
#define STK_PS_GAIN_MASK 0x30
|
|
#define STK_PS_IT_MASK 0x0F
|
|
|
|
/* Define LED ctrl reg */
|
|
#define STK_LED_IRDR_SHIFT 6
|
|
#define STK_LED_DT_SHIFT 0
|
|
|
|
#define STK_LED_IRDR_MASK 0xC0
|
|
#define STK_LED_DT_MASK 0x3F
|
|
|
|
/* Define interrupt reg */
|
|
#define STK_INT_CTRL_SHIFT 7
|
|
#define STK_INT_OUI_SHIFT 4
|
|
#define STK_INT_PS_SHIFT 0
|
|
|
|
#define STK_INT_CTRL_MASK 0x80
|
|
#define STK_INT_OUI_MASK 0x10
|
|
#define STK_INT_PS_MASK 0x07
|
|
|
|
/* Define flag reg */
|
|
#define STK_FLG_PSDR_SHIFT 6
|
|
#define STK_FLG_PSINT_SHIFT 4
|
|
#define STK_FLG_OUI_SHIFT 2
|
|
#define STK_FLG_IR_RDY_SHIFT 1
|
|
#define STK_FLG_NF_SHIFT 0
|
|
|
|
#define STK_FLG_PSDR_MASK 0x40
|
|
#define STK_FLG_PSINT_MASK 0x10
|
|
#define STK_FLG_OUI_MASK 0x04
|
|
#define STK_FLG_IR_RDY_MASK 0x02
|
|
#define STK_FLG_NF_MASK 0x01
|
|
|
|
#define VENDOR "SENSORTEK"
|
|
#define CHIP_ID "STK3013"
|
|
#define MODULE_NAME "proximity_sensor"
|
|
|
|
#define STK3310SA_PID 0x17
|
|
#define STK3311SA_PID 0x1E
|
|
#define STK3311WV_PID 0x1D
|
|
|
|
#define PROXIMITY_FOR_TEST /* for HW to tune up */
|
|
|
|
#define PROXIMITY_CALIBRATION
|
|
#ifdef PROXIMITY_CALIBRATION
|
|
#define CALIBRATION_FILE_PATH "/efs/FactoryApp/prox_cal"
|
|
#endif
|
|
|
|
enum {
|
|
OFF = 0,
|
|
ON,
|
|
};
|
|
|
|
struct stk3013_data {
|
|
struct i2c_client *client;
|
|
struct stk3013_platform_data *pdata;
|
|
int32_t irq;
|
|
struct work_struct stk_work;
|
|
struct workqueue_struct *stk_wq;
|
|
uint16_t ir_code;
|
|
uint8_t psctrl_reg;
|
|
uint8_t ledctrl_reg;
|
|
uint8_t state_reg;
|
|
int int_pin;
|
|
uint8_t wait_reg;
|
|
uint8_t int_reg;
|
|
uint16_t ps_thd_h;
|
|
uint16_t ps_thd_l;
|
|
uint16_t ps_default_thd_h;
|
|
uint16_t ps_default_thd_l;
|
|
uint16_t ps_cancel_thd_h;
|
|
uint16_t ps_cancel_thd_l;
|
|
uint16_t ps_cal_skip_adc;
|
|
uint16_t ps_cal_fail_adc;
|
|
uint16_t ps_default_offset;
|
|
uint16_t ps_offset;
|
|
unsigned int cal_result;
|
|
struct mutex io_lock;
|
|
struct input_dev *ps_input_dev;
|
|
bool ps_enabled;
|
|
bool re_enable_ps;
|
|
struct wake_lock ps_wakelock;
|
|
ktime_t ps_poll_delay;
|
|
bool first_boot;
|
|
atomic_t recv_reg;
|
|
uint8_t pid;
|
|
uint8_t p_wv_r_bd_with_co;
|
|
struct regulator *vdd;
|
|
struct regulator *vio;
|
|
struct device *ps_dev;
|
|
struct hrtimer prox_timer;
|
|
ktime_t prox_poll_delay;
|
|
struct workqueue_struct *prox_wq;
|
|
struct work_struct work_prox;
|
|
int avg[3];
|
|
};
|
|
|
|
static int32_t stk3013_enable_ps(struct device *dev, uint8_t enable,
|
|
uint8_t validate_reg);
|
|
static int32_t stk3013_set_ps_thd_l(struct stk3013_data *ps_data,
|
|
uint16_t thd_l);
|
|
static int32_t stk3013_set_ps_thd_h(struct stk3013_data *ps_data,
|
|
uint16_t thd_h);
|
|
static int32_t stk3013_set_ps_offset(struct stk3013_data *ps_data,
|
|
uint16_t ps_offset);
|
|
#ifdef PROXIMITY_CALIBRATION
|
|
static int check_calibration_offset(struct stk3013_data *ps_data);
|
|
#endif
|
|
#ifdef STK_CHK_REG
|
|
static int stk3013_validate_n_handle(struct i2c_client *client);
|
|
#endif
|
|
static int stk3013_regulator_onoff(struct device *dev, bool onoff);
|
|
static int32_t stk3013_init_all_setting(struct i2c_client *client,
|
|
struct stk3013_platform_data *plat_data);
|
|
|
|
static int stk3013_i2c_read_data(struct i2c_client *client,
|
|
unsigned char command, int length, unsigned char *values)
|
|
{
|
|
uint8_t retry;
|
|
int ret;
|
|
struct i2c_msg msgs[] = {
|
|
{
|
|
.addr = client->addr,
|
|
.flags = 0,
|
|
.len = 1,
|
|
.buf = &command,
|
|
},
|
|
{
|
|
.addr = client->addr,
|
|
.flags = I2C_M_RD,
|
|
.len = length,
|
|
.buf = values,
|
|
},
|
|
};
|
|
|
|
for (retry = 0; retry < 5; retry++) {
|
|
ret = i2c_transfer(client->adapter, msgs, 2);
|
|
if (ret == 2)
|
|
break;
|
|
}
|
|
|
|
if (retry >= 5) {
|
|
SENSOR_ERR("i2c read fail, err=%d\n", ret);
|
|
return -EIO;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int stk3013_i2c_write_data(struct i2c_client *client,
|
|
unsigned char command, int length, unsigned char *values)
|
|
{
|
|
int retry;
|
|
int ret;
|
|
unsigned char data[11];
|
|
struct i2c_msg msg;
|
|
int index;
|
|
|
|
if (!client)
|
|
return -EINVAL;
|
|
else if (length >= 10) {
|
|
SENSOR_ERR("length %d exceeds 10\n", length);
|
|
return -EINVAL;
|
|
}
|
|
|
|
data[0] = command;
|
|
for (index = 1; index <= length; index++)
|
|
data[index] = values[index-1];
|
|
|
|
msg.addr = client->addr;
|
|
msg.flags = 0;
|
|
msg.len = length+1;
|
|
msg.buf = data;
|
|
|
|
for (retry = 0; retry < 5; retry++) {
|
|
ret = i2c_transfer(client->adapter, &msg, 1);
|
|
if (ret == 1)
|
|
break;
|
|
}
|
|
|
|
if (retry >= 5) {
|
|
SENSOR_ERR("i2c write fail, err=%d\n", ret);
|
|
return -EIO;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int stk3013_i2c_smbus_read_byte_data(struct i2c_client *client,
|
|
unsigned char command)
|
|
{
|
|
unsigned char value;
|
|
int ret;
|
|
|
|
ret = stk3013_i2c_read_data(client, command, 1, &value);
|
|
if (ret < 0)
|
|
return ret;
|
|
return value;
|
|
}
|
|
|
|
static int stk3013_i2c_smbus_write_byte_data(struct i2c_client *client,
|
|
unsigned char command, unsigned char value)
|
|
{
|
|
int ret;
|
|
|
|
ret = stk3013_i2c_write_data(client, command, 1, &value);
|
|
return ret;
|
|
}
|
|
|
|
static void stk3013_proc_plat_data(struct stk3013_data *ps_data,
|
|
struct stk3013_platform_data *plat_data)
|
|
{
|
|
uint8_t w_reg;
|
|
|
|
ps_data->state_reg = plat_data->state_reg;
|
|
ps_data->psctrl_reg = plat_data->psctrl_reg;
|
|
ps_data->ledctrl_reg = plat_data->ledctrl_reg;
|
|
if (ps_data->pid == STK3310SA_PID || ps_data->pid == STK3311SA_PID)
|
|
ps_data->ledctrl_reg &= 0x3F;
|
|
|
|
ps_data->wait_reg = plat_data->wait_reg;
|
|
if (ps_data->wait_reg < 2) {
|
|
SENSOR_INFO("wait_reg should be larger than 2, force to write 2\n");
|
|
ps_data->wait_reg = 2;
|
|
} else if (ps_data->wait_reg > 0xFF) {
|
|
SENSOR_INFO("wait_reg should be less than 0xFF, force to write 0xFF\n");
|
|
ps_data->wait_reg = 0xFF;
|
|
}
|
|
if (ps_data->ps_thd_h == 0 && ps_data->ps_thd_l == 0) {
|
|
ps_data->ps_thd_h = plat_data->ps_thd_h;
|
|
ps_data->ps_thd_l = plat_data->ps_thd_l;
|
|
ps_data->ps_default_thd_h = plat_data->ps_thd_h;
|
|
ps_data->ps_default_thd_l = plat_data->ps_thd_l;
|
|
ps_data->ps_cancel_thd_h = plat_data->ps_cancel_thd_h;
|
|
ps_data->ps_cancel_thd_l = plat_data->ps_cancel_thd_l;
|
|
ps_data->ps_cal_skip_adc = plat_data->ps_cal_skip_adc;
|
|
ps_data->ps_cal_fail_adc = plat_data->ps_cal_fail_adc;
|
|
/*initialize the offset data*/
|
|
ps_data->ps_default_offset = plat_data->ps_default_offset;
|
|
ps_data->ps_offset = ps_data->ps_default_offset;
|
|
}
|
|
|
|
w_reg = 0;
|
|
w_reg |= STK_INT_PS_MODE;
|
|
|
|
ps_data->int_reg = w_reg;
|
|
}
|
|
|
|
static int32_t stk3013_init_all_reg(struct stk3013_data *ps_data)
|
|
{
|
|
int32_t ret;
|
|
|
|
ret = stk3013_i2c_smbus_write_byte_data(ps_data->client,
|
|
STK_STATE_REG, ps_data->state_reg);
|
|
if (ret < 0) {
|
|
SENSOR_ERR("write i2c error\n");
|
|
return ret;
|
|
}
|
|
ret = stk3013_i2c_smbus_write_byte_data(ps_data->client,
|
|
STK_PSCTRL_REG, ps_data->psctrl_reg);
|
|
if (ret < 0) {
|
|
SENSOR_ERR("write i2c error\n");
|
|
return ret;
|
|
}
|
|
ret = stk3013_i2c_smbus_write_byte_data(ps_data->client,
|
|
STK_LEDCTRL_REG, ps_data->ledctrl_reg);
|
|
if (ret < 0) {
|
|
SENSOR_ERR("write i2c error\n");
|
|
return ret;
|
|
}
|
|
ret = stk3013_i2c_smbus_write_byte_data(ps_data->client,
|
|
STK_WAIT_REG, ps_data->wait_reg);
|
|
if (ret < 0) {
|
|
SENSOR_ERR("write i2c error\n");
|
|
return ret;
|
|
}
|
|
stk3013_set_ps_thd_h(ps_data, ps_data->ps_thd_h);
|
|
stk3013_set_ps_thd_l(ps_data, ps_data->ps_thd_l);
|
|
stk3013_set_ps_offset(ps_data, ps_data->ps_default_offset);
|
|
ret = stk3013_i2c_smbus_write_byte_data(ps_data->client,
|
|
STK_INT_REG, ps_data->int_reg);
|
|
if (ret < 0) {
|
|
SENSOR_ERR("write i2c error\n");
|
|
return ret;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int32_t stk3013_read_otp25(struct stk3013_data *ps_data)
|
|
{
|
|
int32_t ret, otp25;
|
|
|
|
ret = stk3013_i2c_smbus_write_byte_data(ps_data->client, 0x0, 0x2);
|
|
if (ret < 0) {
|
|
SENSOR_ERR("write i2c error\n");
|
|
return ret;
|
|
}
|
|
|
|
ret = stk3013_i2c_smbus_write_byte_data(ps_data->client, 0x90, 0x25);
|
|
if (ret < 0) {
|
|
SENSOR_ERR("write i2c error\n");
|
|
return ret;
|
|
}
|
|
|
|
ret = stk3013_i2c_smbus_write_byte_data(ps_data->client, 0x92, 0x82);
|
|
if (ret < 0) {
|
|
SENSOR_ERR("write i2c error\n");
|
|
return ret;
|
|
}
|
|
usleep_range(1000, 5000);
|
|
|
|
ret = stk3013_i2c_smbus_read_byte_data(ps_data->client, 0x91);
|
|
if (ret < 0) {
|
|
SENSOR_ERR("fail, ret=%d\n", ret);
|
|
return ret;
|
|
}
|
|
otp25 = ret;
|
|
|
|
ret = stk3013_i2c_smbus_write_byte_data(ps_data->client, 0x0, 0x0);
|
|
if (ret < 0) {
|
|
SENSOR_ERR("write i2c error\n");
|
|
return ret;
|
|
}
|
|
SENSOR_INFO("otp25=0x%x\n", otp25);
|
|
if (otp25 & 0x80)
|
|
return 1;
|
|
return 0;
|
|
}
|
|
|
|
static int32_t stk3013_check_pid(struct stk3013_data *ps_data)
|
|
{
|
|
unsigned char value[2], pid_msb;
|
|
int ret;
|
|
|
|
ps_data->p_wv_r_bd_with_co = 0;
|
|
|
|
ret = stk3013_i2c_read_data(ps_data->client,
|
|
STK_PDT_ID_REG, 2, &value[0]);
|
|
if (ret < 0) {
|
|
SENSOR_ERR("fail, ret=%d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
SENSOR_INFO("PID=0x%x, RID=0x%x\n", value[0], value[1]);
|
|
ps_data->pid = value[0];
|
|
|
|
if (value[0] == STK3311WV_PID)
|
|
ps_data->p_wv_r_bd_with_co |= 0xb100;
|
|
if (value[1] == 0xC3)
|
|
ps_data->p_wv_r_bd_with_co |= 0xb010;
|
|
|
|
if (stk3013_read_otp25(ps_data) == 1)
|
|
ps_data->p_wv_r_bd_with_co |= 0xb001;
|
|
SENSOR_INFO("p_wv_r_bd_with_co = 0x%x\n", ps_data->p_wv_r_bd_with_co);
|
|
|
|
if (value[0] == 0) {
|
|
SENSOR_ERR("PID=0x0, please make sure the chip is stk3013!\n");
|
|
return -ENXIO;
|
|
}
|
|
|
|
pid_msb = value[0] & 0xF0;
|
|
switch (pid_msb) {
|
|
case 0x10:
|
|
case 0x20:
|
|
case 0x30:
|
|
return 0;
|
|
default:
|
|
SENSOR_ERR("invalid PID(%#x)\n", value[0]);
|
|
return -EPERM;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int32_t stk3013_software_reset(struct stk3013_data *ps_data)
|
|
{
|
|
int32_t r;
|
|
uint8_t w_reg;
|
|
|
|
w_reg = 0x7F;
|
|
r = stk3013_i2c_smbus_write_byte_data(ps_data->client,
|
|
STK_WAIT_REG, w_reg);
|
|
if (r < 0) {
|
|
SENSOR_ERR("software reset: write i2c error, ret=%d\n", r);
|
|
return r;
|
|
}
|
|
r = stk3013_i2c_smbus_read_byte_data(ps_data->client, STK_WAIT_REG);
|
|
if (w_reg != r) {
|
|
SENSOR_ERR("software reset: read-back value is not the same\n");
|
|
return -EPERM;
|
|
}
|
|
|
|
r = stk3013_i2c_smbus_write_byte_data(ps_data->client,
|
|
STK_SW_RESET_REG, 0);
|
|
if (r < 0) {
|
|
SENSOR_ERR("software reset: read error after reset\n");
|
|
return r;
|
|
}
|
|
usleep_range(13000, 15000);
|
|
return 0;
|
|
}
|
|
|
|
static int32_t stk3013_set_ps_thd_l(struct stk3013_data *ps_data,
|
|
uint16_t thd_l)
|
|
{
|
|
unsigned char val[2];
|
|
int ret;
|
|
|
|
val[0] = (thd_l & 0xFF00) >> 8;
|
|
val[1] = thd_l & 0x00FF;
|
|
ret = stk3013_i2c_write_data(ps_data->client,
|
|
STK_THDL1_PS_REG, 2, val);
|
|
if (ret < 0)
|
|
SENSOR_ERR("fail, ret=%d\n", ret);
|
|
else
|
|
ps_data->ps_thd_l = thd_l;
|
|
|
|
SENSOR_INFO("thd_l=%d\n", thd_l);
|
|
return ret;
|
|
}
|
|
static int32_t stk3013_set_ps_thd_h(struct stk3013_data *ps_data,
|
|
uint16_t thd_h)
|
|
{
|
|
unsigned char val[2];
|
|
int ret;
|
|
|
|
val[0] = (thd_h & 0xFF00) >> 8;
|
|
val[1] = thd_h & 0x00FF;
|
|
ret = stk3013_i2c_write_data(ps_data->client,
|
|
STK_THDH1_PS_REG, 2, val);
|
|
if (ret < 0)
|
|
SENSOR_ERR("fail, ret=%d\n", ret);
|
|
else
|
|
ps_data->ps_thd_h = thd_h;
|
|
|
|
SENSOR_INFO("thd_h=%d\n", thd_h);
|
|
return ret;
|
|
}
|
|
static int32_t stk3013_set_ps_offset(struct stk3013_data *ps_data,
|
|
uint16_t ps_offset)
|
|
{
|
|
unsigned char val[2];
|
|
int ret;
|
|
|
|
val[0] = (ps_offset & 0xFF00) >> 8;
|
|
val[1] = ps_offset & 0x00FF;
|
|
|
|
ret = stk3013_i2c_write_data(ps_data->client,
|
|
STK_DATA1_OFFSET_REG, 2, val);
|
|
if (ret < 0)
|
|
SENSOR_ERR("fail, ret=%d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
static uint32_t stk3013_get_ps_reading(struct stk3013_data *ps_data)
|
|
{
|
|
unsigned char value[2];
|
|
int ret;
|
|
|
|
ret = stk3013_i2c_read_data(ps_data->client,
|
|
STK_DATA1_PS_REG, 2, &value[0]);
|
|
if (ret < 0) {
|
|
SENSOR_ERR("DATA1 fail, ret=%d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
return (value[0]<<8) | value[1];
|
|
}
|
|
|
|
static int32_t stk3013_set_flag(struct stk3013_data *ps_data,
|
|
uint8_t org_flag_reg, uint8_t clr)
|
|
{
|
|
uint8_t w_flag;
|
|
int ret;
|
|
|
|
w_flag = org_flag_reg | (STK_FLG_PSINT_MASK | STK_FLG_OUI_MASK |
|
|
STK_FLG_IR_RDY_MASK);
|
|
w_flag &= (~clr);
|
|
/*SENSOR_INFO(" org_flag_reg=0x%x, w_flag = 0x%x\n",
|
|
org_flag_reg, w_flag);*/
|
|
ret = stk3013_i2c_smbus_write_byte_data(ps_data->client,
|
|
STK_FLAG_REG, w_flag);
|
|
if (ret < 0)
|
|
SENSOR_ERR("fail, ret=%d\n", ret);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int32_t stk3013_get_flag(struct stk3013_data *ps_data)
|
|
{
|
|
int ret;
|
|
|
|
ret = stk3013_i2c_smbus_read_byte_data(ps_data->client,
|
|
STK_FLAG_REG);
|
|
if (ret < 0)
|
|
SENSOR_ERR("fail, ret=%d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
static int32_t stk3013_set_state(struct stk3013_data *ps_data, uint8_t state)
|
|
{
|
|
int ret;
|
|
|
|
ret = stk3013_i2c_smbus_write_byte_data(ps_data->client,
|
|
STK_STATE_REG, state);
|
|
if (ret < 0)
|
|
SENSOR_ERR("fail, ret=%d\n", ret);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int32_t stk3013_get_state(struct stk3013_data *ps_data)
|
|
{
|
|
int ret;
|
|
|
|
ret = stk3013_i2c_smbus_read_byte_data(ps_data->client, STK_STATE_REG);
|
|
if (ret < 0)
|
|
SENSOR_ERR("fail, ret=%d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
static int32_t stk3013_enable_ps(struct device *dev,
|
|
uint8_t enable, uint8_t validate_reg)
|
|
{
|
|
struct stk3013_data *ps_data = dev_get_drvdata(dev);
|
|
int32_t ret;
|
|
uint8_t w_state_reg;
|
|
uint8_t curr_ps_enable;
|
|
uint32_t read_value;
|
|
int32_t near_far_state;
|
|
|
|
#ifdef STK_CHK_REG
|
|
if (validate_reg) {
|
|
ret = stk3013_validate_n_handle(ps_data->client);
|
|
if (ret < 0)
|
|
SENSOR_ERR("stk3013_validate_n_handle fail: %d\n",
|
|
ret);
|
|
}
|
|
#endif /* #ifdef STK_CHK_REG */
|
|
|
|
curr_ps_enable = ps_data->ps_enabled ? 1 : 0;
|
|
if (curr_ps_enable == enable)
|
|
return 0;
|
|
|
|
if (enable) {
|
|
/*stk3013_regulator_onoff(dev, ON);*/
|
|
msleep(20);
|
|
ret = stk3013_init_all_setting(ps_data->client,
|
|
ps_data->pdata);
|
|
if (ret < 0) {
|
|
SENSOR_ERR("init setting fail, ret=%d\n", ret);
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
if (ps_data->first_boot == true)
|
|
ps_data->first_boot = false;
|
|
|
|
ret = stk3013_get_state(ps_data);
|
|
if (ret < 0)
|
|
return ret;
|
|
w_state_reg = ret;
|
|
|
|
w_state_reg &= ~(STK_STATE_EN_PS_MASK | STK_STATE_EN_WAIT_MASK | STK_STATE_EN_AK_MASK);
|
|
if (enable)
|
|
w_state_reg |= (STK_STATE_EN_PS_MASK | STK_STATE_EN_WAIT_MASK);
|
|
|
|
ret = stk3013_set_state(ps_data, w_state_reg);
|
|
if (ret < 0)
|
|
return ret;
|
|
ps_data->state_reg = w_state_reg;
|
|
|
|
if (enable) {
|
|
#ifdef PROXIMITY_CALIBRATION
|
|
check_calibration_offset(ps_data);
|
|
stk3013_set_ps_offset(ps_data, ps_data->ps_offset);
|
|
#endif
|
|
enable_irq(ps_data->irq);
|
|
ps_data->ps_enabled = true;
|
|
#ifdef STK_CHK_REG
|
|
if (!validate_reg) {
|
|
input_report_abs(ps_data->ps_input_dev,
|
|
ABS_DISTANCE, 1);
|
|
input_sync(ps_data->ps_input_dev);
|
|
wake_lock_timeout(&ps_data->ps_wakelock, 3 * HZ);
|
|
read_value = stk3013_get_ps_reading(ps_data);
|
|
SENSOR_INFO("force report ps input event=1, ps code=%d\n",
|
|
read_value);
|
|
} else
|
|
#endif/* #ifdef STK_CHK_REG */
|
|
{
|
|
usleep_range(4000, 5000);
|
|
ret = stk3013_get_flag(ps_data);
|
|
if (ret < 0)
|
|
return ret;
|
|
near_far_state = ret & STK_FLG_NF_MASK;
|
|
input_report_abs(ps_data->ps_input_dev,
|
|
ABS_DISTANCE, near_far_state);
|
|
input_sync(ps_data->ps_input_dev);
|
|
wake_lock_timeout(&ps_data->ps_wakelock, 3*HZ);
|
|
read_value = stk3013_get_ps_reading(ps_data);
|
|
SENSOR_INFO("ps input event=%d, ps code = %d\n",
|
|
near_far_state, read_value);
|
|
}
|
|
} else {
|
|
disable_irq(ps_data->irq);
|
|
/*stk3013_regulator_onoff(dev, OFF);*/
|
|
ps_data->ps_enabled = false;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
#ifdef STK_CHK_REG
|
|
static int stk3013_chk_reg_valid(struct stk3013_data *ps_data)
|
|
{
|
|
unsigned char value[9];
|
|
int ret;
|
|
/*
|
|
uint8_t cnt;
|
|
for(cnt=0;cnt<9;cnt++)
|
|
{
|
|
value[cnt] = stk3013_i2c_smbus_read_byte_data(ps_data->client,
|
|
(cnt+1));
|
|
if (value[cnt] < 0)
|
|
{
|
|
SENSOR_ERR("%s fail, ret=%d", value[cnt]);
|
|
return value[cnt];
|
|
}
|
|
}
|
|
*/
|
|
ret = stk3013_i2c_read_data(ps_data->client,
|
|
STK_PSCTRL_REG, 9, &value[0]);
|
|
if (ret < 0) {
|
|
SENSOR_ERR(" fail, ret=%d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
if (value[0] != ps_data->psctrl_reg) {
|
|
SENSOR_ERR(" invalid reg 0x01=0x%2x\n", value[0]);
|
|
return 0xFF;
|
|
}
|
|
if (value[2] != ps_data->ledctrl_reg) {
|
|
SENSOR_ERR(" invalid reg 0x03=0x%2x\n", value[2]);
|
|
return 0xFF;
|
|
}
|
|
if (value[3] != ps_data->int_reg) {
|
|
SENSOR_ERR(" invalid reg 0x04=0x%2x\n", value[3]);
|
|
return 0xFF;
|
|
}
|
|
if (value[4] != ps_data->wait_reg) {
|
|
SENSOR_ERR(" invalid reg 0x05=0x%2x\n", value[4]);
|
|
return 0xFF;
|
|
}
|
|
if (value[5] != ((ps_data->ps_thd_h & 0xFF00) >> 8)) {
|
|
SENSOR_ERR(" invalid reg 0x06=0x%2x\n", value[5]);
|
|
return 0xFF;
|
|
}
|
|
if (value[6] != (ps_data->ps_thd_h & 0x00FF)) {
|
|
SENSOR_ERR(" invalid reg 0x07=0x%2x\n", value[6]);
|
|
return 0xFF;
|
|
}
|
|
if (value[7] != ((ps_data->ps_thd_l & 0xFF00) >> 8)) {
|
|
SENSOR_ERR(" invalid reg 0x08=0x%2x\n", value[7]);
|
|
return 0xFF;
|
|
}
|
|
if (value[8] != (ps_data->ps_thd_l & 0x00FF)) {
|
|
SENSOR_ERR(" invalid reg 0x09=0x%2x\n", value[8]);
|
|
return 0xFF;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int stk3013_validate_n_handle(struct i2c_client *client)
|
|
{
|
|
struct stk3013_data *ps_data = i2c_get_clientdata(client);
|
|
int ret;
|
|
|
|
ret = stk3013_chk_reg_valid(ps_data);
|
|
if (ret < 0) {
|
|
SENSOR_ERR("stk3013_chk_reg_valid fail: %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
if (ret == 0xFF) {
|
|
SENSOR_ERR("Re-init chip\n");
|
|
ret = stk3013_software_reset(ps_data);
|
|
if (ret < 0)
|
|
return ret;
|
|
ret = stk3013_init_all_reg(ps_data);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
stk3013_set_ps_thd_h(ps_data, ps_data->ps_thd_h);
|
|
stk3013_set_ps_thd_l(ps_data, ps_data->ps_thd_l);
|
|
stk3013_set_ps_offset(ps_data, ps_data->ps_default_offset);
|
|
return 0xFF;
|
|
}
|
|
return 0;
|
|
}
|
|
#endif /* #ifdef STK_CHK_REG */
|
|
|
|
#ifdef PROXIMITY_CALIBRATION
|
|
static int proximity_store_calibration(struct device *dev, bool do_calib)
|
|
{
|
|
struct stk3013_data *ps_data = dev_get_drvdata(dev);
|
|
struct file *cal_filp = NULL;
|
|
mm_segment_t old_fs;
|
|
unsigned char value[2];
|
|
int ret;
|
|
uint16_t temp[2];
|
|
uint16_t offset_data = 0;
|
|
|
|
SENSOR_INFO("start\n");
|
|
|
|
if (do_calib) {
|
|
ret = stk3013_i2c_read_data(ps_data->client,
|
|
STK_DATA1_PS_REG, 2, &value[0]);
|
|
if (ret < 0) {
|
|
SENSOR_ERR("DATA1 fail, ret=%d\n", ret);
|
|
return ret;
|
|
}
|
|
offset_data = ((value[0]<<8) | value[1]);
|
|
SENSOR_INFO("ps_offset = %d\n", offset_data);
|
|
if (offset_data < ps_data->ps_cal_skip_adc) {
|
|
SENSOR_INFO("skip calibration = %d\n", offset_data);
|
|
ps_data->ps_offset = ps_data->ps_default_offset;
|
|
ps_data->cal_result = 2;
|
|
} else if (offset_data <= ps_data->ps_cal_fail_adc/*DO_CAL*/) {
|
|
SENSOR_INFO("do calibration = %d\n", offset_data);
|
|
temp[0] = ps_data->ps_default_offset;
|
|
ps_data->ps_offset = offset_data + ps_data->ps_default_offset;
|
|
ret = stk3013_set_ps_offset(ps_data,
|
|
ps_data->ps_offset);
|
|
if (ret < 0) {
|
|
SENSOR_ERR("calibration fail\n");
|
|
ps_data->ps_default_offset = temp[0];
|
|
ps_data->cal_result = 0;
|
|
} else {
|
|
stk3013_set_ps_thd_h(ps_data,
|
|
ps_data->ps_cancel_thd_h);
|
|
stk3013_set_ps_thd_l(ps_data,
|
|
ps_data->ps_cancel_thd_l);
|
|
ps_data->cal_result = 1;
|
|
}
|
|
} else {
|
|
SENSOR_INFO("fail offset calibration = %d\n",
|
|
offset_data);
|
|
ps_data->ps_offset = ps_data->ps_default_offset;
|
|
}
|
|
} else {
|
|
/*reset*/
|
|
SENSOR_INFO("reset start\n");
|
|
temp[0] = ps_data->ps_offset;
|
|
temp[1] = ps_data->cal_result;
|
|
ps_data->ps_offset = ps_data->ps_default_offset;
|
|
ps_data->cal_result = 0;
|
|
ret = stk3013_set_ps_offset(ps_data, ps_data->ps_offset);
|
|
if (ret < 0) {
|
|
SENSOR_ERR("calibration reset fail\n");
|
|
ps_data->ps_default_offset = temp[0];
|
|
ps_data->cal_result = temp[1];
|
|
}
|
|
SENSOR_INFO("ps_thd_h=%d, ps_thd_l=%d, ps_offset=%d\n",
|
|
ps_data->ps_thd_h, ps_data->ps_thd_l,
|
|
ps_data->ps_offset);
|
|
stk3013_set_ps_thd_h(ps_data, ps_data->ps_default_thd_h);
|
|
stk3013_set_ps_thd_l(ps_data, ps_data->ps_default_thd_l);
|
|
}
|
|
|
|
old_fs = get_fs();
|
|
set_fs(KERNEL_DS);
|
|
|
|
cal_filp = filp_open(CALIBRATION_FILE_PATH,
|
|
O_CREAT | O_TRUNC | O_WRONLY | O_SYNC, 0666);
|
|
if (IS_ERR(cal_filp)) {
|
|
SENSOR_ERR("Can't open calibration file\n");
|
|
set_fs(old_fs);
|
|
ret = PTR_ERR(cal_filp);
|
|
return ret;
|
|
}
|
|
|
|
ret = vfs_write(cal_filp,
|
|
(char *)&ps_data->ps_offset,
|
|
sizeof(u16), &cal_filp->f_pos);
|
|
if (ret != sizeof(u16)) {
|
|
SENSOR_ERR("Can't write the cancel data to file\n");
|
|
ret = -EIO;
|
|
}
|
|
|
|
filp_close(cal_filp, current->files);
|
|
set_fs(old_fs);
|
|
SENSOR_INFO("end\n");
|
|
return ret;
|
|
}
|
|
|
|
static ssize_t proximity_calibration_store(struct device *dev,
|
|
struct device_attribute *attr, const char *buf, size_t size)
|
|
{
|
|
bool do_calib;
|
|
int err;
|
|
|
|
if (sysfs_streq(buf, "1")) /* calibrate cancelation value */
|
|
do_calib = true;
|
|
else if (sysfs_streq(buf, "0")) /* reset cancelation value */
|
|
do_calib = false;
|
|
else {
|
|
SENSOR_ERR("invalid value %d\n", *buf);
|
|
return -EINVAL;
|
|
}
|
|
SENSOR_INFO("%d\n", do_calib);
|
|
err = proximity_store_calibration(dev, do_calib);
|
|
if (err < 0) {
|
|
SENSOR_ERR("proximity_store_cancelation() failed\n");
|
|
return err;
|
|
}
|
|
|
|
return size;
|
|
}
|
|
|
|
static ssize_t proximity_calibration_show(struct device *dev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
struct stk3013_data *ps_data = dev_get_drvdata(dev);
|
|
|
|
return snprintf(buf, PAGE_SIZE, "%u,%u,%u\n",
|
|
ps_data->ps_offset,
|
|
(ps_data->ps_offset != ps_data->ps_default_offset) ? ps_data->ps_cancel_thd_h : ps_data->ps_thd_h,
|
|
(ps_data->ps_offset != ps_data->ps_default_offset) ? ps_data->ps_cancel_thd_l : ps_data->ps_thd_l);
|
|
}
|
|
|
|
static ssize_t proximity_calibration_pass_show(struct device *dev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
struct stk3013_data *ps_data = dev_get_drvdata(dev);
|
|
|
|
SENSOR_INFO("result = %d\n", ps_data->cal_result);
|
|
return snprintf(buf, PAGE_SIZE, "%u\n",
|
|
ps_data->cal_result);
|
|
}
|
|
#endif
|
|
|
|
static ssize_t proximity_avg_show(struct device *dev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
struct stk3013_data *ps_data = dev_get_drvdata(dev);
|
|
|
|
return snprintf(buf, PAGE_SIZE, "%d,%d,%d\n", ps_data->avg[0],
|
|
ps_data->avg[1], ps_data->avg[2]);
|
|
}
|
|
static void proximity_get_avg_val(struct stk3013_data *ps_data)
|
|
{
|
|
int min = 0, max = 0, avg = 0;
|
|
int i;
|
|
uint32_t read_value;
|
|
|
|
for (i = 0; i < PROX_READ_NUM; i++) {
|
|
msleep(40);
|
|
read_value = stk3013_get_ps_reading(ps_data);
|
|
avg += read_value;
|
|
|
|
if (!i)
|
|
min = read_value;
|
|
else if (read_value < min)
|
|
min = read_value;
|
|
|
|
if (read_value > max)
|
|
max = read_value;
|
|
}
|
|
avg /= PROX_READ_NUM;
|
|
|
|
ps_data->avg[0] = min;
|
|
ps_data->avg[1] = avg;
|
|
ps_data->avg[2] = max;
|
|
}
|
|
|
|
static ssize_t proximity_avg_store(struct device *dev,
|
|
struct device_attribute *attr, const char *buf, size_t size)
|
|
{
|
|
struct stk3013_data *ps_data = dev_get_drvdata(dev);
|
|
bool new_value = false;
|
|
|
|
if (sysfs_streq(buf, "1"))
|
|
new_value = true;
|
|
else if (sysfs_streq(buf, "0"))
|
|
new_value = false;
|
|
else {
|
|
SENSOR_ERR("invalid value %d\n", *buf);
|
|
return -EINVAL;
|
|
}
|
|
|
|
SENSOR_INFO("average enable = %d\n", new_value);
|
|
if (new_value) {
|
|
if ((ps_data->ps_enabled ? 1 : 0) == OFF) {
|
|
mutex_lock(&ps_data->io_lock);
|
|
stk3013_enable_ps(dev, new_value, 1);
|
|
mutex_unlock(&ps_data->io_lock);
|
|
}
|
|
hrtimer_start(&ps_data->prox_timer, ps_data->prox_poll_delay,
|
|
HRTIMER_MODE_REL);
|
|
} else if (!new_value) {
|
|
hrtimer_cancel(&ps_data->prox_timer);
|
|
cancel_work_sync(&ps_data->work_prox);
|
|
if ((ps_data->ps_enabled ? 1 : 0) == OFF) {
|
|
mutex_lock(&ps_data->io_lock);
|
|
stk3013_enable_ps(dev, new_value, 0);
|
|
mutex_unlock(&ps_data->io_lock);
|
|
}
|
|
}
|
|
|
|
return size;
|
|
}
|
|
|
|
static ssize_t proximity_trim_show(struct device *dev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
struct stk3013_data *ps_data = dev_get_drvdata(dev);
|
|
|
|
SENSOR_INFO("trim: %d\n", ps_data->ps_default_offset);
|
|
return snprintf(buf, PAGE_SIZE, "%u\n",
|
|
ps_data->ps_default_offset);
|
|
}
|
|
|
|
static void stk3013_work_func_prox(struct work_struct *work)
|
|
{
|
|
struct stk3013_data *ps_data = container_of(work,
|
|
struct stk3013_data, work_prox);
|
|
|
|
proximity_get_avg_val(ps_data);
|
|
}
|
|
|
|
static enum hrtimer_restart stk3013_prox_timer_func(struct hrtimer *timer)
|
|
{
|
|
struct stk3013_data *ps_data = container_of(timer,
|
|
struct stk3013_data, prox_timer);
|
|
|
|
queue_work(ps_data->prox_wq, &ps_data->work_prox);
|
|
hrtimer_forward_now(&ps_data->prox_timer, ps_data->prox_poll_delay);
|
|
return HRTIMER_RESTART;
|
|
}
|
|
|
|
static ssize_t proximity_thresh_high_show(struct device *dev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
int32_t ps_thd_h1_reg, ps_thd_h2_reg;
|
|
struct stk3013_data *ps_data = dev_get_drvdata(dev);
|
|
|
|
ps_thd_h1_reg = stk3013_i2c_smbus_read_byte_data(ps_data->client,
|
|
STK_THDH1_PS_REG);
|
|
if (ps_thd_h1_reg < 0) {
|
|
SENSOR_ERR("fail, err=0x%x", ps_thd_h1_reg);
|
|
return -EINVAL;
|
|
}
|
|
ps_thd_h2_reg = stk3013_i2c_smbus_read_byte_data(ps_data->client,
|
|
STK_THDH2_PS_REG);
|
|
if (ps_thd_h2_reg < 0) {
|
|
SENSOR_ERR("fail, err=0x%x", ps_thd_h2_reg);
|
|
return -EINVAL;
|
|
}
|
|
ps_thd_h1_reg = ps_thd_h1_reg<<8 | ps_thd_h2_reg;
|
|
SENSOR_INFO("thresh:0x%x", ps_thd_h1_reg);
|
|
return scnprintf(buf, PAGE_SIZE, "%d\n", ps_thd_h1_reg);
|
|
}
|
|
|
|
static ssize_t proximity_thresh_high_store(struct device *dev,
|
|
struct device_attribute *attr, const char *buf, size_t size)
|
|
{
|
|
struct stk3013_data *ps_data = dev_get_drvdata(dev);
|
|
u16 value = 0;
|
|
int ret;
|
|
|
|
ret = kstrtou16(buf, 10, &value);
|
|
if (ret < 0) {
|
|
SENSOR_ERR("kstrtoul failed, ret=0x%x\n", ret);
|
|
return ret;
|
|
}
|
|
SENSOR_INFO("thresh: %d\n", value);
|
|
stk3013_set_ps_thd_h(ps_data, value);
|
|
return size;
|
|
}
|
|
|
|
static ssize_t proximity_thresh_low_show(struct device *dev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
int32_t ps_thd_l1_reg, ps_thd_l2_reg;
|
|
struct stk3013_data *ps_data = dev_get_drvdata(dev);
|
|
|
|
ps_thd_l1_reg = stk3013_i2c_smbus_read_byte_data(ps_data->client,
|
|
STK_THDL1_PS_REG);
|
|
if (ps_thd_l1_reg < 0) {
|
|
SENSOR_ERR("fail, err=0x%x", ps_thd_l1_reg);
|
|
return -EINVAL;
|
|
}
|
|
ps_thd_l2_reg = stk3013_i2c_smbus_read_byte_data(ps_data->client,
|
|
STK_THDL2_PS_REG);
|
|
if (ps_thd_l2_reg < 0) {
|
|
SENSOR_ERR("fail, err=0x%x", ps_thd_l2_reg);
|
|
return -EINVAL;
|
|
}
|
|
ps_thd_l1_reg = ps_thd_l1_reg<<8 | ps_thd_l2_reg;
|
|
|
|
return scnprintf(buf, PAGE_SIZE, "%d\n", ps_thd_l1_reg);
|
|
}
|
|
|
|
static ssize_t proximity_thresh_low_store(struct device *dev,
|
|
struct device_attribute *attr, const char *buf, size_t size)
|
|
{
|
|
struct stk3013_data *ps_data = dev_get_drvdata(dev);
|
|
u16 value = 0;
|
|
int ret;
|
|
|
|
ret = kstrtou16(buf, 10, &value);
|
|
if (ret < 0) {
|
|
SENSOR_ERR("kstrtoul failed, ret=0x%x\n", ret);
|
|
return ret;
|
|
}
|
|
SENSOR_INFO("thresh: %d\n", value);
|
|
stk3013_set_ps_thd_l(ps_data, value);
|
|
return size;
|
|
}
|
|
|
|
static ssize_t proximity_state_show(struct device *dev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
struct stk3013_data *ps_data = dev_get_drvdata(dev);
|
|
uint32_t read_value;
|
|
|
|
read_value = stk3013_get_ps_reading(ps_data);
|
|
return scnprintf(buf, PAGE_SIZE, "%d\n", read_value);
|
|
}
|
|
|
|
static ssize_t stk3013_vendor_show(struct device *dev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
return snprintf(buf, PAGE_SIZE, "%s\n", VENDOR);
|
|
}
|
|
|
|
static ssize_t stk3013_name_show(struct device *dev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
return snprintf(buf, PAGE_SIZE, "%s\n", CHIP_ID);
|
|
}
|
|
|
|
#if defined(PROXIMITY_FOR_TEST)
|
|
static ssize_t proximity_register_write_store(struct device *dev,
|
|
struct device_attribute *attr, const char *buf, size_t count)
|
|
{
|
|
unsigned int regist = 0, val = 0;
|
|
int ret;
|
|
struct stk3013_data *data = dev_get_drvdata(dev);
|
|
|
|
if (sscanf(buf, "%2x,%2x", ®ist, &val) != 2) {
|
|
SENSOR_ERR("The number of data are wrong\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
ret = stk3013_i2c_write_data(data->client, regist, 1, (unsigned char *)&val);
|
|
if (ret < 0)
|
|
SENSOR_ERR("fail, ret=%d\n", ret);
|
|
else
|
|
SENSOR_INFO("Register(0x%2x) data(0x%2x)\n", regist, val);
|
|
|
|
return count;
|
|
}
|
|
|
|
static ssize_t proximity_register_read_show(struct device *dev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
u8 reg;
|
|
unsigned char val = 0;
|
|
int offset = 0;
|
|
struct stk3013_data *data = dev_get_drvdata(dev);
|
|
|
|
for (reg = STK_STATE_REG; reg <= STK_DATA2_OFFSET_REG; reg++) {
|
|
stk3013_i2c_read_data(data->client, reg, 1, &val);
|
|
SENSOR_INFO("Register(0x%2x) data(0x%2x)\n", reg, val);
|
|
offset += snprintf(buf + offset, PAGE_SIZE - offset,
|
|
"Reg: 0x%2x, Val: 0x%2x\n", reg, val);
|
|
}
|
|
|
|
return offset;
|
|
}
|
|
#endif
|
|
|
|
|
|
#ifdef PROXIMITY_CALIBRATION
|
|
static DEVICE_ATTR(prox_cal, S_IRUGO | S_IWUSR | S_IWGRP,
|
|
proximity_calibration_show, proximity_calibration_store);
|
|
static DEVICE_ATTR(prox_offset_pass, S_IRUGO, proximity_calibration_pass_show,
|
|
NULL);
|
|
#endif
|
|
#if defined(PROXIMITY_FOR_TEST)
|
|
static DEVICE_ATTR(prox_register, S_IRUGO | S_IWUSR | S_IWGRP,
|
|
proximity_register_read_show, proximity_register_write_store);
|
|
#endif
|
|
|
|
static DEVICE_ATTR(prox_avg, S_IRUGO | S_IWUSR | S_IWGRP,
|
|
proximity_avg_show, proximity_avg_store);
|
|
static DEVICE_ATTR(prox_trim, S_IRUGO,
|
|
proximity_trim_show, NULL);
|
|
static DEVICE_ATTR(thresh_high, S_IRUGO | S_IWUSR | S_IWGRP,
|
|
proximity_thresh_high_show, proximity_thresh_high_store);
|
|
static DEVICE_ATTR(thresh_low, S_IRUGO | S_IWUSR | S_IWGRP,
|
|
proximity_thresh_low_show, proximity_thresh_low_store);
|
|
static DEVICE_ATTR(state, S_IRUGO, proximity_state_show, NULL);
|
|
static DEVICE_ATTR(raw_data, S_IRUGO, proximity_state_show, NULL);
|
|
static DEVICE_ATTR(vendor, S_IRUGO, stk3013_vendor_show, NULL);
|
|
static DEVICE_ATTR(name, S_IRUGO, stk3013_name_show, NULL);
|
|
|
|
static struct device_attribute *prox_sensor_attrs[] = {
|
|
#ifdef PROXIMITY_CALIBRATION
|
|
&dev_attr_prox_cal,
|
|
&dev_attr_prox_offset_pass,
|
|
#endif
|
|
#if defined(PROXIMITY_FOR_TEST)
|
|
&dev_attr_prox_register,
|
|
#endif
|
|
&dev_attr_prox_avg,
|
|
&dev_attr_prox_trim,
|
|
&dev_attr_thresh_high,
|
|
&dev_attr_thresh_low,
|
|
&dev_attr_state,
|
|
&dev_attr_raw_data,
|
|
&dev_attr_vendor,
|
|
&dev_attr_name,
|
|
NULL,
|
|
};
|
|
|
|
static ssize_t proximity_enable_show(struct device *dev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
int32_t ret;
|
|
struct stk3013_data *ps_data = dev_get_drvdata(dev);
|
|
|
|
ret = stk3013_get_state(ps_data);
|
|
if (ret < 0)
|
|
return ret;
|
|
ret = (ret & STK_STATE_EN_PS_MASK) ? 1 : 0;
|
|
|
|
return scnprintf(buf, PAGE_SIZE, "%d\n", ret);
|
|
}
|
|
|
|
static ssize_t proximity_enable_store(struct device *dev,
|
|
struct device_attribute *attr, const char *buf, size_t size)
|
|
{
|
|
struct stk3013_data *ps_data = dev_get_drvdata(dev);
|
|
uint8_t en;
|
|
|
|
if (sysfs_streq(buf, "1"))
|
|
en = 1;
|
|
else if (sysfs_streq(buf, "0"))
|
|
en = 0;
|
|
else {
|
|
SENSOR_ERR("invalid value %d\n", *buf);
|
|
return size;
|
|
}
|
|
SENSOR_INFO("Enable PS : %d\n", en);
|
|
mutex_lock(&ps_data->io_lock);
|
|
stk3013_enable_ps(dev, en, 1);
|
|
mutex_unlock(&ps_data->io_lock);
|
|
return size;
|
|
}
|
|
|
|
static DEVICE_ATTR(enable, S_IRUGO | S_IWUSR | S_IWGRP,
|
|
proximity_enable_show, proximity_enable_store);
|
|
|
|
static struct attribute *proximity_sysfs_attrs[] = {
|
|
&dev_attr_enable.attr,
|
|
NULL
|
|
};
|
|
|
|
static struct attribute_group proximity_attribute_group = {
|
|
.attrs = proximity_sysfs_attrs,
|
|
};
|
|
|
|
static void stk_work_func(struct work_struct *work)
|
|
{
|
|
uint32_t read_value;
|
|
#if ((STK_INT_PS_MODE != 0x03) && (STK_INT_PS_MODE != 0x02))
|
|
int32_t ret;
|
|
uint8_t disable_flag = 0;
|
|
int32_t org_flag_reg;
|
|
#endif/* #if ((STK_INT_PS_MODE != 0x03) && (STK_INT_PS_MODE != 0x02)) */
|
|
struct stk3013_data *ps_data = container_of(work,
|
|
struct stk3013_data, stk_work);
|
|
int32_t near_far_state;
|
|
|
|
#if (STK_INT_PS_MODE == 0x03)
|
|
near_far_state = gpio_get_value(ps_data->int_pin);
|
|
#elif (STK_INT_PS_MODE == 0x02)
|
|
near_far_state = !(gpio_get_value(ps_data->int_pin));
|
|
#endif
|
|
|
|
#if ((STK_INT_PS_MODE == 0x03) || (STK_INT_PS_MODE == 0x02))
|
|
input_report_abs(ps_data->ps_input_dev, ABS_DISTANCE, near_far_state);
|
|
input_sync(ps_data->ps_input_dev);
|
|
wake_lock_timeout(&ps_data->ps_wakelock, 3 * HZ);
|
|
read_value = stk3013_get_ps_reading(ps_data);
|
|
SENSOR_INFO("ps input event %d cm, ps code = %d\n",
|
|
near_far_state, read_value);
|
|
#else
|
|
/* mode 0x01 or 0x04 */
|
|
org_flag_reg = stk3013_get_flag(ps_data);
|
|
if (org_flag_reg < 0)
|
|
goto err_i2c_rw;
|
|
if (org_flag_reg & STK_FLG_PSINT_MASK) {
|
|
disable_flag |= STK_FLG_PSINT_MASK;
|
|
near_far_state = (org_flag_reg & STK_FLG_NF_MASK) ? 1 : 0;
|
|
read_value = stk3013_get_ps_reading(ps_data);
|
|
|
|
#ifdef CONFIG_SEC_FACTORY
|
|
SENSOR_INFO("FACTORY: near/far=%d, ps code = %d\n",
|
|
near_far_state, read_value);
|
|
#else
|
|
SENSOR_INFO("near/far=%d, ps code = %d\n",
|
|
near_far_state, read_value);
|
|
if ((near_far_state == 0 && read_value >= ps_data->ps_thd_h)
|
|
|| (near_far_state == 1 && read_value <= ps_data->ps_thd_l))
|
|
#endif
|
|
{
|
|
input_report_abs(ps_data->ps_input_dev,
|
|
ABS_DISTANCE, near_far_state);
|
|
input_sync(ps_data->ps_input_dev);
|
|
wake_lock_timeout(&ps_data->ps_wakelock, 3 * HZ);
|
|
}
|
|
}
|
|
|
|
if (disable_flag) {
|
|
ret = stk3013_set_flag(ps_data, org_flag_reg, disable_flag);
|
|
if (ret < 0)
|
|
goto err_i2c_rw;
|
|
}
|
|
#endif
|
|
usleep_range(1000, 2000);
|
|
goto exit;
|
|
|
|
err_i2c_rw:
|
|
msleep(30);
|
|
exit:
|
|
enable_irq(ps_data->irq);
|
|
}
|
|
|
|
static irqreturn_t stk_oss_irq_handler(int irq, void *data)
|
|
{
|
|
struct stk3013_data *pData = data;
|
|
|
|
disable_irq_nosync(irq);
|
|
queue_work(pData->stk_wq, &pData->stk_work);
|
|
return IRQ_HANDLED;
|
|
}
|
|
|
|
static int32_t stk3013_init_all_setting(struct i2c_client *client,
|
|
struct stk3013_platform_data *plat_data)
|
|
{
|
|
int32_t ret;
|
|
struct stk3013_data *ps_data = i2c_get_clientdata(client);
|
|
|
|
ret = stk3013_software_reset(ps_data);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
ret = stk3013_check_pid(ps_data);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
stk3013_proc_plat_data(ps_data, plat_data);
|
|
ret = stk3013_init_all_reg(ps_data);
|
|
if (ret < 0)
|
|
return ret;
|
|
ps_data->ps_enabled = false;
|
|
ps_data->re_enable_ps = false;
|
|
ps_data->ir_code = 0;
|
|
ps_data->first_boot = true;
|
|
|
|
atomic_set(&ps_data->recv_reg, 0);
|
|
return 0;
|
|
}
|
|
|
|
static int stk3013_setup_irq(struct i2c_client *client)
|
|
{
|
|
int irq, ret = -EIO;
|
|
struct stk3013_data *ps_data = i2c_get_clientdata(client);
|
|
|
|
irq = gpio_to_irq(ps_data->int_pin);
|
|
|
|
SENSOR_INFO("int pin #=%d, irq=%d\n", ps_data->int_pin, irq);
|
|
|
|
if (irq <= 0) {
|
|
SENSOR_ERR("irq number is not specified, irq=%d, int pin=%d\n",
|
|
irq, ps_data->int_pin);
|
|
return irq;
|
|
}
|
|
ps_data->irq = irq;
|
|
ret = gpio_request(ps_data->int_pin, "stk-int");
|
|
if (ret < 0) {
|
|
SENSOR_ERR("gpio_request, err=%d", ret);
|
|
return ret;
|
|
}
|
|
ret = gpio_direction_input(ps_data->int_pin);
|
|
if (ret < 0) {
|
|
SENSOR_ERR("gpio_direction_input, err=%d", ret);
|
|
return ret;
|
|
}
|
|
|
|
#if ((STK_INT_PS_MODE == 0x03) || (STK_INT_PS_MODE == 0x02))
|
|
ret = request_any_context_irq(irq, stk_oss_irq_handler,
|
|
IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,
|
|
"proximity_int", ps_data);
|
|
#else
|
|
ret = request_any_context_irq(irq, stk_oss_irq_handler,
|
|
IRQF_TRIGGER_LOW, "proximity_int", ps_data);
|
|
#endif
|
|
if (ret < 0) {
|
|
SENSOR_WARN("request_any_context_irq(%d) failed for (%d)\n",
|
|
irq, ret);
|
|
goto err_request_any_context_irq;
|
|
}
|
|
disable_irq(irq);
|
|
|
|
return 0;
|
|
err_request_any_context_irq:
|
|
gpio_free(ps_data->int_pin);
|
|
return ret;
|
|
}
|
|
|
|
static int stk3013_suspend(struct device *dev)
|
|
{
|
|
struct stk3013_data *ps_data = dev_get_drvdata(dev);
|
|
int ret;
|
|
struct i2c_client *client = to_i2c_client(dev);
|
|
|
|
SENSOR_INFO("\n");
|
|
mutex_lock(&ps_data->io_lock);
|
|
|
|
#ifdef STK_CHK_REG
|
|
ret = stk3013_validate_n_handle(ps_data->client);
|
|
if (ret < 0) {
|
|
SENSOR_ERR("stk3013_validate_n_handle fail: %d\n", ret);
|
|
} else if (ret == 0xFF) {
|
|
if (ps_data->ps_enabled)
|
|
stk3013_enable_ps(ps_data, 1, 0);
|
|
}
|
|
#endif /* #ifdef STK_CHK_REG */
|
|
if (ps_data->ps_enabled) {
|
|
if (device_may_wakeup(&client->dev)) {
|
|
ret = enable_irq_wake(ps_data->irq);
|
|
if (ret)
|
|
SENSOR_WARN("set_irq_wake(%d) failed(%d)\n",
|
|
ps_data->irq, ret);
|
|
} else {
|
|
SENSOR_ERR("not support wakeup source");
|
|
}
|
|
}
|
|
|
|
mutex_unlock(&ps_data->io_lock);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int stk3013_resume(struct device *dev)
|
|
{
|
|
struct stk3013_data *ps_data = dev_get_drvdata(dev);
|
|
int ret;
|
|
struct i2c_client *client = to_i2c_client(dev);
|
|
|
|
SENSOR_INFO("\n");
|
|
|
|
mutex_lock(&ps_data->io_lock);
|
|
#ifdef STK_CHK_REG
|
|
ret = stk3013_validate_n_handle(ps_data->client);
|
|
if (ret < 0) {
|
|
SENSOR_ERR("stk3013_validate_n_handle fail: %d\n", ret);
|
|
} else if (ret == 0xFF) {
|
|
if (ps_data->ps_enabled)
|
|
stk3013_enable_ps(ps_data, 1, 0);
|
|
}
|
|
#endif/* #ifdef STK_CHK_REG */
|
|
if (ps_data->ps_enabled) {
|
|
if (device_may_wakeup(&client->dev)) {
|
|
ret = disable_irq_wake(ps_data->irq);
|
|
if (ret)
|
|
SENSOR_WARN("disable_irq_wake(%d) fail(%d)\n",
|
|
ps_data->irq, ret);
|
|
}
|
|
}
|
|
|
|
mutex_unlock(&ps_data->io_lock);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const struct dev_pm_ops stk3013_pm_ops = {
|
|
SET_SYSTEM_SLEEP_PM_OPS(stk3013_suspend, stk3013_resume)
|
|
};
|
|
|
|
static int stk3013_regulator_onoff(struct device *dev, bool onoff)
|
|
{
|
|
struct stk3013_data *ps_data = dev_get_drvdata(dev);
|
|
int ret;
|
|
|
|
SENSOR_INFO("%s\n", (onoff) ? "on" : "off");
|
|
|
|
if (!ps_data->vdd || IS_ERR(ps_data->vdd)) {
|
|
SENSOR_INFO("VDD get regulator\n");
|
|
ps_data->vdd = devm_regulator_get(dev, "stk,vdd");
|
|
if (IS_ERR(ps_data->vdd)) {
|
|
SENSOR_ERR("cannot get vdd\n");
|
|
return -ENOMEM;
|
|
}
|
|
regulator_set_voltage(ps_data->vdd, 2800000,2800000);
|
|
}
|
|
|
|
if (!ps_data->vio || IS_ERR(ps_data->vio)) {
|
|
SENSOR_INFO("VIO get regulator\n");
|
|
ps_data->vio = devm_regulator_get(dev, "stk,vio");
|
|
if (IS_ERR(ps_data->vio)) {
|
|
SENSOR_ERR("cannot get vio\n");
|
|
devm_regulator_put(ps_data->vdd);
|
|
return -ENOMEM;
|
|
}
|
|
regulator_set_voltage(ps_data->vio, 1800000, 1800000);
|
|
}
|
|
|
|
if (onoff) {
|
|
ret = regulator_enable(ps_data->vdd);
|
|
if (ret)
|
|
SENSOR_ERR("Failed to enable vdd.\n");
|
|
msleep(20);
|
|
|
|
ret = regulator_enable(ps_data->vio);
|
|
if (ret)
|
|
SENSOR_ERR("Failed to enable vio.\n");
|
|
msleep(20);
|
|
} else {
|
|
ret = regulator_disable(ps_data->vdd);
|
|
if (ret)
|
|
SENSOR_ERR("Failed to disable vdd.\n");
|
|
msleep(20);
|
|
|
|
ret = regulator_disable(ps_data->vio);
|
|
if (ret)
|
|
SENSOR_ERR("Failed to disable vio.\n");
|
|
msleep(20);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int stk3013_parse_dt(struct device *dev,
|
|
struct stk3013_platform_data *pdata)
|
|
{
|
|
int rc;
|
|
struct device_node *np = dev->of_node;
|
|
u32 temp_val;
|
|
|
|
if (!pdata)
|
|
return -ENOMEM;
|
|
|
|
pdata->int_pin = of_get_named_gpio_flags(np, "stk,irq-gpio", 0,
|
|
&pdata->int_flags);
|
|
if (pdata->int_pin < 0) {
|
|
dev_err(dev, "Unable to read irq-gpio\n");
|
|
return pdata->int_pin;
|
|
}
|
|
|
|
rc = of_property_read_u32(np, "stk,transmittance", &temp_val);
|
|
if (!rc)
|
|
pdata->transmittance = temp_val;
|
|
else {
|
|
dev_err(dev, "Unable to read transmittance\n");
|
|
return rc;
|
|
}
|
|
|
|
rc = of_property_read_u32(np, "stk,state-reg", &temp_val);
|
|
if (!rc)
|
|
pdata->state_reg = temp_val;
|
|
else {
|
|
dev_err(dev, "Unable to read state-reg\n");
|
|
return rc;
|
|
}
|
|
|
|
rc = of_property_read_u32(np, "stk,psctrl-reg", &temp_val);
|
|
if (!rc)
|
|
pdata->psctrl_reg = (u8)temp_val;
|
|
else {
|
|
dev_err(dev, "Unable to read psctrl-reg\n");
|
|
return rc;
|
|
}
|
|
/*
|
|
rc = of_property_read_u32(np, "stk,alsctrl-reg", &temp_val);
|
|
if (!rc)
|
|
pdata->alsctrl_reg = (u8)temp_val;
|
|
else {
|
|
dev_err(dev, "Unable to read alsctrl-reg\n");
|
|
return rc;
|
|
}
|
|
*/
|
|
rc = of_property_read_u32(np, "stk,ledctrl-reg", &temp_val);
|
|
if (!rc)
|
|
pdata->ledctrl_reg = (u8)temp_val;
|
|
else {
|
|
dev_err(dev, "Unable to read ledctrl-reg\n");
|
|
return rc;
|
|
}
|
|
|
|
rc = of_property_read_u32(np, "stk,wait-reg", &temp_val);
|
|
if (!rc)
|
|
pdata->wait_reg = (u8)temp_val;
|
|
else {
|
|
dev_err(dev, "Unable to read wait-reg\n");
|
|
return rc;
|
|
}
|
|
|
|
rc = of_property_read_u32(np, "stk,ps-thd-h", &temp_val);
|
|
if (!rc)
|
|
pdata->ps_thd_h = (u16)temp_val;
|
|
else {
|
|
dev_err(dev, "Unable to read ps-thd-h\n");
|
|
return rc;
|
|
}
|
|
|
|
rc = of_property_read_u32(np, "stk,ps-thd-l", &temp_val);
|
|
if (!rc)
|
|
pdata->ps_thd_l = (u16)temp_val;
|
|
else {
|
|
dev_err(dev, "Unable to read ps-thd-l\n");
|
|
return rc;
|
|
}
|
|
|
|
rc = of_property_read_u32(np, "stk,ps-cancel-thd-h", &temp_val);
|
|
if (!rc)
|
|
pdata->ps_cancel_thd_h = (u16)temp_val;
|
|
else {
|
|
dev_err(dev, "Unable to read ps-cancel-thd-h\n");
|
|
return rc;
|
|
}
|
|
|
|
rc = of_property_read_u32(np, "stk,ps-cancel-thd-l", &temp_val);
|
|
if (!rc)
|
|
pdata->ps_cancel_thd_l = (u16)temp_val;
|
|
else {
|
|
dev_err(dev, "Unable to read ps-cancel-thd-l\n");
|
|
return rc;
|
|
}
|
|
|
|
rc = of_property_read_u32(np, "stk,ps-cal-skip-adc", &temp_val);
|
|
if (!rc)
|
|
pdata->ps_cal_skip_adc = (u16)temp_val;
|
|
else {
|
|
dev_err(dev, "Unable to read ps-cal-skip-adc\n");
|
|
return rc;
|
|
}
|
|
|
|
rc = of_property_read_u32(np, "stk,ps-cal-fail-adc", &temp_val);
|
|
if (!rc)
|
|
pdata->ps_cal_fail_adc = (u16)temp_val;
|
|
else {
|
|
dev_err(dev, "Unable to read ps-cal-fail-adc\n");
|
|
return rc;
|
|
}
|
|
|
|
rc = of_property_read_u32(np, "stk,ps-default-offset", &temp_val);
|
|
if (!rc)
|
|
pdata->ps_default_offset = (u16)temp_val;
|
|
else {
|
|
dev_err(dev, "Unable to read ps-default-offset\n");
|
|
return rc;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
#ifdef PROXIMITY_CALIBRATION
|
|
static int check_calibration_offset(struct stk3013_data *ps_data)
|
|
{
|
|
struct file *cal_filp = NULL;
|
|
mm_segment_t old_fs;
|
|
uint16_t file_offset_data;
|
|
int ret;
|
|
|
|
old_fs = get_fs();
|
|
set_fs(KERNEL_DS);
|
|
|
|
cal_filp = filp_open(CALIBRATION_FILE_PATH, O_RDONLY, 0);
|
|
if (IS_ERR(cal_filp)) {
|
|
ret = PTR_ERR(cal_filp);
|
|
if (ret != -ENOENT)
|
|
SENSOR_ERR("Can't open calibration file\n");
|
|
set_fs(old_fs);
|
|
ps_data->ps_offset = ps_data->ps_default_offset;
|
|
SENSOR_ERR("Can't open calibration file 2(%d) ps_offset =%d\n",
|
|
ret, ps_data->ps_offset);
|
|
return ret;
|
|
}
|
|
|
|
ret = vfs_read(cal_filp,
|
|
(char *)&file_offset_data,
|
|
sizeof(u16), &cal_filp->f_pos);
|
|
if (ret != sizeof(u16)) {
|
|
SENSOR_ERR("Can't read the cal data from file\n");
|
|
ret = -EIO;
|
|
}
|
|
|
|
if(file_offset_data < ps_data->ps_cal_skip_adc)
|
|
goto exit;
|
|
|
|
if (file_offset_data != ps_data->ps_offset)
|
|
ps_data->ps_offset = file_offset_data;
|
|
if (ps_data->ps_offset != ps_data->ps_default_offset) {
|
|
stk3013_set_ps_thd_h(ps_data, ps_data->ps_cancel_thd_h);
|
|
stk3013_set_ps_thd_l(ps_data, ps_data->ps_cancel_thd_l);
|
|
}
|
|
|
|
exit:
|
|
SENSOR_INFO("file_offset = %d, ps_offset = %d, default_offset = %d\n",
|
|
file_offset_data, ps_data->ps_offset,
|
|
ps_data->ps_default_offset);
|
|
|
|
filp_close(cal_filp, current->files);
|
|
set_fs(old_fs);
|
|
|
|
return ret;
|
|
}
|
|
#endif
|
|
|
|
static int stk3013_set_wq(struct stk3013_data *ps_data)
|
|
{
|
|
ps_data->stk_wq = create_singlethread_workqueue("stk_wq");
|
|
INIT_WORK(&ps_data->stk_work, stk_work_func);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int stk3013_probe(struct i2c_client *client,
|
|
const struct i2c_device_id *id)
|
|
{
|
|
int ret = -ENODEV;
|
|
struct stk3013_data *ps_data;
|
|
struct stk3013_platform_data *plat_data;
|
|
|
|
SENSOR_INFO("driver version = %s\n", DRIVER_VERSION);
|
|
|
|
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
|
|
SENSOR_ERR("No Support for I2C_FUNC_I2C\n");
|
|
return ret;
|
|
}
|
|
|
|
ps_data = kzalloc(sizeof(struct stk3013_data), GFP_KERNEL);
|
|
if (!ps_data) {
|
|
SENSOR_ERR("failed to allocate stk3013_data\n");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
ps_data->client = client;
|
|
i2c_set_clientdata(client, ps_data);
|
|
mutex_init(&ps_data->io_lock);
|
|
wake_lock_init(&ps_data->ps_wakelock, WAKE_LOCK_SUSPEND,
|
|
"stk_input_wakelock");
|
|
|
|
if (client->dev.of_node) {
|
|
SENSOR_INFO("with device tree\n");
|
|
plat_data = devm_kzalloc(&client->dev,
|
|
sizeof(struct stk3013_platform_data), GFP_KERNEL);
|
|
if (!plat_data) {
|
|
dev_err(&client->dev, "Failed to allocate memory\n");
|
|
ret = -ENOMEM;
|
|
goto err_als_input_allocate;
|
|
}
|
|
ret = stk3013_parse_dt(&client->dev, plat_data);
|
|
if (ret) {
|
|
SENSOR_ERR("stk3013_parse_dt ret=%d\n", ret);
|
|
goto err_als_input_allocate;
|
|
}
|
|
} else {
|
|
SENSOR_INFO("with platform data\n");
|
|
plat_data = client->dev.platform_data;
|
|
}
|
|
if (!plat_data) {
|
|
SENSOR_ERR("no stk3013 platform data!\n");
|
|
ret = -ENOMEM;
|
|
goto err_als_input_allocate;
|
|
}
|
|
|
|
stk3013_regulator_onoff(&client->dev, ON);
|
|
|
|
ps_data->int_pin = plat_data->int_pin;
|
|
ps_data->pdata = plat_data;
|
|
|
|
stk3013_set_wq(ps_data);
|
|
ret = stk3013_init_all_setting(client, plat_data);
|
|
if (ret < 0)
|
|
goto err_init_all_setting;
|
|
|
|
ps_data->ps_input_dev = input_allocate_device();
|
|
if (ps_data->ps_input_dev == NULL) {
|
|
SENSOR_ERR("could not allocate ps device\n");
|
|
ret = -ENOMEM;
|
|
goto err_input_alloc_device;
|
|
}
|
|
ps_data->ps_input_dev->name = MODULE_NAME;
|
|
set_bit(EV_ABS, ps_data->ps_input_dev->evbit);
|
|
input_set_capability(ps_data->ps_input_dev, EV_ABS, ABS_DISTANCE);
|
|
input_set_abs_params(ps_data->ps_input_dev, ABS_DISTANCE, 0, 1, 0, 0);
|
|
ret = input_register_device(ps_data->ps_input_dev);
|
|
if (ret < 0) {
|
|
SENSOR_ERR("can not register ps input device\n");
|
|
goto err_input_register_device;
|
|
}
|
|
|
|
ret = sensors_create_symlink(&ps_data->ps_input_dev->dev.kobj,
|
|
ps_data->ps_input_dev->name);
|
|
if (ret < 0) {
|
|
SENSOR_ERR("create_symlink error\n");
|
|
goto err_sensors_create_symlink_prox;
|
|
}
|
|
|
|
ret = sysfs_create_group(&ps_data->ps_input_dev->dev.kobj,
|
|
&proximity_attribute_group);
|
|
if (ret < 0) {
|
|
SENSOR_ERR("could not create sysfs group for ps\n");
|
|
goto err_sysfs_create_group_proximity;
|
|
}
|
|
input_set_drvdata(ps_data->ps_input_dev, ps_data);
|
|
|
|
ret = stk3013_setup_irq(client);
|
|
if (ret < 0)
|
|
goto err_stk3013_setup_irq;
|
|
device_init_wakeup(&client->dev, true);
|
|
|
|
hrtimer_init(&ps_data->prox_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
|
|
ps_data->prox_poll_delay = ns_to_ktime(2000 * NSEC_PER_MSEC);/*2 sec*/
|
|
ps_data->prox_timer.function = stk3013_prox_timer_func;
|
|
|
|
/* the timer just fires off a work queue request. we need a thread
|
|
to read the i2c (can be slow and blocking). */
|
|
ps_data->prox_wq = create_singlethread_workqueue("stk3013_prox_wq");
|
|
if (!ps_data->prox_wq) {
|
|
ret = -ENOMEM;
|
|
SENSOR_ERR("could not create prox workqueue\n");
|
|
goto err_create_prox_workqueue;
|
|
}
|
|
/* this is the thread function we run on the work queue */
|
|
INIT_WORK(&ps_data->work_prox, stk3013_work_func_prox);
|
|
|
|
ret = sensors_register(&ps_data->ps_dev, ps_data,
|
|
prox_sensor_attrs, MODULE_NAME);
|
|
if (ret) {
|
|
SENSOR_ERR("cound not register proximity sensor device(%d)\n",
|
|
ret);
|
|
goto prox_sensor_register_failed;
|
|
}
|
|
|
|
/*stk3013_regulator_onoff(&client->dev, OFF);*/
|
|
SENSOR_INFO("success\n");
|
|
return 0;
|
|
/*device_init_wakeup(&client->dev, false);*/
|
|
|
|
prox_sensor_register_failed:
|
|
destroy_workqueue(ps_data->prox_wq);
|
|
err_create_prox_workqueue:
|
|
err_stk3013_setup_irq:
|
|
free_irq(ps_data->irq, ps_data);
|
|
gpio_free(ps_data->int_pin);
|
|
err_sysfs_create_group_proximity:
|
|
sensors_remove_symlink(&ps_data->ps_input_dev->dev.kobj,
|
|
ps_data->ps_input_dev->name);
|
|
err_sensors_create_symlink_prox:
|
|
input_unregister_device(ps_data->ps_input_dev);
|
|
err_input_register_device:
|
|
err_input_alloc_device:
|
|
err_init_all_setting:
|
|
destroy_workqueue(ps_data->stk_wq);
|
|
/*stk3013_regulator_onoff(&client->dev, OFF);*/
|
|
err_als_input_allocate:
|
|
wake_lock_destroy(&ps_data->ps_wakelock);
|
|
mutex_destroy(&ps_data->io_lock);
|
|
kfree(ps_data);
|
|
return ret;
|
|
}
|
|
|
|
|
|
static int stk3013_remove(struct i2c_client *client)
|
|
{
|
|
SENSOR_INFO("\n");
|
|
return 0;
|
|
}
|
|
|
|
static const struct i2c_device_id stk_ps_id[] = {
|
|
{ "stk_ps", 0},
|
|
{}
|
|
};
|
|
MODULE_DEVICE_TABLE(i2c, stk_ps_id);
|
|
|
|
static struct of_device_id stk_match_table[] = {
|
|
{ .compatible = "stk,stk3013", },
|
|
{ },
|
|
};
|
|
|
|
static struct i2c_driver stk_ps_driver = {
|
|
.driver = {
|
|
.name = CHIP_ID,
|
|
.owner = THIS_MODULE,
|
|
.of_match_table = stk_match_table,
|
|
.pm = &stk3013_pm_ops,
|
|
},
|
|
.probe = stk3013_probe,
|
|
.remove = stk3013_remove,
|
|
.id_table = stk_ps_id,
|
|
};
|
|
|
|
static int __init stk3013_init(void)
|
|
{
|
|
int ret;
|
|
|
|
ret = i2c_add_driver(&stk_ps_driver);
|
|
if (ret)
|
|
i2c_del_driver(&stk_ps_driver);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void __exit stk3013_exit(void)
|
|
{
|
|
i2c_del_driver(&stk_ps_driver);
|
|
}
|
|
|
|
module_init(stk3013_init);
|
|
module_exit(stk3013_exit);
|
|
MODULE_AUTHOR("Samsung Electronics");
|
|
MODULE_DESCRIPTION("Sensortek stk3013 Proximity Sensor driver");
|
|
MODULE_LICENSE("GPL");
|
|
MODULE_VERSION(DRIVER_VERSION);
|