unplugged-kernel/drivers/misc/mediatek/sensors-1.0/alsps/stk3x1x/stk3x1x.c

3539 lines
84 KiB
C

// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2020 MediaTek Inc.
*/
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/gpio.h>
#include <linux/interrupt.h>
#include <linux/i2c.h>
#include <linux/slab.h>
#include <linux/irq.h>
#include <linux/miscdevice.h>
#include <linux/uaccess.h>
#include <linux/delay.h>
#include <linux/input.h>
#include <linux/workqueue.h>
#include <linux/kobject.h>
#include <linux/platform_device.h>
#include <linux/atomic.h>
#include <linux/version.h>
#include <linux/fs.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/pm_wakeup.h>
#include <hwmsensor.h>
#include "cust_alsps.h"
#include "alsps.h"
#include "stk3x1x.h"
#define DRIVER_VERSION "3.9.2 20180403"
#define MTK_AUTO_DETECT_ALSPS
/*
* configuration
*/
#define stk3x1x_DEV_NAME "stk3x1x"
#define APS_TAG "[ALS/PS] "
#define APS_FUN(f) pr_debug(APS_TAG"%s\n", __func__)
#define APS_ERR(fmt, args...) pr_info(APS_TAG"%s %d : "fmt,\
__func__, __LINE__, ##args)
#define APS_DBG(fmt, args...) pr_debug(APS_TAG fmt, ##args)
#define APS_LOG(fmt, args...) pr_info(APS_TAG fmt, ##args)
/*
* extern functions
*/
#define STK_H_PS 400
#define STK_H_HT 300
#define STK_H_LT 200
#define STK_IRC_MAX_ALS_CODE 20000
#define STK_IRC_MIN_ALS_CODE 25
#define STK_IRC_MIN_IR_CODE 50
#define STK_IRC_ALS_DENOMI 2
#define STK_IRC_ALS_NUMERA 5
#define STK_IRC_ALS_CORREC 748
#define STK_IRS_IT_REDUCE 2
#define STK_ALS_READ_IRS_IT_REDUCE 5
#define STK_ALS_THRESHOLD 30
#define STK3310SA_PID 0x17
#define STK3311SA_PID 0x1E
#define STK3311WV_PID 0x1D
#define STK3311X_PID 0x12
#define STK33119_PID 0x11
static unsigned int als_value; /* lux value in test level*/
static unsigned int als_value_cali; /* lux value for ALS calibration */
static unsigned int transmittance_cali; /* transmittance for ALS calibration */
static unsigned int als_cal;
/*
* Since baro sensor driver in AP,
* it should register information to sensorlist.
*/
static struct sensorInfo_NonHub_t alsps_devinfo;
#define ALS_DEFAULT_TRANSMITTANCE 1553
#define ALS_CALIBRATION_LUX 400
/*----------------------------------------------------------------------------*/
static struct i2c_client *stk3x1x_i2c_client;
/*----------------------------------------------------------------------------*/
static const struct i2c_device_id stk3x1x_i2c_id[] = {
{stk3x1x_DEV_NAME, 0},
{}
};
/*----------------------------------------------------------------------------*/
static int stk3x1x_i2c_probe(struct i2c_client *client,
const struct i2c_device_id *id);
static int stk3x1x_i2c_remove(struct i2c_client *client);
static int stk3x1x_i2c_detect(struct i2c_client *client,
struct i2c_board_info *info);
/*----------------------------------------------------------------------------*/
#ifdef CONFIG_PM_SLEEP
static int stk3x1x_suspend(struct device *dev);
static int stk3x1x_resume(struct device *dev);
#endif
#ifndef C_I2C_FIFO_SIZE
#define C_I2C_FIFO_SIZE 8
#endif
static DEFINE_MUTEX(STK3X1X_i2c_mutex);
static int stk3x1x_init_flag = -1; /* 0 <==> OK -1 <==> fail */
static int stk3x1x_local_init(void);
static int stk3x1x_local_uninit(void);
static struct alsps_init_info stk3x1x_init_info = {
.name = "stk3x1x",
.init = stk3x1x_local_init,
.uninit = stk3x1x_local_uninit,
};
struct alsps_hw alsps_cust;
static struct alsps_hw *hw = &alsps_cust;
struct platform_device *alspsPltFmDev;
/* For alsp driver get cust info */
struct alsps_hw *get_cust_alsps(void)
{
return &alsps_cust;
}
/*----------------------------------------------------------------------------*/
enum {
STK_TRC_ALS_DATA = 0x0001,
STK_TRC_PS_DATA = 0x0002,
STK_TRC_EINT = 0x0004,
STK_TRC_IOCTL = 0x0008,
STK_TRC_I2C = 0x0010,
STK_TRC_CVT_ALS = 0x0020,
STK_TRC_CVT_PS = 0x0040,
STK_TRC_DEBUG = 0x8000,
} STK_TRC;
/*----------------------------------------------------------------------------*/
enum {
STK_BIT_ALS = 1,
STK_BIT_PS = 2,
} STK_BIT;
/*----------------------------------------------------------------------------*/
struct stk3x1x_i2c_addr {
/*define a series of i2c slave address*/
u8 state; /* enable/disable state */
u8 psctrl; /* PS control */
u8 alsctrl; /* ALS control */
u8 ledctrl; /* LED control */
u8 intmode; /* INT mode */
u8 wait; /* wait time */
u8 thdh1_ps; /* PS INT threshold high 1 */
u8 thdh2_ps; /* PS INT threshold high 2 */
u8 thdl1_ps; /* PS INT threshold low 1 */
u8 thdl2_ps; /* PS INT threshold low 2 */
u8 thdh1_als; /* ALS INT threshold high 1 */
u8 thdh2_als; /* ALS INT threshold high 2 */
u8 thdl1_als; /* ALS INT threshold low 1 */
u8 thdl2_als; /* ALS INT threshold low 2 */
u8 flag; /* int flag */
u8 data1_ps; /* ps data1 */
u8 data2_ps; /* ps data2 */
u8 data1_als; /* als data1 */
u8 data2_als; /* als data2 */
u8 data1_offset; /* offset data1 */
u8 data2_offset; /* offset data2 */
u8 data1_ir; /* ir data1 */
u8 data2_ir; /* ir data2 */
u8 soft_reset; /* software reset */
};
/*----------------------------------------------------------------------------*/
struct stk3x1x_priv {
struct alsps_hw *hw;
struct i2c_client *client;
struct delayed_work eint_work;
/*i2c address group*/
struct stk3x1x_i2c_addr addr;
/*misc*/
atomic_t trace;
atomic_t i2c_retry;
atomic_t als_suspend;
atomic_t als_debounce; /*debounce time after enabling als*/
atomic_t als_deb_on; /*indicates if the debounce is on*/
atomic_t als_deb_end; /*the jiffies representing the end of debounce*/
atomic_t ps_mask; /*mask ps: always return far away*/
atomic_t ps_debounce; /*debounce time after enabling ps*/
atomic_t ps_deb_on; /*indicates if the debounce is on*/
atomic_t ps_deb_end; /*the jiffies representing the end of debounce*/
atomic_t ps_suspend;
atomic_t init_done;
struct device_node *irq_node;
int irq;
/*data*/
u16 als;
u16 ps;
u8 _align;
atomic_t state_val;
atomic_t psctrl_val;
atomic_t alsctrl_val;
u8 wait_val;
u8 ledctrl_val;
u8 int_val;
atomic_t ps_high_thd_val;
/*the cmd value can't be read, stored in ram*/
atomic_t ps_low_thd_val;
/*the cmd value can't be read, stored in ram*/
ulong enable;
/*enable mask*/
ulong pending_intr;
/*pending interrupt*/
atomic_t recv_reg;
bool first_boot;
uint16_t ir_code;
uint16_t als_correct_factor;
bool als_last;
bool re_enable_ps;
bool re_enable_als;
u16 ps_cali;
uint8_t pid;
uint8_t p_wv_r_bd_with_co;
uint32_t als_code_last;
uint16_t als_data_index;
uint8_t ps_distance_last;
uint8_t p_1x_r_bd_with_co;
uint8_t p_19_r_bc;
uint32_t als_transmittance;
};
/*----------------------------------------------------------------------------*/
#ifdef CONFIG_OF
static const struct of_device_id alsps_of_match[] = {
{.compatible = "mediatek,alsps"},
{},
};
#endif
#ifdef CONFIG_PM_SLEEP
static const struct dev_pm_ops stk3x1x_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(stk3x1x_suspend, stk3x1x_resume)
};
#endif
static struct i2c_driver stk3x1x_i2c_driver = {
.probe = stk3x1x_i2c_probe,
.remove = stk3x1x_i2c_remove,
.detect = stk3x1x_i2c_detect,
.id_table = stk3x1x_i2c_id,
.driver = {
.name = stk3x1x_DEV_NAME,
#ifdef CONFIG_PM_SLEEP
.pm = &stk3x1x_pm_ops,
#endif
#ifdef CONFIG_OF
.of_match_table = alsps_of_match,
#endif
},
};
static struct stk3x1x_priv *stk3x1x_obj;
static int stk3x1x_get_ps_value(struct stk3x1x_priv *obj, u16 ps);
static int stk3x1x_get_ps_value_only(struct stk3x1x_priv *obj, u16 ps);
static uint32_t stk_alscode2lux(struct stk3x1x_priv *obj, uint32_t alscode);
static int stk3x1x_read_als(struct i2c_client *client, u16 *data);
static int stk3x1x_read_ps(struct i2c_client *client, u16 *data);
static int stk3x1x_set_als_int_thd(struct i2c_client *client, u16 als_data_reg);
static int32_t stk3x1x_get_ir_value(struct stk3x1x_priv *obj,
int32_t als_it_reduce);
#ifdef STK_CHK_REG
static int stk3x1x_validate_n_handle(struct i2c_client *client);
#endif
static int stk3x1x_init_client(struct i2c_client *client);
static struct wakeup_source mps_lock;
static DEFINE_MUTEX(run_cali_mutex);
/*----------------------------------------------------------------------------*/
int stk3x1x_get_addr(struct alsps_hw *hw, struct stk3x1x_i2c_addr *addr)
{
if (!hw || !addr)
return -EFAULT;
addr->state = STK_STATE_REG;
addr->psctrl = STK_PSCTRL_REG;
addr->alsctrl = STK_ALSCTRL_REG;
addr->ledctrl = STK_LEDCTRL_REG;
addr->intmode = STK_INT_REG;
addr->wait = STK_WAIT_REG;
addr->thdh1_ps = STK_THDH1_PS_REG;
addr->thdh2_ps = STK_THDH2_PS_REG;
addr->thdl1_ps = STK_THDL1_PS_REG;
addr->thdl2_ps = STK_THDL2_PS_REG;
addr->thdh1_als = STK_THDH1_ALS_REG;
addr->thdh2_als = STK_THDH2_ALS_REG;
addr->thdl1_als = STK_THDL1_ALS_REG;
addr->thdl2_als = STK_THDL2_ALS_REG;
addr->flag = STK_FLAG_REG;
addr->data1_ps = STK_DATA1_PS_REG;
addr->data2_ps = STK_DATA2_PS_REG;
addr->data1_als = STK_DATA1_ALS_REG;
addr->data2_als = STK_DATA2_ALS_REG;
addr->data1_offset = STK_DATA1_OFFSET_REG;
addr->data2_offset = STK_DATA2_OFFSET_REG;
addr->data1_ir = STK_DATA1_IR_REG;
addr->data2_ir = STK_DATA2_IR_REG;
addr->soft_reset = STK_SW_RESET_REG;
return 0;
}
/*----------------------------------------------------------------------------*/
int stk3x1x_hwmsen_read_block(struct i2c_client *client,
u8 addr, u8 *data, u8 len)
{
int err;
u8 beg = addr;
struct i2c_msg msgs[2] = {
{
.addr = client->addr,
.flags = 0,
.len = 1,
.buf = &beg
},
{
.addr = client->addr,
.flags = I2C_M_RD,
.len = len,
.buf = data,
}
};
mutex_lock(&STK3X1X_i2c_mutex);
if (!client) {
mutex_unlock(&STK3X1X_i2c_mutex);
return -EINVAL;
} else if (len > C_I2C_FIFO_SIZE) {
mutex_unlock(&STK3X1X_i2c_mutex);
APS_ERR(" length %d exceeds %d\n", len, C_I2C_FIFO_SIZE);
return -EINVAL;
}
err = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
mutex_unlock(&STK3X1X_i2c_mutex);
if (err != 2) {
APS_ERR("i2c_transfer error: (%d %p %d) %d\n",
addr, data, len, err);
err = -EIO;
} else {
err = 0;/*no error*/
}
return err;
}
/*----------------------------------------------------------------------------*/
int stk3x1x_get_timing(void)
{
return 200;
}
/*----------------------------------------------------------------------------*/
int stk3x1x_master_recv(struct i2c_client *client, u16 addr, u8 *buf, int count)
{
struct stk3x1x_priv *obj = i2c_get_clientdata(client);
int ret = 0, retry = 0;
int trc = atomic_read(&obj->trace);
int max_try = atomic_read(&obj->i2c_retry);
while (retry++ < max_try) {
ret = stk3x1x_hwmsen_read_block(client, addr, buf, count);
if (ret == 0)
break;
udelay(100);
}
if (unlikely(trc)) {
if ((retry != 1) && (trc & STK_TRC_DEBUG))
APS_DBG("(recv) %d/%d\n", retry - 1, max_try);
}
/*
* If everything went ok (i.e. 1 msg transmitted), return #bytes
* transmitted, else error code.
*/
return (ret == 0) ? count : ret;
}
/*----------------------------------------------------------------------------*/
int stk3x1x_master_send(struct i2c_client *client, u16 addr, u8 *buf, int count)
{
int ret = 0, retry = 0;
struct stk3x1x_priv *obj = i2c_get_clientdata(client);
int trc = atomic_read(&obj->trace);
int max_try = atomic_read(&obj->i2c_retry);
while (retry++ < max_try) {
ret = hwmsen_write_block(client, addr, buf, count);
if (ret == 0)
break;
udelay(100);
}
if (unlikely(trc)) {
if ((retry != 1) && (trc & STK_TRC_DEBUG))
APS_DBG("(send) %d/%d\n", retry - 1, max_try);
}
/* If everything went ok (i.e. 1 msg transmitted) */
/* return bytes transmitted, else error code. */
return (ret == 0) ? count : ret;
}
/*----------------------------------------------------------------------------*/
static int stk3x1x_otp_read_byte_data(struct i2c_client *client, u8 command)
{
int ret;
int value;
u8 data;
data = 0x2;
ret = stk3x1x_master_send(client, 0x0, &data, 1);
if (ret < 0) {
APS_ERR("write 0x0 = %d\n", ret);
return -EFAULT;
}
data = command;
ret = stk3x1x_master_send(client, 0x90, &data, 1);
if (ret < 0) {
APS_ERR("write 0x90 = %d\n", ret);
return -EFAULT;
}
data = 0x82;
ret = stk3x1x_master_send(client, 0x92, &data, 1);
if (ret < 0) {
APS_ERR("write 0x0 = %d\n", ret);
return -EFAULT;
}
usleep_range(2000, 4000);
ret = stk3x1x_master_recv(client, 0x91, &data, 1);
if (ret < 0) {
APS_ERR("error: %d\n", ret);
return -EFAULT;
}
value = data;
APS_DBG("%s: 0x%x=0x%x\n", __func__, command, value);
data = 0x0;
ret = stk3x1x_master_send(client, 0x0, &data, 1);
if (ret < 0) {
APS_ERR("write 0x0 = %d\n", ret);
return -EFAULT;
}
return value;
}
/*----------------------------------------------------------------------------*/
int stk3x1x_write_led(struct i2c_client *client, u8 data)
{
struct stk3x1x_priv *obj = i2c_get_clientdata(client);
int ret = 0;
ret = stk3x1x_master_send(client, obj->addr.ledctrl, &data, 1);
if (ret < 0) {
APS_ERR("write led = %d\n", ret);
return -EFAULT;
}
return 0;
}
/*----------------------------------------------------------------------------*/
int stk_als_ir_get_corr(struct stk3x1x_priv *obj, u32 als_data)
{
int32_t als_comperator;
if (obj->ir_code) {
obj->als_correct_factor = 1000;
if (als_data < STK_IRC_MAX_ALS_CODE
&& als_data > STK_IRC_MIN_ALS_CODE &&
obj->ir_code > STK_IRC_MIN_IR_CODE) {
als_comperator = als_data *
STK_IRC_ALS_NUMERA / STK_IRC_ALS_DENOMI;
if (obj->ir_code > als_comperator)
obj->als_correct_factor = STK_IRC_ALS_CORREC;
}
APS_DBG("%s: als=%d, ir=%d, als_correct_factor=%d",
__func__, als_data,
obj->ir_code, obj->als_correct_factor);
obj->ir_code = 0;
}
return 0;
}
/*----------------------------------------------------------------------------*/
#ifdef STK_IRS
int stk_als_ir_skip_als(struct stk3x1x_priv *obj)
{
int ret;
u8 buf[2];
if (obj->als_data_index < 60000)
obj->als_data_index++;
else
obj->als_data_index = 0;
if (obj->als_data_index % 10 == 1) {
ret = stk3x1x_master_recv(obj->client,
obj->addr.data1_als, buf, 0x02);
if (ret < 0) {
APS_ERR("error: %d\n", ret);
return -EFAULT;
}
return 1;
}
return 0;
}
/*----------------------------------------------------------------------------*/
static int stk_als_ir_run(struct stk3x1x_priv *obj)
{
u32 ir_data;
if (obj->als_data_index % 10 == 0) {
if (obj->ps_distance_last != 0 && obj->ir_code == 0) {
ir_data = stk3x1x_get_ir_value(obj, STK_IRS_IT_REDUCE);
if (ir_data > 0)
obj->ir_code = ir_data;
}
return ir_data;
}
return 0;
}
#endif /* #ifdef STK_IRS */
/*----------------------------------------------------------------------------*/
#define STK_ALS_THRESHOLD 30
int stk3x1x_read_als(struct i2c_client *client, u16 *data)
{
struct stk3x1x_priv *obj = i2c_get_clientdata(client);
int ret = 0;
u8 buf[2];
u32 als_data;
int32_t ir_data;
#ifdef STK_IRS
const int ir_enlarge =
1 << (STK_ALS_READ_IRS_IT_REDUCE - STK_IRS_IT_REDUCE);
#endif
if (!client)
return -EINVAL;
#ifdef STK_IRS
ret = stk_als_ir_skip_als(obj);
if (ret == 1)
return 0;
#endif
if (atomic_read(&obj->als_suspend)) {
als_data = 0;
goto out;
}
ret = stk3x1x_master_recv(client, obj->addr.data1_als, buf, 0x02);
if (ret < 0) {
APS_ERR("error: %d\n", ret);
return -EFAULT;
}
als_data = (buf[0] << 8) | (buf[1]);
if (obj->p_1x_r_bd_with_co == 0x07 || obj->p_19_r_bc == 0x03) {
als_data = als_data * 16 / 10;
if (als_data > 65535)
als_data = 65535;
}
if (obj->p_wv_r_bd_with_co & 0x02) {
if (als_data < STK_ALS_THRESHOLD &&
obj->als_code_last > 10000) {
ir_data = stk3x1x_get_ir_value(obj,
STK_ALS_READ_IRS_IT_REDUCE);
#ifdef STK_IRS
if (ir_data > 0)
obj->ir_code = ir_data * ir_enlarge;
#endif
if (ir_data > (STK_ALS_THRESHOLD * 3))
als_data = obj->als_code_last;
}
#ifdef STK_IRS
else
obj->ir_code = 0;
#endif
}
obj->als_code_last = als_data;
#ifdef STK_IRS
stk_als_ir_get_corr(obj, als_data);
als_data = als_data * obj->als_correct_factor / 1000;
#endif
out:
*data = (u16)als_data;
#ifdef STK_IRS
stk_als_ir_run(obj);
#endif
if (atomic_read(&obj->trace) & STK_TRC_ALS_DATA)
APS_DBG("ALS: 0x%04X\n", (u32)(*data));
return 0;
}
/*----------------------------------------------------------------------------*/
int stk3x1x_write_als(struct i2c_client *client, u8 data)
{
struct stk3x1x_priv *obj = i2c_get_clientdata(client);
int ret = 0;
ret = stk3x1x_master_send(client, obj->addr.alsctrl, &data, 1);
if (ret < 0) {
APS_ERR("write als = %d\n", ret);
return -EFAULT;
}
return 0;
}
/*----------------------------------------------------------------------------*/
int stk3x1x_read_state(struct i2c_client *client, u8 *data)
{
int ret = 0;
u8 buf;
if (!client)
return -EINVAL;
ret = stk3x1x_master_recv(client, STK_STATE_REG, &buf, 0x01);
if (ret < 0) {
APS_ERR("error: %d\n", ret);
return -EFAULT;
}
*data = buf;
return 0;
}
/*----------------------------------------------------------------------------*/
int stk3x1x_read_flag(struct i2c_client *client, u8 *data)
{
struct stk3x1x_priv *obj = i2c_get_clientdata(client);
int ret = 0;
u8 buf;
if (!client)
return -EINVAL;
ret = stk3x1x_master_recv(client, obj->addr.flag, &buf, 0x01);
if (ret < 0) {
APS_ERR("error: %d\n", ret);
return -EFAULT;
}
*data = buf;
if (atomic_read(&obj->trace) & STK_TRC_ALS_DATA)
APS_DBG("PS NF flag: 0x%04X\n", (u32)(*data));
return 0;
}
/*----------------------------------------------------------------------------*/
int stk3x1x_read_id(struct i2c_client *client)
{
struct stk3x1x_priv *obj = i2c_get_clientdata(client);
int ret = 0;
u8 buf[2];
u8 pid_msb;
int otp25 = 0;
if (!client)
return -EINVAL;
obj->p_wv_r_bd_with_co = 0;
obj->p_1x_r_bd_with_co = 0;
obj->p_19_r_bc = 0;
ret = stk3x1x_master_recv(client, STK_PDT_ID_REG, buf, 0x02);
if (ret < 0) {
APS_ERR("error: %d\n", ret);
return -EFAULT;
}
obj->pid = buf[0];
APS_DBG("%s: PID=0x%x, VID=0x%x\n", __func__, buf[0], buf[1]);
if (obj->pid == STK3310SA_PID || obj->pid == STK3311SA_PID)
obj->ledctrl_val &= 0x3F;
if (buf[0] == STK3311WV_PID)
obj->p_wv_r_bd_with_co |= 0x04;
else if (buf[0] == STK3311X_PID)
obj->p_1x_r_bd_with_co |= 0x04;
else if (buf[0] == STK33119_PID)
obj->p_19_r_bc |= 0x02;
if (buf[1] == 0xC3) {
obj->p_wv_r_bd_with_co |= 0x02;
obj->p_1x_r_bd_with_co |= 0x02;
} else if (buf[1] == 0xC2) {
obj->p_19_r_bc |= 0x01;
}
ret = stk3x1x_otp_read_byte_data(client, 0x25);
if (ret < 0)
return ret;
otp25 = ret;
if (otp25 & 0x80)
obj->p_wv_r_bd_with_co |= 0x01;
APS_DBG("%s: p_wv_r_bd_with_co = 0x%x\n",
__func__, obj->p_wv_r_bd_with_co);
if (otp25 & 0x40)
obj->p_1x_r_bd_with_co |= 0x01;
APS_DBG("%s: p_1x_r_bd_with_co = 0x%x\n",
__func__, obj->p_1x_r_bd_with_co);
APS_DBG("%s: p_19_r_bc = 0x%x\n",
__func__, obj->p_19_r_bc);
if (buf[0] == 0) {
APS_ERR("PID=0x0, please make sure the chip is stk3x1x!\n");
return -2;
}
pid_msb = buf[0] & 0xF0;
switch (pid_msb) {
case 0x10:
case 0x20:
case 0x30:
return 0;
default:
APS_ERR("invalid PID(%#x)\n", buf[0]);
return -1;
}
return 0;
}
/*----------------------------------------------------------------------------*/
int stk3x1x_read_ps(struct i2c_client *client, u16 *data)
{
struct stk3x1x_priv *obj = i2c_get_clientdata(client);
int ret = 0;
u8 buf[2];
if (!client) {
APS_ERR("i2c client is NULL\n");
return -EINVAL;
}
ret = stk3x1x_master_recv(client, obj->addr.data1_ps, buf, 0x02);
if (ret < 0) {
APS_ERR("error: %d\n", ret);
return -EFAULT;
}
*data = (buf[0] << 8) | (buf[1]);
if (atomic_read(&obj->trace) & STK_TRC_ALS_DATA)
APS_DBG("PS: 0x%04X\n", (u32)(*data));
return 0;
}
/*----------------------------------------------------------------------------*/
int stk3x1x_write_ps(struct i2c_client *client, u8 data)
{
struct stk3x1x_priv *obj = i2c_get_clientdata(client);
int ret = 0;
ret = stk3x1x_master_send(client, obj->addr.psctrl, &data, 1);
if (ret < 0) {
APS_ERR("write ps = %d\n", ret);
return -EFAULT;
}
return 0;
}
/*----------------------------------------------------------------------------*/
int stk3x1x_write_wait(struct i2c_client *client, u8 data)
{
struct stk3x1x_priv *obj = i2c_get_clientdata(client);
int ret = 0;
ret = stk3x1x_master_send(client, obj->addr.wait, &data, 1);
if (ret < 0) {
APS_ERR("write wait = %d\n", ret);
return -EFAULT;
}
return 0;
}
/*----------------------------------------------------------------------------*/
int stk3x1x_write_int(struct i2c_client *client, u8 data)
{
struct stk3x1x_priv *obj = i2c_get_clientdata(client);
int ret = 0;
ret = stk3x1x_master_send(client, obj->addr.intmode, &data, 1);
if (ret < 0) {
APS_ERR("write intmode = %d\n", ret);
return -EFAULT;
}
return 0;
}
/*----------------------------------------------------------------------------*/
int stk3x1x_write_state(struct i2c_client *client, u8 data)
{
struct stk3x1x_priv *obj = i2c_get_clientdata(client);
int ret = 0;
ret = stk3x1x_master_send(client, obj->addr.state, &data, 1);
if (ret < 0) {
APS_ERR("write state = %d\n", ret);
return -EFAULT;
}
return 0;
}
/*----------------------------------------------------------------------------*/
int stk3x1x_write_flag(struct i2c_client *client, u8 data)
{
struct stk3x1x_priv *obj = i2c_get_clientdata(client);
int ret = 0;
ret = stk3x1x_master_send(client, obj->addr.flag, &data, 1);
if (ret < 0) {
APS_ERR("write ps = %d\n", ret);
return -EFAULT;
}
return 0;
}
/*----------------------------------------------------------------------------*/
int stk3x1x_write_sw_reset(struct i2c_client *client)
{
struct stk3x1x_priv *obj = i2c_get_clientdata(client);
u8 buf = 0, r_buf = 0;
int ret = 0;
APS_LOG("%s: In\n", __func__);
buf = 0x7F;
ret = stk3x1x_master_send(client, obj->addr.wait,
(char *)&buf, sizeof(buf));
if (ret < 0) {
APS_ERR("i2c write test error = %d\n", ret);
return -EFAULT;
}
ret = stk3x1x_master_recv(client, obj->addr.wait, &r_buf, 1);
if (ret < 0) {
APS_ERR("i2c read test error = %d\n", ret);
return -EFAULT;
}
if (buf != r_buf) {
APS_ERR("read-back is not the same, write=0x%x, read=0x%x\n",
buf, r_buf);
return -EIO;
}
buf = 0;
ret = stk3x1x_master_send(client, obj->addr.soft_reset,
(char *)&buf, sizeof(buf));
if (ret < 0) {
APS_ERR("write software reset error = %d\n", ret);
return -EFAULT;
}
msleep(20);
return 0;
}
/*----------------------------------------------------------------------------*/
int stk3x1x_write_ps_high_thd(struct i2c_client *client, u16 thd)
{
struct stk3x1x_priv *obj = i2c_get_clientdata(client);
u8 buf[2];
int ret = 0;
buf[0] = (u8)((0xFF00 & thd) >> 8);
buf[1] = (u8)(0x00FF & thd);
ret = stk3x1x_master_send(client, obj->addr.thdh1_ps, &buf[0], 1);
if (ret < 0) {
APS_ERR("WARNING: %d\n", ret);
return -EFAULT;
}
ret = stk3x1x_master_send(client, obj->addr.thdh2_ps, &(buf[1]), 1);
if (ret < 0) {
APS_ERR("WARNING: %d\n", ret);
return -EFAULT;
}
return 0;
}
/*----------------------------------------------------------------------------*/
int stk3x1x_write_ps_low_thd(struct i2c_client *client, u16 thd)
{
struct stk3x1x_priv *obj = i2c_get_clientdata(client);
u8 buf[2];
int ret = 0;
buf[0] = (u8)((0xFF00 & thd) >> 8);
buf[1] = (u8)(0x00FF & thd);
ret = stk3x1x_master_send(client, obj->addr.thdl1_ps, &buf[0], 1);
if (ret < 0) {
APS_ERR("WARNING: %s: %d\n", __func__, ret);
return -EFAULT;
}
ret = stk3x1x_master_send(client, obj->addr.thdl2_ps, &(buf[1]), 1);
if (ret < 0) {
APS_ERR("WARNING: %s: %d\n", __func__, ret);
return -EFAULT;
}
return 0;
}
/*----------------------------------------------------------------------------*/
int stk3x1x_write_als_high_thd(struct i2c_client *client, u16 thd)
{
struct stk3x1x_priv *obj = i2c_get_clientdata(client);
u8 buf[2];
int ret = 0;
buf[0] = (u8)((0xFF00 & thd) >> 8);
buf[1] = (u8)(0x00FF & thd);
ret = stk3x1x_master_send(client, obj->addr.thdh1_als, &buf[0], 1);
if (ret < 0) {
APS_ERR("WARNING: %s: %d\n", __func__, ret);
return -EFAULT;
}
ret = stk3x1x_master_send(client, obj->addr.thdh2_als, &(buf[1]), 1);
if (ret < 0) {
APS_ERR("WARNING: %s: %d\n", __func__, ret);
return -EFAULT;
}
return 0;
}
/*----------------------------------------------------------------------------*/
int stk3x1x_write_als_low_thd(struct i2c_client *client, u16 thd)
{
struct stk3x1x_priv *obj = i2c_get_clientdata(client);
u8 buf[2];
int ret = 0;
buf[0] = (u8)((0xFF00 & thd) >> 8);
buf[1] = (u8)(0x00FF & thd);
ret = stk3x1x_master_send(client, obj->addr.thdl1_als, &buf[0], 1);
if (ret < 0) {
APS_ERR("WARNING: %s: %d\n", __func__, ret);
return -EFAULT;
}
ret = stk3x1x_master_send(client, obj->addr.thdl2_als, &(buf[1]), 1);
if (ret < 0) {
APS_ERR("WARNING: %s: %d\n", __func__, ret);
return -EFAULT;
}
return 0;
}
/*----------------------------------------------------------------------------*/
#ifdef STK_PS_DEBUG
static int show_allreg(void)
{
int ret = 0;
u8 rbuf[0x22];
int cnt;
memset(rbuf, 0, sizeof(rbuf));
if (!stk3x1x_obj) {
APS_ERR("stk3x1x_obj is null!!\n");
return 0;
}
ret = stk3x1x_master_recv(stk3x1x_obj->client, 0, &rbuf[0], 7);
if (ret < 0) {
APS_ERR("error: %d\n", ret);
return -EFAULT;
}
ret = stk3x1x_master_recv(stk3x1x_obj->client, 7, &rbuf[7], 7);
if (ret < 0) {
APS_ERR("error: %d\n", ret);
return -EFAULT;
}
ret = stk3x1x_master_recv(stk3x1x_obj->client, 14, &rbuf[14], 7);
if (ret < 0) {
APS_ERR("error: %d\n", ret);
return -EFAULT;
}
ret = stk3x1x_master_recv(stk3x1x_obj->client, 21, &rbuf[21], 7);
if (ret < 0) {
APS_ERR("error: %d\n", ret);
return -EFAULT;
}
ret = stk3x1x_master_recv(stk3x1x_obj->client, 28, &rbuf[28], 4);
if (ret < 0) {
APS_ERR("error: %d\n", ret);
return -EFAULT;
}
ret = stk3x1x_master_recv(stk3x1x_obj->client,
STK_PDT_ID_REG, &rbuf[32], 2);
if (ret < 0) {
APS_ERR("error: %d\n", ret);
return -EFAULT;
}
for (cnt = 0; cnt < 0x20; cnt++)
APS_DBG("reg[0x%x]=0x%x\n", cnt, rbuf[cnt]);
APS_DBG("reg[0x3E]=0x%x\n", rbuf[cnt]);
APS_DBG("reg[0x3F]=0x%x\n", rbuf[cnt++]);
return 0;
}
#endif
/*----------------------------------------------------------------------------*/
static void stk_ps_report(struct stk3x1x_priv *obj, int nf)
{
if (ps_report_interrupt_data(nf))
APS_ERR("call ps_report_interrupt_data fail\n");
APS_DBG("%s:ps raw 0x%x -> value 0x%x\n", __func__, obj->ps, nf);
obj->ps_distance_last = nf;
}
/*----------------------------------------------------------------------------*/
static int stk3x1x_enable_als(struct i2c_client *client, int enable)
{
struct stk3x1x_priv *obj = i2c_get_clientdata(client);
int err, cur = 0, old = atomic_read(&obj->state_val);
int trc = atomic_read(&obj->trace);
APS_LOG("%s: enable=%d\n", __func__, enable);
cur = old & (~(STK_STATE_EN_ALS_MASK | STK_STATE_EN_WAIT_MASK));
if (enable)
cur |= STK_STATE_EN_ALS_MASK;
else if (old & STK_STATE_EN_PS_MASK)
cur |= STK_STATE_EN_WAIT_MASK;
if (trc & STK_TRC_DEBUG)
APS_DBG("%s: %08X, %08X, %d\n", __func__, cur, old, enable);
if (0 == (cur ^ old))
return 0;
#ifdef STK_IRS
if (enable && !(old & STK_STATE_EN_PS_MASK)) {
err = stk3x1x_get_ir_value(obj, STK_IRS_IT_REDUCE);
if (err > 0)
obj->ir_code = err;
}
#endif
if (enable && obj->hw->polling_mode_als == 0) {
stk3x1x_write_als_high_thd(client, 0x0);
stk3x1x_write_als_low_thd(client, 0xFFFF);
}
err = stk3x1x_write_state(client, cur);
if (err < 0)
return err;
atomic_set(&obj->state_val, cur);
if (enable) {
obj->als_last = 0;
if (obj->hw->polling_mode_als) {
atomic_set(&obj->als_deb_on, 1);
atomic_set(&obj->als_deb_end, jiffies +
atomic_read(&obj->als_debounce)*HZ / 1000);
} else {
schedule_delayed_work(&obj->eint_work, 220 * HZ / 1000);
}
}
#ifdef STK_IRS
obj->als_data_index = 0;
#endif
if (trc & STK_TRC_DEBUG)
APS_DBG("enable als (%d)\n", enable);
return err;
}
/*----------------------------------------------------------------------------*/
static int stk3x1x_enable_ps_set_thd(struct stk3x1x_priv *obj)
{
int err;
err = stk3x1x_write_ps_high_thd(obj->client,
atomic_read(&obj->ps_high_thd_val));
if (err) {
APS_ERR("write high thd error: %d\n", err);
return err;
}
err = stk3x1x_write_ps_low_thd(obj->client,
atomic_read(&obj->ps_low_thd_val));
if (err) {
APS_ERR("write low thd error: %d\n", err);
return err;
}
APS_DBG("%s:in reg, HT=%d, LT=%d\n", __func__,
atomic_read(&obj->ps_high_thd_val),
atomic_read(&obj->ps_low_thd_val));
APS_DBG("%s:in driver, HT=%d, LT=%d\n", __func__,
atomic_read(&obj->ps_high_thd_val),
atomic_read(&obj->ps_low_thd_val));
return 0;
}
/*----------------------------------------------------------------------------*/
static int stk3x1x_enable_ps(struct i2c_client *client,
int enable, int validate_reg)
{
struct stk3x1x_priv *obj = i2c_get_clientdata(client);
int err, cur = 0, old = atomic_read(&obj->state_val);
int trc = atomic_read(&obj->trace);
#ifdef STK_PS_DEBUG
if (enable) {
APS_LOG("%s: before enable ps, HT=%d, LT=%d\n", __func__,
atomic_read(&obj->ps_high_thd_val),
atomic_read(&obj->ps_low_thd_val));
APS_LOG("%s: before enable ps, show all reg\n", __func__);
show_allreg(); /*for debug*/
}
#endif
pr_info("%s: enable=%d\n", __func__, enable);
cur = old;
cur &= (~(0x45));
if (enable) {
cur |= (STK_STATE_EN_PS_MASK);
if (!(old & STK_STATE_EN_ALS_MASK))
cur |= STK_STATE_EN_WAIT_MASK;
}
if (0 == (cur ^ old)) {
pr_info("%s: repeat enable=%d, reg=0x%x\n",
__func__, enable, cur);
return 0;
}
#ifdef STK_CHK_REG
if (validate_reg) {
err = stk3x1x_validate_n_handle(obj->client);
if (err < 0)
APS_ERR("stk3x1x_validate_n_handle fail: %d\n", err);
}
#endif
if (obj->first_boot == true)
obj->first_boot = false;
if (enable) {
stk3x1x_enable_ps_set_thd(obj);
if (obj->hw->polling_mode_ps == 1)
__pm_stay_awake(&mps_lock);
} else {
if (obj->hw->polling_mode_ps == 1)
__pm_relax(&mps_lock);
}
if (trc & STK_TRC_DEBUG)
APS_DBG("%s: %08X, %08X, %d\n", __func__, cur, old, enable);
err = stk3x1x_write_state(client, cur);
if (err < 0)
return err;
atomic_set(&obj->state_val, cur);
if (enable) {
if (obj->hw->polling_mode_ps) {
atomic_set(&obj->ps_deb_on, 1);
atomic_set(&obj->ps_deb_end,
jiffies + atomic_read(&obj->ps_debounce)*HZ / 1000);
} else {
#ifdef STK_CHK_REG
if (!validate_reg) {
stk_ps_report(obj, 1);
} else
#endif
{
msleep(20);
err = stk3x1x_read_ps(obj->client, &obj->ps);
if (err) {
APS_ERR("read ps data:%d\n", err);
return err;
}
err = stk3x1x_get_ps_value_only(obj, obj->ps);
if (err < 0) {
APS_ERR("get ps value: %d\n", err);
return err;
} else if (stk3x1x_obj->hw->polling_mode_ps
== 0)
stk_ps_report(obj, err);
#ifdef STK_PS_DEBUG
APS_LOG("%s: after stk_ps_report, show reg\n",
__func__);
show_allreg();
/*for debug*/
#endif
}
}
}
if (trc & STK_TRC_DEBUG)
APS_DBG("enable ps (%d)\n", enable);
return 0;
}
/*----------------------------------------------------------------------------*/
static int stk3x1x_ps_calibration(struct i2c_client *client)
{
struct stk3x1x_priv *obj = i2c_get_clientdata(client);
u8 reg;
int err = 0;
int org_state_reg = atomic_read(&obj->state_val);
bool re_en_ps = false, re_en_als = false;
int counter = 0;
uint32_t ps_sum = 0;
const int PS_CALI_COUNT = 5;
if (org_state_reg & STK_STATE_EN_PS_MASK) {
APS_LOG("%s: force disable PS\n", __func__);
stk3x1x_enable_ps(obj->client, 0, 1);
re_en_ps = true;
}
if (org_state_reg & STK_STATE_EN_ALS_MASK) {
APS_LOG("%s: force disable ALS\n", __func__);
stk3x1x_enable_als(obj->client, 0);
re_en_als = true;
}
#if defined(CONFIG_OF)
disable_irq(obj->irq);
#else
mt_eint_mask(CUST_EINT_ALS_NUM);
#endif
reg = STK_STATE_EN_WAIT_MASK | STK_STATE_EN_PS_MASK;
err = stk3x1x_write_state(client, reg);
if (err < 0)
goto err_out;
while (counter < PS_CALI_COUNT) {
msleep(60);
err = stk3x1x_read_ps(obj->client, &obj->ps);
if (err)
goto err_out;
APS_LOG("%s: ps=%d\n", __func__, obj->ps);
ps_sum += obj->ps;
counter++;
}
obj->ps_cali = (ps_sum / PS_CALI_COUNT);
err_out:
#if defined(CONFIG_OF)
disable_irq(obj->irq);
#else
mt_eint_mask(CUST_EINT_ALS_NUM);
#endif
if (re_en_als) {
APS_LOG("%s: re-enable ALS\n", __func__);
stk3x1x_enable_als(obj->client, 1);
}
if (re_en_ps) {
APS_LOG("%s: re-enable PS\n", __func__);
stk3x1x_enable_ps(obj->client, 1, 1);
}
return err;
}
/*----------------------------------------------------------------------------*/
static int stk3x1x_check_intr(struct i2c_client *client, u8 *status)
{
struct stk3x1x_priv *obj = i2c_get_clientdata(client);
int err;
err = stk3x1x_read_flag(client, status);
if (err < 0) {
APS_ERR("WARNING: read flag reg error: %d\n", err);
return -EFAULT;
}
APS_DBG("%s: read status reg: 0x%x\n", __func__, *status);
if (*status & STK_FLG_ALSINT_MASK)
set_bit(STK_BIT_ALS, &obj->pending_intr);
else
clear_bit(STK_BIT_ALS, &obj->pending_intr);
if (*status & STK_FLG_PSINT_MASK)
set_bit(STK_BIT_PS, &obj->pending_intr);
else
clear_bit(STK_BIT_PS, &obj->pending_intr);
if (atomic_read(&obj->trace) & STK_TRC_DEBUG)
APS_DBG("check intr: 0x%02X => 0x%08lX\n",
*status, obj->pending_intr);
return 0;
}
static int stk3x1x_clear_intr(struct i2c_client *client,
u8 status, u8 disable_flag)
{
int err = 0;
status = status | (STK_FLG_ALSINT_MASK | STK_FLG_PSINT_MASK
| STK_FLG_OUI_MASK | STK_FLG_IR_RDY_MASK);
status &= (~disable_flag);
err = stk3x1x_write_flag(client, status);
if (err)
APS_ERR("stk3x1x_write_flag failed, err=%d\n", err);
return err;
}
/*----------------------------------------------------------------------------*/
#ifdef STK_CHK_REG
static int stk3x1x_chk_reg_valid(struct stk3x1x_priv *obj)
{
int ret = 0;
u8 buf[9];
if (!obj)
return -EINVAL;
memset(buf, 0, sizeof(buf));
ret = stk3x1x_master_recv(stk3x1x_obj->client, 1, &buf[0], 7);
if (ret < 0) {
APS_ERR("error: %d\n", ret);
return -EFAULT;
}
ret = stk3x1x_master_recv(stk3x1x_obj->client, 8, &buf[7], 2);
if (ret < 0) {
APS_ERR("error: %d\n", ret);
return -EFAULT;
}
if (buf[0] != atomic_read(&obj->psctrl_val)) {
APS_ERR("%s: invalid reg 0x01=0x%2x\n", __func__, buf[0]);
return 0xFF;
}
#ifdef STK_IRS
if (buf[1] != atomic_read(&obj->alsctrl_val)
&& buf[1] != (atomic_read(&obj->alsctrl_val) -
STK_IRS_IT_REDUCE)
&& buf[1] != (atomic_read(&obj->alsctrl_val) -
STK_ALS_READ_IRS_IT_REDUCE))
#else
if (buf[1] != atomic_read(&obj->alsctrl_val)
&& buf[1] != (atomic_read(&obj->alsctrl_val) -
STK_ALS_READ_IRS_IT_REDUCE))
#endif
{
APS_ERR("%s: invalid reg 0x02=0x%2x\n", __func__, buf[1]);
return 0xFF;
}
if (buf[2] != obj->ledctrl_val) {
APS_ERR("%s: invalid reg 0x03=0x%2x\n", __func__, buf[2]);
return 0xFF;
}
if (buf[3] != obj->int_val) {
APS_ERR("%s: invalid reg 0x04=0x%2x\n", __func__, buf[3]);
return 0xFF;
}
if (buf[4] != obj->wait_val) {
APS_ERR("%s: invalid reg 0x05=0x%2x\n", __func__, buf[4]);
return 0xFF;
}
if (buf[5] != (atomic_read(&obj->ps_high_thd_val) & 0xFF00) >> 8) {
APS_ERR("%s: invalid reg 0x06=0x%2x\n", __func__, buf[5]);
return 0xFF;
}
if (buf[6] != (atomic_read(&obj->ps_high_thd_val) & 0x00FF)) {
APS_ERR("%s: invalid reg 0x07=0x%2x\n", __func__, buf[6]);
return 0xFF;
}
if (buf[7] != (atomic_read(&obj->ps_low_thd_val) & 0xFF00) >> 8) {
APS_ERR("%s: invalid reg 0x08=0x%2x\n", __func__, buf[7]);
return 0xFF;
}
if (buf[8] != (atomic_read(&obj->ps_low_thd_val) & 0x00FF)) {
APS_ERR("%s: invalid reg 0x09=0x%2x\n", __func__, buf[8]);
return 0xFF;
}
return 0;
}
static int stk3x1x_validate_n_handle(struct i2c_client *client)
{
struct stk3x1x_priv *obj = i2c_get_clientdata(client);
int err;
err = stk3x1x_chk_reg_valid(obj);
if (err < 0) {
APS_ERR("stk3x1x_chk_reg_valid fail: %d\n", err);
return err;
}
if (err == 0xFF) {
APS_ERR("%s: Re-init chip\n", __func__);
stk3x1x_init_client(obj->client);
err = stk3x1x_write_ps_high_thd(client,
atomic_read(&obj->ps_high_thd_val);
if (err) {
APS_ERR("write high thd error: %d\n", err);
return err;
}
err = stk3x1x_write_ps_low_thd(client,
atomic_read(&obj->ps_low_thd_val);
if (err) {
APS_ERR("write low thd error: %d\n", err);
return err;
}
return 0xFF;
}
return 0;
}
#endif /* #ifdef STK_CHK_REG */
/*----------------------------------------------------------------------------*/
static int stk3x1x_set_als_int_thd(struct i2c_client *client, u16 als_data_reg)
{
s32 als_thd_h, als_thd_l;
als_thd_h = als_data_reg + STK_ALS_CODE_CHANGE_THD;
als_thd_l = als_data_reg - STK_ALS_CODE_CHANGE_THD;
if (als_thd_h >= (1 << 16))
als_thd_h = (1 << 16) - 1;
if (als_thd_l < 0)
als_thd_l = 0;
APS_DBG("%s:als_thd_h:%d,als_thd_l:%d\n",
__func__, als_thd_h, als_thd_l);
stk3x1x_write_als_high_thd(client, als_thd_h);
stk3x1x_write_als_low_thd(client, als_thd_l);
return 0;
}
/*----------------------------------------------------------------------------*/
static int stk_als_int_handle(struct stk3x1x_priv *obj, u16 als_reading)
{
struct hwm_sensor_data sensor_data;
memset(&sensor_data, 0, sizeof(sensor_data));
stk3x1x_set_als_int_thd(obj->client, obj->als);
sensor_data.values[0] = stk_alscode2lux(obj, obj->als);
sensor_data.value_divide = 1;
sensor_data.status = SENSOR_STATUS_ACCURACY_MEDIUM;
APS_DBG("%s:als raw 0x%x -> value 0x%x\n", __func__,
obj->als, sensor_data.values[0]);
/*let up layer to know*/
obj->als_code_last = als_reading;
return 0;
}
/*----------------------------------------------------------------------------*/
void stk3x1x_eint_func(void)
{
struct stk3x1x_priv *obj = stk3x1x_obj;
APS_DBG(" interrupt fuc\n");
if (!obj)
return;
if (obj->hw->polling_mode_ps == 0 || obj->hw->polling_mode_als == 0)
schedule_delayed_work(&obj->eint_work, 0);
if (atomic_read(&obj->trace) & STK_TRC_EINT)
APS_DBG("eint: als/ps intrs\n");
}
/*----------------------------------------------------------------------------*/
static void stk3x1x_eint_work(struct work_struct *work)
{
struct stk3x1x_priv *obj = stk3x1x_obj;
int err;
u8 flag_reg, disable_flag = 0;
APS_DBG(" eint work\n");
err = stk3x1x_check_intr(obj->client, &flag_reg);
if (err) {
APS_ERR("stk3x1x_check_intr fail: %d\n", err);
goto err_i2c_rw;
}
APS_DBG(" &obj->pending_intr =%lx\n", obj->pending_intr);
if (((1 << STK_BIT_ALS) & obj->pending_intr)
&& (obj->hw->polling_mode_als == 0)) {
/*get raw data*/
APS_DBG("stk als change\n");
disable_flag |= STK_FLG_ALSINT_MASK;
err = stk3x1x_read_als(obj->client, &obj->als);
if (err) {
APS_ERR("stk3x1x_read_als failed %d\n", err);
goto err_i2c_rw;
}
err = stk_als_int_handle(obj, obj->als);
if (err < 0)
goto err_i2c_rw;
}
if (((1 << STK_BIT_PS) & obj->pending_intr)
&& (obj->hw->polling_mode_ps == 0)) {
APS_DBG("stk ps change\n");
disable_flag |= STK_FLG_PSINT_MASK;
err = stk3x1x_read_ps(obj->client, &obj->ps);
if (err) {
APS_ERR("stk3x1x read ps data: %d\n", err);
goto err_i2c_rw;
}
#ifdef STK_PS_DEBUG
APS_DBG("%s: ps interrupt, show all reg\n", __func__);
show_allreg(); /*for debug*/
#endif
}
if (disable_flag) {
err = stk3x1x_clear_intr(obj->client, flag_reg, disable_flag);
if (err) {
APS_ERR("fail: %d\n", err);
goto err_i2c_rw;
}
}
msleep(20);
#if defined(CONFIG_OF)
enable_irq(obj->irq);
#endif
return;
err_i2c_rw:
msleep(30);
#if defined(CONFIG_OF)
enable_irq(obj->irq);
#endif
}
#if defined(CONFIG_OF)
static irqreturn_t stk3x1x_eint_handler(int irq, void *desc)
{
pr_info("%s\n", __func__);
disable_irq_nosync(stk3x1x_obj->irq);
stk3x1x_eint_func();
return IRQ_HANDLED;
}
#endif
/*----------------------------------------------------------------------------*/
int stk3x1x_setup_eint(struct i2c_client *client)
{
int ret;
struct pinctrl *pinctrl;
struct pinctrl_state *pins_default;
struct pinctrl_state *pins_cfg;
u32 ints[2] = { 0, 0 };
/* gpio setting */
pinctrl = devm_pinctrl_get(&client->dev);
if (IS_ERR(pinctrl)) {
ret = PTR_ERR(pinctrl);
APS_ERR("Cannot find alsps pinctrl!\n");
}
pins_default = pinctrl_lookup_state(pinctrl, "pin_default");
if (IS_ERR(pins_default)) {
ret = PTR_ERR(pins_default);
APS_ERR("Cannot find alsps pinctrl default!\n");
}
pins_cfg = pinctrl_lookup_state(pinctrl, "pin_cfg");
if (IS_ERR(pins_cfg)) {
ret = PTR_ERR(pins_cfg);
APS_ERR("Cannot find alsps pinctrl pin_cfg!\n");
}
/* eint request */
if (stk3x1x_obj->irq_node) {
of_property_read_u32_array(stk3x1x_obj->irq_node,
"debounce", ints, ARRAY_SIZE(ints));
gpio_request(ints[0], "p-sensor");
gpio_set_debounce(ints[0], ints[1]);
pinctrl_select_state(pinctrl, pins_cfg);
APS_DBG("ints[0] = %d, ints[1] = %d!!\n", ints[0], ints[1]);
stk3x1x_obj->irq =
irq_of_parse_and_map(stk3x1x_obj->irq_node, 0);
APS_DBG("stk3x1x_obj->irq = %d\n", stk3x1x_obj->irq);
if (!stk3x1x_obj->irq) {
APS_ERR("irq_of_parse_and_map fail!!\n");
return -EINVAL;
}
ret = request_irq(stk3x1x_obj->irq, stk3x1x_eint_handler,
IRQ_TYPE_LEVEL_LOW, "ALS-eint", NULL);
if (ret) {
APS_ERR("IRQ LINE NOT AVAILABLE!!\n");
return -EINVAL;
}
} else {
APS_ERR("null irq node!!\n");
return -EINVAL;
}
return 0;
}
/*----------------------------------------------------------------------------*/
static int stk3x1x_init_client(struct i2c_client *client)
{
struct stk3x1x_priv *obj = i2c_get_clientdata(client);
int err;
int ps_ctrl;
APS_LOG("%s: In\n", __func__);
err = stk3x1x_write_sw_reset(client);
if (err) {
APS_ERR("software reset error, err=%d", err);
return err;
}
err = stk3x1x_read_id(client);
if (err) {
APS_ERR("stk3x1x_read_id error, err=%d", err);
return err;
}
if (obj->first_boot == true) {
if (obj->hw->polling_mode_ps == 0
|| obj->hw->polling_mode_als == 0) {
err = stk3x1x_setup_eint(client);
if (err) {
APS_ERR("setup eint error: %d\n", err);
return err;
}
}
}
err = stk3x1x_write_state(client, atomic_read(&obj->state_val));
if (err) {
APS_ERR("write stete error: %d\n", err);
return err;
}
ps_ctrl = atomic_read(&obj->psctrl_val);
if (obj->hw->polling_mode_ps == 1)
ps_ctrl &= 0x3F;
err = stk3x1x_write_ps(client, ps_ctrl);
if (err) {
APS_ERR("write ps error: %d\n", err);
return err;
}
err = stk3x1x_write_als(client, atomic_read(&obj->alsctrl_val));
if (err) {
APS_ERR("write als error: %d\n", err);
return err;
}
err = stk3x1x_write_led(client, obj->ledctrl_val);
if (err) {
APS_ERR("write led error: %d\n", err);
return err;
}
err = stk3x1x_write_wait(client, obj->wait_val);
if (err) {
APS_ERR("write wait error: %d\n", err);
return err;
}
err = stk3x1x_write_int(client, obj->int_val);
if (err) {
APS_ERR("write int mode error: %d\n", err);
return err;
}
obj->re_enable_ps = false;
obj->re_enable_als = false;
obj->als_code_last = 100;
obj->ps_distance_last = -1;
obj->als_code_last = 100;
return 0;
}
/*----------------------------------------------------------------------------*/
static inline uint32_t stk3x1x_get_als_reading_avg(struct i2c_client *client,
int sSampleNo)
{
int res;
uint16_t ALSData = 0;
uint16_t DataCount = 0;
uint32_t sAveAlsData = 0;
while (DataCount < sSampleNo) {
msleep(100);
res = stk3x1x_read_als(stk3x1x_obj->client, &stk3x1x_obj->als);
if (res)
APS_ERR("get_als_reading_avg: stk3x1x_read_als!!\n");
ALSData = stk3x1x_obj->als;
APS_DBG("%s: [STK]als code = %d\n", __func__, ALSData);
sAveAlsData += ALSData;
DataCount++;
}
sAveAlsData /= sSampleNo;
return sAveAlsData;
}
/*
* Sysfs attributes
*/
static ssize_t config_show(struct device_driver *ddri, char *buf)
{
ssize_t res;
if (!stk3x1x_obj) {
APS_ERR("stk3x1x_obj is null!!\n");
return 0;
}
res = scnprintf(buf, PAGE_SIZE, "(%d %d %d %d %d %d)\n",
atomic_read(&stk3x1x_obj->i2c_retry),
atomic_read(&stk3x1x_obj->als_debounce),
atomic_read(&stk3x1x_obj->ps_mask),
atomic_read(&stk3x1x_obj->ps_high_thd_val),
atomic_read(&stk3x1x_obj->ps_low_thd_val),
atomic_read(&stk3x1x_obj->ps_debounce));
return res;
}
/*----------------------------------------------------------------------------*/
static ssize_t config_store(struct device_driver *ddri,
const char *buf, size_t count)
{
int retry, als_deb, ps_deb, mask, hthres, lthres, err;
struct i2c_client *client;
client = stk3x1x_i2c_client;
if (!stk3x1x_obj) {
APS_ERR("stk3x1x_obj is null!!\n");
return 0;
}
if (sscanf(buf, "%d %d %d %d %d %d",
&retry, &als_deb, &mask, &hthres, &lthres, &ps_deb) == 6) {
atomic_set(&stk3x1x_obj->i2c_retry, retry);
atomic_set(&stk3x1x_obj->als_debounce, als_deb);
atomic_set(&stk3x1x_obj->ps_mask, mask);
atomic_set(&stk3x1x_obj->ps_high_thd_val, hthres);
atomic_set(&stk3x1x_obj->ps_low_thd_val, lthres);
atomic_set(&stk3x1x_obj->ps_debounce, ps_deb);
err = stk3x1x_write_ps_high_thd(client,
atomic_read(&stk3x1x_obj->ps_high_thd_val));
if (err) {
APS_ERR("write high thd error: %d\n", err);
return err;
}
err = stk3x1x_write_ps_low_thd(client,
atomic_read(&stk3x1x_obj->ps_low_thd_val));
if (err) {
APS_ERR("write low thd error: %d\n", err);
return err;
}
} else {
APS_ERR("invalid content: '%s'\n", buf);
}
return count;
}
/*----------------------------------------------------------------------------*/
static ssize_t trace_show(struct device_driver *ddri, char *buf)
{
ssize_t res;
if (!stk3x1x_obj) {
APS_ERR("stk3x1x_obj is null!!\n");
return 0;
}
res = scnprintf(buf, PAGE_SIZE, "0x%04X\n",
atomic_read(&stk3x1x_obj->trace));
return res;
}
/*----------------------------------------------------------------------------*/
static ssize_t trace_store(struct device_driver *ddri,
const char *buf, size_t count)
{
int trace;
if (!stk3x1x_obj) {
APS_ERR("stk3x1x_obj is null!!\n");
return 0;
}
if (sscanf(buf, "0x%x", &trace) == 1)
atomic_set(&stk3x1x_obj->trace, trace);
else
APS_ERR("invalid content: '%s', count = %d\n", buf, (int)count);
return count;
}
/*----------------------------------------------------------------------------*/
static ssize_t ir_show(struct device_driver *ddri, char *buf)
{
int32_t reading;
if (!stk3x1x_obj) {
APS_ERR("stk3x1x_obj is null!!\n");
return 0;
}
reading = stk3x1x_get_ir_value(stk3x1x_obj, STK_IRS_IT_REDUCE);
if (reading < 0)
return scnprintf(buf, PAGE_SIZE, "ERROR: %d\n", reading);
stk3x1x_obj->ir_code = reading;
return scnprintf(buf, PAGE_SIZE, "0x%04X\n", stk3x1x_obj->ir_code);
}
/*----------------------------------------------------------------------------*/
static ssize_t als_show(struct device_driver *ddri, char *buf)
{
int res;
if (!stk3x1x_obj) {
APS_ERR("stk3x1x_obj is null!!\n");
return 0;
}
if (stk3x1x_obj->hw->polling_mode_als == 1) {
res = stk3x1x_read_als(stk3x1x_obj->client, &stk3x1x_obj->als);
if (res)
return scnprintf(buf, PAGE_SIZE, "ERROR: %d\n", res);
else
return scnprintf(buf, PAGE_SIZE,
"%d\n", stk3x1x_obj->als);
}
return scnprintf(buf, PAGE_SIZE, "%d\n", stk3x1x_obj->als_code_last);
}
/*----------------------------------------------------------------------------*/
static ssize_t ps_show(struct device_driver *ddri, char *buf)
{
int res;
if (!stk3x1x_obj) {
APS_ERR("stk3x1x_obj is null!!\n");
return 0;
}
res = stk3x1x_read_ps(stk3x1x_obj->client, &stk3x1x_obj->ps);
if (res)
return scnprintf(buf, PAGE_SIZE, "ERROR: %d\n", res);
else
return scnprintf(buf, PAGE_SIZE, "%d\n", stk3x1x_obj->ps);
}
/*----------------------------------------------------------------------------*/
static ssize_t reg_show(struct device_driver *ddri, char *buf)
{
u8 int_status;
if (!stk3x1x_obj) {
APS_ERR("stk3x1x_obj is null!!\n");
return 0;
}
/*read*/
stk3x1x_check_intr(stk3x1x_obj->client, &int_status);
stk3x1x_read_ps(stk3x1x_obj->client, &stk3x1x_obj->ps);
stk3x1x_read_als(stk3x1x_obj->client, &stk3x1x_obj->als);
/*write*/
stk3x1x_write_als(stk3x1x_obj->client,
atomic_read(&stk3x1x_obj->alsctrl_val));
stk3x1x_write_ps(stk3x1x_obj->client,
atomic_read(&stk3x1x_obj->psctrl_val));
stk3x1x_write_ps_high_thd(stk3x1x_obj->client,
atomic_read(&stk3x1x_obj->ps_high_thd_val));
stk3x1x_write_ps_low_thd(stk3x1x_obj->client,
atomic_read(&stk3x1x_obj->ps_low_thd_val));
return 0;
}
/*----------------------------------------------------------------------------*/
static ssize_t send_show(struct device_driver *ddri, char *buf)
{
return 0;
}
/*----------------------------------------------------------------------------*/
static ssize_t send_store(struct device_driver *ddri,
const char *buf, size_t count)
{
int addr, cmd;
u8 dat;
if (!stk3x1x_obj) {
APS_ERR("stk3x1x_obj is null!!\n");
return 0;
} else if (sscanf(buf, "%x %x", &addr, &cmd) != 2) {
APS_ERR("invalid format: '%s'\n", buf);
return 0;
}
dat = (u8)cmd;
APS_DBG("send(%02X, %02X) = %d\n", addr, cmd,
stk3x1x_master_send(stk3x1x_obj->client,
(u16)addr, &dat, sizeof(dat)));
return count;
}
/*----------------------------------------------------------------------------*/
static ssize_t recv_show(struct device_driver *ddri, char *buf)
{
if (!stk3x1x_obj) {
APS_ERR("stk3x1x_obj is null!!\n");
return 0;
}
return scnprintf(buf, PAGE_SIZE, "0x%04X\n",
atomic_read(&stk3x1x_obj->recv_reg));
}
/*----------------------------------------------------------------------------*/
static ssize_t recv_store(struct device_driver *ddri,
const char *buf, size_t count)
{
int addr;
u8 dat;
if (!stk3x1x_obj) {
APS_ERR("stk3x1x_obj is null!!\n");
return 0;
} else if (kstrtouint(buf, 16, &addr) != 0) {
APS_ERR("invalid format: '%s'\n", buf);
return 0;
}
APS_DBG("recv(%02X) = %d, 0x%02X\n", addr,
stk3x1x_master_recv(stk3x1x_obj->client,
(u16)addr, (char *)&dat, sizeof(dat)), dat);
atomic_set(&stk3x1x_obj->recv_reg, dat);
return count;
}
/*----------------------------------------------------------------------------*/
static ssize_t allreg_show(struct device_driver *ddri, char *buf)
{
int ret = 0;
u8 rbuf[0x22];
int cnt;
int len = 0;
memset(rbuf, 0, sizeof(rbuf));
if (!stk3x1x_obj) {
APS_ERR("stk3x1x_obj is null!!\n");
return 0;
}
ret = stk3x1x_master_recv(stk3x1x_obj->client, 0, &rbuf[0], 7);
if (ret < 0) {
APS_ERR("error: %d\n", ret);
return -EFAULT;
}
ret = stk3x1x_master_recv(stk3x1x_obj->client, 7, &rbuf[7], 7);
if (ret < 0) {
APS_ERR("error: %d\n", ret);
return -EFAULT;
}
ret = stk3x1x_master_recv(stk3x1x_obj->client, 14, &rbuf[14], 7);
if (ret < 0) {
APS_ERR("error: %d\n", ret);
return -EFAULT;
}
ret = stk3x1x_master_recv(stk3x1x_obj->client, 21, &rbuf[21], 7);
if (ret < 0) {
APS_ERR("error: %d\n", ret);
return -EFAULT;
}
ret = stk3x1x_master_recv(stk3x1x_obj->client, 28, &rbuf[28], 4);
if (ret < 0) {
APS_ERR("error: %d\n", ret);
return -EFAULT;
}
ret = stk3x1x_master_recv(stk3x1x_obj->client,
STK_PDT_ID_REG, &rbuf[32], 2);
if (ret < 0) {
APS_ERR("error: %d\n", ret);
return -EFAULT;
}
for (cnt = 0; cnt < 0x20; cnt++) {
APS_LOG("reg[0x%x]=0x%x\n", cnt, rbuf[cnt]);
len += scnprintf(buf + len, PAGE_SIZE - len,
"[%2X]%2X,", cnt, rbuf[cnt]);
}
APS_DBG("reg[0x3E]=0x%x\n", rbuf[cnt]);
APS_DBG("reg[0x3F]=0x%x\n", rbuf[cnt++]);
len += scnprintf(buf + len, PAGE_SIZE - len, "[0x3E]%2X,[0x3F]%2X\n",
rbuf[cnt - 1], rbuf[cnt]);
return len;
}
/*----------------------------------------------------------------------------*/
static ssize_t status_show(struct device_driver *ddri, char *buf)
{
ssize_t len = 0;
u8 rbuf[25];
int ret = 0;
if (!stk3x1x_obj) {
APS_ERR("stk3x1x_obj is null!!\n");
return 0;
}
if (stk3x1x_obj->hw) {
len += scnprintf(buf + len, PAGE_SIZE - len,
"CUST: %d, (%d %d) (%02X)(%02X %02X %02X) (%02X %02X %02X %02X)\n",
stk3x1x_obj->hw->i2c_num,
stk3x1x_obj->hw->power_id,
stk3x1x_obj->hw->power_vol,
stk3x1x_obj->addr.flag,
stk3x1x_obj->addr.alsctrl,
stk3x1x_obj->addr.data1_als,
stk3x1x_obj->addr.data2_als,
stk3x1x_obj->addr.psctrl,
stk3x1x_obj->addr.data1_ps,
stk3x1x_obj->addr.data2_ps,
stk3x1x_obj->addr.thdh1_ps);
} else {
len += scnprintf(buf + len, PAGE_SIZE - len, "CUST: NULL\n");
}
len += scnprintf(buf + len, PAGE_SIZE - len,
"REGS: %02X %02X %02X %02X %02X %02X %02X %02X %02lX %02lX\n",
atomic_read(&stk3x1x_obj->state_val),
atomic_read(&stk3x1x_obj->psctrl_val),
atomic_read(&stk3x1x_obj->alsctrl_val),
stk3x1x_obj->ledctrl_val, stk3x1x_obj->int_val,
stk3x1x_obj->wait_val,
atomic_read(&stk3x1x_obj->ps_high_thd_val),
atomic_read(&stk3x1x_obj->ps_low_thd_val),
stk3x1x_obj->enable,
stk3x1x_obj->pending_intr);
#ifdef MT6516
len += scnprintf(buf + len, PAGE_SIZE - len, "EINT: %d (%d %d %d %d)\n",
mt_get_gpio_in(GPIO_ALS_EINT_PIN),
CUST_EINT_ALS_NUM,
CUST_EINT_ALS_POLARITY,
CUST_EINT_ALS_DEBOUNCE_EN,
CUST_EINT_ALS_DEBOUNCE_CN);
len += scnprintf(buf + len, PAGE_SIZE - len, "GPIO: %d (%d %d %d %d)\n",
GPIO_ALS_EINT_PIN,
mt_get_gpio_dir(GPIO_ALS_EINT_PIN),
mt_get_gpio_mode(GPIO_ALS_EINT_PIN),
mt_get_gpio_pull_enable(GPIO_ALS_EINT_PIN),
mt_get_gpio_pull_select(GPIO_ALS_EINT_PIN));
#endif
len += scnprintf(buf + len, PAGE_SIZE - len, "MISC: %d %d\n",
atomic_read(&stk3x1x_obj->als_suspend),
atomic_read(&stk3x1x_obj->ps_suspend));
len += scnprintf(buf + len, PAGE_SIZE - len, "VER.: %s\n",
DRIVER_VERSION);
memset(rbuf, 0, sizeof(rbuf));
ret = stk3x1x_master_recv(stk3x1x_obj->client, 0, &rbuf[0], 7);
if (ret < 0) {
APS_ERR("error: %d\n", ret);
return -EFAULT;
}
ret = stk3x1x_master_recv(stk3x1x_obj->client, 7, &rbuf[7], 7);
if (ret < 0) {
APS_ERR("error: %d\n", ret);
return -EFAULT;
}
ret = stk3x1x_master_recv(stk3x1x_obj->client, 14, &rbuf[14], 7);
if (ret < 0) {
APS_ERR("error: %d\n", ret);
return -EFAULT;
}
len += scnprintf(buf + len, PAGE_SIZE - len,
"[PS=%2X] [ALS=%2X] [WAIT=%4Xms]\n",
rbuf[0] & 0x01, (rbuf[0] & 0x02) >> 1,
((rbuf[0] & 0x04) >> 2) * rbuf[5] * 6);
len += scnprintf(buf + len, PAGE_SIZE - len,
"[EN_ASO=%2X] [EN_AK=%2X] [NEAR/FAR=%2X]\n",
(rbuf[0] & 0x20) >> 5, (rbuf[0] & 0x40) >> 6,
rbuf[16] & 0x01);
len += scnprintf(buf + len, PAGE_SIZE - len,
"[FLAG_OUI=%2X] [FLAG_PSINT=%2X] [FLAG_ALSINT=%2X]\n",
(rbuf[16] & 0x04) >> 2,
(rbuf[16] & 0x10) >> 4, (rbuf[16] & 0x20) >> 5);
return len;
}
/*----------------------------------------------------------------------------*/
#define IS_SPACE(CH) (((CH) == ' ') || ((CH) == '\n'))
/*----------------------------------------------------------------------------*/
static ssize_t pscalibration_show(struct device_driver *ddri, char *buf)
{
if (!stk3x1x_obj) {
APS_ERR("stk3x1x_obj is null!!\n");
return 0;
}
return 0;
}
/*----------------------------------------------------------------------------*/
static ssize_t pscalibration_store(struct device_driver *ddri,
const char *buf, size_t count)
{
int ret;
ret = stk3x1x_ps_calibration(stk3x1x_obj->client);
return count;
}
/*----------------------------------------------------------------------------*/
static ssize_t pthredcalibration_show(struct device_driver *ddri,
char *buf)
{
return 0;
}
static ssize_t ps_offset_store(struct device_driver *ddri,
const char *buf, size_t count)
{
return count;
}
/*----------------------------------------------------------------------------*/
static ssize_t enable_show(struct device_driver *ddri, char *buf)
{
int32_t enable;
u8 r_buf;
int ret;
ret = stk3x1x_master_recv(stk3x1x_obj->client,
STK_STATE_REG, &r_buf, 0x01);
if (ret < 0) {
APS_ERR("error: %d\n", ret);
return -EFAULT;
}
enable = (r_buf & STK_STATE_EN_ALS_MASK) ? 1 : 0;
return scnprintf(buf, PAGE_SIZE, "%d\n", enable);
}
static ssize_t enable_store(struct device_driver *ddri,
const char *buf, size_t size)
{
uint8_t en;
if (sysfs_streq(buf, "1")) {
en = 1;
} else if (sysfs_streq(buf, "0")) {
en = 0;
} else {
APS_ERR("%s, invalid value %d\n", __func__, *buf);
return -EINVAL;
}
APS_LOG("%s: Enable ALS : %d\n", __func__, en);
stk3x1x_enable_als(stk3x1x_obj->client, en);
return size;
}
/*----------------------------------------------------------------------------*/
static ssize_t alstest_show(struct device_driver *ddri, char *buf)
{
int32_t als_reading;
APS_LOG("%s: [STK]Start testing light...\n", __func__);
msleep(150);
als_reading = stk3x1x_get_als_reading_avg(stk3x1x_obj->client, 5);
APS_LOG("%s: [STK]als_reading = %d\n", __func__, als_reading);
als_value = stk_alscode2lux(stk3x1x_obj, als_reading);
APS_LOG("%s: [STK]Start testing light done!!! als_value = %d\n",
__func__, als_value);
APS_LOG("%s: [STK]Start testing light done!!! als_test_adc = %d\n",
__func__, als_reading);
return scnprintf(buf, PAGE_SIZE,
"als_value = %5d lux, cci_als_test_adc = %5d\n",
als_value, als_reading);
}
/*----------------------------------------------------------------------------*/
static ssize_t alscali_show(struct device_driver *ddri, char *buf)
{
int32_t als_reading;
unsigned int als_adc_cali;
bool result = true;
int als_calibration_lux;
APS_LOG("%s: [STK]Start Cali light...\n", __func__);
als_calibration_lux = ALS_CALIBRATION_LUX;
msleep(150);
als_reading = stk3x1x_get_als_reading_avg(stk3x1x_obj->client, 5);
als_adc_cali = als_reading;
als_value_cali = stk_alscode2lux(stk3x1x_obj, als_reading);
if (((als_value_cali * stk3x1x_obj->als_transmittance)
/ als_calibration_lux) > 0 && (als_adc_cali <= 65535)) {
transmittance_cali = (als_value_cali
* stk3x1x_obj->als_transmittance) / als_calibration_lux;
/* transmittance for cali */
stk3x1x_obj->als_transmittance = transmittance_cali;
/* calculate lux base on calibrated transmittance */
als_value_cali = stk_alscode2lux(stk3x1x_obj, als_reading);
APS_LOG("%s: [STK]cali light done!!! als_value_cali = %d lux\n",
__func__, als_value_cali);
APS_LOG("%s:[STK]cali light done!!! transmittance_cali = %d\n",
__func__, transmittance_cali);
APS_LOG("%s:[STK]cali light done!!! als_adc_cali = %d code\n",
__func__, als_adc_cali);
} else {
APS_LOG("%s: [STK]cali light fail!!! als_value_cali = %d lux\n",
__func__, als_value_cali);
APS_LOG("%s:[STK]cali light fail!!! transmittance_cali = %d\n",
__func__, transmittance_cali);
APS_LOG("%s:[STK]cali light fail!!! als_adc_cali = %d code\n",
__func__, als_adc_cali);
result = false;
}
/* compute transmittance for ALS calibration end */
return scnprintf(buf, PAGE_SIZE,
"%s: cali =%d lux, transmittance_cali= %d,adc_cali= %d code\n",
result ? "PASSED" : "FAIL", als_value_cali,
transmittance_cali, als_adc_cali);
}
/*----------------------------------------------------------------------------*/
static DRIVER_ATTR_RO(als);
static DRIVER_ATTR_RO(ps);
static DRIVER_ATTR_RO(ir);
static DRIVER_ATTR_RW(config);
static DRIVER_ATTR_RW(trace);
static DRIVER_ATTR_RO(status);
static DRIVER_ATTR_RW(send);
static DRIVER_ATTR_RW(recv);
static DRIVER_ATTR_RO(reg);
static DRIVER_ATTR_RO(allreg);
static DRIVER_ATTR_RW(pscalibration);
static DRIVER_ATTR_RO(pthredcalibration);
static DRIVER_ATTR_WO(ps_offset);
/*----------------------------------------------------------------------------*/
static DRIVER_ATTR_RW(enable);
static DRIVER_ATTR_RO(alstest);
static DRIVER_ATTR_RO(alscali);
/*----------------------------------------------------------------------------*/
static struct driver_attribute *stk3x1x_attr_list[] = {
&driver_attr_als,
&driver_attr_ps,
&driver_attr_ir,
&driver_attr_trace, /*trace log*/
&driver_attr_config,
&driver_attr_status,
&driver_attr_send,
&driver_attr_recv,
&driver_attr_allreg,
&driver_attr_reg,
&driver_attr_pscalibration,
&driver_attr_pthredcalibration,
&driver_attr_ps_offset,
&driver_attr_enable,
&driver_attr_alstest,
&driver_attr_alscali,
};
/*----------------------------------------------------------------------------*/
static int stk3x1x_create_attr(struct device_driver *driver)
{
int idx, err = 0;
int num = (int)(sizeof(stk3x1x_attr_list) /
sizeof(stk3x1x_attr_list[0]));
if (driver == NULL)
return -EINVAL;
for (idx = 0; idx < num; idx++) {
err = driver_create_file(driver, stk3x1x_attr_list[idx]);
if (err) {
APS_ERR("driver_create_file (%s) = %d\n",
stk3x1x_attr_list[idx]->attr.name, err);
break;
}
}
return err;
}
/*----------------------------------------------------------------------------*/
static int stk3x1x_delete_attr(struct device_driver *driver)
{
int idx, err = 0;
int num = (int)(sizeof(stk3x1x_attr_list) /
sizeof(stk3x1x_attr_list[0]));
if (!driver)
return -EINVAL;
for (idx = 0; idx < num; idx++)
driver_remove_file(driver, stk3x1x_attr_list[idx]);
return err;
}
/*
* Function Configuration
*/
static uint32_t stk_alscode2lux(struct stk3x1x_priv *obj, uint32_t alscode)
{
/*
* Lux = ALS ADC data / Display Transmittance
* Formula for ADC to LUX
*/
alscode += ((alscode<<7)+(alscode<<3)+(alscode>>1));
alscode <<= 3;
alscode /= obj->als_transmittance;
return alscode;
}
/*----------------------------------------------------------------------------*/
static int stk3x1x_get_ps_value_only(struct stk3x1x_priv *obj, u16 ps)
{
int mask = atomic_read(&obj->ps_mask);
int invalid = 0, val;
int err;
u8 flag;
err = stk3x1x_read_flag(obj->client, &flag);
if (err)
return err;
val = (flag & STK_FLG_NF_MASK) ? 1 : 0;
if (atomic_read(&obj->ps_suspend)) {
invalid = 1;
} else if (atomic_read(&obj->ps_deb_on) == 1) {
unsigned long endt = atomic_read(&obj->ps_deb_end);
if (time_after(jiffies, endt))
atomic_set(&obj->ps_deb_on, 0);
if (atomic_read(&obj->ps_deb_on) == 1)
invalid = 1;
}
if (!invalid) {
if (unlikely(atomic_read(&obj->trace) & STK_TRC_CVT_PS)) {
if (mask)
APS_DBG("PS: %05d => %05d [M]\n", ps, val);
else
APS_DBG("PS: %05d => %05d\n", ps, val);
}
return val;
}
APS_ERR(" ps value is invalid, PS: %05d => %05d\n", ps, val);
if (unlikely(atomic_read(&obj->trace) & STK_TRC_CVT_PS))
APS_ERR("PS: %05d => %05d (-1)\n", ps, val);
return -1;
}
/*----------------------------------------------------------------------------*/
static int stk3x1x_get_ps_value(struct stk3x1x_priv *obj, u16 ps)
{
int mask = atomic_read(&obj->ps_mask);
int invalid = 0, val;
int err;
u8 flag;
err = stk3x1x_read_flag(obj->client, &flag);
if (err)
return err;
val = (flag & STK_FLG_NF_MASK) ? 1 : 0;
APS_DBG("%s: read_flag = 0x%x, val = %d\n", __func__, flag, val);
if (atomic_read(&obj->ps_suspend)) {
invalid = 1;
} else if (atomic_read(&obj->ps_deb_on) == 1) {
unsigned long endt = atomic_read(&obj->ps_deb_end);
if (time_after(jiffies, endt))
atomic_set(&obj->ps_deb_on, 0);
if (atomic_read(&obj->ps_deb_on) == 1)
invalid = 1;
}
if (!invalid) {
if (unlikely(atomic_read(&obj->trace) & STK_TRC_CVT_PS)) {
if (mask)
APS_DBG("PS: %05d => %05d [M]\n", ps, val);
else
APS_DBG("PS: %05d => %05d\n", ps, val);
}
return val;
}
APS_ERR("ps value is invalid, PS: %05d => %05d\n", ps, val);
if (unlikely(atomic_read(&obj->trace) & STK_TRC_CVT_PS))
APS_ERR("PS: %05d => %05d (-1)\n", ps, val);
return -1;
}
/*----------------------------------------------------------------------------*/
static int32_t stk3x1x_set_irs_it_slp(struct stk3x1x_priv *obj,
uint16_t *slp_time, int32_t ials_it_reduce)
{
uint8_t irs_alsctrl;
int32_t ret;
irs_alsctrl = (atomic_read(&obj->alsctrl_val) & 0x0F) - ials_it_reduce;
switch (irs_alsctrl) {
case 2:
*slp_time = 1;
break;
case 3:
*slp_time = 2;
break;
case 4:
*slp_time = 3;
break;
case 5:
*slp_time = 6;
break;
case 6:
*slp_time = 12;
break;
case 7:
*slp_time = 24;
break;
case 8:
*slp_time = 48;
break;
case 9:
*slp_time = 96;
break;
case 10:
*slp_time = 192;
break;
default:
APS_ERR("%s: unknown ALS IT=0x%x\n", __func__, irs_alsctrl);
ret = -EINVAL;
return ret;
}
irs_alsctrl |= (atomic_read(&obj->alsctrl_val) & 0xF0);
ret = i2c_smbus_write_byte_data(obj->client,
STK_ALSCTRL_REG, irs_alsctrl);
if (ret < 0) {
APS_ERR("%s: write i2c error\n", __func__);
return ret;
}
return 0;
}
static int32_t stk3x1x_get_ir_value(struct stk3x1x_priv *obj,
int32_t als_it_reduce)
{
int32_t word_data, ret;
uint8_t w_reg, retry = 0;
uint16_t irs_slp_time = 100;
u8 flag;
u8 buf[2];
ret = stk3x1x_set_irs_it_slp(obj, &irs_slp_time, als_it_reduce);
if (ret < 0)
goto irs_err_i2c_rw;
w_reg = atomic_read(&obj->state_val) | STK_STATE_EN_IRS_MASK;
ret = i2c_smbus_write_byte_data(obj->client, STK_STATE_REG, w_reg);
if (ret < 0) {
APS_ERR("%s: write i2c error\n", __func__);
goto irs_err_i2c_rw;
}
msleep(irs_slp_time);
do {
msleep(20);
ret = stk3x1x_read_flag(obj->client, &flag);
if (ret < 0) {
APS_ERR("WARNING: read flag reg error: %d\n", ret);
goto irs_err_i2c_rw;
}
retry++;
} while (retry < 10 && ((flag & STK_FLG_IR_RDY_MASK) == 0));
if (retry == 10) {
APS_ERR("%s: ir data is not ready for a long time\n", __func__);
ret = -EINVAL;
goto irs_err_i2c_rw;
}
ret = stk3x1x_clear_intr(obj->client, flag, STK_FLG_IR_RDY_MASK);
if (ret < 0) {
APS_ERR("%s: write i2c error\n", __func__);
goto irs_err_i2c_rw;
}
ret = stk3x1x_master_recv(obj->client, STK_DATA1_IR_REG, buf, 2);
if (ret < 0) {
APS_ERR("%s fail, ret=0x%x", __func__, ret);
goto irs_err_i2c_rw;
}
word_data = (buf[0] << 8) | buf[1];
ret = i2c_smbus_write_byte_data(obj->client, STK_ALSCTRL_REG,
atomic_read(&obj->alsctrl_val));
if (ret < 0) {
APS_ERR("%s: write i2c error\n", __func__);
goto irs_err_i2c_rw;
}
return word_data;
irs_err_i2c_rw:
return ret;
}
/*
* Function Configuration
*/
static int stk3x1x_open(struct inode *inode, struct file *file)
{
file->private_data = stk3x1x_i2c_client;
if (!file->private_data) {
APS_ERR("null pointer!!\n");
return -EINVAL;
}
return nonseekable_open(inode, file);
}
/*----------------------------------------------------------------------------*/
static int stk3x1x_release(struct inode *inode, struct file *file)
{
file->private_data = NULL;
return 0;
}
/*----------------------------------------------------------------------------*/
static long stk3x1x_unlocked_ioctl(struct file *file,
unsigned int cmd, unsigned long arg)
{
struct i2c_client *client = (struct i2c_client *)file->private_data;
struct stk3x1x_priv *obj = i2c_get_clientdata(client);
long err = 0;
void __user *ptr = (void __user *) arg;
int dat;
uint32_t enable;
int ps_result, ps_cali_result;
int threshold[2];
switch (cmd) {
case ALSPS_SET_PS_MODE:
if (copy_from_user(&enable, ptr, sizeof(enable))) {
err = -EFAULT;
goto err_out;
}
if (enable) {
err = stk3x1x_enable_ps(obj->client, 1, 1);
if (err) {
APS_ERR("enable ps fail: %ld\n", err);
goto err_out;
}
set_bit(STK_BIT_PS, &obj->enable);
} else {
err = stk3x1x_enable_ps(obj->client, 0, 1);
if (err) {
APS_ERR("disable ps fail: %ld\n", err);
goto err_out;
}
clear_bit(STK_BIT_PS, &obj->enable);
}
break;
case ALSPS_GET_PS_MODE:
enable = test_bit(STK_BIT_PS, &obj->enable) ? (1) : (0);
if (copy_to_user(ptr, &enable, sizeof(enable))) {
err = -EFAULT;
goto err_out;
}
break;
case ALSPS_GET_PS_DATA:
err = stk3x1x_read_ps(obj->client, &obj->ps);
if (err)
goto err_out;
dat = stk3x1x_get_ps_value(obj, obj->ps);
if (dat < 0) {
err = dat;
goto err_out;
}
if (copy_to_user(ptr, &dat, sizeof(dat))) {
err = -EFAULT;
goto err_out;
}
break;
case ALSPS_GET_PS_RAW_DATA:
err = stk3x1x_read_ps(obj->client, &obj->ps);
if (err)
goto err_out;
dat = obj->ps;
if (copy_to_user(ptr, &dat, sizeof(dat))) {
err = -EFAULT;
goto err_out;
}
break;
case ALSPS_SET_ALS_MODE:
if (copy_from_user(&enable, ptr, sizeof(enable))) {
err = -EFAULT;
goto err_out;
}
if (enable) {
err = stk3x1x_enable_als(obj->client, 1);
if (err) {
APS_ERR("enable als fail: %ld\n", err);
goto err_out;
}
set_bit(STK_BIT_ALS, &obj->enable);
} else {
err = stk3x1x_enable_als(obj->client, 0);
if (err) {
APS_ERR("disable als fail: %ld\n", err);
goto err_out;
}
clear_bit(STK_BIT_ALS, &obj->enable);
}
break;
case ALSPS_GET_ALS_MODE:
enable = test_bit(STK_BIT_ALS, &obj->enable) ? (1) : (0);
if (copy_to_user(ptr, &enable, sizeof(enable))) {
err = -EFAULT;
goto err_out;
}
break;
case ALSPS_GET_ALS_DATA:
err = stk3x1x_read_als(obj->client, &obj->als);
if (err)
goto err_out;
dat = stk_alscode2lux(obj, obj->als);
if (copy_to_user(ptr, &dat, sizeof(dat))) {
err = -EFAULT;
goto err_out;
}
break;
case ALSPS_GET_ALS_RAW_DATA:
err = stk3x1x_read_als(obj->client, &obj->als);
if (err)
goto err_out;
dat = obj->als;
if (copy_to_user(ptr, &dat, sizeof(dat))) {
err = -EFAULT;
goto err_out;
}
break;
case ALSPS_GET_PS_THRESHOLD_HIGH:
dat = atomic_read(&obj->ps_high_thd_val);
APS_LOG("%s:ps_high_thd_val:%d\n", __func__, dat);
if (copy_to_user(ptr, &dat, sizeof(dat))) {
err = -EFAULT;
goto err_out;
}
break;
case ALSPS_GET_PS_THRESHOLD_LOW:
dat = atomic_read(&obj->ps_low_thd_val);
APS_LOG("%s:ps_low_thd_val:%d\n", __func__, dat);
if (copy_to_user(ptr, &dat, sizeof(dat))) {
err = -EFAULT;
goto err_out;
}
break;
case ALSPS_GET_PS_TEST_RESULT:
err = stk3x1x_read_ps(obj->client, &obj->ps);
if (err)
goto err_out;
if (obj->ps > atomic_read(&obj->ps_high_thd_val))
ps_result = 0;
else
ps_result = 1;
APS_LOG("ALSPS_GET_PS_TEST: ps_result = %d\n", ps_result);
if (copy_to_user(ptr, &ps_result, sizeof(ps_result))) {
err = -EFAULT;
goto err_out;
}
break;
case ALSPS_IOCTL_CLR_CALI:
if (copy_from_user(&dat, ptr, sizeof(dat))) {
err = -EFAULT;
goto err_out;
}
obj->ps_cali = 0;
atomic_set(&obj->ps_high_thd_val, obj->hw->ps_threshold_high);
atomic_set(&obj->ps_low_thd_val, obj->hw->ps_threshold_low);
APS_LOG("ALSPS_IOCTL_CLR_CAL : ps_cali:%d high:%d low:%d\n",
obj->ps_cali,
atomic_read(&obj->ps_high_thd_val),
atomic_read(&obj->ps_low_thd_val));
break;
case ALSPS_IOCTL_GET_CALI:
stk3x1x_ps_calibration(obj->client);
ps_cali_result = obj->ps_cali;
APS_LOG("ALSPS_IOCTL_GET_CAL : ps_cali = %d\n", obj->ps_cali);
if (copy_to_user(ptr, &ps_cali_result,
sizeof(ps_cali_result))) {
err = -EFAULT;
goto err_out;
}
break;
case ALSPS_IOCTL_SET_CALI:
/*1. libhwm.so calc value store in ps_cali;*/
/*2. nvram_daemon update ps_cali in driver*/
if (copy_from_user(&ps_cali_result, ptr,
sizeof(ps_cali_result))) {
err = -EFAULT;
goto err_out;
}
obj->ps_cali = ps_cali_result;
atomic_set(&obj->ps_high_thd_val, obj->ps_cali + 200);
atomic_set(&obj->ps_low_thd_val, obj->ps_cali + 150);
err = stk3x1x_write_ps_high_thd(obj->client,
atomic_read(&obj->ps_high_thd_val));
if (err)
goto err_out;
err = stk3x1x_write_ps_low_thd(obj->client,
atomic_read(&obj->ps_low_thd_val));
if (err)
goto err_out;
APS_LOG("ALSPS_IOCTL_SET_CAL :ps_cali_result = %d\n",
ps_cali_result);
APS_LOG("ALSPS_IOCTL_SET_CAL :obj->ps_cali:%d high:%d low:%d\n",
obj->ps_cali,
atomic_read(&obj->ps_high_thd_val),
atomic_read(&obj->ps_low_thd_val));
break;
case ALSPS_SET_PS_THRESHOLD:
if (copy_from_user(threshold, ptr, sizeof(threshold))) {
err = -EFAULT;
goto err_out;
}
APS_ERR("%s set threshold high: 0x%x, low: 0x%x\n",
__func__, threshold[0], threshold[1]);
atomic_set(&obj->ps_high_thd_val,
(threshold[0] + obj->ps_cali));
atomic_set(&obj->ps_low_thd_val,
(threshold[1] + obj->ps_cali));
err = stk3x1x_write_ps_high_thd(obj->client,
atomic_read(&obj->ps_high_thd_val));
if (err) {
APS_ERR("write high thd error: %ld\n", err);
goto err_out;
}
err = stk3x1x_write_ps_low_thd(obj->client,
atomic_read(&obj->ps_low_thd_val));
if (err) {
APS_ERR("write low thd error: %ld\n", err);
goto err_out;
}
break;
default:
APS_ERR("%s not supported = 0x%04x", __func__, cmd);
err = -ENOIOCTLCMD;
break;
}
err_out:
return err;
}
/*----------------------------------------------------------------------------*/
static const struct file_operations stk3x1x_fops = {
.open = stk3x1x_open,
.release = stk3x1x_release,
.unlocked_ioctl = stk3x1x_unlocked_ioctl,
};
/*----------------------------------------------------------------------------*/
static struct miscdevice stk3x1x_device = {
.minor = MISC_DYNAMIC_MINOR,
.name = "als_ps",
.fops = &stk3x1x_fops,
};
/*----------------------------------------------------------------------------*/
#ifdef CONFIG_PM_SLEEP
static int stk3x1x_suspend(struct device *dev)
{
struct stk3x1x_priv *obj = dev_get_drvdata(dev);
int err = 0;
APS_FUN();
if (!obj) {
APS_ERR("null pointer!!\n");
return -EINVAL;
}
atomic_set(&obj->als_suspend, 1);
err = stk3x1x_enable_als(obj->client, 0);
if (err) {
APS_ERR("disable als fail: %d\n", err);
return err;
}
atomic_set(&obj->ps_suspend, 1);
err = stk3x1x_enable_ps(obj->client, 0, 1);
if (err) {
APS_ERR("disable ps fail: %d\n", err);
return err;
}
return 0;
}
static int stk3x1x_resume(struct device *dev)
{
struct stk3x1x_priv *obj = dev_get_drvdata(dev);
int err = 0;
APS_FUN();
if (!obj) {
APS_ERR("null pointer!!\n");
return -EINVAL;
}
err = stk3x1x_init_client(obj->client);
if (err) {
APS_ERR("initialize client fail!!\n");
return err;
}
err = stk3x1x_enable_als(obj->client, 1);
if (err) {
APS_ERR("enable als fail: %d\n", err);
return err;
}
atomic_set(&obj->als_suspend, 0);
err = stk3x1x_enable_ps(obj->client, 1, 1);
if (err) {
APS_ERR("enable ps fail: %d\n", err);
return err;
}
atomic_set(&obj->ps_suspend, 0);
return 0;
}
#endif
/*----------------------------------------------------------------------------*/
static int stk3x1x_i2c_detect(struct i2c_client *client,
struct i2c_board_info *info)
{
strcpy(info->type, stk3x1x_DEV_NAME);
return 0;
}
/*----------------------------------------------------------------------------*/
static int als_open_report_data(int open)
{
return 0;
}
static int als_enable_nodata(int en)
{
int res = 0;
#ifdef CUSTOM_KERNEL_SENSORHUB
SCP_SENSOR_HUB_DATA req;
int len;
#endif /*#ifdef CUSTOM_KERNEL_SENSORHUB*/
APS_LOG("stk3x1x_obj als enable value = %d\n", en);
#ifdef CUSTOM_KERNEL_SENSORHUB
req.activate_req.sensorType = ID_LIGHT;
req.activate_req.action = SENSOR_HUB_ACTIVATE;
req.activate_req.enable = en;
len = sizeof(req.activate_req);
res = SCP_sensorHub_req_send(&req, &len, 1);
#else /*#ifdef CUSTOM_KERNEL_SENSORHUB*/
if (!stk3x1x_obj) {
APS_ERR("stk3x1x_obj is null!!\n");
return -1;
}
res = stk3x1x_enable_als(stk3x1x_obj->client, en);
#endif /*#ifdef CUSTOM_KERNEL_SENSORHUB*/
if (res) {
APS_ERR("%s is failed!!\n", __func__);
return -1;
}
return 0;
}
static int als_set_delay(u64 ns)
{
/* TODO */
return 0;
}
static int als_batch(int flag, int64_t samplingPeriodNs,
int64_t maxBatchReportLatencyNs)
{
return als_set_delay(samplingPeriodNs);
}
static int als_flush(void)
{
return als_flush_report();
}
static int als_get_data(int *value, int *status)
{
int err = 0;
#ifdef CUSTOM_KERNEL_SENSORHUB
SCP_SENSOR_HUB_DATA req;
int len;
#else
struct stk3x1x_priv *obj = NULL;
#endif /*#ifdef CUSTOM_KERNEL_SENSORHUB*/
#ifdef CUSTOM_KERNEL_SENSORHUB
req.get_data_req.sensorType = ID_LIGHT;
req.get_data_req.action = SENSOR_HUB_GET_DATA;
len = sizeof(req.get_data_req);
err = SCP_sensorHub_req_send(&req, &len, 1);
if (err) {
APS_ERR("SCP_sensorHub_req_send fail!\n");
} else {
*value = req.get_data_rsp.int16_Data[0];
*status = SENSOR_STATUS_ACCURACY_MEDIUM;
}
if (atomic_read(&stk3x1x_obj->trace) & CMC_TRC_PS_DATA) {
APS_DBG("value = %d\n", *value);
/*show data*/
}
#else /*#ifdef CUSTOM_KERNEL_SENSORHUB*/
if (!stk3x1x_obj) {
APS_ERR("stk3x1x_obj is null!!\n");
return -1;
}
obj = stk3x1x_obj;
err = stk3x1x_read_als(obj->client, &obj->als);
if (err) {
err = -1;
goto out;
}
*value = stk_alscode2lux(obj, obj->als);
*status = SENSOR_STATUS_ACCURACY_MEDIUM;
#endif /*#ifdef CUSTOM_KERNEL_SENSORHUB*/
out:
return err;
}
static int ps_open_report_data(int open)
{
return 0;
}
static int ps_enable_nodata(int en)
{
int res = 0;
#ifdef CUSTOM_KERNEL_SENSORHUB
SCP_SENSOR_HUB_DATA req;
int len;
#endif /*#ifdef CUSTOM_KERNEL_SENSORHUB*/
APS_LOG("stk3x1x_obj ps enable value = %d\n", en);
#ifdef CUSTOM_KERNEL_SENSORHUB
req.activate_req.sensorType = ID_PROXIMITY;
req.activate_req.action = SENSOR_HUB_ACTIVATE;
req.activate_req.enable = en;
len = sizeof(req.activate_req);
res = SCP_sensorHub_req_send(&req, &len, 1);
#else /*#ifdef CUSTOM_KERNEL_SENSORHUB*/
if (!stk3x1x_obj) {
APS_ERR("stk3x1x_obj is null!!\n");
return -1;
}
res = stk3x1x_enable_ps(stk3x1x_obj->client, en, 1);
if (res) {
APS_ERR("als_enable_nodata is failed!!\n");
return -1;
}
#endif /*#ifdef CUSTOM_KERNEL_SENSORHUB*/
return 0;
}
static int ps_set_delay(u64 ns)
{
return 0;
}
static int ps_batch(int flag, int64_t samplingPeriodNs,
int64_t maxBatchReportLatencyNs)
{
return 0;
}
static int ps_flush(void)
{
return ps_flush_report();
}
static int ps_get_data(int *value, int *status)
{
int err = 0;
#ifdef CUSTOM_KERNEL_SENSORHUB
SCP_SENSOR_HUB_DATA req;
int len;
#endif /*#ifdef CUSTOM_KERNEL_SENSORHUB*/
#ifdef CUSTOM_KERNEL_SENSORHUB
req.get_data_req.sensorType = ID_PROXIMITY;
req.get_data_req.action = SENSOR_HUB_GET_DATA;
len = sizeof(req.get_data_req);
err = SCP_sensorHub_req_send(&req, &len, 1);
if (err) {
APS_ERR("SCP_sensorHub_req_send fail!\n");
} else {
*value = req.get_data_rsp.int16_Data[0];
*status = SENSOR_STATUS_ACCURACY_MEDIUM;
}
if (atomic_read(&stk3x1x_obj->trace) & CMC_TRC_PS_DATA) {
APS_DBG("value = %d\n", *value);
/*show data*/
}
#else /*#ifdef CUSTOM_KERNEL_SENSORHUB*/
if (!stk3x1x_obj) {
APS_ERR("stk3x1x_obj is null!!\n");
return -1;
}
err = stk3x1x_read_ps(stk3x1x_obj->client, &stk3x1x_obj->ps);
if (err) {
err = -1;
} else {
*value = stk3x1x_get_ps_value(stk3x1x_obj, stk3x1x_obj->ps);
*status = SENSOR_STATUS_ACCURACY_MEDIUM;
}
#endif /*#ifdef CUSTOM_KERNEL_SENSORHUB*/
return 0;
}
/*----------------------------------------------------------------------------*/
static int stk3x1x_i2c_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct stk3x1x_priv *obj = NULL;
int err = 0;
struct als_control_path als_ctl = { 0 };
struct als_data_path als_data = { 0 };
struct ps_control_path ps_ctl = { 0 };
struct ps_data_path ps_data = { 0 };
APS_LOG("%s: driver version: %s\n", __func__, DRIVER_VERSION);
err = get_alsps_dts_func(client->dev.of_node, hw);
if (err < 0) {
APS_ERR("get customization info from dts failed\n");
goto exit;
}
obj = kzalloc(sizeof(*obj), GFP_KERNEL);
if (!obj) {
err = -ENOMEM;
goto exit;
}
memset(obj, 0, sizeof(*obj));
stk3x1x_obj = obj;
obj->hw = hw;
stk3x1x_get_addr(obj->hw, &obj->addr);
INIT_DELAYED_WORK(&obj->eint_work, stk3x1x_eint_work);
client->addr = *hw->i2c_addr;
obj->client = client;
i2c_set_clientdata(client, obj);
atomic_set(&obj->als_debounce, 200);
atomic_set(&obj->als_deb_on, 0);
atomic_set(&obj->als_deb_end, 0);
atomic_set(&obj->ps_debounce, 10);
atomic_set(&obj->ps_deb_on, 0);
atomic_set(&obj->ps_deb_end, 0);
atomic_set(&obj->ps_mask, 0);
atomic_set(&obj->trace, 0x00);
atomic_set(&obj->als_suspend, 0);
atomic_set(&obj->init_done, 0);
obj->irq_node = client->dev.of_node;
atomic_set(&obj->state_val, 0);
atomic_set(&obj->psctrl_val, 0x30); /*0x31*/
atomic_set(&obj->alsctrl_val, 0x39);
obj->ledctrl_val = 0xFF;
obj->wait_val = 0xF;
obj->int_val = 0;
obj->first_boot = true;
obj->als_correct_factor = 1000;
atomic_set(&obj->ps_high_thd_val, obj->hw->ps_threshold_high);
atomic_set(&obj->ps_low_thd_val, obj->hw->ps_threshold_low);
atomic_set(&obj->recv_reg, 0);
if (obj->hw->polling_mode_ps == 0)
APS_LOG("%s: enable PS interrupt\n", __func__);
obj->int_val |= STK_INT_PS_MODE1;
if (obj->hw->polling_mode_als == 0) {
obj->int_val |= STK_INT_ALS;
APS_LOG("%s: enable ALS interrupt\n", __func__);
}
obj->enable = 0;
obj->pending_intr = 0;
atomic_set(&obj->i2c_retry, 3);
if (atomic_read(&obj->state_val) & STK_STATE_EN_ALS_MASK)
set_bit(STK_BIT_ALS, &obj->enable);
if (atomic_read(&obj->state_val) & STK_STATE_EN_PS_MASK)
set_bit(STK_BIT_PS, &obj->enable);
stk3x1x_i2c_client = client;
err = stk3x1x_init_client(client);
if (err) {
APS_ERR("stk3x1x init client failed\n");
goto exit_init_failed;
}
err = misc_register(&stk3x1x_device);
if (err) {
APS_ERR("stk3x1x_device register failed\n");
goto exit_misc_device_register_failed;
}
err =
stk3x1x_create_attr(&(stk3x1x_init_info.platform_diver_addr->driver));
if (err) {
APS_ERR("create attribute err = %d\n", err);
goto exit_create_attr_failed;
}
als_ctl.open_report_data = als_open_report_data;
als_ctl.enable_nodata = als_enable_nodata;
als_ctl.set_delay = als_set_delay;
als_ctl.batch = als_batch;
als_ctl.flush = als_flush;
als_ctl.is_report_input_direct = false;
als_ctl.is_use_common_factory = false;
if (obj->hw->polling_mode_als == 1)
als_ctl.is_polling_mode = true;
else
als_ctl.is_polling_mode = false;
#ifdef CUSTOM_KERNEL_SENSORHUB
als_ctl.is_support_batch = obj->hw->is_batch_supported_als;
#else
als_ctl.is_support_batch = false;
#endif
err = als_register_control_path(&als_ctl);
if (err) {
APS_ERR("als_control register fail = %d\n", err);
goto exit_sensor_obj_attach_fail;
}
als_data.get_data = als_get_data;
als_data.vender_div = 100;
err = als_register_data_path(&als_data);
if (err) {
APS_ERR("als_data register fail = %d\n", err);
goto exit_sensor_obj_attach_fail;
}
ps_ctl.open_report_data = ps_open_report_data;
ps_ctl.enable_nodata = ps_enable_nodata;
ps_ctl.set_delay = ps_set_delay;
ps_ctl.batch = ps_batch;
ps_ctl.flush = ps_flush;
ps_ctl.ps_calibration = NULL;
ps_ctl.ps_calibration = NULL;
ps_ctl.is_use_common_factory = false;
if (obj->hw->polling_mode_ps == 1) {
ps_ctl.is_polling_mode = true;
ps_ctl.is_report_input_direct = false;
wakeup_source_init(&mps_lock, "ps wakelock");
} else {
ps_ctl.is_polling_mode = false;
ps_ctl.is_report_input_direct = true;
}
#ifdef CUSTOM_KERNEL_SENSORHUB
ps_ctl.is_support_batch = obj->hw->is_batch_supported_ps;
#else
ps_ctl.is_support_batch = false;
#endif
err = ps_register_control_path(&ps_ctl);
if (err) {
APS_ERR("ps_control register fail = %d\n", err);
goto exit_sensor_obj_attach_fail;
}
ps_data.get_data = ps_get_data;
ps_data.vender_div = 100;
err = ps_register_data_path(&ps_data);
if (err) {
APS_ERR("ps_data register fail = %d\n", err);
goto exit_sensor_obj_attach_fail;
}
stk3x1x_init_flag = 0;
APS_LOG("%s: state_val=0x%x, psctrl_val=0x%x, alsctrl_val=0x%x\n",
__func__, atomic_read(&obj->state_val),
atomic_read(&obj->psctrl_val), atomic_read(&obj->alsctrl_val));
APS_LOG("ledctrl_val=0x%x, wait_val=0x%x, int_val=0x%x\n",
obj->ledctrl_val, obj->wait_val, obj->int_val);
/*
* Since alsps sensor driver in AP,
* it should register information to sensorlist.
*/
strncpy(alsps_devinfo.name, STK3x1x_DEV_NAME, sizeof(STK3x1x_DEV_NAME));
sensorlist_register_deviceinfo(ID_PRESSURE, &alsps_devinfo);
APS_LOG("%s: OK\n", __func__);
return 0;
exit_sensor_obj_attach_fail:
exit_create_attr_failed:
misc_deregister(&stk3x1x_device);
exit_misc_device_register_failed:
exit_init_failed:
kfree(obj);
exit:
stk3x1x_i2c_client = NULL;
#ifdef MT6516
MT6516_EINTIRQMask(CUST_EINT_ALS_NUM); /*mask interrupt if fail*/
#endif
stk3x1x_init_flag = -1;
APS_ERR("%s: err = %d\n", __func__, err);
return err;
}
/*----------------------------------------------------------------------------*/
static int stk3x1x_i2c_remove(struct i2c_client *client)
{
int err = 0;
err =
stk3x1x_delete_attr(&(stk3x1x_init_info.platform_diver_addr->driver));
if (err)
APS_ERR("stk3x1x_delete_attr fail: %d\n", err);
misc_deregister(&stk3x1x_device);
stk3x1x_i2c_client = NULL;
i2c_unregister_device(client);
kfree(i2c_get_clientdata(client));
return 0;
}
static int stk3x1x_local_uninit(void)
{
APS_FUN();
i2c_del_driver(&stk3x1x_i2c_driver);
stk3x1x_i2c_client = NULL;
return 0;
}
/*----------------------------------------------------------------------------*/
static int stk3x1x_local_init(void)
{
struct stk3x1x_i2c_addr addr;
APS_FUN();
stk3x1x_get_addr(hw, &addr);
if (i2c_add_driver(&stk3x1x_i2c_driver)) {
APS_ERR("add driver error\n");
return -1;
}
if (-1 == stk3x1x_init_flag) {
APS_ERR("%s fail with stk3x1x_init_flag=%d\n",
__func__, stk3x1x_init_flag);
return -1;
}
/* wait for idme for calibration ready open it */
als_cal = 65536; //idme_get_alscal_value();
if (als_cal > 0 && als_cal <= 65535)
stk3x1x_obj->als_transmittance = als_cal;
else
stk3x1x_obj->als_transmittance = ALS_DEFAULT_TRANSMITTANCE;
return 0;
}
/*----------------------------------------------------------------------------*/
static int __init stk3x1x_init(void)
{
alsps_driver_add(&stk3x1x_init_info);
/* hwmsen_alsps_add(&stk3x1x_init_info);*/
pr_info("%s done\n", __func__);
return 0;
}
/*----------------------------------------------------------------------------*/
static void __exit stk3x1x_exit(void)
{
APS_FUN();
}
/*----------------------------------------------------------------------------*/
module_init(stk3x1x_init);
module_exit(stk3x1x_exit);
/*----------------------------------------------------------------------------*/
MODULE_AUTHOR("MingHsien Hsieh");
MODULE_DESCRIPTION("SensorTek stk3x1x proximity and light sensor driver");
MODULE_LICENSE("GPL");