1902 lines
52 KiB
C
1902 lines
52 KiB
C
/* STK3X3X Proximity Sensor Driver
|
|
*
|
|
* Copyright (C) 2018 Samsung Electronics. All rights reserved.
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* version 2 as published by the Free Software Foundation.
|
|
*
|
|
* This program is distributed in the hope that it will be useful, but
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
|
|
* 02110-1301 USA
|
|
*/
|
|
|
|
#include <linux/module.h>
|
|
#include <linux/init.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/i2c.h>
|
|
#include <linux/mutex.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 <asm/uaccess.h>
|
|
#include <linux/of.h>
|
|
#include <linux/types.h>
|
|
#include <linux/pm.h>
|
|
#include <linux/string.h>
|
|
#include <linux/sensor/sensors_core.h>
|
|
#ifdef CONFIG_OF
|
|
#include <linux/of_gpio.h>
|
|
#endif
|
|
#include "stk3x3x.h"
|
|
|
|
#define MODULE_NAME "proximity_sensor"
|
|
#define VENDOR_NAME "Sensortek"
|
|
#define CHIP_NAME "STK3031"
|
|
#define PROX_READ_NUM 25
|
|
#define PS_WAIT 52000
|
|
#define PROX_MIN_DATA 0
|
|
#define PDATA_MIN 0
|
|
#define PDATA_MAX 0xFFFF
|
|
#define FIRST_CAL_ADC_LIMIT 120
|
|
#define MIN_INT -99999
|
|
#define MAX_INT 99999
|
|
#define AVG_SAMPLE_COUNT 6
|
|
#define CAL_FAIL_MAX_MIN_RANGE 20
|
|
#define CAL_RESET_CLOSE_CNT 3
|
|
#define POCKET_DATA_NUM 3
|
|
|
|
stk3x3x_register_table stk3x3x_default_register_table[] = {
|
|
{STK3X3X_PSCTRL_REG, (STK3X3X_PS_PRS4 | STK3X3X_PS_GAIN8 | STK3X3X_PS_IT400), 0xFF},
|
|
{STK3X3X_LEDCTRL_REG, STK3X3X_LED_150mA, 0xFF},
|
|
{STK3X3X_INT_REG, STK3X3X_INT_NF_EN, 0xFF},
|
|
{STK3X3X_WAIT_REG, STK3X3X_WAIT50, 0xFF},
|
|
{0xDB, 0x14, 0xFF},
|
|
{0xF4, 0xC0, 0xFF},
|
|
#ifdef STK_INTELLI_PERSIST
|
|
{STK3X3X_INTELLI_WAIT_PS_REG, STK3X3X_INTELL_13, 0xFF},
|
|
#endif
|
|
#ifdef STK_CTIR
|
|
{0xF3, 0x00, 0x07},
|
|
#endif
|
|
#ifdef STK_BGIR
|
|
{0xA0, 0x10, 0xFF},
|
|
{0xAA, 0x00, 0xFF},
|
|
#endif
|
|
};
|
|
|
|
uint8_t stk3x3x_reg_map[] =
|
|
{
|
|
STK3X3X_STATE_REG,
|
|
STK3X3X_PSCTRL_REG,
|
|
STK3X3X_ALSCTRL_REG,
|
|
STK3X3X_LEDCTRL_REG,
|
|
STK3X3X_INT_REG,
|
|
STK3X3X_WAIT_REG,
|
|
STK3X3X_THDH1_PS_REG,
|
|
STK3X3X_THDH2_PS_REG,
|
|
STK3X3X_THDL1_PS_REG,
|
|
STK3X3X_THDL2_PS_REG,
|
|
STK3X3X_THDH1_ALS_REG,
|
|
STK3X3X_THDH2_ALS_REG,
|
|
STK3X3X_THDL1_ALS_REG,
|
|
STK3X3X_THDL2_ALS_REG,
|
|
STK3X3X_FLAG_REG,
|
|
STK3X3X_DATA1_PS_REG,
|
|
STK3X3X_DATA2_PS_REG,
|
|
STK3X3X_DATA1_ALS_REG,
|
|
STK3X3X_DATA2_ALS_REG,
|
|
STK3X3X_DATA1_R_REG,
|
|
STK3X3X_DATA2_R_REG,
|
|
STK3X3X_DATA1_G_REG,
|
|
STK3X3X_DATA2_G_REG,
|
|
STK3X3X_DATA1_B_REG,
|
|
STK3X3X_DATA2_B_REG,
|
|
STK3X3X_DATA1_C_REG,
|
|
STK3X3X_DATA2_C_REG,
|
|
STK3X3X_DATA1_OFFSET_REG,
|
|
STK3X3X_DATA2_OFFSET_REG,
|
|
STK3X3X_DATA_CTIR1_REG,
|
|
STK3X3X_DATA_CTIR2_REG,
|
|
STK3X3X_DATA_CTIR3_REG,
|
|
STK3X3X_DATA_CTIR4_REG,
|
|
STK3X3X_PDT_ID_REG,
|
|
STK3X3X_RSRVD_REG,
|
|
STK3X3X_ALSCTRL2_REG,
|
|
STK3X3X_INTELLI_WAIT_PS_REG,
|
|
0xFA,
|
|
STK3X3X_SW_RESET_REG
|
|
};
|
|
|
|
static int stk3x3x_reg_read(struct stk3x3x_data *drv_data,
|
|
unsigned char reg)
|
|
{
|
|
int ret = 0;
|
|
|
|
ret = i2c_smbus_read_byte_data(drv_data->client, reg);
|
|
if (ret < 0)
|
|
SENSOR_ERR("failed, reg=0x%x, ret=%d\n", reg, ret);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int stk3x3x_reg_write(struct stk3x3x_data *drv_data,
|
|
unsigned char reg, unsigned char val)
|
|
{
|
|
int ret = 0;
|
|
|
|
ret = i2c_smbus_write_byte_data(drv_data->client, reg, val);
|
|
if (ret < 0)
|
|
SENSOR_ERR("failed, reg=0x%x, ret=%d\n", reg, ret);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int stk3x3x_reg_write_block(struct stk3x3x_data *drv_data,
|
|
unsigned char reg, unsigned char *val, unsigned char length)
|
|
{
|
|
int ret = 0;
|
|
|
|
ret = i2c_smbus_write_i2c_block_data(drv_data->client, reg, length, val);
|
|
if (ret < 0)
|
|
SENSOR_ERR("failed, reg=0x%x, ret=%d\n", reg, ret);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int stk3x3x_reg_read_modify_write(struct stk3x3x_data *drv_data,
|
|
unsigned char reg, unsigned char val, unsigned char mask)
|
|
{
|
|
uint8_t rw_buffer = 0;
|
|
int ret = 0;
|
|
|
|
if ((mask == 0xFF) || (mask == 0x0)) {
|
|
ret = stk3x3x_reg_write(drv_data, reg, val);
|
|
if (ret < 0)
|
|
SENSOR_ERR("failed, reg=0x%x, ret=%d\n", reg, ret);
|
|
} else {
|
|
rw_buffer = (uint8_t)stk3x3x_reg_read(drv_data, reg);
|
|
if (rw_buffer < 0) {
|
|
SENSOR_ERR("failed, reg=0x%x, rw_buffer=%d\n", reg, rw_buffer);
|
|
return rw_buffer;
|
|
} else {
|
|
rw_buffer = (rw_buffer & (~mask)) | (val & mask);
|
|
|
|
ret = stk3x3x_reg_write(drv_data, reg, rw_buffer);
|
|
if (ret < 0)
|
|
SENSOR_ERR("failed, reg=0x%x, ret=%d\n", reg, ret);
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int stk3x3x_reg_read_block(struct stk3x3x_data *drv_data,
|
|
unsigned char reg, int count, void *buf)
|
|
{
|
|
int ret;
|
|
|
|
ret = i2c_smbus_read_i2c_block_data(drv_data->client, reg, count, buf);
|
|
if (ret < 0)
|
|
SENSOR_ERR("failed, reg=0x%x, ret=%d\n", reg, ret);
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
|
|
static irqreturn_t stk3x3x_irq_handler(int irq, void *data)
|
|
{
|
|
struct stk3x3x_data *drv_data = data;
|
|
|
|
SENSOR_INFO("\n");
|
|
|
|
disable_irq_nosync(irq);
|
|
queue_work(drv_data->prox_irq_wq, &drv_data->prox_irq_work);
|
|
|
|
return IRQ_HANDLED;
|
|
}
|
|
|
|
static int32_t stk3x3x_get_mid(struct stk3x3x_data *drv_data)
|
|
{
|
|
int32_t ret = 0;
|
|
|
|
ret = STK3X3X_REG_READ(drv_data, 0xE0);
|
|
if (ret < 0)
|
|
SENSOR_ERR("failed, ret=%d\n", ret);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int32_t stk3x3x_prst_cnt_rst_sel(struct stk3x3x_data *drv_data)
|
|
{
|
|
uint8_t reg_value = 0x0;
|
|
int32_t ret = 0;
|
|
|
|
if (0 == (stk3x3x_get_mid(drv_data) & 0x1))
|
|
reg_value = 0x1;
|
|
|
|
ret = STK3X3X_REG_READ_MODIFY_WRITE(drv_data, 0xFA, reg_value, 0xFF);
|
|
if (ret < 0)
|
|
SENSOR_ERR("failed, ret=%d\n", ret);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int32_t stk3x3x_device_id_check(struct stk3x3x_data *drv_data)
|
|
{
|
|
u8 val;
|
|
int ret;
|
|
|
|
ret = STK3X3X_REG_READ(drv_data, STK3X3X_PDT_ID_REG);
|
|
if (ret < 0) {
|
|
SENSOR_ERR("Device id read failed, ret=%d\n", ret);
|
|
} else {
|
|
val = (u8) ret;
|
|
|
|
if (val == STK3331_DEVICE_ID_VAL || val == STK3031_DEVICE_ID_VAL) {
|
|
SENSOR_INFO("Device matched, device_id=0x%2x\n", val);
|
|
return 0;
|
|
} else {
|
|
SENSOR_ERR("Device not matched, device_id=0x%2x\n", val);
|
|
return -ENODEV;
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int32_t stk3x3x_software_reset(struct stk3x3x_data *drv_data)
|
|
{
|
|
int32_t ret = 0;
|
|
|
|
ret = STK3X3X_REG_WRITE(drv_data, STK3X3X_SW_RESET_REG, 0x0);
|
|
if (ret < 0) {
|
|
SENSOR_ERR("failed, ret=%d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
usleep_range(13000, 15000);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int stk3x3x_bgir_check(struct stk3x3x_data *drv_data)
|
|
{
|
|
int32_t ret = 0;
|
|
uint8_t ps_invalid_flag, bgir_raw_data[4] = {0};
|
|
bool bgir_out_of_range = false;
|
|
uint8_t i;
|
|
|
|
ret = STK3X3X_REG_READ(drv_data, 0xA7);
|
|
if (ret < 0) {
|
|
SENSOR_ERR("0xA7 read failed, ret=%d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
ps_invalid_flag = (uint8_t)ret;
|
|
|
|
ret = STK3X3X_REG_BLOCK_READ(drv_data, 0x34, 4, &bgir_raw_data[0]);
|
|
if (ret < 0) {
|
|
SENSOR_ERR("0x34 read failed, ret=%d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
for (i = 0; i < 4; i++) {
|
|
if (*(bgir_raw_data + i) > STK3X3X_PS_BGIR_THRESHOLD) {
|
|
bgir_out_of_range = true;
|
|
SENSOR_ERR("BGIR invalid, bgir[%d]=0x%X\n", i, *(bgir_raw_data + i));
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (((ps_invalid_flag >> 5) & 0x1) || bgir_out_of_range) {
|
|
ret = 0x7FFF0001;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
int32_t stk3x3x_set_ps_thd(struct stk3x3x_data *drv_data, uint16_t thd_h, uint16_t thd_l)
|
|
{
|
|
unsigned char val[4];
|
|
int ret;
|
|
|
|
val[0] = (thd_h & 0xFF00) >> 8;
|
|
val[1] = thd_h & 0x00FF;
|
|
val[2] = (thd_l & 0xFF00) >> 8;
|
|
val[3] = thd_l & 0x00FF;
|
|
|
|
ret = STK3X3X_REG_WRITE_BLOCK(drv_data, STK3X3X_THDH1_PS_REG, val, sizeof(val));
|
|
if (ret < 0)
|
|
SENSOR_ERR("failed, ret=%d\n", ret);
|
|
else {
|
|
drv_data->prox_thd_h = thd_h;
|
|
drv_data->prox_thd_l = thd_l;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static uint32_t stk3x3x_get_ps_reading(struct stk3x3x_data *ps_data)
|
|
{
|
|
unsigned char value[2];
|
|
int ret;
|
|
|
|
ret = STK3X3X_REG_BLOCK_READ(ps_data, STK3X3X_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 stk3x3x_first_cal_enable_ps(struct stk3x3x_data *drv_data, bool en)
|
|
{
|
|
int32_t ret = 0;
|
|
uint8_t reg_value = 0;
|
|
|
|
ret = STK3X3X_REG_READ(drv_data, STK3X3X_STATE_REG);
|
|
if (ret < 0) {
|
|
goto done;
|
|
} else {
|
|
reg_value = (uint8_t)ret;
|
|
}
|
|
|
|
reg_value &= (~(STK3X3X_STATE_EN_PS_MASK | STK3X3X_STATE_EN_WAIT_MASK | STK3X3X_STATE_EN_INTELL_PRST_MASK));
|
|
|
|
if (en) {
|
|
SENSOR_INFO("First Cal Enable\n");
|
|
stk3x3x_set_ps_thd(drv_data, drv_data->prox_thd_h, drv_data->prox_thd_l);
|
|
|
|
reg_value |= (STK3X3X_STATE_EN_WAIT_MASK | STK3X3X_STATE_EN_PS_MASK | STK3X3X_STATE_EN_INTELL_PRST_MASK);
|
|
|
|
ret = STK3X3X_REG_READ_MODIFY_WRITE(drv_data, STK3X3X_STATE_REG, reg_value, 0xFF);
|
|
if (ret < 0)
|
|
goto done;
|
|
} else {
|
|
SENSOR_INFO("First Cal Disable\n");
|
|
|
|
reg_value &= (~(STK3X3X_STATE_EN_PS_MASK | STK3X3X_STATE_EN_WAIT_MASK | STK3X3X_STATE_EN_INTELL_PRST_MASK));
|
|
|
|
ret = STK3X3X_REG_READ_MODIFY_WRITE(drv_data, STK3X3X_STATE_REG, reg_value, 0xFF);
|
|
if (ret < 0)
|
|
goto done;
|
|
}
|
|
|
|
done:
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void stk3x3x_prox_cal(struct stk3x3x_data *ps_data)
|
|
{
|
|
int i, sum_cnt = 0, ret;
|
|
uint16_t read_value, avg_value = 0, min = PDATA_MAX, max = PDATA_MIN;
|
|
uint32_t sum = 0;
|
|
uint8_t sunlight_protection_mode = 0;
|
|
|
|
if (ps_data->enable == false) {
|
|
if (ps_data->cal_status == STK3X3X_FIRST_CAL) {
|
|
stk3x3x_first_cal_enable_ps(ps_data, 1);
|
|
} else {
|
|
ps_data->cal_status = STK3X3X_CAL_DISABLED;
|
|
SENSOR_ERR("prox sensor is disabled\n");
|
|
return;
|
|
}
|
|
}
|
|
|
|
// set wait time as 10ms
|
|
ret = STK3X3X_REG_WRITE(ps_data, STK3X3X_WAIT_REG, 0x5);
|
|
if(ret < 0) {
|
|
SENSOR_ERR("WAIT_REG failed %d\n", ret);
|
|
goto exit;
|
|
}
|
|
if (ps_data->cal_status == STK3X3X_FIRST_CAL) {
|
|
usleep_range(500000, 500000);
|
|
} else
|
|
usleep_range(60000, 60000);
|
|
|
|
for (i = 0; i < AVG_SAMPLE_COUNT; i++) {
|
|
if (ps_data->ps_it == STK3X3X_PS_IT1540) // wait-time = 9.24ms and IT-PS time = 1.54 ms
|
|
usleep_range(12000, 12000);
|
|
else
|
|
usleep_range(10000, 10000); // wait-time = 9.24ms and IT-PS time = 368 us
|
|
|
|
if (ps_data->cal_status == STK3X3X_CAL_ONGOING && ps_data->enable == false) {
|
|
SENSOR_ERR("sensor disabled exit\n");
|
|
goto exit;
|
|
}
|
|
|
|
// check sunlight mode
|
|
ret = STK3X3X_REG_READ(ps_data, STK3X3X_SUNLIGHT_CHECK_REG);
|
|
if (ret < 0) {
|
|
SENSOR_ERR("STK3X3X_SUNLIGHT_CHECK_REG read fail, ret=%d\n", ret);
|
|
goto exit;
|
|
}
|
|
|
|
sunlight_protection_mode = (uint8_t)ret;
|
|
|
|
// read adc value
|
|
read_value = stk3x3x_get_ps_reading(ps_data);
|
|
|
|
if (read_value > max)
|
|
max = read_value;
|
|
if (read_value < min)
|
|
min = read_value;
|
|
|
|
SENSOR_INFO("read_value = %d, (0x%x)\n", read_value, sunlight_protection_mode);
|
|
if (((sunlight_protection_mode >> 5) & 0x1) && read_value == 0) {
|
|
if (ps_data->cal_status == STK3X3X_FIRST_CAL) {
|
|
if (ps_data->sunlight_thd_h != PDATA_MAX && ps_data->sunlight_thd_l != PDATA_MAX) {
|
|
stk3x3x_set_ps_thd(ps_data, ps_data->sunlight_thd_h, ps_data->sunlight_thd_l);
|
|
SENSOR_INFO("SUNLIGHT PROTECTION, set as sunlight thd h(%u) l(%u)\n",
|
|
ps_data->sunlight_thd_h, ps_data->sunlight_thd_l);
|
|
} else
|
|
SENSOR_INFO("SUNLIGHT PROTECTION, set as default thd h(%u) l(%u)\n",
|
|
ps_data->prox_thd_h, ps_data->prox_thd_l);
|
|
} else
|
|
SENSOR_ERR("SUNLIGHT PROTECTION, calibration is failed, (0x%x)\n"
|
|
, sunlight_protection_mode);
|
|
goto exit;
|
|
} else if ((ps_data->cal_status == STK3X3X_CAL_ONGOING)
|
|
&& (read_value > ps_data->prox_default_thd_l - 20)) {
|
|
SENSOR_ERR("cal failed ps_data = %d, thd l %u\n", read_value, ps_data->prox_default_thd_l);
|
|
goto exit;
|
|
} else if (!ps_data->first_limit_skip && (ps_data->cal_status == STK3X3X_FIRST_CAL)
|
|
&& read_value > ps_data->first_cal_adc_limit) {
|
|
if (ps_data->first_cal_thd_h != PDATA_MAX && ps_data->first_cal_thd_l != PDATA_MAX) {
|
|
stk3x3x_set_ps_thd(ps_data, ps_data->first_cal_thd_h, ps_data->first_cal_thd_l);
|
|
SENSOR_INFO("first cal adc is too big. set as thd h(%u) l(%u)\n",
|
|
ps_data->first_cal_thd_h, ps_data->first_cal_thd_l);
|
|
} else
|
|
SENSOR_INFO("first cal adc is too big. set as default thd h(%u) l(%u)\n",
|
|
ps_data->prox_thd_h, ps_data->prox_thd_l);
|
|
goto exit;
|
|
} else if (read_value >= PDATA_MIN && read_value <= PDATA_MAX) {
|
|
sum += read_value;
|
|
sum_cnt++;
|
|
} else {
|
|
SENSOR_ERR("ps_data is invalid(%d)\n", read_value);
|
|
goto exit;
|
|
}
|
|
}
|
|
|
|
if (max - min > CAL_FAIL_MAX_MIN_RANGE) {
|
|
SENSOR_ERR("cal failed max(%u) - min(%u) = %u\n", max, min, max-min);
|
|
goto exit;
|
|
}
|
|
|
|
if (sum_cnt == AVG_SAMPLE_COUNT) {
|
|
avg_value = sum/AVG_SAMPLE_COUNT;
|
|
SENSOR_INFO("sum = %d, avg = %d", sum, avg_value);
|
|
ps_data->prox_thd_h = avg_value + ps_data->thd_h_offset;
|
|
if (ps_data->cal_status == STK3X3X_FIRST_CAL)
|
|
ps_data->prox_thd_l = avg_value + 30;
|
|
else
|
|
ps_data->prox_thd_l = avg_value + 20;
|
|
stk3x3x_set_ps_thd(ps_data, ps_data->prox_thd_h, ps_data->prox_thd_l);
|
|
SENSOR_INFO("cal done h=%u, l=%u\n", ps_data->prox_thd_h,
|
|
ps_data->prox_thd_l);
|
|
} else
|
|
SENSOR_ERR("cal is failed (%d)\n", sum_cnt);
|
|
|
|
exit :
|
|
ret = STK3X3X_REG_WRITE(ps_data, STK3X3X_WAIT_REG, STK3X3X_WAIT50);
|
|
if (ret < 0)
|
|
SENSOR_ERR("WAIT_REG failed %d\n", ret);
|
|
|
|
if (ps_data->cal_status == STK3X3X_FIRST_CAL && !ps_data->factory_cal)
|
|
stk3x3x_first_cal_enable_ps(ps_data, 0);
|
|
ps_data->cal_status = STK3X3X_CAL_DISABLED;
|
|
ps_data->factory_cal = false;
|
|
}
|
|
|
|
static void stk3x3x_work_func_prox_cal(struct work_struct *work)
|
|
{
|
|
struct stk3x3x_data *ps_data = container_of(work,
|
|
struct stk3x3x_data, work_cal_prox);
|
|
|
|
mutex_lock(&ps_data->control_mutex);
|
|
stk3x3x_prox_cal(ps_data);
|
|
mutex_unlock(&ps_data->control_mutex);
|
|
}
|
|
|
|
static void stk3x3x_work_func_pocket_read(struct work_struct *work)
|
|
{
|
|
struct stk3x3x_data *ps_data = container_of(work,
|
|
struct stk3x3x_data, work_pocket);
|
|
|
|
int32_t ret = 0;
|
|
uint16_t read_adc = 0;
|
|
uint8_t reg_value = 0, read_val[2] = {0,};
|
|
uint8_t sunlight_protection_mode = 0;
|
|
int i = 0;
|
|
|
|
SENSOR_INFO("start\n");
|
|
ps_data->pocket_prox = STK3X3X_POCKET_UNKNOWN;
|
|
|
|
ret = STK3X3X_REG_READ(ps_data, STK3X3X_STATE_REG);
|
|
if (ret < 0)
|
|
goto exit;
|
|
else
|
|
reg_value = (uint8_t)ret;
|
|
|
|
reg_value &= (~(STK3X3X_STATE_EN_PS_MASK | STK3X3X_STATE_EN_WAIT_MASK | STK3X3X_STATE_EN_INTELL_PRST_MASK));
|
|
|
|
stk3x3x_set_ps_thd(ps_data, ps_data->prox_thd_h, ps_data->prox_thd_l);
|
|
|
|
reg_value |= (STK3X3X_STATE_EN_WAIT_MASK | STK3X3X_STATE_EN_PS_MASK | STK3X3X_STATE_EN_INTELL_PRST_MASK);
|
|
|
|
|
|
ret = STK3X3X_REG_READ_MODIFY_WRITE(ps_data, STK3X3X_STATE_REG, reg_value, 0xFF);
|
|
if (ret < 0)
|
|
goto exit;
|
|
|
|
// set wait time as 1.74ms
|
|
ret = STK3X3X_REG_WRITE(ps_data, STK3X3X_WAIT_REG, 0x1);
|
|
if(ret < 0)
|
|
SENSOR_ERR("WAIT_REG failed %d\n", ret);
|
|
|
|
usleep_range(10000, 10000);
|
|
|
|
// check sunlight mode
|
|
ret = STK3X3X_REG_READ(ps_data, STK3X3X_SUNLIGHT_CHECK_REG);
|
|
if (ret < 0) {
|
|
SENSOR_ERR("STK3X3X_SUNLIGHT_CHECK_REG read fail, ret=%d\n", ret);
|
|
goto exit;
|
|
}
|
|
|
|
sunlight_protection_mode = (uint8_t)ret;
|
|
|
|
for (i = 0; i < POCKET_DATA_NUM; i++) {
|
|
ret = STK3X3X_REG_BLOCK_READ(ps_data, STK3X3X_DATA1_PS_REG, 2, &read_val[0]);
|
|
if (ret < 0) {
|
|
SENSOR_ERR("ADC read failed, ret=%d\n", ret);
|
|
return;
|
|
} else {
|
|
read_adc += (read_val[0] << 8 | read_val[1]);
|
|
if (((sunlight_protection_mode >> 5) & 0x1) && read_adc == 0) {
|
|
SENSOR_ERR("SUNLIGHT PROTECTION, prox read is failed, (0x%x)\n"
|
|
, sunlight_protection_mode);
|
|
ps_data->pocket_prox = STK3X3X_POCKET_FAR_AWAY;
|
|
// turn off proximity sensor
|
|
reg_value = 0;
|
|
STK3X3X_REG_READ(ps_data, STK3X3X_STATE_REG);
|
|
reg_value &= (~(STK3X3X_STATE_EN_PS_MASK | STK3X3X_STATE_EN_WAIT_MASK | STK3X3X_STATE_EN_INTELL_PRST_MASK));
|
|
STK3X3X_REG_READ_MODIFY_WRITE(ps_data, STK3X3X_STATE_REG, reg_value, 0xFF);
|
|
goto exit;
|
|
}
|
|
}
|
|
if (i < POCKET_DATA_NUM - 1)
|
|
usleep_range(10000, 10000);
|
|
}
|
|
read_adc = read_adc / POCKET_DATA_NUM;
|
|
|
|
reg_value = 0;
|
|
ret = STK3X3X_REG_READ(ps_data, STK3X3X_STATE_REG);
|
|
if (ret < 0) {
|
|
goto exit;
|
|
} else {
|
|
reg_value = (uint8_t)ret;
|
|
}
|
|
|
|
reg_value &= (~(STK3X3X_STATE_EN_PS_MASK | STK3X3X_STATE_EN_WAIT_MASK | STK3X3X_STATE_EN_INTELL_PRST_MASK));
|
|
|
|
ret = STK3X3X_REG_READ_MODIFY_WRITE(ps_data, STK3X3X_STATE_REG, reg_value, 0xFF);
|
|
if (ret < 0)
|
|
goto exit;
|
|
|
|
if (read_adc < ps_data->prox_thd_h)
|
|
ps_data->pocket_prox = STK3X3X_POCKET_FAR_AWAY;
|
|
else
|
|
ps_data->pocket_prox = STK3X3X_POCKET_NEAR_BY;
|
|
|
|
SENSOR_INFO("adc=%d, thd_h=%d, prox=%d\n", read_adc, ps_data->prox_thd_h, ps_data->pocket_prox);
|
|
|
|
|
|
exit:
|
|
ps_data->pocket_running = false;
|
|
}
|
|
|
|
static void stk_ps_report(struct stk3x3x_data *drv_data, int nf)
|
|
{
|
|
uint8_t reg_value = 0x0;
|
|
uint8_t state_reg = 0x00;
|
|
int32_t ret;
|
|
|
|
input_report_rel(drv_data->prox_input_dev, REL_MISC, nf + 1);
|
|
input_sync(drv_data->prox_input_dev);
|
|
wake_lock_timeout(&drv_data->prox_wakelock, 3 * HZ);
|
|
|
|
// A20 model only
|
|
if(drv_data->intel_prst == 0) {
|
|
ret = STK3X3X_REG_READ(drv_data, STK3X3X_STATE_REG);
|
|
if (ret < 0) {
|
|
SENSOR_ERR("state register read failed, ret=%d\n", ret);
|
|
} else {
|
|
state_reg = (uint8_t)ret;
|
|
SENSOR_INFO("state register read value, state_reg=%d\n", state_reg);
|
|
}
|
|
|
|
if (nf == STK3X3X_PRX_NEAR_BY) {
|
|
state_reg &= (~STK3X3X_STATE_EN_INTELL_PRST_MASK); //disable intel persistance
|
|
} else if (nf == STK3X3X_PRX_FAR_AWAY) {
|
|
state_reg |= (STK3X3X_STATE_EN_INTELL_PRST_MASK); //enable intel persistance
|
|
}
|
|
|
|
SENSOR_INFO("state register write value, state_reg=%d\n", state_reg);
|
|
ret = STK3X3X_REG_READ_MODIFY_WRITE(drv_data, STK3X3X_STATE_REG, state_reg, 0xFF);
|
|
if (ret < 0)
|
|
SENSOR_ERR("state register write failed, ret=%d\n", ret);
|
|
}
|
|
// PS persistance adjustment
|
|
if (nf == STK3X3X_PRX_NEAR_BY) {
|
|
reg_value = (STK3X3X_PS_PRS2 | STK3X3X_PS_GAIN8);
|
|
drv_data->pocket_prox = STK3X3X_POCKET_NEAR_BY;
|
|
} else if (nf == STK3X3X_PRX_FAR_AWAY) {
|
|
reg_value = (STK3X3X_PS_PRS4 | STK3X3X_PS_GAIN8);
|
|
drv_data->pocket_prox = STK3X3X_POCKET_FAR_AWAY;
|
|
}
|
|
|
|
reg_value = (reg_value | drv_data->ps_it);
|
|
|
|
ret = STK3X3X_REG_READ_MODIFY_WRITE(drv_data, STK3X3X_PSCTRL_REG, reg_value, 0xFF);
|
|
if (ret < 0) {
|
|
SENSOR_ERR("Adjust persistance failed, ret=%d\n", ret);
|
|
}
|
|
|
|
if (nf == STK3X3X_PRX_FAR_AWAY) {
|
|
if (drv_data->cal_status == STK3X3X_CAL_DISABLED) {
|
|
SENSOR_INFO("call calibration work\n");
|
|
drv_data->cal_status = STK3X3X_CAL_ONGOING;
|
|
// control mutex already applied
|
|
stk3x3x_prox_cal(drv_data);
|
|
}
|
|
if (drv_data->cal_status == STX3X3X_CAL_SKIP)
|
|
drv_data->cal_status = STK3X3X_CAL_DISABLED;
|
|
}
|
|
}
|
|
|
|
static void check_first_far_event(struct stk3x3x_data *drv_data)
|
|
{
|
|
int32_t ret = 0;
|
|
uint8_t reg_value[2];
|
|
|
|
ret = STK3X3X_REG_BLOCK_READ(drv_data, STK3X3X_DATA1_PS_REG, 2, ®_value[0]);
|
|
if (ret < 0) {
|
|
SENSOR_ERR("ADC read failed, ret=%d\n", ret);
|
|
return;
|
|
} else {
|
|
drv_data->adc = (reg_value[0] << 8 | reg_value[1]);
|
|
}
|
|
|
|
SENSOR_INFO("adc=%d\n", drv_data->adc);
|
|
|
|
if (drv_data->adc < drv_data->prox_thd_h) {
|
|
SENSOR_INFO("First far event reported\n");
|
|
if (drv_data->adc < drv_data->prox_thd_l && drv_data->check_far_state != 1) {
|
|
drv_data->close_cnt = 0;
|
|
SENSOR_ERR("stk close_cnt(%u) \n", drv_data->close_cnt);
|
|
}
|
|
drv_data->cal_status = STX3X3X_CAL_SKIP;
|
|
stk_ps_report(drv_data, STK3X3X_PRX_FAR_AWAY);
|
|
}
|
|
}
|
|
|
|
static int32_t stk3x3x_enable_ps(struct stk3x3x_data *drv_data, bool en)
|
|
{
|
|
int32_t ret = 0;
|
|
uint8_t reg_value = 0;
|
|
|
|
if (drv_data->pocket_running == true) {
|
|
SENSOR_INFO("pockek_prox cancel work\n");
|
|
cancel_work_sync(&drv_data->work_pocket);
|
|
drv_data->pocket_running = false;
|
|
}
|
|
|
|
if (drv_data->enable == en) {
|
|
SENSOR_INFO("Prox sensor already on/off, en=%d\n", en);
|
|
goto done;
|
|
}
|
|
|
|
ret = STK3X3X_REG_READ(drv_data, STK3X3X_STATE_REG);
|
|
if (ret < 0) {
|
|
goto done;
|
|
} else {
|
|
reg_value = (uint8_t)ret;
|
|
}
|
|
|
|
reg_value &= (~(STK3X3X_STATE_EN_PS_MASK | STK3X3X_STATE_EN_WAIT_MASK | STK3X3X_STATE_EN_INTELL_PRST_MASK));
|
|
|
|
if (en) {
|
|
SENSOR_INFO("Enable ( close_cnt : %u)\n", drv_data->close_cnt);
|
|
drv_data->pocket_prox = STK3X3X_POCKET_UNKNOWN;
|
|
enable_irq_wake(drv_data->irq);
|
|
enable_irq(drv_data->irq);
|
|
|
|
stk3x3x_set_ps_thd(drv_data, drv_data->prox_thd_h, drv_data->prox_thd_l);
|
|
|
|
reg_value |= (STK3X3X_STATE_EN_WAIT_MASK | STK3X3X_STATE_EN_PS_MASK | STK3X3X_STATE_EN_INTELL_PRST_MASK);
|
|
|
|
ret = STK3X3X_REG_READ_MODIFY_WRITE(drv_data, STK3X3X_STATE_REG, reg_value, 0xFF);
|
|
if (ret < 0)
|
|
goto done;
|
|
|
|
ret = STK3X3X_REG_WRITE(drv_data, STK3X3X_WAIT_REG, STK3X3X_WAIT50);
|
|
if(ret < 0)
|
|
SENSOR_ERR("WAIT_REG failed %d\n", ret);
|
|
|
|
usleep_range(60000, 60000);
|
|
drv_data->check_far_state = 0;
|
|
check_first_far_event(drv_data);
|
|
} else {
|
|
SENSOR_INFO("Disable (close_cnt : %u)\n", drv_data->close_cnt);
|
|
if (drv_data->cal_status == STK3X3X_CAL_ONGOING) {
|
|
drv_data->cal_status = STK3X3X_CAL_DISABLED;
|
|
cancel_work_sync(&drv_data->work_cal_prox);
|
|
}
|
|
disable_irq(drv_data->irq);
|
|
disable_irq_wake(drv_data->irq);
|
|
|
|
reg_value &= (~(STK3X3X_STATE_EN_PS_MASK | STK3X3X_STATE_EN_WAIT_MASK | STK3X3X_STATE_EN_INTELL_PRST_MASK));
|
|
|
|
ret = STK3X3X_REG_READ_MODIFY_WRITE(drv_data, STK3X3X_STATE_REG, reg_value, 0xFF);
|
|
if (ret < 0)
|
|
goto done;
|
|
|
|
drv_data->adc = 0;
|
|
}
|
|
|
|
drv_data->enable = en;
|
|
|
|
done:
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int32_t stk3x3x_get_data_and_report(struct stk3x3x_data *drv_data, uint8_t flag)
|
|
{
|
|
int32_t ret = 0;
|
|
uint8_t reg_value[2];
|
|
|
|
int nf_state = flag & STK3X3X_FLG_NF_MASK;
|
|
|
|
if ((flag >> 6) & 0x1) {
|
|
ret = STK3X3X_REG_BLOCK_READ(drv_data, STK3X3X_DATA1_PS_REG, 2, ®_value[0]);
|
|
if (ret < 0) {
|
|
SENSOR_ERR("ADC read failed, ret=%d\n", ret);
|
|
return ret;
|
|
} else {
|
|
drv_data->adc = (reg_value[0] << 8 | reg_value[1]);
|
|
}
|
|
}
|
|
|
|
SENSOR_INFO("nf_state=%d, adc=%d\n", nf_state, drv_data->adc);
|
|
ret = stk3x3x_bgir_check(drv_data);
|
|
|
|
if (ret < 0) {
|
|
SENSOR_ERR("stk3x3x_bgir_check read failed, ret=%d\n", ret);
|
|
return ret;
|
|
} else {
|
|
if (ret == 0x7FFF0001) { //BGIR failed
|
|
SENSOR_ERR("stk3x3x_bgir_check failed, ret=%d\n", ret);
|
|
if (drv_data->close_cnt > 0) {
|
|
drv_data->close_cnt--;
|
|
SENSOR_ERR("stk close_cnt(%u) \n", drv_data->close_cnt);
|
|
}
|
|
drv_data->cal_status = STX3X3X_CAL_SKIP;
|
|
stk_ps_report(drv_data, STK3X3X_PRX_FAR_AWAY);
|
|
} else {
|
|
if (nf_state == STK3X3X_PRX_NEAR_BY) {
|
|
drv_data->close_cnt++;
|
|
if (drv_data->close_cnt >= CAL_RESET_CLOSE_CNT) {
|
|
SENSOR_ERR("stk close_cnt(%u) threshold reset as default cur (h:%u, l:%u) default (h:%u, l:%u)\n",\
|
|
drv_data->close_cnt, drv_data->prox_thd_h, drv_data->prox_thd_l,\
|
|
drv_data->prox_default_thd_h, drv_data->prox_default_thd_l);
|
|
// set as default
|
|
drv_data->close_cnt = 0;
|
|
drv_data->prox_thd_h = drv_data->prox_default_thd_h;
|
|
drv_data->prox_thd_l = drv_data->prox_default_thd_l;
|
|
stk3x3x_set_ps_thd(drv_data, drv_data->prox_thd_h, drv_data->prox_thd_l);
|
|
}
|
|
} else if (nf_state == STK3X3X_PRX_FAR_AWAY) {
|
|
drv_data->close_cnt = 0;
|
|
SENSOR_ERR("stk close_cnt(%u) \n", drv_data->close_cnt);
|
|
}
|
|
stk_ps_report(drv_data, nf_state);
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
int32_t stk3x3x_get_data(struct stk3x3x_data *drv_data, uint16_t *raw_data)
|
|
{
|
|
int32_t ret = 0;
|
|
uint8_t flag_value;
|
|
uint8_t reg_value[2];
|
|
|
|
ret = STK3X3X_REG_READ(drv_data, STK3X3X_FLAG_REG);
|
|
if (ret < 0) {
|
|
SENSOR_ERR("STK3X3X_FLAG_REG read failed, ret=%d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
flag_value = (uint8_t)ret;
|
|
|
|
if ((flag_value >> 6) & 0x1) {
|
|
ret = STK3X3X_REG_BLOCK_READ(drv_data, STK3X3X_DATA1_PS_REG, 2, ®_value[0]);
|
|
if (ret < 0) {
|
|
SENSOR_ERR("ADC read failed, ret=%d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
*raw_data = (reg_value[0] << 8 | reg_value[1]);
|
|
drv_data->adc = *raw_data;
|
|
} else {
|
|
*raw_data = drv_data->adc;
|
|
}
|
|
|
|
SENSOR_INFO("adc=%d\n", drv_data->adc);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static ssize_t stk_ps_code_show(struct device *dev, struct device_attribute *attr, char *buf)
|
|
{
|
|
struct stk3x3x_data *drv_data = dev_get_drvdata(dev);
|
|
uint16_t reading;
|
|
|
|
stk3x3x_get_data(drv_data, &reading);
|
|
return scnprintf(buf, PAGE_SIZE, "%d\n", (uint32_t)reading);
|
|
}
|
|
|
|
static ssize_t stk_ps_enable_show(struct device *dev, struct device_attribute *attr, char *buf)
|
|
{
|
|
int32_t ret;
|
|
struct stk3x3x_data *drv_data = dev_get_drvdata(dev);
|
|
ret = STK3X3X_REG_READ(drv_data, STK3X3X_STATE_REG);
|
|
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
ret = (ret & STK3X3X_STATE_EN_PS_MASK) ? 1 : 0;
|
|
return scnprintf(buf, PAGE_SIZE, "%d\n", ret);
|
|
}
|
|
|
|
static ssize_t stk_ps_enable_store(struct device *dev,
|
|
struct device_attribute *attr,
|
|
const char *buf,
|
|
size_t size)
|
|
{
|
|
struct stk3x3x_data *drv_data = dev_get_drvdata(dev);
|
|
unsigned int en;
|
|
int ret;
|
|
|
|
ret = kstrtouint(buf, 10, &en);
|
|
if (ret) {
|
|
SENSOR_ERR("kstrtouint failed, ret=%d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
if ((1 == en) || (0 == en)) {
|
|
SENSOR_INFO("en=%d\n", en);
|
|
mutex_lock(&drv_data->control_mutex);
|
|
stk3x3x_enable_ps(drv_data, en);
|
|
mutex_unlock(&drv_data->control_mutex);
|
|
}
|
|
|
|
return size;
|
|
}
|
|
|
|
static ssize_t stk_ps_distance_show(struct device *dev, struct device_attribute *attr, char *buf)
|
|
{
|
|
struct stk3x3x_data *drv_data = dev_get_drvdata(dev);
|
|
int32_t nf = 1;
|
|
int32_t ret;
|
|
|
|
ret = STK3X3X_REG_READ(drv_data, STK3X3X_FLAG_REG);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
nf = (ret & STK3X3X_FLG_NF_MASK) ? 1 : 0;
|
|
stk_ps_report(drv_data, nf);
|
|
|
|
SENSOR_INFO("ps input event=%d\n", nf);
|
|
|
|
return scnprintf(buf, PAGE_SIZE, "%d\n", nf);
|
|
}
|
|
|
|
static ssize_t stk_ps_distance_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size)
|
|
{
|
|
struct stk3x3x_data *drv_data = dev_get_drvdata(dev);
|
|
unsigned long value = 0;
|
|
int ret;
|
|
ret = kstrtoul(buf, 10, &value);
|
|
|
|
if (ret < 0) {
|
|
SENSOR_ERR("kstrtoul failed, ret=0x%x\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
stk_ps_report(drv_data, value);
|
|
SENSOR_INFO("ps input event=%d\n", (int)value);
|
|
|
|
return size;
|
|
}
|
|
|
|
static void proximity_get_avg_val(struct stk3x3x_data *ps_data)
|
|
{
|
|
int min = 0, max = 0, avg = 0;
|
|
int i;
|
|
uint32_t read_value;
|
|
|
|
for (i = 0; i < PROX_READ_NUM; i++) {
|
|
usleep_range(PS_WAIT, PS_WAIT);
|
|
|
|
read_value = stk3x3x_get_ps_reading(ps_data);
|
|
if( read_value > PROX_MIN_DATA) {
|
|
avg += read_value;
|
|
if (!i)
|
|
min = read_value;
|
|
else if (read_value < min)
|
|
min = read_value;
|
|
if (read_value > max)
|
|
max = read_value;
|
|
}else {
|
|
read_value =PROX_MIN_DATA;
|
|
}
|
|
}
|
|
avg /= PROX_READ_NUM;
|
|
|
|
ps_data->avg[0] = min;
|
|
ps_data->avg[1] = avg;
|
|
ps_data->avg[2] = max;
|
|
}
|
|
|
|
static void stk3x3x_work_func_prox(struct work_struct *work)
|
|
{
|
|
struct stk3x3x_data *ps_data = container_of(work,
|
|
struct stk3x3x_data, work_prox);
|
|
|
|
proximity_get_avg_val(ps_data);
|
|
}
|
|
|
|
static enum hrtimer_restart stk3x3x_prox_timer_func(struct hrtimer *timer)
|
|
{
|
|
struct stk3x3x_data *data = container_of(timer,
|
|
struct stk3x3x_data, prox_timer);
|
|
|
|
queue_work(data->prox_wq, &data->work_prox);
|
|
hrtimer_forward_now(&data->prox_timer, data->prox_poll_delay);
|
|
return HRTIMER_RESTART;
|
|
}
|
|
|
|
static ssize_t proximity_avg_show(struct device *dev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
struct stk3x3x_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 ssize_t proximity_check_far_state_show(struct device *dev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
struct stk3x3x_data *ps_data = dev_get_drvdata(dev);
|
|
return scnprintf(buf, PAGE_SIZE, "%d\n", ps_data->check_far_state);
|
|
}
|
|
|
|
static ssize_t proximity_check_far_state_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size)
|
|
{
|
|
int ret;
|
|
struct stk3x3x_data *ps_data = dev_get_drvdata(dev);
|
|
|
|
if (sscanf(buf, "%d", &ret) != 1) {
|
|
SENSOR_ERR("invalid value\n");
|
|
return size;
|
|
}
|
|
|
|
ps_data->check_far_state = ret;
|
|
|
|
if(ret == 1)
|
|
check_first_far_event(ps_data);
|
|
|
|
SENSOR_INFO("check_far_state = %d\n", ret);
|
|
|
|
return size;
|
|
}
|
|
|
|
static ssize_t pocket_prox_show(struct device *dev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
struct stk3x3x_data *ps_data = dev_get_drvdata(dev);
|
|
|
|
SENSOR_INFO("stk pocket_prox = %u\n", ps_data->pocket_prox);
|
|
|
|
return scnprintf(buf, PAGE_SIZE, "%d\n", ps_data->pocket_prox);
|
|
}
|
|
|
|
static ssize_t pocket_prox_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size)
|
|
{
|
|
int en;
|
|
struct stk3x3x_data *ps_data = dev_get_drvdata(dev);
|
|
|
|
if (sscanf(buf, "%d", &en) != 1) {
|
|
SENSOR_ERR("invalid value\n");
|
|
return size;
|
|
}
|
|
|
|
ps_data->pocket_prox = STK3X3X_POCKET_UNKNOWN;
|
|
if (en == 1)
|
|
ps_data->pocket_enable = true;
|
|
else {
|
|
if (ps_data->pocket_running) {
|
|
cancel_work_sync(&ps_data->work_pocket);
|
|
ps_data->pocket_running = false;
|
|
}
|
|
ps_data->pocket_enable = false;
|
|
}
|
|
|
|
SENSOR_INFO("stk pocket_enable = %u\n", ps_data->pocket_enable);
|
|
|
|
return size;
|
|
}
|
|
|
|
|
|
static ssize_t proximity_avg_store(struct device *dev,
|
|
struct device_attribute *attr, const char *buf, size_t size)
|
|
{
|
|
struct stk3x3x_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\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
SENSOR_INFO("average enable = %d\n", new_value);
|
|
if (new_value) {
|
|
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);
|
|
}
|
|
|
|
return size;
|
|
}
|
|
|
|
static ssize_t stk_ps_code_cal_show(struct device *dev, struct device_attribute *attr, char *buf)
|
|
{
|
|
uint8_t ps_thd[2] = {0};
|
|
int16_t ps_thd_l = 0;
|
|
int16_t ps_thd_h = 0;
|
|
int ret;
|
|
struct stk3x3x_data *drv_data = dev_get_drvdata(dev);
|
|
|
|
ret = STK3X3X_REG_BLOCK_READ(drv_data, STK3X3X_THDL1_PS_REG, 2, ps_thd);
|
|
|
|
if (ret < 0) {
|
|
SENSOR_ERR("fail, ret=0x%x", ret);
|
|
return -EINVAL;
|
|
}
|
|
|
|
ps_thd_l = (int16_t)((ps_thd[0] << 8) | ps_thd[1]);
|
|
|
|
ret = STK3X3X_REG_BLOCK_READ(drv_data, STK3X3X_THDH1_PS_REG, 2, ps_thd);
|
|
if (ret < 0) {
|
|
SENSOR_ERR("failed, ret=0x%x", ret);
|
|
return -EINVAL;
|
|
}
|
|
|
|
ps_thd_h = (int16_t)((ps_thd[0] << 8) | ps_thd[1]);
|
|
|
|
return scnprintf(buf, PAGE_SIZE, "%d,%d,%d\n", drv_data->offset ,ps_thd_l, ps_thd_h);
|
|
}
|
|
|
|
static ssize_t stk_ps_code_cal_store(struct device *dev,
|
|
struct device_attribute *attr, const char *buf, size_t size)
|
|
{
|
|
struct stk3x3x_data *drv_data = dev_get_drvdata(dev);
|
|
|
|
if (sysfs_streq(buf, "1")){ /* calibrate cancelation value */
|
|
SENSOR_INFO("call calibration work\n");
|
|
drv_data->cal_status = STK3X3X_FIRST_CAL;
|
|
drv_data->factory_cal = true;
|
|
mutex_lock(&drv_data->control_mutex);
|
|
stk3x3x_prox_cal(drv_data);
|
|
mutex_unlock(&drv_data->control_mutex);
|
|
} else {
|
|
SENSOR_ERR("invalid value %d\n", *buf);
|
|
return size;
|
|
}
|
|
|
|
return size;
|
|
}
|
|
|
|
static ssize_t stk_fac_cal_show(struct device *dev, struct device_attribute *attr, char *buf)
|
|
{
|
|
return snprintf(buf, PAGE_SIZE, "1\n");
|
|
}
|
|
|
|
static ssize_t stk_ps_code_thd_l_show(struct device *dev, struct device_attribute *attr, char *buf)
|
|
{
|
|
uint8_t ps_thd[2] = {0};
|
|
int16_t ps_thd_l = 0;
|
|
int ret;
|
|
struct stk3x3x_data *drv_data = dev_get_drvdata(dev);
|
|
ret = STK3X3X_REG_BLOCK_READ(drv_data, STK3X3X_THDL1_PS_REG, 2, ps_thd);
|
|
|
|
if (ret < 0) {
|
|
SENSOR_ERR("fail, ret=0x%x", ret);
|
|
return -EINVAL;
|
|
}
|
|
|
|
ps_thd_l = (int16_t)((ps_thd[0] << 8) | ps_thd[1]);
|
|
return scnprintf(buf, PAGE_SIZE, "%d\n", ps_thd_l);
|
|
}
|
|
|
|
|
|
static ssize_t stk_ps_code_thd_l_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size)
|
|
{
|
|
struct stk3x3x_data *drv_data = dev_get_drvdata(dev);
|
|
unsigned long value = 0;
|
|
uint8_t ps_thd[2] = {0};
|
|
int ret;
|
|
ret = kstrtoul(buf, 10, &value);
|
|
|
|
if (ret < 0) {
|
|
SENSOR_ERR("kstrtoul failed, ret=0x%x\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
ret = STK3X3X_REG_BLOCK_READ(drv_data, STK3X3X_THDH1_PS_REG, 2, ps_thd);
|
|
if (ret < 0) {
|
|
SENSOR_ERR("Read PS THD failed, ret=0x%x\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
stk3x3x_set_ps_thd(drv_data, (uint16_t)((ps_thd[0] << 8) | ps_thd[1]), value);
|
|
return size;
|
|
}
|
|
|
|
static ssize_t stk_ps_code_thd_h_show(struct device *dev, struct device_attribute *attr, char *buf)
|
|
{
|
|
uint8_t ps_thd[2] = {0};
|
|
int16_t ps_thd_h = 0;
|
|
int ret;
|
|
struct stk3x3x_data *drv_data = dev_get_drvdata(dev);
|
|
|
|
ret = STK3X3X_REG_BLOCK_READ(drv_data, STK3X3X_THDH1_PS_REG, 2, ps_thd);
|
|
if (ret < 0) {
|
|
SENSOR_ERR("failed, ret=0x%x", ret);
|
|
return -EINVAL;
|
|
}
|
|
|
|
ps_thd_h = (int16_t)((ps_thd[0] << 8) | ps_thd[1]);
|
|
|
|
return scnprintf(buf, PAGE_SIZE, "%d\n", ps_thd_h);
|
|
}
|
|
|
|
static ssize_t stk_ps_code_thd_h_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size)
|
|
{
|
|
struct stk3x3x_data *drv_data = dev_get_drvdata(dev);
|
|
unsigned long value = 0;
|
|
unsigned char ps_thd[2] = {0};
|
|
int ret;
|
|
ret = kstrtoul(buf, 10, &value);
|
|
|
|
if (ret < 0) {
|
|
SENSOR_ERR("kstrtoul failed, ret=0x%x\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
ret = STK3X3X_REG_BLOCK_READ(drv_data, STK3X3X_THDL1_PS_REG, 2, ps_thd);
|
|
|
|
if (ret < 0) {
|
|
SENSOR_ERR("Read PS THD failed, ret=0x%x\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
stk3x3x_set_ps_thd(drv_data, value, ((ps_thd[0] << 8) | ps_thd[1]));
|
|
|
|
return size;
|
|
}
|
|
|
|
static ssize_t stk_ps_code_register_show(struct device *dev, struct device_attribute *attr, char *buf)
|
|
{
|
|
int16_t reg_data;
|
|
uint8_t i = 0;
|
|
struct stk3x3x_data *drv_data = dev_get_drvdata(dev);
|
|
uint16_t reg_total = sizeof(stk3x3x_reg_map) / sizeof(stk3x3x_reg_map[0]);
|
|
int offset = 0;
|
|
|
|
for (i = 0; i < reg_total; i++) {
|
|
reg_data = STK3X3X_REG_READ(drv_data, stk3x3x_reg_map[i]);
|
|
|
|
if (reg_data < 0) {
|
|
SENSOR_ERR("failed, ret=%d", reg_data);
|
|
return reg_data;
|
|
}
|
|
else {
|
|
SENSOR_INFO("Reg[0x%2X] = 0x%2X\n", stk3x3x_reg_map[i], (uint8_t)reg_data);
|
|
offset += scnprintf(buf + offset, PAGE_SIZE - offset, "Reg[0x%2X] = 0x%2X\n", stk3x3x_reg_map[i], (uint8_t)reg_data);
|
|
}
|
|
}
|
|
|
|
return offset;
|
|
}
|
|
|
|
static ssize_t stk_ps_code_register_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size)
|
|
{
|
|
unsigned int reg, val;
|
|
int ret;
|
|
struct stk3x3x_data *drv_data = dev_get_drvdata(dev);
|
|
|
|
if (sscanf(buf, "%2x,%2x", ®, &val) != 2) {
|
|
SENSOR_ERR("invalid value\n");
|
|
return size;
|
|
}
|
|
|
|
ret = STK3X3X_REG_WRITE(drv_data, reg, val);
|
|
if(ret < 0)
|
|
SENSOR_ERR("failed %d\n", ret);
|
|
else
|
|
SENSOR_INFO("Register(0x%2x) data(0x%2x)\n", reg, val);
|
|
|
|
return size;
|
|
}
|
|
|
|
static ssize_t stk_ps_code_trim_show(struct device *dev, struct device_attribute *attr, char *buf)
|
|
{
|
|
struct stk3x3x_data *drv_data = dev_get_drvdata(dev);
|
|
return scnprintf(buf, PAGE_SIZE, "%d\n", drv_data->offset);
|
|
}
|
|
|
|
int32_t stk3x3x_clr_flag(struct stk3x3x_data *drv_data, uint8_t org_flag_reg, uint8_t clr)
|
|
{
|
|
uint8_t w_flag;
|
|
int32_t ret;
|
|
|
|
w_flag = org_flag_reg | STK3X3X_FLG_PSINT_MASK;
|
|
w_flag &= (~clr);
|
|
|
|
ret = STK3X3X_REG_WRITE(drv_data, STK3X3X_FLAG_REG, w_flag);
|
|
if (ret < 0)
|
|
SENSOR_ERR("STK3X3X_FLAG_REG write failed, ret=%d\n", ret);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void stk3x3x_irq_work_func(struct work_struct *work)
|
|
{
|
|
struct stk3x3x_data *drv_data = container_of(work, struct stk3x3x_data, prox_irq_work);
|
|
|
|
int32_t ret;
|
|
uint8_t prox_flag;
|
|
|
|
mutex_lock(&drv_data->control_mutex);
|
|
|
|
// Normally, this condition will never occur
|
|
if (drv_data->cal_status == STK3X3X_CAL_ONGOING) {
|
|
pr_info("%s, calibration is on going skip!!!!\n", __func__);
|
|
goto err_i2c_rw;
|
|
}
|
|
|
|
ret = STK3X3X_REG_READ(drv_data, STK3X3X_FLAG_REG);
|
|
if (ret < 0) {
|
|
SENSOR_ERR("STK3X3X_FLAG_REG read failed, ret=%d\n", ret);
|
|
goto err_i2c_rw;
|
|
}
|
|
|
|
prox_flag = (uint8_t)ret;
|
|
SENSOR_INFO("prox_flag=%d\n", prox_flag);
|
|
|
|
ret = stk3x3x_clr_flag(drv_data, prox_flag, (prox_flag & STK3X3X_FLG_PSINT_MASK));
|
|
if (ret < 0) {
|
|
SENSOR_ERR("stk3x3x_clr_flag failed, ret=%d\n", ret);
|
|
goto err_i2c_rw;
|
|
}
|
|
|
|
if (prox_flag & STK3X3X_FLG_PSINT_MASK) {
|
|
stk3x3x_get_data_and_report(drv_data, prox_flag);
|
|
}
|
|
|
|
usleep_range(1000, 2000);
|
|
mutex_unlock(&drv_data->control_mutex);
|
|
enable_irq(drv_data->irq);
|
|
|
|
return;
|
|
|
|
err_i2c_rw:
|
|
usleep_range(30000, 30000);
|
|
mutex_unlock(&drv_data->control_mutex);
|
|
enable_irq(drv_data->irq);
|
|
return;
|
|
}
|
|
|
|
static ssize_t stk_vendor_show(struct device *dev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
return snprintf(buf, PAGE_SIZE, "%s\n", VENDOR_NAME);
|
|
}
|
|
|
|
static ssize_t stk_name_show(struct device *dev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
return snprintf(buf, PAGE_SIZE, "%s\n", CHIP_NAME);
|
|
}
|
|
|
|
static DEVICE_ATTR(psenable, 0664, stk_ps_enable_show, stk_ps_enable_store);
|
|
static DEVICE_ATTR(psdistance, 0664, stk_ps_distance_show, stk_ps_distance_store);
|
|
static DEVICE_ATTR(raw_data, 0444, stk_ps_code_show, NULL);
|
|
static DEVICE_ATTR(thresh_low, 0664, stk_ps_code_thd_l_show, stk_ps_code_thd_l_store);
|
|
static DEVICE_ATTR(thresh_high, 0664, stk_ps_code_thd_h_show, stk_ps_code_thd_h_store);
|
|
static DEVICE_ATTR(prox_register, 0664, stk_ps_code_register_show, stk_ps_code_register_store);
|
|
static DEVICE_ATTR(prox_trim, 0444, stk_ps_code_trim_show, NULL);
|
|
static DEVICE_ATTR(prox_cal, 0664, stk_ps_code_cal_show, stk_ps_code_cal_store);
|
|
static DEVICE_ATTR(prox_offset_pass, 0444, stk_fac_cal_show, NULL);
|
|
static DEVICE_ATTR(vendor, 0444, stk_vendor_show, NULL);
|
|
static DEVICE_ATTR(name, 0444, stk_name_show, NULL);
|
|
static DEVICE_ATTR(prox_avg, 0664, proximity_avg_show, proximity_avg_store);
|
|
static DEVICE_ATTR(check_far_state, 0664, proximity_check_far_state_show, proximity_check_far_state_store);
|
|
static DEVICE_ATTR(pocket_prox, 0664, pocket_prox_show, pocket_prox_store);
|
|
|
|
static struct device_attribute *prox_sysfs_attrs[] = {
|
|
&dev_attr_psenable,
|
|
&dev_attr_psdistance,
|
|
&dev_attr_raw_data,
|
|
&dev_attr_thresh_low,
|
|
&dev_attr_thresh_high,
|
|
&dev_attr_prox_register,
|
|
&dev_attr_prox_trim,
|
|
&dev_attr_prox_cal,
|
|
&dev_attr_prox_offset_pass,
|
|
&dev_attr_vendor,
|
|
&dev_attr_name,
|
|
&dev_attr_prox_avg,
|
|
&dev_attr_check_far_state,
|
|
&dev_attr_pocket_prox,
|
|
NULL
|
|
};
|
|
|
|
static DEVICE_ATTR(enable, S_IRUGO | S_IWUSR | S_IWGRP,
|
|
stk_ps_enable_show, stk_ps_enable_store);
|
|
|
|
static struct attribute *prox_input_attrs[] = {
|
|
&dev_attr_enable.attr,
|
|
NULL
|
|
};
|
|
|
|
static struct attribute_group prox_input_attribute_group = {
|
|
.attrs = prox_input_attrs,
|
|
};
|
|
|
|
static int stk3x3x_setup_irq(struct stk3x3x_data *drv_data)
|
|
{
|
|
int irq, ret;
|
|
|
|
drv_data->prox_irq_wq = create_singlethread_workqueue("prox_irq_wq");
|
|
INIT_WORK(&drv_data->prox_irq_work, stk3x3x_irq_work_func);
|
|
|
|
ret = gpio_request(drv_data->irq_gpio, "prox_int");
|
|
if (ret < 0) {
|
|
SENSOR_ERR("gpio_request failed, ret=%d\n", ret);
|
|
goto err_gpio_request;
|
|
}
|
|
|
|
ret = gpio_direction_input(drv_data->irq_gpio);
|
|
if (ret < 0) {
|
|
SENSOR_ERR("gpio_direction_input failed, ret=%d\n", ret);
|
|
goto err_gpio_direction_input;
|
|
}
|
|
|
|
irq = gpio_to_irq(drv_data->irq_gpio);
|
|
if (irq < 0) {
|
|
SENSOR_INFO("gpio_to_irq failed, irq=%d, gpio=%d\n", irq, drv_data->irq_gpio);
|
|
goto err_gpio_to_irq;
|
|
}
|
|
|
|
SENSOR_INFO("irq=%d, gpio=%d\n", irq, drv_data->irq_gpio);
|
|
|
|
drv_data->irq = irq;
|
|
|
|
ret = request_any_context_irq(irq, stk3x3x_irq_handler,
|
|
IRQF_TRIGGER_FALLING, "prox_irq", drv_data);
|
|
if (ret < 0) {
|
|
SENSOR_INFO("request_any_context_irq failed, ret=%d\n", ret);
|
|
goto err_request_any_context_irq;
|
|
}
|
|
|
|
disable_irq(irq);
|
|
|
|
SENSOR_INFO("Success\n");
|
|
|
|
return 0;
|
|
|
|
err_request_any_context_irq:
|
|
err_gpio_to_irq:
|
|
err_gpio_direction_input:
|
|
gpio_free(drv_data->irq_gpio);
|
|
err_gpio_request:
|
|
destroy_workqueue(drv_data->prox_irq_wq);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int32_t stk3x3x_init_registers(struct stk3x3x_data *drv_data)
|
|
{
|
|
int32_t ret;
|
|
u16 reg_count = 0;
|
|
u16 reg_num = sizeof(stk3x3x_default_register_table) / sizeof(stk3x3x_register_table);
|
|
|
|
ret = stk3x3x_software_reset(drv_data);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
for(reg_count = 0; reg_count < reg_num; reg_count++) {
|
|
if (stk3x3x_default_register_table[reg_count].address == STK3X3X_PSCTRL_REG) {
|
|
if (drv_data->ps_it == 0xff) {
|
|
drv_data->ps_it = STK3X3X_PS_IT400;
|
|
} else {
|
|
stk3x3x_default_register_table[reg_count].value = (STK3X3X_PS_PRS4 | STK3X3X_PS_GAIN8 | drv_data->ps_it);
|
|
}
|
|
SENSOR_INFO("PS_IT = %d\n", drv_data->ps_it);
|
|
}
|
|
ret = STK3X3X_REG_READ_MODIFY_WRITE(drv_data,
|
|
stk3x3x_default_register_table[reg_count].address,
|
|
stk3x3x_default_register_table[reg_count].value,
|
|
stk3x3x_default_register_table[reg_count].mask_bit);
|
|
if (ret < 0)
|
|
return ret;
|
|
}
|
|
|
|
ret = stk3x3x_prst_cnt_rst_sel(drv_data);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
SENSOR_INFO("success\n");
|
|
|
|
return 0;
|
|
}
|
|
|
|
int stk3x3x_suspend(struct device *dev)
|
|
{
|
|
struct stk3x3x_data *drv_data = dev_get_drvdata(dev);
|
|
SENSOR_INFO("\n");
|
|
|
|
if (drv_data->pocket_running == true) {
|
|
cancel_work_sync(&drv_data->work_pocket);
|
|
drv_data->pocket_running = false;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int stk3x3x_resume(struct device *dev)
|
|
{
|
|
struct stk3x3x_data *drv_data = dev_get_drvdata(dev);
|
|
SENSOR_INFO("\n");
|
|
if (drv_data->enable == false && drv_data->pocket_enable == true) {
|
|
drv_data->pocket_prox = STK3X3X_POCKET_UNKNOWN;
|
|
drv_data->pocket_running = true;
|
|
queue_work(drv_data->prox_pocket_wq, &drv_data->work_pocket);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int stk3x3x_parse_dt(struct device *dev, struct stk3x3x_data *drv_data)
|
|
{
|
|
struct device_node *np = dev->of_node;
|
|
u32 temp_val;
|
|
int ret;
|
|
|
|
ret = of_get_named_gpio_flags(np, "stk,gpio_int", 0, NULL);
|
|
if (ret < 0) {
|
|
SENSOR_ERR("stk,gpio_int read failed, ret=%d\n", ret);
|
|
return ret;
|
|
} else {
|
|
drv_data->irq_gpio = ret;
|
|
SENSOR_INFO("irq_gpio=%d\n", drv_data->irq_gpio);
|
|
}
|
|
|
|
ret = of_property_read_u32(np, "stk,prox_thd_h", &temp_val);
|
|
if (ret < 0) {
|
|
SENSOR_ERR("stk,prox_thd_h read failed, ret=%d\n", ret);
|
|
return ret;
|
|
} else {
|
|
drv_data->prox_default_thd_h = drv_data->prox_thd_h = (u16) temp_val;
|
|
SENSOR_INFO("prox_thd_h=%d\n", drv_data->prox_thd_h);
|
|
}
|
|
|
|
ret = of_property_read_u32(np, "stk,prox_thd_l", &temp_val);
|
|
if (ret < 0) {
|
|
SENSOR_ERR("stk,prox_thd_l read failed, ret=%d\n", ret);
|
|
return ret;
|
|
} else {
|
|
drv_data->prox_default_thd_l = drv_data->prox_thd_l = (u16) temp_val;
|
|
SENSOR_INFO("prox_thd_l=%d\n", drv_data->prox_thd_l);
|
|
}
|
|
|
|
ret = of_property_read_u32(np, "stk,thd_h_offset", &temp_val);
|
|
if (ret < 0) {
|
|
drv_data->thd_h_offset = 40;
|
|
SENSOR_ERR("stk,thd_h_offset read failed, ret=%d\n", ret);
|
|
} else {
|
|
drv_data->thd_h_offset = (u16) temp_val;
|
|
SENSOR_INFO("thd_h_offset=%d\n", drv_data->thd_h_offset);
|
|
}
|
|
|
|
ret = of_property_read_u32(np, "stk,sunlight_thd_h", &temp_val);
|
|
if (ret < 0) {
|
|
drv_data->sunlight_thd_h = PDATA_MAX;
|
|
SENSOR_INFO("stk,sunlight_thd_h read failed, ret=%d\n", ret);
|
|
} else {
|
|
drv_data->sunlight_thd_h = (u16) temp_val;
|
|
SENSOR_INFO("sunlight_thd_h=%d\n", drv_data->sunlight_thd_h);
|
|
}
|
|
|
|
ret = of_property_read_u32(np, "stk,sunlight_thd_l", &temp_val);
|
|
if (ret < 0) {
|
|
drv_data->sunlight_thd_l = PDATA_MAX;
|
|
SENSOR_INFO("stk,sunlight_thd_l read failed, ret=%d\n", ret);
|
|
} else {
|
|
drv_data->sunlight_thd_l = (u16) temp_val;
|
|
SENSOR_INFO("sunlight_thd_l=%d\n", drv_data->sunlight_thd_l);
|
|
}
|
|
|
|
ret = of_property_read_u32(np, "stk,first_cal_adc_limit", &temp_val);
|
|
if (ret < 0) {
|
|
drv_data->first_cal_adc_limit = FIRST_CAL_ADC_LIMIT;
|
|
SENSOR_INFO("stk,first_cal_adc_limit read failed, ret=%d\n", ret);
|
|
} else {
|
|
drv_data->first_cal_adc_limit = (u16) temp_val;
|
|
SENSOR_INFO("first_cal_adc_limit=%d\n", drv_data->first_cal_adc_limit);
|
|
}
|
|
|
|
ret = of_property_read_u32(np, "stk,first_cal_thd_h", &temp_val);
|
|
if (ret < 0) {
|
|
drv_data->first_cal_thd_h = PDATA_MAX;
|
|
SENSOR_INFO("stk,first_cal_thd_h read failed, ret=%d\n", ret);
|
|
} else {
|
|
drv_data->first_cal_thd_h = (u16) temp_val;
|
|
SENSOR_INFO("first_cal_thd_h=%d\n", drv_data->first_cal_thd_h);
|
|
}
|
|
|
|
ret = of_property_read_u32(np, "stk,first_cal_thd_l", &temp_val);
|
|
if (ret < 0) {
|
|
drv_data->first_cal_thd_l = PDATA_MAX;
|
|
SENSOR_INFO("stk,first_cal_thd_l read failed, ret=%d\n", ret);
|
|
} else {
|
|
drv_data->first_cal_thd_l = (u16) temp_val;
|
|
SENSOR_INFO("first_cal_thd_l=%d\n", drv_data->first_cal_thd_l);
|
|
}
|
|
|
|
if (drv_data->prox_thd_h == 0 && drv_data->prox_thd_l == 0)
|
|
drv_data->first_limit_skip = true;
|
|
|
|
ret = of_property_read_u32(np, "stk,intel_prst", &temp_val);
|
|
if (ret < 0) {
|
|
drv_data->intel_prst = 0x01;
|
|
SENSOR_ERR("stk,intel_prst read failed, ret=%d prst=%d", ret , drv_data->intel_prst);
|
|
} else {
|
|
drv_data->intel_prst = (u8) temp_val;
|
|
SENSOR_INFO("intel_prst=%d\n", drv_data->intel_prst);
|
|
}
|
|
|
|
ret = of_property_read_u32(np, "stk,ps_it", &temp_val);
|
|
if (ret < 0) {
|
|
drv_data->ps_it = 0xff;
|
|
SENSOR_ERR("stk,ps_it read failed, ret=%d ps_it=%d\n", ret, drv_data->ps_it);
|
|
} else {
|
|
drv_data->ps_it = (u8) temp_val;
|
|
SENSOR_INFO("stk,ps_it=%d\n", drv_data->ps_it);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int stk3x3x_init_input_device(struct stk3x3x_data *drv_data)
|
|
{
|
|
int ret = 0;
|
|
struct input_dev *dev;
|
|
|
|
dev = input_allocate_device();
|
|
if (!dev)
|
|
return -ENOMEM;
|
|
|
|
dev->name = MODULE_NAME;
|
|
dev->id.bustype = BUS_I2C;
|
|
|
|
input_set_capability(dev, EV_REL, REL_MISC);
|
|
input_set_drvdata(dev, drv_data);
|
|
|
|
ret = input_register_device(dev);
|
|
if (ret < 0) {
|
|
input_free_device(dev);
|
|
goto err_register_input_dev;
|
|
}
|
|
|
|
ret = sensors_create_symlink(&dev->dev.kobj, dev->name);
|
|
if (ret < 0)
|
|
goto err_create_sensor_symlink;
|
|
|
|
ret = sysfs_create_group(&dev->dev.kobj, &prox_input_attribute_group);
|
|
if (ret < 0)
|
|
goto err_create_sysfs_group;
|
|
|
|
drv_data->prox_input_dev = dev;
|
|
return 0;
|
|
|
|
err_create_sysfs_group:
|
|
sensors_remove_symlink(&dev->dev.kobj, dev->name);
|
|
err_create_sensor_symlink:
|
|
input_unregister_device(dev);
|
|
err_register_input_dev:
|
|
dev = NULL;
|
|
return ret;
|
|
}
|
|
|
|
static const struct stk3x3x_bus_ops stk3x3x_i2c_bops =
|
|
{
|
|
.bustype = BUS_I2C,
|
|
.write = stk3x3x_reg_write,
|
|
.write_block = stk3x3x_reg_write_block,
|
|
.read = stk3x3x_reg_read,
|
|
.read_block = stk3x3x_reg_read_block,
|
|
.read_modify_write = stk3x3x_reg_read_modify_write,
|
|
};
|
|
|
|
int stk3x3x_probe(struct i2c_client *client, const struct i2c_device_id *id)
|
|
{
|
|
struct stk3x3x_data *drv_data;
|
|
int ret = 0;
|
|
|
|
SENSOR_INFO("probe called\n");
|
|
|
|
ret = i2c_check_functionality(client->adapter, I2C_FUNC_I2C);
|
|
if (!ret) {
|
|
SENSOR_ERR("i2c_check_functionality failed\n");
|
|
return ret;
|
|
}
|
|
|
|
drv_data = kzalloc(sizeof(struct stk3x3x_data), GFP_KERNEL);
|
|
if (!drv_data) {
|
|
SENSOR_ERR("drv_data memory allocation failed\n");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
drv_data->client = client;
|
|
drv_data->dev = &client->dev;
|
|
drv_data->bops = &stk3x3x_i2c_bops;
|
|
|
|
i2c_set_clientdata(client, drv_data);
|
|
|
|
// Check if STK3331 IC exists
|
|
ret = stk3x3x_device_id_check(drv_data);
|
|
if (ret) {
|
|
SENSOR_ERR("STK3331 not found, ret=%d\n", ret);
|
|
goto err_stk3x3x_device_id_check;
|
|
}
|
|
|
|
mutex_init(&drv_data->control_mutex);
|
|
wake_lock_init(&drv_data->prox_wakelock, WAKE_LOCK_SUSPEND, "prox_wakelock");
|
|
device_init_wakeup(&client->dev, true);
|
|
|
|
drv_data->first_limit_skip = false;
|
|
ret = stk3x3x_parse_dt(&client->dev, drv_data);
|
|
if (ret) {
|
|
SENSOR_ERR("stk3x3x_parse_dt failed, ret=%d\n", ret);
|
|
goto err_stk3x3x_parse_dt;
|
|
}
|
|
|
|
ret = stk3x3x_init_registers(drv_data);
|
|
if (ret < 0) {
|
|
SENSOR_ERR("stk3x3x_init_registers failed, ret=%d\n", ret);
|
|
goto err_stk3x3x_init_registers;
|
|
}
|
|
|
|
ret = stk3x3x_init_input_device(drv_data);
|
|
if (ret < 0) {
|
|
SENSOR_ERR("stk3x3x_init_input_device failed, ret=%d\n", ret);
|
|
goto err_stk3x3x_init_input_device;
|
|
}
|
|
|
|
ret = sensors_register(&drv_data->dev, drv_data, prox_sysfs_attrs, MODULE_NAME);
|
|
if (ret < 0) {
|
|
SENSOR_ERR("sensors_register failed, ret=%d\n", ret);
|
|
goto err_sensors_register;
|
|
}
|
|
/* For factory test mode, we use timer to get average proximity data. */
|
|
/* prox_timer settings. we poll for light values using a timer. */
|
|
hrtimer_init(&drv_data->prox_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
|
|
drv_data->prox_poll_delay = ns_to_ktime(2000 * NSEC_PER_MSEC);/*2 sec*/
|
|
drv_data->prox_timer.function = stk3x3x_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). */
|
|
drv_data->prox_wq = create_singlethread_workqueue("stk3x3x_prox_wq");
|
|
if (!drv_data->prox_wq) {
|
|
ret = -ENOMEM;
|
|
SENSOR_ERR("could not create prox workqueue\n");
|
|
goto err_create_prox_workqueue;
|
|
}
|
|
INIT_WORK(&drv_data->work_prox, stk3x3x_work_func_prox);
|
|
|
|
drv_data->prox_cal_wq = create_singlethread_workqueue("stk3x3x_prox_cal_wq");
|
|
if (!drv_data->prox_cal_wq) {
|
|
ret = -ENOMEM;
|
|
SENSOR_ERR("could not create prox cal workqueue\n");
|
|
goto err_create_prox_cal_workqueue;
|
|
}
|
|
|
|
drv_data->prox_pocket_wq = create_singlethread_workqueue("stk3x3x_prox_pocket");
|
|
if (!drv_data->prox_pocket_wq) {
|
|
ret = -ENOMEM;
|
|
SENSOR_ERR("could not create prox_pocket workqueue\n");
|
|
goto err_create_prox_pocket_workqueue;
|
|
}
|
|
|
|
INIT_WORK(&drv_data->work_cal_prox, stk3x3x_work_func_prox_cal);
|
|
INIT_WORK(&drv_data->work_pocket, stk3x3x_work_func_pocket_read);
|
|
|
|
ret = stk3x3x_setup_irq(drv_data);
|
|
if (ret < 0) {
|
|
SENSOR_ERR("stk3x3x_setup_irq failed, ret=%d\n", ret);
|
|
goto err_stk3x3x_setup_irq;
|
|
}
|
|
drv_data->cal_status = STK3X3X_FIRST_CAL;
|
|
drv_data->factory_cal = false;
|
|
drv_data->pocket_running = false;
|
|
drv_data->pocket_enable = false;
|
|
SENSOR_INFO("call calibration work\n");
|
|
queue_work(drv_data->prox_cal_wq, &drv_data->work_cal_prox);
|
|
drv_data->check_far_state = 0;
|
|
drv_data->close_cnt = 0;
|
|
drv_data->pocket_prox = STK3X3X_POCKET_UNKNOWN;
|
|
|
|
SENSOR_INFO("probe done\n");
|
|
|
|
return 0;
|
|
|
|
err_stk3x3x_setup_irq:
|
|
destroy_workqueue(drv_data->prox_pocket_wq);
|
|
err_create_prox_pocket_workqueue:
|
|
destroy_workqueue(drv_data->prox_cal_wq);
|
|
err_create_prox_cal_workqueue:
|
|
destroy_workqueue(drv_data->prox_wq);
|
|
err_create_prox_workqueue:
|
|
sensors_unregister(drv_data->dev, prox_sysfs_attrs);
|
|
err_sensors_register:
|
|
sensors_remove_symlink(&drv_data->prox_input_dev->dev.kobj, drv_data->prox_input_dev->name);
|
|
sysfs_remove_group(&drv_data->prox_input_dev->dev.kobj, &prox_input_attribute_group);
|
|
input_unregister_device(drv_data->prox_input_dev);
|
|
err_stk3x3x_init_input_device:
|
|
err_stk3x3x_init_registers:
|
|
err_stk3x3x_parse_dt:
|
|
wake_lock_destroy(&drv_data->prox_wakelock);
|
|
mutex_destroy(&drv_data->control_mutex);
|
|
err_stk3x3x_device_id_check:
|
|
kfree(drv_data);
|
|
|
|
return ret;
|
|
}
|
|
|
|
int stk3x3x_remove(struct i2c_client *client)
|
|
{
|
|
struct stk3x3x_data *drv_data = i2c_get_clientdata(client);
|
|
|
|
stk3x3x_enable_ps(drv_data, 0);
|
|
|
|
free_irq(drv_data->irq, drv_data);
|
|
gpio_free(drv_data->irq_gpio);
|
|
|
|
if (drv_data->cal_status != STK3X3X_CAL_DISABLED)
|
|
cancel_work_sync(&drv_data->work_cal_prox);
|
|
destroy_workqueue(drv_data->prox_cal_wq);
|
|
destroy_workqueue(drv_data->prox_irq_wq);
|
|
if (drv_data->pocket_running)
|
|
cancel_work_sync(&drv_data->work_pocket);
|
|
destroy_workqueue(drv_data->prox_pocket_wq);
|
|
|
|
sensors_unregister(drv_data->dev, prox_sysfs_attrs);
|
|
destroy_workqueue(drv_data->prox_wq);
|
|
|
|
sensors_remove_symlink(&drv_data->prox_input_dev->dev.kobj, drv_data->prox_input_dev->name);
|
|
sysfs_remove_group(&drv_data->prox_input_dev->dev.kobj, &prox_input_attribute_group);
|
|
input_unregister_device(drv_data->prox_input_dev);
|
|
|
|
wake_lock_destroy(&drv_data->prox_wakelock);
|
|
mutex_destroy(&drv_data->control_mutex);
|
|
|
|
kfree(drv_data);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void stk3x3x_shutdown(struct i2c_client *client)
|
|
{
|
|
struct stk3x3x_data *drv_data = i2c_get_clientdata(client);
|
|
|
|
mutex_lock(&drv_data->control_mutex);
|
|
stk3x3x_enable_ps(drv_data, 0);
|
|
mutex_unlock(&drv_data->control_mutex);
|
|
}
|
|
|
|
static const struct dev_pm_ops stk3x3x_pm_ops = {
|
|
.suspend = stk3x3x_suspend,
|
|
.resume = stk3x3x_resume,
|
|
};
|
|
|
|
static const struct i2c_device_id stk3x3x_ps_id[] =
|
|
{
|
|
{CHIP_NAME, 0},
|
|
{}
|
|
};
|
|
|
|
MODULE_DEVICE_TABLE(i2c, stk3x3x_ps_id);
|
|
|
|
static struct of_device_id stk3x3x_match_table[] =
|
|
{
|
|
{.compatible = "stk,stk3x3x",},
|
|
{},
|
|
};
|
|
|
|
static struct i2c_driver stk3x3x_ps_driver =
|
|
{
|
|
.probe = stk3x3x_probe,
|
|
.remove = stk3x3x_remove,
|
|
.shutdown = stk3x3x_shutdown,
|
|
.id_table = stk3x3x_ps_id,
|
|
.driver = {
|
|
.name = CHIP_NAME,
|
|
.owner = THIS_MODULE,
|
|
.of_match_table = stk3x3x_match_table,
|
|
.pm = &stk3x3x_pm_ops,
|
|
},
|
|
};
|
|
|
|
static int __init stk3x3x_init(void)
|
|
{
|
|
SENSOR_INFO("\n");
|
|
return i2c_add_driver(&stk3x3x_ps_driver);
|
|
}
|
|
|
|
static void __exit stk3x3x_exit(void)
|
|
{
|
|
SENSOR_INFO("\n");
|
|
i2c_del_driver(&stk3x3x_ps_driver);
|
|
}
|
|
|
|
module_init(stk3x3x_init);
|
|
module_exit(stk3x3x_exit);
|
|
|
|
MODULE_AUTHOR("Samsung Electronics");
|
|
MODULE_DESCRIPTION("Proximity sensor driver for sensortek stk3x3x IC");
|
|
MODULE_LICENSE("GPL"); |