2542 lines
67 KiB
C
2542 lines
67 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* Copyright (c) 2020 MediaTek Inc.
|
|
*/
|
|
|
|
/*****************************************************************************
|
|
*** HEADER FILES
|
|
*****************************************************************************/
|
|
|
|
#include "mc3410-i2c.h"
|
|
#include "accel.h"
|
|
#include "cust_acc.h"
|
|
|
|
/*****************************************************************************
|
|
*** CONFIGURATION
|
|
*****************************************************************************/
|
|
#define _MC3XXX_SUPPORT_LPF_
|
|
#define _MC3XXX_SUPPORT_CONCURRENCY_PROTECTION_
|
|
#define _MC3XXX_SUPPORT_LRF_
|
|
#define C_MAX_FIR_LENGTH (32)
|
|
#define VIRTUAL_Z 0
|
|
#define MC3XXX_SAME_NUM 4 /*4 data is same*/
|
|
|
|
/*****************************************************************************
|
|
*** CONSTANT / DEFINITION
|
|
*****************************************************************************/
|
|
/**************************
|
|
*** CONFIGURATION
|
|
**************************/
|
|
#define MC3XXX_DEV_NAME "MC3XXX"
|
|
#define MC3XXX_DEV_DRIVER_VERSION "2.1.6"
|
|
#define MC3XXX_DEV_DRIVER_VERSION_VIRTUAL_Z "1.0.1"
|
|
|
|
/**************************
|
|
*** COMMON
|
|
**************************/
|
|
#define MC3XXX_AXIS_X 0
|
|
#define MC3XXX_AXIS_Y 1
|
|
#define MC3XXX_AXIS_Z 2
|
|
#define MC3XXX_AXES_NUM 3
|
|
#define MC3XXX_DATA_LEN 6
|
|
#define MC3XXX_RESOLUTION_LOW 1
|
|
#define MC3XXX_RESOLUTION_HIGH 2
|
|
#define MC3XXX_LOW_REOLUTION_DATA_SIZE 3
|
|
#define MC3XXX_HIGH_REOLUTION_DATA_SIZE 6
|
|
#define MC3XXX_INIT_SUCC (0)
|
|
#define MC3XXX_INIT_FAIL (-1)
|
|
#define MC3XXX_REGMAP_LENGTH (64)
|
|
#define DEBUG_SWITCH 1
|
|
#define C_I2C_FIFO_SIZE 8
|
|
|
|
#define DRIVER_ATTR(_name, _mode, _show, _store) \
|
|
struct driver_attribute driver_attr_##_name = \
|
|
__ATTR(_name, _mode, _show, _store)
|
|
|
|
static struct GSENSOR_VECTOR3D gsensor_gain;
|
|
|
|
static int g_samedataCounter; /*count the same data number*/
|
|
static int g_predata[3] = { 0, 0, 0 }; /*save the pre data of acc*/
|
|
|
|
/*****************************************************************************
|
|
*** DATA TYPE / STRUCTURE DEFINITION / ENUM
|
|
*****************************************************************************/
|
|
enum MCUBE_TRC {
|
|
MCUBE_TRC_FILTER = 0x01,
|
|
MCUBE_TRC_RAWDATA = 0x02,
|
|
MCUBE_TRC_IOCTL = 0x04,
|
|
MCUBE_TRC_CALI = 0X08,
|
|
MCUBE_TRC_INFO = 0X10,
|
|
MCUBE_TRC_REGXYZ = 0X20,
|
|
};
|
|
|
|
struct scale_factor {
|
|
u8 whole;
|
|
u8 fraction;
|
|
};
|
|
|
|
struct data_resolution {
|
|
struct scale_factor scalefactor;
|
|
int sensitivity;
|
|
};
|
|
|
|
struct data_filter {
|
|
s16 raw[C_MAX_FIR_LENGTH][MC3XXX_AXES_NUM];
|
|
int sum[MC3XXX_AXES_NUM];
|
|
unsigned int num;
|
|
int idx;
|
|
};
|
|
|
|
struct mc3xxx_i2c_data {
|
|
/* ================================================ */
|
|
struct i2c_client *client;
|
|
struct acc_hw hw;
|
|
struct hwmsen_convert cvt;
|
|
|
|
/* ================================================ */
|
|
struct data_resolution *reso;
|
|
atomic_t trace;
|
|
atomic_t suspend;
|
|
atomic_t selftest;
|
|
atomic_t filter;
|
|
s16 cali_sw[MC3XXX_AXES_NUM + 1];
|
|
|
|
/* ================================================ */
|
|
s16 offset[MC3XXX_AXES_NUM + 1];
|
|
s16 data[MC3XXX_AXES_NUM + 1];
|
|
|
|
/* ================================================ */
|
|
#if defined(_MC3XXX_SUPPORT_LPF_)
|
|
atomic_t firlen;
|
|
atomic_t fir_en;
|
|
struct data_filter fir;
|
|
#endif
|
|
bool flush;
|
|
};
|
|
|
|
#ifdef _MC3XXX_SUPPORT_LRF_
|
|
struct S_LRF_CB {
|
|
s16 nIsNewRound;
|
|
s16 nPreDiff;
|
|
s16 nPreValue;
|
|
s16 nMaxValue;
|
|
s16 nMinValue;
|
|
s16 nRepValue;
|
|
s16 nNewDataMonitorCount;
|
|
};
|
|
#endif
|
|
|
|
/*****************************************************************************
|
|
*** EXTERNAL FUNCTION
|
|
*****************************************************************************/
|
|
/* extern struct acc_hw* mc3xxx_get_cust_acc_hw(void); */
|
|
|
|
/*****************************************************************************
|
|
*** STATIC FUNCTION
|
|
*****************************************************************************/
|
|
static int mc3xxx_i2c_probe(struct i2c_client *client,
|
|
const struct i2c_device_id *id);
|
|
static int mc3xxx_i2c_remove(struct i2c_client *client);
|
|
static int _mc3xxx_i2c_auto_probe(struct i2c_client *client);
|
|
static int mc3xxx_suspend(struct device *dev);
|
|
static int mc3xxx_resume(struct device *dev);
|
|
static int mc3xxx_local_init(void);
|
|
static int mc3xxx_remove(void);
|
|
static int MC3XXX_SetPowerMode(struct i2c_client *client, bool enable);
|
|
static int MC3XXX_WriteCalibration(struct i2c_client *client,
|
|
int dat[MC3XXX_AXES_NUM]);
|
|
static void MC3XXX_SetGain(void);
|
|
static int mc3410_flush(void);
|
|
|
|
/*****************************************************************************
|
|
*** STATIC VARIABLE & CONTROL BLOCK DECLARATION
|
|
*****************************************************************************/
|
|
static unsigned char s_bResolution;
|
|
static unsigned char s_bPCODE;
|
|
static unsigned char s_bPCODER;
|
|
static unsigned char s_bHWID;
|
|
static unsigned char s_bMPOL;
|
|
static int s_nInitFlag = MC3XXX_INIT_FAIL;
|
|
static struct acc_init_info mc3xxx_init_info = {
|
|
.name = MC3XXX_DEV_NAME,
|
|
.init = mc3xxx_local_init,
|
|
.uninit = mc3xxx_remove,
|
|
};
|
|
#ifdef CONFIG_OF
|
|
static const struct of_device_id accel_of_match[] = {
|
|
{.compatible = "mediatek,gsensor" }, {},
|
|
};
|
|
#endif
|
|
#ifdef CONFIG_PM_SLEEP
|
|
static const struct dev_pm_ops mc3xxx_i2c_pm_ops = { SET_SYSTEM_SLEEP_PM_OPS(
|
|
mc3xxx_suspend, mc3xxx_resume) };
|
|
#endif
|
|
|
|
static const struct i2c_device_id mc3xxx_i2c_id[] = { { MC3XXX_DEV_NAME, 0 },
|
|
{} };
|
|
/* static struct i2c_board_info __initdata mc3xxx_i2c_board_info = {
|
|
* I2C_BOARD_INFO(MC3XXX_DEV_NAME, 0x4C) };
|
|
*/
|
|
static unsigned short mc3xxx_i2c_auto_probe_addr[] = { 0x4C, 0x6C, 0x4E,
|
|
0x6D, 0x6E, 0x6F };
|
|
static struct i2c_driver mc3xxx_i2c_driver = {
|
|
.driver = {
|
|
.name = MC3XXX_DEV_NAME,
|
|
#ifdef CONFIG_PM_SLEEP
|
|
.pm = &mc3xxx_i2c_pm_ops,
|
|
#endif
|
|
#ifdef CONFIG_OF
|
|
.of_match_table = accel_of_match,
|
|
#endif
|
|
},
|
|
.probe = mc3xxx_i2c_probe,
|
|
.remove = mc3xxx_i2c_remove,
|
|
|
|
.id_table = mc3xxx_i2c_id,
|
|
};
|
|
|
|
static struct i2c_client *mc3xxx_i2c_client;
|
|
static struct mc3xxx_i2c_data *mc3xxx_obj_i2c_data;
|
|
static bool mc3xxx_sensor_power;
|
|
static char selftestRes[10] = { 0 };
|
|
static struct file *fd_file;
|
|
static mm_segment_t oldfs;
|
|
static unsigned char offset_buf[6];
|
|
static signed int offset_data[3];
|
|
static signed int gain_data[3];
|
|
static unsigned char s_baOTP_OffsetData[6] = { 0 };
|
|
static signed int s_nIsRBM_Enabled;
|
|
static DEFINE_MUTEX(MC3XXX_i2c_mutex);
|
|
|
|
#ifdef _MC3XXX_SUPPORT_LRF_
|
|
static struct S_LRF_CB s_taLRF_CB[MC3XXX_AXES_NUM];
|
|
#endif
|
|
|
|
#ifdef _MC3XXX_SUPPORT_CONCURRENCY_PROTECTION_
|
|
static struct semaphore s_tSemaProtect;
|
|
#endif
|
|
|
|
static int LPF_SamplingRate = 5;
|
|
static int LPF_CutoffFrequency = 0x00000004;
|
|
static unsigned int iAReal0_X;
|
|
static unsigned int iAcc0Lpf0_X;
|
|
static unsigned int iAcc0Lpf1_X;
|
|
static unsigned int iAcc1Lpf0_X;
|
|
static unsigned int iAcc1Lpf1_X;
|
|
|
|
static unsigned int iAReal0_Y;
|
|
static unsigned int iAcc0Lpf0_Y;
|
|
static unsigned int iAcc0Lpf1_Y;
|
|
static unsigned int iAcc1Lpf0_Y;
|
|
static unsigned int iAcc1Lpf1_Y;
|
|
|
|
static unsigned int iAReal0_Z;
|
|
static unsigned int iAcc0Lpf0_Z;
|
|
static unsigned int iAcc0Lpf1_Z;
|
|
static unsigned int iAcc1Lpf0_Z;
|
|
static unsigned int iAcc1Lpf1_Z;
|
|
|
|
static signed char s_bAccuracyStatus = SENSOR_STATUS_ACCURACY_MEDIUM;
|
|
static int mc3xxx_mutex_lock(void);
|
|
static void mc3xxx_mutex_unlock(void);
|
|
static void mc3xxx_mutex_init(void);
|
|
/*****************************************************************************
|
|
*** MACRO
|
|
*****************************************************************************/
|
|
#ifdef _MC3XXX_SUPPORT_CONCURRENCY_PROTECTION_
|
|
static void mc3xxx_mutex_init(void) { sema_init(&s_tSemaProtect, 1); }
|
|
|
|
static int mc3xxx_mutex_lock(void)
|
|
{
|
|
if (down_interruptible(&s_tSemaProtect))
|
|
return (-ERESTARTSYS);
|
|
return 0;
|
|
}
|
|
|
|
static void mc3xxx_mutex_unlock(void) { up(&s_tSemaProtect); }
|
|
#else
|
|
#define mc3xxx_mutex_lock() \
|
|
do { \
|
|
} while (0)
|
|
#define mc3xxx_mutex_lock() \
|
|
do { \
|
|
} while (0)
|
|
#define mc3xxx_mutex_unlock() \
|
|
do { \
|
|
} while (0)
|
|
#endif
|
|
|
|
#define IS_MCFM12() ((s_bHWID >= 0xC0) && (s_bHWID <= 0xCF))
|
|
#define IS_MCFM3X() \
|
|
((s_bHWID == 0x20) || ((s_bHWID >= 0x22) && (s_bHWID <= 0x2F)))
|
|
|
|
/*****************************************************************************
|
|
*** TODO
|
|
*****************************************************************************/
|
|
#define DATA_PATH "/sdcard2/mcube-register-map.txt"
|
|
|
|
/*****************************************************************************
|
|
*** FUNCTION
|
|
*****************************************************************************/
|
|
|
|
/**************I2C operate API*****************************/
|
|
static int MC3XXX_i2c_read_block(struct i2c_client *client, u8 addr, u8 *data,
|
|
u8 len)
|
|
{
|
|
u8 beg = addr;
|
|
int err;
|
|
struct i2c_msg msgs[2] = { { 0 }, { 0 } };
|
|
|
|
if (!client)
|
|
return -EINVAL;
|
|
else if (len > C_I2C_FIFO_SIZE) {
|
|
pr_err_ratelimited("[Gsensor]%s: length %d exceeds %d\n",
|
|
__func__, len, C_I2C_FIFO_SIZE);
|
|
mutex_unlock(&MC3XXX_i2c_mutex);
|
|
return -EINVAL;
|
|
}
|
|
|
|
mutex_lock(&MC3XXX_i2c_mutex);
|
|
|
|
msgs[0].addr = client->addr;
|
|
msgs[0].flags = 0;
|
|
msgs[0].len = 1;
|
|
msgs[0].buf = &beg;
|
|
|
|
msgs[1].addr = client->addr;
|
|
msgs[1].flags = I2C_M_RD;
|
|
msgs[1].len = len;
|
|
msgs[1].buf = data;
|
|
|
|
err = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
|
|
if (err != 2) {
|
|
pr_err_ratelimited("[Gsensor]%s:i2c_transfer error: (%d %p %d) %d\n",
|
|
__func__, addr, data, len, err);
|
|
err = -EIO;
|
|
} else
|
|
err = 0;
|
|
|
|
mutex_unlock(&MC3XXX_i2c_mutex);
|
|
return err;
|
|
}
|
|
|
|
static int MC3XXX_i2c_write_block(struct i2c_client *client, u8 addr, u8 *data,
|
|
u8 len)
|
|
{
|
|
/*because address also occupies one byte,
|
|
*the maximum length for write is 7 bytes
|
|
*/
|
|
int err, num;
|
|
unsigned int idx;
|
|
char buf[C_I2C_FIFO_SIZE];
|
|
|
|
err = 0;
|
|
mutex_lock(&MC3XXX_i2c_mutex);
|
|
if (!client) {
|
|
mutex_unlock(&MC3XXX_i2c_mutex);
|
|
return -EINVAL;
|
|
} else if (len >= C_I2C_FIFO_SIZE) {
|
|
pr_err_ratelimited("[Gsensor]%s: length %d exceeds %d\n",
|
|
__func__, len, C_I2C_FIFO_SIZE);
|
|
mutex_unlock(&MC3XXX_i2c_mutex);
|
|
return -EINVAL;
|
|
}
|
|
|
|
num = 0;
|
|
buf[num++] = addr;
|
|
for (idx = 0; idx < len; idx++)
|
|
buf[num++] = data[idx];
|
|
|
|
err = i2c_master_send(client, buf, num);
|
|
if (err < 0) {
|
|
pr_err_ratelimited("[Gsensor]%s:send command error!!\n",
|
|
__func__);
|
|
mutex_unlock(&MC3XXX_i2c_mutex);
|
|
return -EFAULT;
|
|
}
|
|
err = 0;
|
|
|
|
mutex_unlock(&MC3XXX_i2c_mutex);
|
|
return err;
|
|
}
|
|
|
|
/*****************************************
|
|
*** GetLowPassFilter
|
|
*****************************************/
|
|
static unsigned int GetLowPassFilter(unsigned int X0, unsigned int Y1)
|
|
{
|
|
unsigned int lTemp;
|
|
|
|
lTemp = Y1;
|
|
lTemp *= LPF_CutoffFrequency; /* 4HZ LPF RC=0.04 */
|
|
X0 *= LPF_SamplingRate;
|
|
lTemp += X0;
|
|
lTemp += LPF_CutoffFrequency;
|
|
lTemp /= (LPF_CutoffFrequency + LPF_SamplingRate);
|
|
Y1 = lTemp;
|
|
|
|
return Y1;
|
|
}
|
|
|
|
/*****************************************
|
|
*** openFile
|
|
*****************************************/
|
|
static struct file *openFile(char *path, int flag, int mode)
|
|
{
|
|
struct file *fp = NULL;
|
|
|
|
fp = filp_open(path, flag, mode);
|
|
|
|
if (IS_ERR(fp) || !fp->f_op) {
|
|
GSE_LOG("Calibration File filp_open return NULL\n");
|
|
return NULL;
|
|
} else
|
|
return fp;
|
|
}
|
|
|
|
/*****************************************
|
|
*** writeFile
|
|
*****************************************/
|
|
static int writeFile(struct file *fp, char *buf, int writelen)
|
|
{
|
|
if (fp->f_op && fp->f_op->write)
|
|
return fp->f_op->write(fp, buf, writelen, &fp->f_pos);
|
|
else
|
|
return -1;
|
|
}
|
|
|
|
/*****************************************
|
|
*** closeFile
|
|
*****************************************/
|
|
static int closeFile(struct file *fp)
|
|
{
|
|
filp_close(fp, NULL);
|
|
return 0;
|
|
}
|
|
|
|
/*****************************************
|
|
*** initKernelEnv
|
|
*****************************************/
|
|
static void initKernelEnv(void)
|
|
{
|
|
oldfs = get_fs();
|
|
set_fs(KERNEL_DS);
|
|
}
|
|
|
|
/*****************************************
|
|
*** mcube_write_log_data
|
|
*****************************************/
|
|
static int mcube_write_log_data(struct i2c_client *client, u8 data[0x3f])
|
|
{
|
|
#define _WRT_LOG_DATA_BUFFER_SIZE (66 * 50)
|
|
|
|
s16 rbm_data[3] = { 0 }, raw_data[3] = { 0 };
|
|
int err = 0;
|
|
char *_pszBuffer = NULL;
|
|
int n = 0, i = 0;
|
|
|
|
initKernelEnv();
|
|
fd_file = openFile(DATA_PATH, O_RDWR | O_CREAT, 0);
|
|
if (fd_file == NULL)
|
|
GSE_LOG("%s fail to open\n", __func__);
|
|
else {
|
|
rbm_data[MC3XXX_AXIS_X] =
|
|
(s16)((data[0x0d]) | (data[0x0e] << 8));
|
|
rbm_data[MC3XXX_AXIS_Y] =
|
|
(s16)((data[0x0f]) | (data[0x10] << 8));
|
|
rbm_data[MC3XXX_AXIS_Z] =
|
|
(s16)((data[0x11]) | (data[0x12] << 8));
|
|
|
|
raw_data[MC3XXX_AXIS_X] =
|
|
(rbm_data[MC3XXX_AXIS_X] + offset_data[0] / 2) *
|
|
gsensor_gain.x / gain_data[0];
|
|
raw_data[MC3XXX_AXIS_Y] =
|
|
(rbm_data[MC3XXX_AXIS_Y] + offset_data[1] / 2) *
|
|
gsensor_gain.y / gain_data[1];
|
|
raw_data[MC3XXX_AXIS_Z] =
|
|
(rbm_data[MC3XXX_AXIS_Z] + offset_data[2] / 2) *
|
|
gsensor_gain.z / gain_data[2];
|
|
|
|
_pszBuffer = kzalloc(_WRT_LOG_DATA_BUFFER_SIZE, GFP_KERNEL);
|
|
|
|
if (_pszBuffer == NULL)
|
|
return -1;
|
|
|
|
memset(_pszBuffer, 0, _WRT_LOG_DATA_BUFFER_SIZE);
|
|
|
|
n += sprintf(_pszBuffer + n,
|
|
"G-sensor RAW X = %d Y = %d Z = %d\n",
|
|
raw_data[0], raw_data[1], raw_data[2]);
|
|
n += sprintf(_pszBuffer + n,
|
|
"G-sensor RBM X = %d Y = %d Z = %d\n",
|
|
rbm_data[0], rbm_data[1], rbm_data[2]);
|
|
|
|
for (i = 0; i < 63; i++)
|
|
n += sprintf(_pszBuffer + n,
|
|
"mCube register map Register[%x] = 0x%x\n",
|
|
i, data[i]);
|
|
|
|
mdelay(50);
|
|
|
|
err = writeFile(fd_file, _pszBuffer, n);
|
|
if (err <= 0)
|
|
GSE_LOG("write file error %d\n", err);
|
|
|
|
kfree(_pszBuffer);
|
|
set_fs(oldfs);
|
|
closeFile(fd_file);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*****************************************
|
|
*** MC3XXX_ValidateSensorIC
|
|
*****************************************/
|
|
static int MC3XXX_ValidateSensorIC(unsigned char *pbPCode,
|
|
unsigned char *pbHwID)
|
|
{
|
|
if ((*pbHwID == 0x01) || (*pbHwID == 0x03) ||
|
|
((*pbHwID >= 0x04) && (*pbHwID <= 0x0F))) {
|
|
if ((*pbPCode == MC3XXX_PCODE_3210) ||
|
|
(*pbPCode == MC3XXX_PCODE_3230) ||
|
|
(*pbPCode == MC3XXX_PCODE_3250))
|
|
return MC3XXX_RETCODE_SUCCESS;
|
|
} else if ((*pbHwID == 0x02) || (*pbHwID == 0x21) ||
|
|
((*pbHwID >= 0x10) && (*pbHwID <= 0x1F))) {
|
|
if ((*pbPCode == MC3XXX_PCODE_3210) ||
|
|
(*pbPCode == MC3XXX_PCODE_3230) ||
|
|
(*pbPCode == MC3XXX_PCODE_3250) ||
|
|
(*pbPCode == MC3XXX_PCODE_3410) ||
|
|
(*pbPCode == MC3XXX_PCODE_3410N) ||
|
|
(*pbPCode == MC3XXX_PCODE_3430) ||
|
|
(*pbPCode == MC3XXX_PCODE_3430N)) {
|
|
return MC3XXX_RETCODE_SUCCESS;
|
|
}
|
|
} else if ((*pbHwID >= 0xC0) && (*pbHwID <= 0xCF)) {
|
|
*pbPCode = (*pbPCode & 0x71);
|
|
|
|
if ((*pbPCode == MC3XXX_PCODE_3510) ||
|
|
(*pbPCode == MC3XXX_PCODE_3530))
|
|
return MC3XXX_RETCODE_SUCCESS;
|
|
} else if ((*pbHwID == 0x20) ||
|
|
((*pbHwID >= 0x22) && (*pbHwID <= 0x2F))) {
|
|
*pbPCode = (*pbPCode & 0xF1);
|
|
|
|
if ((*pbPCode == MC3XXX_PCODE_3210) ||
|
|
(*pbPCode == MC3XXX_PCODE_3216) ||
|
|
(*pbPCode == MC3XXX_PCODE_3236) ||
|
|
(*pbPCode == MC3XXX_PCODE_RESERVE_1) ||
|
|
(*pbPCode == MC3XXX_PCODE_RESERVE_2) ||
|
|
(*pbPCode == MC3XXX_PCODE_RESERVE_3) ||
|
|
(*pbPCode == MC3XXX_PCODE_RESERVE_4) ||
|
|
(*pbPCode == MC3XXX_PCODE_RESERVE_5) ||
|
|
(*pbPCode == MC3XXX_PCODE_RESERVE_6) ||
|
|
(*pbPCode == MC3XXX_PCODE_RESERVE_7) ||
|
|
(*pbPCode == MC3XXX_PCODE_RESERVE_8) ||
|
|
(*pbPCode == MC3XXX_PCODE_RESERVE_9))
|
|
return MC3XXX_RETCODE_SUCCESS;
|
|
}
|
|
|
|
return MC3XXX_RETCODE_ERROR_IDENTIFICATION;
|
|
}
|
|
|
|
/*****************************************
|
|
*** MC3XXX_Read_Reg_Map
|
|
*****************************************/
|
|
static int MC3XXX_Read_Reg_Map(struct i2c_client *p_i2c_client, u8 *pbUserBuf)
|
|
{
|
|
u8 _baData[MC3XXX_REGMAP_LENGTH] = { 0 };
|
|
int _nIndex = 0;
|
|
|
|
if (p_i2c_client == NULL)
|
|
return (-EINVAL);
|
|
|
|
for (_nIndex = 0; _nIndex < MC3XXX_REGMAP_LENGTH; _nIndex++) {
|
|
MC3XXX_i2c_read_block(p_i2c_client, _nIndex, &_baData[_nIndex],
|
|
1);
|
|
|
|
if (pbUserBuf != NULL)
|
|
pbUserBuf[_nIndex] = _baData[_nIndex];
|
|
}
|
|
|
|
mcube_write_log_data(p_i2c_client, _baData);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*****************************************
|
|
*** MC3XXX_SaveDefaultOffset
|
|
*****************************************/
|
|
static void MC3XXX_SaveDefaultOffset(struct i2c_client *p_i2c_client)
|
|
{
|
|
MC3XXX_i2c_read_block(p_i2c_client, 0x21, &s_baOTP_OffsetData[0], 3);
|
|
MC3XXX_i2c_read_block(p_i2c_client, 0x24, &s_baOTP_OffsetData[3], 3);
|
|
}
|
|
|
|
/*****************************************
|
|
*** MC3XXX_LPF
|
|
*****************************************/
|
|
#ifdef _MC3XXX_SUPPORT_LPF_
|
|
static void MC3XXX_LPF(struct mc3xxx_i2c_data *priv, s16 data[MC3XXX_AXES_NUM])
|
|
{
|
|
if (atomic_read(&priv->filter)) {
|
|
if (atomic_read(&priv->fir_en) &&
|
|
!atomic_read(&priv->suspend)) {
|
|
unsigned int idx, firlen = atomic_read(&priv->firlen);
|
|
|
|
if (priv->fir.num < firlen) {
|
|
priv->fir.raw[priv->fir.num][MC3XXX_AXIS_X] =
|
|
data[MC3XXX_AXIS_X];
|
|
priv->fir.raw[priv->fir.num][MC3XXX_AXIS_Y] =
|
|
data[MC3XXX_AXIS_Y];
|
|
priv->fir.raw[priv->fir.num][MC3XXX_AXIS_Z] =
|
|
data[MC3XXX_AXIS_Z];
|
|
priv->fir.sum[MC3XXX_AXIS_X] +=
|
|
data[MC3XXX_AXIS_X];
|
|
priv->fir.sum[MC3XXX_AXIS_Y] +=
|
|
data[MC3XXX_AXIS_Y];
|
|
priv->fir.sum[MC3XXX_AXIS_Z] +=
|
|
data[MC3XXX_AXIS_Z];
|
|
priv->fir.num++;
|
|
priv->fir.idx++;
|
|
} else {
|
|
idx = priv->fir.idx % firlen;
|
|
priv->fir.sum[MC3XXX_AXIS_X] -=
|
|
priv->fir.raw[idx][MC3XXX_AXIS_X];
|
|
priv->fir.sum[MC3XXX_AXIS_Y] -=
|
|
priv->fir.raw[idx][MC3XXX_AXIS_Y];
|
|
priv->fir.sum[MC3XXX_AXIS_Z] -=
|
|
priv->fir.raw[idx][MC3XXX_AXIS_Z];
|
|
priv->fir.raw[idx][MC3XXX_AXIS_X] =
|
|
data[MC3XXX_AXIS_X];
|
|
priv->fir.raw[idx][MC3XXX_AXIS_Y] =
|
|
data[MC3XXX_AXIS_Y];
|
|
priv->fir.raw[idx][MC3XXX_AXIS_Z] =
|
|
data[MC3XXX_AXIS_Z];
|
|
priv->fir.sum[MC3XXX_AXIS_X] +=
|
|
data[MC3XXX_AXIS_X];
|
|
priv->fir.sum[MC3XXX_AXIS_Y] +=
|
|
data[MC3XXX_AXIS_Y];
|
|
priv->fir.sum[MC3XXX_AXIS_Z] +=
|
|
data[MC3XXX_AXIS_Z];
|
|
priv->fir.idx++;
|
|
data[MC3XXX_AXIS_X] =
|
|
priv->fir.sum[MC3XXX_AXIS_X] / firlen;
|
|
data[MC3XXX_AXIS_Y] =
|
|
priv->fir.sum[MC3XXX_AXIS_Y] / firlen;
|
|
data[MC3XXX_AXIS_Z] =
|
|
priv->fir.sum[MC3XXX_AXIS_Z] / firlen;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endif /* END OF #ifdef _MC3XXX_SUPPORT_LPF_ */
|
|
|
|
#ifdef _MC3XXX_SUPPORT_LRF_
|
|
/*****************************************
|
|
*** _MC3XXX_LowResFilter
|
|
*****************************************/
|
|
static void _MC3XXX_LowResFilter(u16 nAxis, s16 naData[MC3XXX_AXES_NUM])
|
|
{
|
|
#define _LRF_DIFF_COUNT_POS 2
|
|
#define _LRF_DIFF_COUNT_NEG (-_LRF_DIFF_COUNT_POS)
|
|
#define _LRF_DIFF_BOUNDARY_POS (_LRF_DIFF_COUNT_POS + 1)
|
|
#define _LRF_DIFF_BOUNDARY_NEG (_LRF_DIFF_COUNT_NEG - 1)
|
|
#define _LRF_DIFF_DATA_UNCHANGE_MAX_COUNT 11
|
|
|
|
signed int _nCurrDiff = 0;
|
|
signed int _nSumDiff = 0;
|
|
s16 _nCurrData = naData[nAxis];
|
|
|
|
_nCurrDiff = (_nCurrData - s_taLRF_CB[nAxis].nRepValue);
|
|
|
|
if ((_nCurrDiff > _LRF_DIFF_COUNT_NEG) &&
|
|
(_nCurrDiff < _LRF_DIFF_COUNT_POS)) {
|
|
if (s_taLRF_CB[nAxis].nIsNewRound) {
|
|
s_taLRF_CB[nAxis].nMaxValue = _nCurrData;
|
|
s_taLRF_CB[nAxis].nMinValue = _nCurrData;
|
|
|
|
s_taLRF_CB[nAxis].nIsNewRound = 0;
|
|
s_taLRF_CB[nAxis].nNewDataMonitorCount = 0;
|
|
} else {
|
|
if (_nCurrData > s_taLRF_CB[nAxis].nMaxValue)
|
|
s_taLRF_CB[nAxis].nMaxValue = _nCurrData;
|
|
else if (_nCurrData < s_taLRF_CB[nAxis].nMinValue)
|
|
s_taLRF_CB[nAxis].nMinValue = _nCurrData;
|
|
|
|
if (s_taLRF_CB[nAxis].nMinValue !=
|
|
s_taLRF_CB[nAxis].nMaxValue) {
|
|
if (_nCurrData == s_taLRF_CB[nAxis].nPreValue)
|
|
s_taLRF_CB[nAxis]
|
|
.nNewDataMonitorCount++;
|
|
else
|
|
s_taLRF_CB[nAxis].nNewDataMonitorCount =
|
|
0;
|
|
}
|
|
}
|
|
|
|
if (1 !=
|
|
(s_taLRF_CB[nAxis].nMaxValue - s_taLRF_CB[nAxis].nMinValue))
|
|
s_taLRF_CB[nAxis].nRepValue =
|
|
((s_taLRF_CB[nAxis].nMaxValue +
|
|
s_taLRF_CB[nAxis].nMinValue) /
|
|
2);
|
|
|
|
_nSumDiff = (_nCurrDiff + s_taLRF_CB[nAxis].nPreDiff);
|
|
|
|
if (_nCurrDiff)
|
|
s_taLRF_CB[nAxis].nPreDiff = _nCurrDiff;
|
|
|
|
if ((_nSumDiff > _LRF_DIFF_BOUNDARY_NEG) &&
|
|
(_nSumDiff < _LRF_DIFF_BOUNDARY_POS)) {
|
|
if (s_taLRF_CB[nAxis].nNewDataMonitorCount <
|
|
_LRF_DIFF_DATA_UNCHANGE_MAX_COUNT) {
|
|
naData[nAxis] = s_taLRF_CB[nAxis].nRepValue;
|
|
goto _LRF_RETURN;
|
|
}
|
|
}
|
|
}
|
|
|
|
s_taLRF_CB[nAxis].nRepValue = _nCurrData;
|
|
s_taLRF_CB[nAxis].nPreDiff = 0;
|
|
s_taLRF_CB[nAxis].nIsNewRound = 1;
|
|
|
|
_LRF_RETURN:
|
|
s_taLRF_CB[nAxis].nPreValue = _nCurrData;
|
|
|
|
#undef _LRF_DIFF_COUNT_POS
|
|
#undef _LRF_DIFF_COUNT_NEG
|
|
#undef _LRF_DIFF_BOUNDARY_POS
|
|
#undef _LRF_DIFF_BOUNDARY_NEG
|
|
#undef _LRF_DIFF_DATA_UNCHANGE_MAX_COUNT
|
|
}
|
|
#endif /* END OF #ifdef _MC3XXX_SUPPORT_LRF_ */
|
|
|
|
/*****************************************
|
|
*** _MC3XXX_ReadData_RBM2RAW
|
|
*****************************************/
|
|
static void _MC3XXX_ReadData_RBM2RAW(s16 waData[MC3XXX_AXES_NUM])
|
|
{
|
|
waData[MC3XXX_AXIS_X] =
|
|
(waData[MC3XXX_AXIS_X] + offset_data[MC3XXX_AXIS_X] / 2) * 1024 /
|
|
gain_data[MC3XXX_AXIS_X] +
|
|
8096;
|
|
waData[MC3XXX_AXIS_Y] =
|
|
(waData[MC3XXX_AXIS_Y] + offset_data[MC3XXX_AXIS_Y] / 2) * 1024 /
|
|
gain_data[MC3XXX_AXIS_Y] +
|
|
8096;
|
|
waData[MC3XXX_AXIS_Z] =
|
|
(waData[MC3XXX_AXIS_Z] + offset_data[MC3XXX_AXIS_Z] / 2) * 1024 /
|
|
gain_data[MC3XXX_AXIS_Z] +
|
|
8096;
|
|
|
|
iAReal0_X = (0x0010 * waData[MC3XXX_AXIS_X]);
|
|
iAcc1Lpf0_X = GetLowPassFilter(iAReal0_X, iAcc1Lpf1_X);
|
|
iAcc0Lpf0_X = GetLowPassFilter(iAcc1Lpf0_X, iAcc0Lpf1_X);
|
|
waData[MC3XXX_AXIS_X] = (iAcc0Lpf0_X / 0x0010);
|
|
|
|
iAReal0_Y = (0x0010 * waData[MC3XXX_AXIS_Y]);
|
|
iAcc1Lpf0_Y = GetLowPassFilter(iAReal0_Y, iAcc1Lpf1_Y);
|
|
iAcc0Lpf0_Y = GetLowPassFilter(iAcc1Lpf0_Y, iAcc0Lpf1_Y);
|
|
waData[MC3XXX_AXIS_Y] = (iAcc0Lpf0_Y / 0x0010);
|
|
|
|
iAReal0_Z = (0x0010 * waData[MC3XXX_AXIS_Z]);
|
|
iAcc1Lpf0_Z = GetLowPassFilter(iAReal0_Z, iAcc1Lpf1_Z);
|
|
iAcc0Lpf0_Z = GetLowPassFilter(iAcc1Lpf0_Z, iAcc0Lpf1_Z);
|
|
waData[MC3XXX_AXIS_Z] = (iAcc0Lpf0_Z / 0x0010);
|
|
waData[MC3XXX_AXIS_X] =
|
|
(waData[MC3XXX_AXIS_X] - 8096) * gsensor_gain.x / 1024;
|
|
waData[MC3XXX_AXIS_Y] =
|
|
(waData[MC3XXX_AXIS_Y] - 8096) * gsensor_gain.y / 1024;
|
|
waData[MC3XXX_AXIS_Z] =
|
|
(waData[MC3XXX_AXIS_Z] - 8096) * gsensor_gain.z / 1024;
|
|
|
|
iAcc0Lpf1_X = iAcc0Lpf0_X;
|
|
iAcc1Lpf1_X = iAcc1Lpf0_X;
|
|
iAcc0Lpf1_Y = iAcc0Lpf0_Y;
|
|
iAcc1Lpf1_Y = iAcc1Lpf0_Y;
|
|
iAcc0Lpf1_Z = iAcc0Lpf0_Z;
|
|
iAcc1Lpf1_Z = iAcc1Lpf0_Z;
|
|
}
|
|
|
|
/*****************************************
|
|
*** MC3XXX_ReadData
|
|
*****************************************/
|
|
static int MC3XXX_ReadData(struct i2c_client *pt_i2c_client,
|
|
s16 waData[MC3XXX_AXES_NUM])
|
|
{
|
|
u8 _baData[MC3XXX_DATA_LEN] = { 0 };
|
|
s16 _nTemp = 0;
|
|
#ifdef _MC3XXX_SUPPORT_LPF_
|
|
struct mc3xxx_i2c_data *_ptPrivData = NULL;
|
|
#endif
|
|
struct mc3xxx_i2c_data *_pt_i2c_obj = NULL;
|
|
|
|
if (pt_i2c_client == NULL) {
|
|
pr_err_ratelimited("[Gsensor]%s:ERR: Null Pointer\n",
|
|
__func__);
|
|
return MC3XXX_RETCODE_ERROR_NULL_POINTER;
|
|
}
|
|
_pt_i2c_obj =
|
|
((struct mc3xxx_i2c_data *)i2c_get_clientdata(pt_i2c_client));
|
|
|
|
if (!s_nIsRBM_Enabled) {
|
|
if (s_bResolution == MC3XXX_RESOLUTION_LOW) {
|
|
if (MC3XXX_i2c_read_block(
|
|
pt_i2c_client, MC3XXX_REG_XOUT, _baData,
|
|
MC3XXX_LOW_REOLUTION_DATA_SIZE)) {
|
|
pr_err_ratelimited("[Gsensor]%s:ERR: fail to read data via I2C!\n",
|
|
__func__);
|
|
|
|
return MC3XXX_RETCODE_ERROR_I2C;
|
|
}
|
|
|
|
waData[MC3XXX_AXIS_X] = ((s8)_baData[0]);
|
|
waData[MC3XXX_AXIS_Y] = ((s8)_baData[1]);
|
|
waData[MC3XXX_AXIS_Z] = ((s8)_baData[2]);
|
|
|
|
#ifdef _MC3XXX_SUPPORT_LRF_
|
|
_MC3XXX_LowResFilter(MC3XXX_AXIS_X, waData);
|
|
_MC3XXX_LowResFilter(MC3XXX_AXIS_Y, waData);
|
|
_MC3XXX_LowResFilter(MC3XXX_AXIS_Z, waData);
|
|
#endif
|
|
} else if (s_bResolution == MC3XXX_RESOLUTION_HIGH) {
|
|
if (MC3XXX_i2c_read_block(
|
|
pt_i2c_client, MC3XXX_REG_XOUT_EX_L, _baData,
|
|
MC3XXX_HIGH_REOLUTION_DATA_SIZE)) {
|
|
pr_err_ratelimited("[Gsensor]%s:ERR: fail to read data via I2C!\n",
|
|
__func__);
|
|
|
|
return MC3XXX_RETCODE_ERROR_I2C;
|
|
}
|
|
|
|
waData[MC3XXX_AXIS_X] =
|
|
((signed short)((_baData[0]) | (_baData[1] << 8)));
|
|
waData[MC3XXX_AXIS_Y] =
|
|
((signed short)((_baData[2]) | (_baData[3] << 8)));
|
|
waData[MC3XXX_AXIS_Z] =
|
|
((signed short)((_baData[4]) | (_baData[5] << 8)));
|
|
}
|
|
|
|
#ifdef _MC3XXX_SUPPORT_LPF_
|
|
_ptPrivData = i2c_get_clientdata(pt_i2c_client);
|
|
|
|
MC3XXX_LPF(_ptPrivData, waData);
|
|
#endif
|
|
} else {
|
|
if (MC3XXX_i2c_read_block(pt_i2c_client, MC3XXX_REG_XOUT_EX_L,
|
|
_baData,
|
|
MC3XXX_HIGH_REOLUTION_DATA_SIZE)) {
|
|
pr_err_ratelimited("[Gsensor]%s:ERR: fail to read data via I2C!\n",
|
|
__func__);
|
|
|
|
return MC3XXX_RETCODE_ERROR_I2C;
|
|
}
|
|
|
|
waData[MC3XXX_AXIS_X] =
|
|
((s16)((_baData[0]) | (_baData[1] << 8)));
|
|
waData[MC3XXX_AXIS_Y] =
|
|
((s16)((_baData[2]) | (_baData[3] << 8)));
|
|
waData[MC3XXX_AXIS_Z] =
|
|
((s16)((_baData[4]) | (_baData[5] << 8)));
|
|
|
|
_MC3XXX_ReadData_RBM2RAW(waData);
|
|
}
|
|
|
|
if (s_bPCODE == MC3XXX_PCODE_3250) {
|
|
_nTemp = waData[MC3XXX_AXIS_X];
|
|
waData[MC3XXX_AXIS_X] = waData[MC3XXX_AXIS_Y];
|
|
waData[MC3XXX_AXIS_Y] = -_nTemp;
|
|
} else {
|
|
if (s_bMPOL & 0x01)
|
|
waData[MC3XXX_AXIS_X] = -waData[MC3XXX_AXIS_X];
|
|
if (s_bMPOL & 0x02)
|
|
waData[MC3XXX_AXIS_Y] = -waData[MC3XXX_AXIS_Y];
|
|
}
|
|
return MC3XXX_RETCODE_SUCCESS;
|
|
}
|
|
|
|
/*****************************************
|
|
*** MC3XXX_ResetCalibration
|
|
*****************************************/
|
|
static int MC3XXX_ResetCalibration(struct i2c_client *client)
|
|
{
|
|
struct mc3xxx_i2c_data *obj = i2c_get_clientdata(client);
|
|
u8 buf[MC3XXX_AXES_NUM] = { 0x00, 0x00, 0x00 };
|
|
s16 tmp = 0;
|
|
int err = 0;
|
|
u8 bMsbFilter = 0x3F;
|
|
s16 wSignBitMask = 0x2000;
|
|
s16 wSignPaddingBits = 0xC000;
|
|
|
|
buf[0] = 0x43;
|
|
err = MC3XXX_i2c_write_block(client, 0x07, buf, 1);
|
|
if (err) {
|
|
pr_err_ratelimited("[Gsensor]%s:error 0x07: %d\n",
|
|
__func__, err);
|
|
return err;
|
|
}
|
|
|
|
err = MC3XXX_i2c_write_block(client, 0x21, offset_buf, 6);
|
|
if (err) {
|
|
pr_err_ratelimited("[Gsensor]%s:error: %d\n",
|
|
__func__, err);
|
|
return err;
|
|
}
|
|
|
|
buf[0] = 0x41;
|
|
err = MC3XXX_i2c_write_block(client, 0x07, buf, 1);
|
|
if (err) {
|
|
pr_err_ratelimited("[Gsensor]%s:error: %d\n",
|
|
__func__, err);
|
|
return err;
|
|
}
|
|
|
|
mdelay(20);
|
|
|
|
if (IS_MCFM12() || IS_MCFM3X()) {
|
|
bMsbFilter = 0x7F;
|
|
wSignBitMask = 0x4000;
|
|
wSignPaddingBits = 0x8000;
|
|
}
|
|
|
|
tmp = ((offset_buf[1] & bMsbFilter) << 8) + offset_buf[0];
|
|
if (tmp & wSignBitMask)
|
|
tmp |= wSignPaddingBits;
|
|
offset_data[0] = tmp;
|
|
|
|
tmp = ((offset_buf[3] & bMsbFilter) << 8) + offset_buf[2];
|
|
if (tmp & wSignBitMask)
|
|
tmp |= wSignPaddingBits;
|
|
offset_data[1] = tmp;
|
|
|
|
tmp = ((offset_buf[5] & bMsbFilter) << 8) + offset_buf[4];
|
|
if (tmp & wSignBitMask)
|
|
tmp |= wSignPaddingBits;
|
|
offset_data[2] = tmp;
|
|
|
|
memset(obj->cali_sw, 0x00, sizeof(obj->cali_sw));
|
|
|
|
return err;
|
|
}
|
|
|
|
/*****************************************
|
|
*** MC3XXX_WriteCalibration
|
|
*****************************************/
|
|
static int MC3XXX_WriteCalibration(struct i2c_client *client,
|
|
int dat[MC3XXX_AXES_NUM])
|
|
{
|
|
struct mc3xxx_i2c_data *obj = i2c_get_clientdata(client);
|
|
int err = 0;
|
|
u8 buf[9] = { 0 };
|
|
s16 tmp = 0, x_gain = 0, y_gain = 0, z_gain = 0;
|
|
s32 x_off = 0, y_off = 0, z_off = 0;
|
|
int cali[MC3XXX_AXES_NUM] = { 0 };
|
|
int _nTemp = 0;
|
|
|
|
u8 bMsbFilter = 0x3F;
|
|
s16 wSignBitMask = 0x2000;
|
|
s16 wSignPaddingBits = 0xC000;
|
|
s32 dwRangePosLimit = 0x1FFF;
|
|
s32 dwRangeNegLimit = -0x2000;
|
|
|
|
cali[MC3XXX_AXIS_X] =
|
|
obj->cvt.sign[MC3XXX_AXIS_X] * (dat[obj->cvt.map[MC3XXX_AXIS_X]]);
|
|
cali[MC3XXX_AXIS_Y] =
|
|
obj->cvt.sign[MC3XXX_AXIS_Y] * (dat[obj->cvt.map[MC3XXX_AXIS_Y]]);
|
|
cali[MC3XXX_AXIS_Z] =
|
|
obj->cvt.sign[MC3XXX_AXIS_Z] * (dat[obj->cvt.map[MC3XXX_AXIS_Z]]);
|
|
|
|
if (s_bPCODE == MC3XXX_PCODE_3250) {
|
|
_nTemp = cali[MC3XXX_AXIS_X];
|
|
cali[MC3XXX_AXIS_X] = -cali[MC3XXX_AXIS_Y];
|
|
cali[MC3XXX_AXIS_Y] = _nTemp;
|
|
} else {
|
|
if (s_bMPOL & 0x01)
|
|
cali[MC3XXX_AXIS_X] = -cali[MC3XXX_AXIS_X];
|
|
if (s_bMPOL & 0x02)
|
|
cali[MC3XXX_AXIS_Y] = -cali[MC3XXX_AXIS_Y];
|
|
}
|
|
|
|
/* read registers 0x21~0x29 */
|
|
err = MC3XXX_i2c_read_block(client, 0x21, buf, 3);
|
|
if (err) {
|
|
pr_err_ratelimited("[Gsensor]%s:error: %d\n", __func__, err);
|
|
return err;
|
|
}
|
|
err = MC3XXX_i2c_read_block(client, 0x24, &buf[3], 3);
|
|
if (err) {
|
|
pr_err_ratelimited("[Gsensor]%s:error: %d\n", __func__, err);
|
|
return err;
|
|
}
|
|
err = MC3XXX_i2c_read_block(client, 0x27, &buf[6], 3);
|
|
if (err) {
|
|
pr_err_ratelimited("[Gsensor]%s:error: %d\n", __func__, err);
|
|
return err;
|
|
}
|
|
|
|
if (IS_MCFM12() || IS_MCFM3X()) {
|
|
bMsbFilter = 0x7F;
|
|
wSignBitMask = 0x4000;
|
|
wSignPaddingBits = 0x8000;
|
|
dwRangePosLimit = 0x3FFF;
|
|
dwRangeNegLimit = -0x4000;
|
|
}
|
|
|
|
/* get x,y,z offset */
|
|
tmp = ((buf[1] & bMsbFilter) << 8) + buf[0];
|
|
if (tmp & wSignBitMask)
|
|
tmp |= wSignPaddingBits;
|
|
x_off = tmp;
|
|
|
|
tmp = ((buf[3] & bMsbFilter) << 8) + buf[2];
|
|
if (tmp & wSignBitMask)
|
|
tmp |= wSignPaddingBits;
|
|
y_off = tmp;
|
|
|
|
tmp = ((buf[5] & bMsbFilter) << 8) + buf[4];
|
|
if (tmp & wSignBitMask)
|
|
tmp |= wSignPaddingBits;
|
|
z_off = tmp;
|
|
|
|
/* get x,y,z gain */
|
|
x_gain = ((buf[1] >> 7) << 8) + buf[6];
|
|
y_gain = ((buf[3] >> 7) << 8) + buf[7];
|
|
z_gain = ((buf[5] >> 7) << 8) + buf[8];
|
|
|
|
/* prepare new offset */
|
|
x_off = x_off +
|
|
16 * cali[MC3XXX_AXIS_X] * 256 * 128 / 3 / gsensor_gain.x /
|
|
(40 + x_gain);
|
|
y_off = y_off +
|
|
16 * cali[MC3XXX_AXIS_Y] * 256 * 128 / 3 / gsensor_gain.y /
|
|
(40 + y_gain);
|
|
z_off = z_off +
|
|
16 * cali[MC3XXX_AXIS_Z] * 256 * 128 / 3 / gsensor_gain.z /
|
|
(40 + z_gain);
|
|
|
|
/* add for over range */
|
|
if (x_off > dwRangePosLimit)
|
|
x_off = dwRangePosLimit;
|
|
else if (x_off < dwRangeNegLimit)
|
|
x_off = dwRangeNegLimit;
|
|
|
|
if (y_off > dwRangePosLimit)
|
|
y_off = dwRangePosLimit;
|
|
else if (y_off < dwRangeNegLimit)
|
|
y_off = dwRangeNegLimit;
|
|
|
|
if (z_off > dwRangePosLimit)
|
|
z_off = dwRangePosLimit;
|
|
else if (z_off < dwRangeNegLimit)
|
|
z_off = dwRangeNegLimit;
|
|
|
|
/* storege the cerrunt offset data with DOT format */
|
|
offset_data[0] = x_off;
|
|
offset_data[1] = y_off;
|
|
offset_data[2] = z_off;
|
|
|
|
/* storege the cerrunt Gain data with GOT format */
|
|
gain_data[0] = 256 * 8 * 128 / 3 / (40 + x_gain);
|
|
gain_data[1] = 256 * 8 * 128 / 3 / (40 + y_gain);
|
|
gain_data[2] = 256 * 8 * 128 / 3 / (40 + z_gain);
|
|
|
|
buf[0] = 0x43;
|
|
MC3XXX_i2c_write_block(client, 0x07, buf, 1);
|
|
|
|
buf[0] = x_off & 0xff;
|
|
buf[1] = ((x_off >> 8) & bMsbFilter) | (x_gain & 0x0100 ? 0x80 : 0);
|
|
buf[2] = y_off & 0xff;
|
|
buf[3] = ((y_off >> 8) & bMsbFilter) | (y_gain & 0x0100 ? 0x80 : 0);
|
|
buf[4] = z_off & 0xff;
|
|
buf[5] = ((z_off >> 8) & bMsbFilter) | (z_gain & 0x0100 ? 0x80 : 0);
|
|
|
|
MC3XXX_i2c_write_block(client, 0x21, buf, 6);
|
|
|
|
buf[0] = 0x41;
|
|
MC3XXX_i2c_write_block(client, 0x07, buf, 1);
|
|
|
|
mdelay(50);
|
|
|
|
return err;
|
|
}
|
|
|
|
/*****************************************
|
|
*** MC3XXX_SetPowerMode
|
|
*****************************************/
|
|
static int MC3XXX_SetPowerMode(struct i2c_client *client, bool enable)
|
|
{
|
|
u8 databuf[2] = { 0 };
|
|
int res = 0;
|
|
u8 addr = MC3XXX_REG_MODE_FEATURE;
|
|
|
|
if (MC3XXX_i2c_read_block(client, addr, databuf, 1)) {
|
|
pr_err_ratelimited("[Gsensor]%s:read power ctl register err!\n",
|
|
__func__);
|
|
return MC3XXX_RETCODE_ERROR_I2C;
|
|
}
|
|
|
|
/* GSE_LOG("set power read MC3XXX_REG_MODE_FEATURE =%x\n", databuf[0]);
|
|
*/
|
|
|
|
if (enable) {
|
|
databuf[0] = 0x41;
|
|
res = MC3XXX_i2c_write_block(client, MC3XXX_REG_MODE_FEATURE,
|
|
databuf, 1);
|
|
#ifdef _MC3XXX_SUPPORT_DOT_CALIBRATION_
|
|
mcube_load_cali(client);
|
|
#endif
|
|
} else {
|
|
databuf[0] = 0x43;
|
|
res = MC3XXX_i2c_write_block(client, MC3XXX_REG_MODE_FEATURE,
|
|
databuf, 1);
|
|
}
|
|
|
|
if (res < 0) {
|
|
GSE_LOG("fwq set power mode failed!\n");
|
|
return MC3XXX_RETCODE_ERROR_I2C;
|
|
}
|
|
|
|
mc3xxx_sensor_power = enable;
|
|
if (mc3xxx_obj_i2c_data->flush) {
|
|
if (mc3xxx_sensor_power)
|
|
mc3410_flush();
|
|
else
|
|
mc3xxx_obj_i2c_data->flush = false;
|
|
}
|
|
return MC3XXX_RETCODE_SUCCESS;
|
|
}
|
|
|
|
/*****************************************
|
|
*** MC3XXX_SetResolution
|
|
*****************************************/
|
|
static void MC3XXX_SetResolution(void)
|
|
{
|
|
/* GSE_LOG("[%s]\n", __func__); */
|
|
|
|
switch (s_bPCODE) {
|
|
case MC3XXX_PCODE_3230:
|
|
case MC3XXX_PCODE_3430:
|
|
case MC3XXX_PCODE_3430N:
|
|
case MC3XXX_PCODE_3530:
|
|
case MC3XXX_PCODE_3236:
|
|
s_bResolution = MC3XXX_RESOLUTION_LOW;
|
|
break;
|
|
|
|
case MC3XXX_PCODE_3210:
|
|
case MC3XXX_PCODE_3250:
|
|
case MC3XXX_PCODE_3410:
|
|
case MC3XXX_PCODE_3410N:
|
|
case MC3XXX_PCODE_3510:
|
|
case MC3XXX_PCODE_3216:
|
|
s_bResolution = MC3XXX_RESOLUTION_HIGH;
|
|
break;
|
|
|
|
case MC3XXX_PCODE_RESERVE_10:
|
|
pr_err_ratelimited("[Gsensor]%s:RESERVED ONLINE!\n",
|
|
__func__);
|
|
/* TODO: should have a default configuration... */
|
|
break;
|
|
|
|
case MC3XXX_PCODE_RESERVE_1:
|
|
case MC3XXX_PCODE_RESERVE_3:
|
|
case MC3XXX_PCODE_RESERVE_4:
|
|
case MC3XXX_PCODE_RESERVE_5:
|
|
case MC3XXX_PCODE_RESERVE_6:
|
|
case MC3XXX_PCODE_RESERVE_8:
|
|
case MC3XXX_PCODE_RESERVE_9:
|
|
pr_err_ratelimited("[Gsensor]%s:RESERVED ONLINE!\n", __func__);
|
|
s_bResolution = MC3XXX_RESOLUTION_LOW;
|
|
break;
|
|
|
|
case MC3XXX_PCODE_RESERVE_2:
|
|
case MC3XXX_PCODE_RESERVE_7:
|
|
pr_err_ratelimited("[Gsensor]%s:RESERVED ONLINE!\n", __func__);
|
|
s_bResolution = MC3XXX_RESOLUTION_HIGH;
|
|
break;
|
|
|
|
default:
|
|
pr_err_ratelimited("[Gsensor]%s:ERR: no resolution assigned!\n",
|
|
__func__);
|
|
}
|
|
}
|
|
|
|
/*****************************************
|
|
*** MC3XXX_SetSampleRate
|
|
*****************************************/
|
|
static int MC3XXX_SetSampleRate(struct i2c_client *pt_i2c_client)
|
|
{
|
|
int err = 0;
|
|
unsigned char _baDataBuf[2] = { 0 };
|
|
|
|
/* GSE_LOG("[%s]\n", __func__); */
|
|
|
|
_baDataBuf[0] = MC3XXX_REG_SAMPLE_RATE;
|
|
_baDataBuf[1] = 0x00;
|
|
|
|
if (IS_MCFM12() || IS_MCFM3X()) {
|
|
unsigned char _baData2Buf[2] = { 0 };
|
|
|
|
_baData2Buf[0] = 0x2A;
|
|
MC3XXX_i2c_read_block(pt_i2c_client, 0x2A, _baData2Buf, 1);
|
|
|
|
/* GSE_LOG("[%s] REG(0x2A) = 0x%02X\n", __func__,
|
|
* _baData2Buf[0]);
|
|
*/
|
|
|
|
_baData2Buf[0] = (_baData2Buf[0] & 0xC0);
|
|
|
|
switch (_baData2Buf[0]) {
|
|
case 0x00:
|
|
_baDataBuf[0] = 0x00;
|
|
break;
|
|
case 0x40:
|
|
_baDataBuf[0] = 0x08;
|
|
break;
|
|
case 0x80:
|
|
_baDataBuf[0] = 0x09;
|
|
break;
|
|
case 0xC0:
|
|
_baDataBuf[0] = 0x0A;
|
|
break;
|
|
default:
|
|
pr_err_ratelimited("[Gsensor]%s: no chance to get here... check code!\n",
|
|
__func__);
|
|
break;
|
|
}
|
|
} else
|
|
_baDataBuf[0] = 0x00;
|
|
|
|
err = MC3XXX_i2c_write_block(pt_i2c_client, MC3XXX_REG_SAMPLE_RATE,
|
|
_baDataBuf, 1);
|
|
return err;
|
|
}
|
|
|
|
/*****************************************
|
|
*** MC3XXX_ConfigRegRange
|
|
*****************************************/
|
|
static void MC3XXX_ConfigRegRange(struct i2c_client *pt_i2c_client)
|
|
{
|
|
unsigned char _baDataBuf[2] = { 0 };
|
|
int res = 0;
|
|
|
|
/* _baDataBuf[0] = 0x3F;
|
|
*/
|
|
/* Modify low pass filter bandwidth to 512hz, for solving sensor data
|
|
* don't change issue
|
|
*/
|
|
_baDataBuf[0] = 0x0F;
|
|
|
|
if (s_bResolution == MC3XXX_RESOLUTION_LOW)
|
|
_baDataBuf[0] = 0x32;
|
|
|
|
if (IS_MCFM12() || IS_MCFM3X()) {
|
|
if (s_bResolution == MC3XXX_RESOLUTION_LOW)
|
|
_baDataBuf[0] = 0x02;
|
|
else
|
|
_baDataBuf[0] = 0x25;
|
|
}
|
|
res = MC3XXX_i2c_write_block(pt_i2c_client, MC3XXX_REG_RANGE_CONTROL,
|
|
_baDataBuf, 1);
|
|
if (res < 0)
|
|
pr_err_ratelimited("[Gsensor]%s: fail\n", __func__);
|
|
|
|
/* GSE_LOG("[%s] set 0x%X\n", __func__, _baDataBuf[1]); */
|
|
}
|
|
|
|
/*****************************************
|
|
*** MC3XXX_SetGain
|
|
*****************************************/
|
|
static void MC3XXX_SetGain(void)
|
|
{
|
|
gsensor_gain.x = gsensor_gain.y = gsensor_gain.z = 1024;
|
|
|
|
if (s_bResolution == MC3XXX_RESOLUTION_LOW) {
|
|
gsensor_gain.x = gsensor_gain.y = gsensor_gain.z = 86;
|
|
|
|
if (IS_MCFM12() || IS_MCFM3X())
|
|
gsensor_gain.x = gsensor_gain.y = gsensor_gain.z = 64;
|
|
}
|
|
|
|
/* GSE_LOG("[%s] gain: %d / %d / %d\n", __func__, gsensor_gain.x,
|
|
* gsensor_gain.y, gsensor_gain.z);
|
|
*/
|
|
}
|
|
|
|
/*****************************************
|
|
*** MC3XXX_Init
|
|
*****************************************/
|
|
static int MC3XXX_Init(struct i2c_client *client, int reset_cali)
|
|
{
|
|
unsigned char _baDataBuf[2] = { 0 };
|
|
|
|
/* GSE_LOG("[%s]\n", __func__); */
|
|
|
|
#ifdef _MC3XXX_SUPPORT_POWER_SAVING_SHUTDOWN_POWER_
|
|
if (_mc3xxx_i2c_auto_probe(client != MC3XXX_RETCODE_SUCCESS))
|
|
return MC3XXX_RETCODE_ERROR_I2C;
|
|
|
|
/* GSE_LOG("[%s] confirmed i2c addr: 0x%X\n", __FUNCTION__, client->addr); */
|
|
#endif
|
|
|
|
_baDataBuf[0] = 0x43;
|
|
MC3XXX_i2c_write_block(client, MC3XXX_REG_MODE_FEATURE, _baDataBuf, 1);
|
|
|
|
MC3XXX_SetResolution();
|
|
MC3XXX_SetSampleRate(client);
|
|
MC3XXX_ConfigRegRange(client);
|
|
MC3XXX_SetGain();
|
|
|
|
_baDataBuf[0] = 0x00;
|
|
MC3XXX_i2c_write_block(client, MC3XXX_REG_TAP_DETECTION_ENABLE,
|
|
_baDataBuf, 1);
|
|
|
|
_baDataBuf[0] = 0x00;
|
|
MC3XXX_i2c_write_block(client, MC3XXX_REG_INTERRUPT_ENABLE, _baDataBuf,
|
|
1);
|
|
|
|
_baDataBuf[0] = 0;
|
|
MC3XXX_i2c_read_block(client, 0x2A, _baDataBuf, 1);
|
|
s_bMPOL = (_baDataBuf[0] & 0x03);
|
|
|
|
#ifdef _MC3XXX_SUPPORT_DOT_CALIBRATION_
|
|
MC3XXX_rbm(client, 0);
|
|
#endif
|
|
|
|
#ifdef _MC3XXX_SUPPORT_LPF_
|
|
{
|
|
struct mc3xxx_i2c_data *_pt_i2c_data =
|
|
i2c_get_clientdata(client);
|
|
|
|
memset(&_pt_i2c_data->fir, 0x00, sizeof(_pt_i2c_data->fir));
|
|
}
|
|
#endif
|
|
|
|
#ifdef _MC3XXX_SUPPORT_LRF_
|
|
memset(&s_taLRF_CB, 0, sizeof(s_taLRF_CB));
|
|
#endif
|
|
|
|
#ifdef _MC3XXX_SUPPORT_PERIODIC_DOC_
|
|
init_waitqueue_head(&wq_mc3xxx_open_status);
|
|
#endif
|
|
|
|
/* GSE_LOG("[%s] init ok.\n", __func__); */
|
|
|
|
return MC3XXX_RETCODE_SUCCESS;
|
|
}
|
|
|
|
/*****************************************
|
|
*** MC3XXX_ReadChipInfo
|
|
*****************************************/
|
|
static int MC3XXX_ReadChipInfo(struct i2c_client *client, char *buf,
|
|
int bufsize)
|
|
{
|
|
int ret = 0;
|
|
if ((buf == NULL) || (bufsize <= 30))
|
|
return -1;
|
|
|
|
if (client == NULL) {
|
|
*buf = 0;
|
|
return -2;
|
|
}
|
|
|
|
ret = sprintf(buf, "MC3XXX Chip");
|
|
if (ret < 0)
|
|
pr_debug("%s:Chipname sprintf fail:%d\n", __func__, ret);
|
|
return 0;
|
|
}
|
|
|
|
/*****************************************
|
|
*** MC3XXX_ReadSensorData
|
|
*****************************************/
|
|
static int MC3XXX_ReadSensorData(struct i2c_client *pt_i2c_client, char *pbBuf,
|
|
int nBufSize)
|
|
{
|
|
int _naAccelData[MC3XXX_AXES_NUM] = { 0 };
|
|
struct mc3xxx_i2c_data *_pt_i2c_obj = NULL;
|
|
|
|
if ((pt_i2c_client == NULL) || (pbBuf == NULL)) {
|
|
pr_err_ratelimited("[Gsensor]%s:ERR: Null Pointer\n", __func__);
|
|
return MC3XXX_RETCODE_ERROR_NULL_POINTER;
|
|
}
|
|
_pt_i2c_obj =
|
|
((struct mc3xxx_i2c_data *)i2c_get_clientdata(pt_i2c_client));
|
|
if (false == mc3xxx_sensor_power) {
|
|
if (MC3XXX_SetPowerMode(pt_i2c_client, true) !=
|
|
MC3XXX_RETCODE_SUCCESS) {
|
|
pr_err_ratelimited("[Gsensor]%s:ERR: fail to set power mode!\n",
|
|
__func__);
|
|
return MC3XXX_RETCODE_ERROR_I2C;
|
|
}
|
|
}
|
|
|
|
#ifdef _MC3XXX_SUPPORT_DOT_CALIBRATION_
|
|
mcube_load_cali(pt_i2c_client);
|
|
|
|
if ((s_nIsRBM_Enabled) && (LPF_FirstRun == 1)) {
|
|
int _nLoopIndex = 0;
|
|
|
|
LPF_FirstRun = 0;
|
|
|
|
for (_nLoopIndex = 0;
|
|
_nLoopIndex < (LPF_SamplingRate + LPF_CutoffFrequency);
|
|
_nLoopIndex++)
|
|
MC3XXX_ReadData(pt_i2c_client, _pt_i2c_obj->data);
|
|
}
|
|
#endif
|
|
|
|
if (MC3XXX_ReadData(pt_i2c_client, _pt_i2c_obj->data) !=
|
|
MC3XXX_RETCODE_SUCCESS) {
|
|
pr_err_ratelimited("[Gsensor]%s:ERR: fail to read data!\n",
|
|
__func__);
|
|
|
|
return MC3XXX_RETCODE_ERROR_I2C;
|
|
}
|
|
|
|
_naAccelData[(_pt_i2c_obj->cvt.map[MC3XXX_AXIS_X])] =
|
|
(_pt_i2c_obj->cvt.sign[MC3XXX_AXIS_X] *
|
|
_pt_i2c_obj->data[MC3XXX_AXIS_X]);
|
|
_naAccelData[(_pt_i2c_obj->cvt.map[MC3XXX_AXIS_Y])] =
|
|
(_pt_i2c_obj->cvt.sign[MC3XXX_AXIS_Y] *
|
|
_pt_i2c_obj->data[MC3XXX_AXIS_Y]);
|
|
_naAccelData[(_pt_i2c_obj->cvt.map[MC3XXX_AXIS_Z])] =
|
|
(_pt_i2c_obj->cvt.sign[MC3XXX_AXIS_Z] *
|
|
_pt_i2c_obj->data[MC3XXX_AXIS_Z]);
|
|
|
|
_naAccelData[MC3XXX_AXIS_X] =
|
|
(_naAccelData[MC3XXX_AXIS_X] * GRAVITY_EARTH_1000 / gsensor_gain.x);
|
|
_naAccelData[MC3XXX_AXIS_Y] =
|
|
(_naAccelData[MC3XXX_AXIS_Y] * GRAVITY_EARTH_1000 / gsensor_gain.y);
|
|
_naAccelData[MC3XXX_AXIS_Z] =
|
|
(_naAccelData[MC3XXX_AXIS_Z] * GRAVITY_EARTH_1000 / gsensor_gain.z);
|
|
|
|
sprintf(pbBuf, "%04x %04x %04x", _naAccelData[MC3XXX_AXIS_X],
|
|
_naAccelData[MC3XXX_AXIS_Y], _naAccelData[MC3XXX_AXIS_Z]);
|
|
|
|
return MC3XXX_RETCODE_SUCCESS;
|
|
}
|
|
|
|
/*****************************************
|
|
*** MC3XXX_ReadRawData
|
|
*****************************************/
|
|
static int MC3XXX_ReadRawData(struct i2c_client *client, char *buf)
|
|
{
|
|
int res = 0;
|
|
|
|
if (!buf || !client)
|
|
return -EINVAL;
|
|
|
|
if (mc3xxx_sensor_power == false) {
|
|
res = MC3XXX_SetPowerMode(client, true);
|
|
if (res) {
|
|
pr_err_ratelimited("[Gsensor]%s:Power on mc3xxx error %d!\n",
|
|
__func__, res);
|
|
return MC3XXX_RETCODE_ERROR_SETUP;
|
|
}
|
|
}
|
|
|
|
#ifdef _MC3XXX_SUPPORT_APPLY_AVERAGE_AGORITHM_
|
|
return _MC3XXX_ReadAverageData(client, buf);
|
|
#else
|
|
{
|
|
s16 sensor_data[3] = { 0 };
|
|
res = MC3XXX_ReadData(client, sensor_data);
|
|
if (res) {
|
|
pr_err_ratelimited("[Gsensor]%s:I2C error: ret value=%d",
|
|
__func__, res);
|
|
return -EIO;
|
|
}
|
|
res = sprintf(buf, "%04x %04x %04x", sensor_data[MC3XXX_AXIS_X],
|
|
sensor_data[MC3XXX_AXIS_Y], sensor_data[MC3XXX_AXIS_Z]);
|
|
if (res < 0)
|
|
pr_debug("%s: sprintf failed: %d\n", __func__, res);
|
|
}
|
|
#endif
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*****************************************
|
|
*** show_chipinfo_value
|
|
*****************************************/
|
|
static ssize_t show_chipinfo_value(struct device_driver *ddri, char *buf)
|
|
{
|
|
struct i2c_client *client = mc3xxx_i2c_client;
|
|
char strbuf[MC3XXX_BUF_SIZE] = { 0 };
|
|
|
|
if (client == NULL) {
|
|
pr_err_ratelimited("[Gsensor]%s:i2c client is null!!\n",
|
|
__func__);
|
|
return 0;
|
|
}
|
|
|
|
MC3XXX_ReadChipInfo(client, strbuf, MC3XXX_BUF_SIZE);
|
|
return snprintf(buf, PAGE_SIZE, "%s\n", strbuf);
|
|
}
|
|
|
|
/*****************************************
|
|
*** show_sensordata_value
|
|
*****************************************/
|
|
static ssize_t show_sensordata_value(struct device_driver *ddri, char *buf)
|
|
{
|
|
struct i2c_client *client = mc3xxx_i2c_client;
|
|
char strbuf[MC3XXX_BUF_SIZE] = { 0 };
|
|
|
|
if (client == NULL) {
|
|
pr_err_ratelimited("[Gsensor]%s:i2c client is null!!\n",
|
|
__func__);
|
|
return 0;
|
|
}
|
|
|
|
mc3xxx_mutex_lock();
|
|
MC3XXX_ReadSensorData(client, strbuf, MC3XXX_BUF_SIZE);
|
|
mc3xxx_mutex_unlock();
|
|
return snprintf(buf, PAGE_SIZE, "%s\n", strbuf);
|
|
}
|
|
|
|
/*****************************************
|
|
*** show_cali_value
|
|
*****************************************/
|
|
static ssize_t show_cali_value(struct device_driver *ddri, char *buf)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
/*****************************************
|
|
*** store_cali_value
|
|
*****************************************/
|
|
static ssize_t store_cali_value(struct device_driver *ddri, const char *buf,
|
|
size_t count)
|
|
{
|
|
struct i2c_client *client = mc3xxx_i2c_client;
|
|
int err = 0;
|
|
int x = 0;
|
|
int y = 0;
|
|
int z = 0;
|
|
int dat[MC3XXX_AXES_NUM] = { 0 };
|
|
|
|
if (!strncmp(buf, "rst", 3)) {
|
|
mc3xxx_mutex_lock();
|
|
err = MC3XXX_ResetCalibration(client);
|
|
mc3xxx_mutex_unlock();
|
|
|
|
if (err)
|
|
pr_err_ratelimited("[Gsensor]%s:reset offset err = %d\n",
|
|
__func__, err);
|
|
} else if (sscanf(buf, "0x%02X 0x%02X 0x%02X", &x, &y, &z) == 3) {
|
|
dat[MC3XXX_AXIS_X] = x;
|
|
dat[MC3XXX_AXIS_Y] = y;
|
|
dat[MC3XXX_AXIS_Z] = z;
|
|
|
|
mc3xxx_mutex_lock();
|
|
err = MC3XXX_WriteCalibration(client, dat);
|
|
mc3xxx_mutex_unlock();
|
|
if (err)
|
|
pr_err_ratelimited("[Gsensor]%s:write calibration err = %d\n",
|
|
__func__, err);
|
|
} else
|
|
pr_err_ratelimited("[Gsensor]%s:invalid format\n", __func__);
|
|
|
|
return count;
|
|
}
|
|
|
|
/*****************************************
|
|
*** show_selftest_value
|
|
*****************************************/
|
|
static ssize_t show_selftest_value(struct device_driver *ddri, char *buf)
|
|
{
|
|
struct i2c_client *client = mc3xxx_i2c_client;
|
|
|
|
if (client == NULL) {
|
|
pr_err_ratelimited("[Gsensor]%s:i2c client is null!!\n",
|
|
__func__);
|
|
return 0;
|
|
}
|
|
|
|
return snprintf(buf, 8, "%s\n", selftestRes);
|
|
}
|
|
|
|
/*****************************************
|
|
*** store_selftest_value
|
|
*****************************************/
|
|
static ssize_t store_selftest_value(struct device_driver *ddri, const char *buf,
|
|
size_t count)
|
|
{
|
|
return count;
|
|
}
|
|
|
|
/*****************************************
|
|
*** show_firlen_value
|
|
*****************************************/
|
|
static ssize_t show_firlen_value(struct device_driver *ddri, char *buf)
|
|
{
|
|
#ifdef _MC3XXX_SUPPORT_LPF_
|
|
struct i2c_client *client = mc3xxx_i2c_client;
|
|
struct mc3xxx_i2c_data *obj = i2c_get_clientdata(client);
|
|
|
|
return snprintf(buf, PAGE_SIZE, "%d\n", atomic_read(&obj->firlen));
|
|
#else
|
|
return snprintf(buf, PAGE_SIZE, "not support\n");
|
|
#endif
|
|
}
|
|
|
|
/*****************************************
|
|
*** store_firlen_value
|
|
*****************************************/
|
|
static ssize_t store_firlen_value(struct device_driver *ddri, const char *buf,
|
|
size_t count)
|
|
{
|
|
#ifdef _MC3XXX_SUPPORT_LPF_
|
|
struct i2c_client *client = mc3xxx_i2c_client;
|
|
struct mc3xxx_i2c_data *obj = i2c_get_clientdata(client);
|
|
int firlen = 0;
|
|
int ret = 0;
|
|
|
|
ret = kstrtoint(buf, 10, &firlen);
|
|
if (ret != 0)
|
|
pr_err_ratelimited("[Gsensor]%s:invallid format\n", __func__);
|
|
else if (firlen > C_MAX_FIR_LENGTH)
|
|
pr_err_ratelimited("[Gsensor]%s:exceeds maximum filter length\n",
|
|
__func__);
|
|
else {
|
|
atomic_set(&obj->firlen, firlen);
|
|
if (firlen == 0)
|
|
atomic_set(&obj->fir_en, 0);
|
|
else {
|
|
memset(&obj->fir, 0x00, sizeof(obj->fir));
|
|
atomic_set(&obj->fir_en, 1);
|
|
}
|
|
}
|
|
#endif
|
|
return count;
|
|
}
|
|
|
|
/*****************************************
|
|
*** show_trace_value
|
|
*****************************************/
|
|
static ssize_t show_trace_value(struct device_driver *ddri, char *buf)
|
|
{
|
|
ssize_t res = 0;
|
|
struct mc3xxx_i2c_data *obj = mc3xxx_obj_i2c_data;
|
|
|
|
if (obj == NULL) {
|
|
pr_err_ratelimited("[Gsensor]%s:i2c_data obj is null!!\n",
|
|
__func__);
|
|
return 0;
|
|
}
|
|
|
|
res = snprintf(buf, PAGE_SIZE, "0x%04X\n", atomic_read(&obj->trace));
|
|
return res;
|
|
}
|
|
|
|
/*****************************************
|
|
*** store_trace_value
|
|
*****************************************/
|
|
static ssize_t store_trace_value(struct device_driver *ddri, const char *buf,
|
|
size_t count)
|
|
{
|
|
struct mc3xxx_i2c_data *obj = mc3xxx_obj_i2c_data;
|
|
int trace = 0;
|
|
|
|
if (obj == NULL) {
|
|
pr_err_ratelimited("[Gsensor]%s:i2c_data obj is null!!\n",
|
|
__func__);
|
|
return 0;
|
|
}
|
|
|
|
if (sscanf(buf, "0x%x", &trace) == 1)
|
|
atomic_set(&obj->trace, trace);
|
|
else
|
|
pr_err_ratelimited("[Gsensor]%s:invalid content: '%s', length = %zu\n",
|
|
__func__, buf, count);
|
|
|
|
return count;
|
|
}
|
|
|
|
/*****************************************
|
|
*** show_status_value
|
|
*****************************************/
|
|
static ssize_t show_status_value(struct device_driver *ddri, char *buf)
|
|
{
|
|
ssize_t len = 0;
|
|
struct mc3xxx_i2c_data *obj = mc3xxx_obj_i2c_data;
|
|
|
|
if (obj == NULL) {
|
|
pr_err_ratelimited("[Gsensor]%s:i2c_data obj is null!!\n",
|
|
__func__);
|
|
return 0;
|
|
}
|
|
|
|
len += snprintf(buf + len, PAGE_SIZE - len, "CUST: %d %d (%d %d)\n",
|
|
obj->hw.i2c_num, obj->hw.direction, obj->hw.power_id,
|
|
obj->hw.power_vol);
|
|
|
|
return len;
|
|
}
|
|
|
|
/*****************************************
|
|
*** show_power_status
|
|
*****************************************/
|
|
static ssize_t show_power_status(struct device_driver *ddri, char *buf)
|
|
{
|
|
ssize_t res = 0;
|
|
u8 uData = 0;
|
|
struct mc3xxx_i2c_data *obj = mc3xxx_obj_i2c_data;
|
|
|
|
if (obj == NULL) {
|
|
pr_err_ratelimited("[Gsensor]%s:i2c_data obj is null!!\n",
|
|
__func__);
|
|
return 0;
|
|
}
|
|
MC3XXX_i2c_read_block(obj->client, MC3XXX_REG_MODE_FEATURE, &uData, 1);
|
|
|
|
res = snprintf(buf, PAGE_SIZE, "0x%04X\n", uData);
|
|
if (res < 0)
|
|
pr_debug("%s: snprintf failed: %d\n", __func__, res);
|
|
return res;
|
|
}
|
|
|
|
/*****************************************
|
|
*** show_version_value
|
|
*****************************************/
|
|
static ssize_t show_version_value(struct device_driver *ddri, char *buf)
|
|
{
|
|
if (1 == VIRTUAL_Z)
|
|
return snprintf(buf, PAGE_SIZE, "%s\n",
|
|
MC3XXX_DEV_DRIVER_VERSION_VIRTUAL_Z);
|
|
else
|
|
return snprintf(buf, PAGE_SIZE, "%s\n",
|
|
MC3XXX_DEV_DRIVER_VERSION);
|
|
}
|
|
|
|
/*****************************************
|
|
*** show_chip_id
|
|
*****************************************/
|
|
static ssize_t show_chip_id(struct device_driver *ddri, char *buf) { return 0; }
|
|
|
|
/*****************************************
|
|
*** show_virtual_z
|
|
*****************************************/
|
|
static ssize_t show_virtual_z(struct device_driver *ddri, char *buf)
|
|
{
|
|
return snprintf(buf, PAGE_SIZE, "%s\n", VIRTUAL_Z == 1
|
|
? "Virtual Z Support"
|
|
: "No Virtual Z Support");
|
|
}
|
|
|
|
/*****************************************
|
|
*** show_regiter_map
|
|
*****************************************/
|
|
static ssize_t show_regiter_map(struct device_driver *ddri, char *buf)
|
|
{
|
|
u8 _bIndex = 0;
|
|
u8 _baRegMap[64] = { 0 };
|
|
ssize_t _tLength = 0;
|
|
|
|
struct i2c_client *client = mc3xxx_i2c_client;
|
|
|
|
if ((buf[0] == 0xA5) && (buf[1] == 0x7B) && (buf[2] == 0x40)) {
|
|
mc3xxx_mutex_lock();
|
|
MC3XXX_Read_Reg_Map(client, buf);
|
|
mc3xxx_mutex_unlock();
|
|
|
|
buf[0x21] = s_baOTP_OffsetData[0];
|
|
buf[0x22] = s_baOTP_OffsetData[1];
|
|
buf[0x23] = s_baOTP_OffsetData[2];
|
|
buf[0x24] = s_baOTP_OffsetData[3];
|
|
buf[0x25] = s_baOTP_OffsetData[4];
|
|
buf[0x26] = s_baOTP_OffsetData[5];
|
|
|
|
_tLength = 64;
|
|
} else {
|
|
mc3xxx_mutex_lock();
|
|
MC3XXX_Read_Reg_Map(client, _baRegMap);
|
|
mc3xxx_mutex_unlock();
|
|
|
|
for (_bIndex = 0; _bIndex < 64; _bIndex++)
|
|
_tLength +=
|
|
snprintf((buf + _tLength), (PAGE_SIZE - _tLength),
|
|
"Reg[0x%02X]: 0x%02X\n", _bIndex,
|
|
_baRegMap[_bIndex]);
|
|
}
|
|
|
|
return _tLength;
|
|
}
|
|
|
|
/*****************************************
|
|
*** store_regiter_map
|
|
*****************************************/
|
|
static ssize_t store_regiter_map(struct device_driver *ddri, const char *buf,
|
|
size_t count)
|
|
{
|
|
return count;
|
|
}
|
|
|
|
/*****************************************
|
|
*** show_chip_orientation
|
|
*****************************************/
|
|
static ssize_t show_chip_orientation(struct device_driver *ptDevDrv,
|
|
char *pbBuf)
|
|
{
|
|
ssize_t _tLength = 0;
|
|
struct mc3xxx_i2c_data *obj = mc3xxx_obj_i2c_data;
|
|
|
|
if (obj == NULL)
|
|
return 0;
|
|
|
|
_tLength = snprintf(pbBuf, PAGE_SIZE, "default direction = %d\n",
|
|
obj->hw.direction);
|
|
|
|
return _tLength;
|
|
}
|
|
|
|
/*****************************************
|
|
*** store_chip_orientation
|
|
*****************************************/
|
|
static ssize_t store_chip_orientation(struct device_driver *ptDevDrv,
|
|
const char *pbBuf, size_t tCount)
|
|
{
|
|
int _nDirection = 0;
|
|
int ret = 0;
|
|
struct mc3xxx_i2c_data *_pt_i2c_obj = mc3xxx_obj_i2c_data;
|
|
|
|
if (_pt_i2c_obj == NULL)
|
|
return 0;
|
|
|
|
ret = kstrtoint(pbBuf, 10, &_nDirection);
|
|
if (ret == 0) {
|
|
if (hwmsen_get_convert(_nDirection, &_pt_i2c_obj->cvt)) {
|
|
pr_err_ratelimited("[Gsensor]%s:ERR: fail to set direction\n",
|
|
__func__);
|
|
}
|
|
}
|
|
|
|
return tCount;
|
|
}
|
|
|
|
/*****************************************
|
|
*** show_accuracy_status
|
|
*****************************************/
|
|
static ssize_t show_accuracy_status(struct device_driver *ddri, char *buf)
|
|
{
|
|
return snprintf(buf, PAGE_SIZE, "%d\n", s_bAccuracyStatus);
|
|
}
|
|
|
|
/*****************************************
|
|
*** store_accuracy_status
|
|
*****************************************/
|
|
static ssize_t store_accuracy_status(struct device_driver *ddri,
|
|
const char *buf, size_t count)
|
|
{
|
|
int _nAccuracyStatus = 0;
|
|
int ret = 0;
|
|
|
|
ret = kstrtoint(buf, 10, &_nAccuracyStatus);
|
|
if (ret != 0) {
|
|
pr_err_ratelimited("[Gsensor]%s:incorrect argument\n",
|
|
__func__);
|
|
return count;
|
|
}
|
|
|
|
if (_nAccuracyStatus > SENSOR_STATUS_ACCURACY_HIGH) {
|
|
pr_err_ratelimited("[Gsensor]%s:illegal accuracy status\n",
|
|
__func__);
|
|
return count;
|
|
}
|
|
|
|
s_bAccuracyStatus = ((int8_t)_nAccuracyStatus);
|
|
|
|
return count;
|
|
}
|
|
|
|
/*****************************************
|
|
*** show_selfcheck_value
|
|
*****************************************/
|
|
static ssize_t show_selfcheck_value(struct device_driver *ptDevDriver,
|
|
char *pbBuf)
|
|
{
|
|
return 64;
|
|
}
|
|
|
|
/*****************************************
|
|
*** store_selfcheck_value
|
|
*****************************************/
|
|
static ssize_t store_selfcheck_value(struct device_driver *ddri,
|
|
const char *buf, size_t count)
|
|
{
|
|
/* reserved */
|
|
/* GSE_LOG("[%s] buf[0]: 0x%02X\n", __FUNCTION__, buf[0]); */
|
|
|
|
return count;
|
|
}
|
|
|
|
/*****************************************
|
|
*** show_chip_validate_value
|
|
*****************************************/
|
|
static ssize_t show_chip_validate_value(struct device_driver *ptDevDriver,
|
|
char *pbBuf)
|
|
{
|
|
unsigned char _bChipValidation = 0;
|
|
|
|
_bChipValidation = MC3XXX_ValidateSensorIC(&s_bPCODE, &s_bHWID);
|
|
|
|
return snprintf(pbBuf, PAGE_SIZE, "%d\n", _bChipValidation);
|
|
}
|
|
|
|
/*****************************************
|
|
*** show_pdoc_enable_value
|
|
*****************************************/
|
|
static ssize_t show_pdoc_enable_value(struct device_driver *ptDevDriver,
|
|
char *pbBuf)
|
|
{
|
|
unsigned char _bIsPDOC_Enabled = false;
|
|
|
|
return snprintf(pbBuf, PAGE_SIZE, "%d\n", _bIsPDOC_Enabled);
|
|
}
|
|
|
|
/*****************************************
|
|
*** DRIVER ATTRIBUTE LIST TABLE
|
|
*****************************************/
|
|
static DRIVER_ATTR(chipinfo, 0444, show_chipinfo_value, NULL);
|
|
static DRIVER_ATTR(sensordata, 0444, show_sensordata_value, NULL);
|
|
static DRIVER_ATTR(cali, 0644, show_cali_value, store_cali_value);
|
|
static DRIVER_ATTR(selftest, 0644, show_selftest_value,
|
|
store_selftest_value);
|
|
static DRIVER_ATTR(firlen, 0644, show_firlen_value,
|
|
store_firlen_value);
|
|
static DRIVER_ATTR(trace, 0644, show_trace_value,
|
|
store_trace_value);
|
|
static DRIVER_ATTR(status, 0444, show_status_value, NULL);
|
|
static DRIVER_ATTR(power, 0444, show_power_status, NULL);
|
|
static DRIVER_ATTR(version, 0444, show_version_value, NULL);
|
|
static DRIVER_ATTR(chipid, 0444, show_chip_id, NULL);
|
|
static DRIVER_ATTR(virtualz, 0444, show_virtual_z, NULL);
|
|
static DRIVER_ATTR(regmap, 0644, show_regiter_map,
|
|
store_regiter_map);
|
|
static DRIVER_ATTR(orientation, 0644, show_chip_orientation,
|
|
store_chip_orientation);
|
|
static DRIVER_ATTR(accuracy, 0644, show_accuracy_status,
|
|
store_accuracy_status);
|
|
static DRIVER_ATTR(selfcheck, 0644, show_selfcheck_value,
|
|
store_selfcheck_value);
|
|
static DRIVER_ATTR(validate, 0444, show_chip_validate_value, NULL);
|
|
static DRIVER_ATTR(pdoc, 0444, show_pdoc_enable_value, NULL);
|
|
|
|
static struct driver_attribute *mc3xxx_attr_list[] = {
|
|
&driver_attr_chipinfo, &driver_attr_sensordata,
|
|
&driver_attr_cali, &driver_attr_selftest,
|
|
&driver_attr_firlen, &driver_attr_trace,
|
|
&driver_attr_status, &driver_attr_power,
|
|
&driver_attr_version, &driver_attr_chipid,
|
|
&driver_attr_virtualz, &driver_attr_regmap,
|
|
&driver_attr_orientation, &driver_attr_accuracy,
|
|
&driver_attr_selfcheck, &driver_attr_validate,
|
|
&driver_attr_pdoc,
|
|
};
|
|
|
|
/*****************************************
|
|
*** mc3xxx_create_attr
|
|
*****************************************/
|
|
static int mc3xxx_create_attr(struct device_driver *driver)
|
|
{
|
|
int idx, err = 0;
|
|
int num = (int)ARRAY_SIZE(mc3xxx_attr_list);
|
|
|
|
if (driver == NULL)
|
|
return -EINVAL;
|
|
|
|
for (idx = 0; idx < num; idx++) {
|
|
err = driver_create_file(driver, mc3xxx_attr_list[idx]);
|
|
if (err) {
|
|
pr_err_ratelimited("[Gsensor]%s:driver_create_file (%s) = %d\n",
|
|
__func__,
|
|
mc3xxx_attr_list[idx]->attr.name,
|
|
err);
|
|
break;
|
|
}
|
|
}
|
|
return err;
|
|
}
|
|
|
|
/*****************************************
|
|
*** mc3xxx_delete_attr
|
|
*****************************************/
|
|
static int mc3xxx_delete_attr(struct device_driver *driver)
|
|
{
|
|
int idx, err = 0;
|
|
int num = (int)ARRAY_SIZE(mc3xxx_attr_list);
|
|
|
|
if (driver == NULL)
|
|
return -EINVAL;
|
|
|
|
for (idx = 0; idx < num; idx++)
|
|
driver_remove_file(driver, mc3xxx_attr_list[idx]);
|
|
|
|
return err;
|
|
}
|
|
|
|
/*****************************************
|
|
*** MC3XXX_reset
|
|
*****************************************/
|
|
static void MC3XXX_reset(struct i2c_client *client)
|
|
{
|
|
unsigned char _baBuf[2] = { 0 };
|
|
|
|
_baBuf[0] = 0x43;
|
|
|
|
MC3XXX_i2c_write_block(client, MC3XXX_REG_MODE_FEATURE, _baBuf, 0x01);
|
|
|
|
MC3XXX_i2c_read_block(client, 0x04, _baBuf, 0x01);
|
|
|
|
if (0x00 == (_baBuf[0] & 0x40)) {
|
|
_baBuf[0] = 0x6D;
|
|
MC3XXX_i2c_write_block(client, 0x1B, _baBuf, 0x01);
|
|
|
|
_baBuf[0] = 0x43;
|
|
MC3XXX_i2c_write_block(client, 0x1B, _baBuf, 0x01);
|
|
}
|
|
|
|
_baBuf[0] = 0x43;
|
|
MC3XXX_i2c_write_block(client, 0x07, _baBuf, 1);
|
|
|
|
_baBuf[0] = 0x80;
|
|
MC3XXX_i2c_write_block(client, 0x1C, _baBuf, 1);
|
|
|
|
_baBuf[0] = 0x80;
|
|
MC3XXX_i2c_write_block(client, 0x17, _baBuf, 1);
|
|
|
|
mdelay(5);
|
|
|
|
_baBuf[0] = 0x00;
|
|
MC3XXX_i2c_write_block(client, 0x1C, _baBuf, 1);
|
|
|
|
_baBuf[0] = 0x00;
|
|
MC3XXX_i2c_write_block(client, 0x17, _baBuf, 1);
|
|
|
|
mdelay(5);
|
|
|
|
MC3XXX_i2c_read_block(client, 0x21, offset_buf, 6);
|
|
|
|
MC3XXX_i2c_read_block(client, 0x04, _baBuf, 0x01);
|
|
|
|
if (_baBuf[0] & 0x40) {
|
|
_baBuf[0] = 0x6D;
|
|
MC3XXX_i2c_write_block(client, 0x1B, _baBuf, 0x01);
|
|
|
|
_baBuf[0] = 0x43;
|
|
MC3XXX_i2c_write_block(client, 0x1B, _baBuf, 0x01);
|
|
}
|
|
|
|
_baBuf[0] = 0x41;
|
|
|
|
MC3XXX_i2c_write_block(client, MC3XXX_REG_MODE_FEATURE, _baBuf, 0x01);
|
|
}
|
|
|
|
#ifdef CONFIG_PM_SLEEP
|
|
/*****************************************
|
|
*** mc3xxx_suspend
|
|
*****************************************/
|
|
static int mc3xxx_suspend(struct device *dev)
|
|
{
|
|
struct i2c_client *client = to_i2c_client(dev);
|
|
struct mc3xxx_i2c_data *obj = i2c_get_clientdata(client);
|
|
int err = 0;
|
|
|
|
/* GSE_LOG("mc3xxx_suspend\n"); */
|
|
|
|
if (obj == NULL) {
|
|
pr_err_ratelimited("[Gsensor]%s:null pointer!!\n", __func__);
|
|
return -EINVAL;
|
|
}
|
|
|
|
atomic_set(&obj->suspend, 1);
|
|
mc3xxx_mutex_lock();
|
|
err = MC3XXX_SetPowerMode(client, false);
|
|
mc3xxx_mutex_unlock();
|
|
if (err) {
|
|
pr_err_ratelimited("[Gsensor]%s:write power control fail!!\n",
|
|
__func__);
|
|
return err;
|
|
}
|
|
return err;
|
|
}
|
|
|
|
/*****************************************
|
|
*** mc3xxx_resume
|
|
*****************************************/
|
|
static int mc3xxx_resume(struct device *dev)
|
|
{
|
|
struct i2c_client *client = to_i2c_client(dev);
|
|
struct mc3xxx_i2c_data *obj = i2c_get_clientdata(client);
|
|
int err;
|
|
|
|
/* GSE_LOG("mc3xxx_resume\n");
|
|
*/
|
|
if (obj == NULL) {
|
|
pr_err_ratelimited("[Gsensor]%s:null pointer!!\n", __func__);
|
|
return -EINVAL;
|
|
}
|
|
|
|
mc3xxx_mutex_lock();
|
|
err = MC3XXX_Init(client, 0);
|
|
if (err) {
|
|
mc3xxx_mutex_unlock();
|
|
pr_err_ratelimited("[Gsensor]%s:initialize client fail!!\n",
|
|
__func__);
|
|
return err;
|
|
}
|
|
|
|
err = MC3XXX_SetPowerMode(client, true);
|
|
mc3xxx_mutex_unlock();
|
|
if (err) {
|
|
pr_err_ratelimited("[Gsensor]%s:write power control fail!!\n",
|
|
__func__);
|
|
return err;
|
|
}
|
|
atomic_set(&obj->suspend, 0);
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
/* if use this typ of enable , Gsensor should report inputEvent(x, y, z ,stats,
|
|
* div) to HAL
|
|
*/
|
|
static int mc3xxx_open_report_data(int open)
|
|
{
|
|
/* should queuq work to report event if is_report_input_direct=true
|
|
*/
|
|
return 0;
|
|
}
|
|
|
|
static int _mc3xxx_i2c_auto_probe(struct i2c_client *client)
|
|
{
|
|
#define _MC3XXX_I2C_PROBE_ADDR_COUNT_ (ARRAY_SIZE(mc3xxx_i2c_auto_probe_addr))
|
|
unsigned char _baData1Buf[2] = { 0 };
|
|
unsigned char _baData2Buf[2] = { 0 };
|
|
int _nCount = 0;
|
|
int _naCheckCount[_MC3XXX_I2C_PROBE_ADDR_COUNT_] = { 0 };
|
|
|
|
memset(_naCheckCount, 0, sizeof(_naCheckCount));
|
|
_I2C_AUTO_PROBE_RECHECK_:
|
|
s_bPCODE = 0x00;
|
|
s_bPCODER = 0x00;
|
|
s_bHWID = 0x00;
|
|
|
|
for (_nCount = 0; _nCount < _MC3XXX_I2C_PROBE_ADDR_COUNT_; _nCount++) {
|
|
client->addr = mc3xxx_i2c_auto_probe_addr[_nCount];
|
|
_baData1Buf[0] = 0;
|
|
if (MC3XXX_i2c_read_block(client, 0x3B, _baData1Buf, 1) < 0)
|
|
continue;
|
|
_naCheckCount[_nCount]++;
|
|
|
|
if (_baData1Buf[0] == 0x00) {
|
|
if (_naCheckCount[_nCount] == 1) {
|
|
MC3XXX_reset(client);
|
|
mdelay(3);
|
|
goto _I2C_AUTO_PROBE_RECHECK_;
|
|
} else
|
|
continue;
|
|
}
|
|
|
|
_baData2Buf[0] = 0;
|
|
MC3XXX_i2c_read_block(client, 0x18, _baData2Buf, 1);
|
|
s_bPCODER = _baData1Buf[0];
|
|
if (MC3XXX_ValidateSensorIC(&_baData1Buf[0], &_baData2Buf[0]) ==
|
|
MC3XXX_RETCODE_SUCCESS) {
|
|
s_bPCODE = _baData1Buf[0];
|
|
s_bHWID = _baData2Buf[0];
|
|
MC3XXX_SaveDefaultOffset(client);
|
|
|
|
return MC3XXX_RETCODE_SUCCESS;
|
|
}
|
|
}
|
|
|
|
return MC3XXX_RETCODE_ERROR_I2C;
|
|
#undef _MC3XXX_I2C_PROBE_ADDR_COUNT_
|
|
}
|
|
|
|
/* if use this typ of enable , Gsensor only
|
|
* enabled but not report inputEvent to HAL
|
|
*/
|
|
static int mc3xxx_enable_nodata(int en)
|
|
{
|
|
int res = 0;
|
|
int retry = 0;
|
|
bool power = false;
|
|
|
|
if (en == 1)
|
|
power = true;
|
|
if (en == 0)
|
|
power = false;
|
|
|
|
for (retry = 0; retry < 3; retry++) {
|
|
res = MC3XXX_SetPowerMode(mc3xxx_obj_i2c_data->client, power);
|
|
if (res == 0)
|
|
break;
|
|
GSE_LOG("MC3XXX_SetPowerMode fail\n");
|
|
}
|
|
|
|
if (res != 0) {
|
|
GSE_LOG("MC3XXX_SetPowerMode fail!\n");
|
|
return -1;
|
|
}
|
|
/* GSE_LOG("mc3xxx_enable_nodata OK!\n"); */
|
|
return 0;
|
|
}
|
|
|
|
static int mc3xxx_set_delay(u64 ns)
|
|
{
|
|
int value = 0;
|
|
|
|
value = (int)ns / 1000 / 1000;
|
|
return 0;
|
|
}
|
|
|
|
static int mc3xxx_get_data(int *x, int *y, int *z, int *status)
|
|
{
|
|
int err = 0;
|
|
char buff[MC3XXX_BUF_SIZE] = {0};
|
|
int ret = 0;
|
|
u8 databuf[2] = { 0 };
|
|
|
|
MC3XXX_ReadSensorData(mc3xxx_obj_i2c_data->client, buff,
|
|
MC3XXX_BUF_SIZE);
|
|
ret = sscanf(buff, "%x %x %x", x, y, z);
|
|
if (ret < 0) {
|
|
GSE_LOG("format sensor data fail!!\n");
|
|
return -1;
|
|
}
|
|
*status = SENSOR_STATUS_ACCURACY_MEDIUM;
|
|
|
|
/*Judge the same data*/
|
|
if ((g_predata[0] == *x) && (g_predata[1] == *y) &&
|
|
(g_predata[2] == *z)) {
|
|
if (g_samedataCounter < MC3XXX_SAME_NUM) {
|
|
g_samedataCounter++;
|
|
} else {
|
|
g_samedataCounter = 0;
|
|
/*MC3XXX_reset(mc3xxx_i2c_client);*/
|
|
err = MC3XXX_Init(mc3xxx_i2c_client, 0); /*init acc hw*/
|
|
|
|
databuf[0] = 0x41; /*set power on*/
|
|
err = MC3XXX_i2c_write_block(
|
|
mc3xxx_i2c_client, MC3XXX_REG_MODE_FEATURE, databuf,
|
|
1); /*set power mode*/
|
|
if (err) {
|
|
pr_err_ratelimited("[Gsensor]%s:write power control fail!!\n",
|
|
__func__);
|
|
}
|
|
}
|
|
} else {
|
|
g_predata[0] = *x;
|
|
g_predata[1] = *y;
|
|
g_predata[2] = *z;
|
|
g_samedataCounter = 0;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int mc3410_batch(int flag, int64_t samplingPeriodNs,
|
|
int64_t maxBatchReportLatencyNs)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static int mc3410_flush(void)
|
|
{
|
|
int err = 0;
|
|
/*Only flush after sensor was enabled*/
|
|
if (!mc3xxx_sensor_power) {
|
|
mc3xxx_obj_i2c_data->flush = true;
|
|
return 0;
|
|
}
|
|
err = acc_flush_report();
|
|
if (err >= 0)
|
|
mc3xxx_obj_i2c_data->flush = false;
|
|
return err;
|
|
}
|
|
|
|
static int mc3410_factory_enable_sensor(bool enabledisable,
|
|
int64_t sample_periods_ms)
|
|
{
|
|
int err;
|
|
|
|
err = mc3xxx_enable_nodata(enabledisable == true ? 1 : 0);
|
|
if (err) {
|
|
pr_err_ratelimited("[Gsensor]%s: enable sensor failed!\n",
|
|
__func__);
|
|
return -1;
|
|
}
|
|
err = mc3410_batch(0, sample_periods_ms * 1000000, 0);
|
|
if (err) {
|
|
pr_err_ratelimited("[Gsensor]%s: enable set batch failed!\n",
|
|
__func__);
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
static int mc3410_factory_get_data(int32_t data[3], int *status)
|
|
{
|
|
return mc3xxx_get_data(&data[0], &data[1], &data[2], status);
|
|
}
|
|
static int mc3410_factory_get_raw_data(int32_t data[3])
|
|
{
|
|
char strbuf[MC3XXX_BUF_SIZE] = { 0 };
|
|
|
|
MC3XXX_ReadRawData(mc3xxx_i2c_client, strbuf);
|
|
return 0;
|
|
}
|
|
static int mc3410_factory_enable_calibration(void) { return 0; }
|
|
static int mc3410_factory_clear_cali(void)
|
|
{
|
|
int err = 0;
|
|
|
|
err = MC3XXX_ResetCalibration(mc3xxx_i2c_client);
|
|
if (err) {
|
|
pr_err_ratelimited("[Gsensor]%s:mc3410_ResetCalibration failed!\n",
|
|
__func__);
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
static int mc3410_factory_set_cali(int32_t data[3])
|
|
{
|
|
int err = 0;
|
|
int cali[3] = { 0 };
|
|
|
|
/* obj */
|
|
mc3xxx_obj_i2c_data->cali_sw[MC3XXX_AXIS_X] += data[0];
|
|
mc3xxx_obj_i2c_data->cali_sw[MC3XXX_AXIS_Y] += data[1];
|
|
mc3xxx_obj_i2c_data->cali_sw[MC3XXX_AXIS_Z] += data[2];
|
|
|
|
cali[MC3XXX_AXIS_X] = data[0] * gsensor_gain.x / GRAVITY_EARTH_1000;
|
|
cali[MC3XXX_AXIS_Y] = data[1] * gsensor_gain.y / GRAVITY_EARTH_1000;
|
|
cali[MC3XXX_AXIS_Z] = data[2] * gsensor_gain.z / GRAVITY_EARTH_1000;
|
|
err = MC3XXX_WriteCalibration(mc3xxx_i2c_client, cali);
|
|
if (err) {
|
|
pr_err_ratelimited("[Gsensor]%s:mc3410_WriteCalibration failed!\n",
|
|
__func__);
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
static int mc3410_factory_get_cali(int32_t data[3])
|
|
{
|
|
data[0] = mc3xxx_obj_i2c_data->cali_sw[MC3XXX_AXIS_X];
|
|
data[1] = mc3xxx_obj_i2c_data->cali_sw[MC3XXX_AXIS_Y];
|
|
data[2] = mc3xxx_obj_i2c_data->cali_sw[MC3XXX_AXIS_Z];
|
|
return 0;
|
|
}
|
|
static int mc3410_factory_do_self_test(void) { return 0; }
|
|
|
|
static struct accel_factory_fops mc3410_factory_fops = {
|
|
.enable_sensor = mc3410_factory_enable_sensor,
|
|
.get_data = mc3410_factory_get_data,
|
|
.get_raw_data = mc3410_factory_get_raw_data,
|
|
.enable_calibration = mc3410_factory_enable_calibration,
|
|
.clear_cali = mc3410_factory_clear_cali,
|
|
.set_cali = mc3410_factory_set_cali,
|
|
.get_cali = mc3410_factory_get_cali,
|
|
.do_self_test = mc3410_factory_do_self_test,
|
|
};
|
|
|
|
static struct accel_factory_public mc3410_factory_device = {
|
|
.gain = 1, .sensitivity = 1, .fops = &mc3410_factory_fops,
|
|
};
|
|
|
|
/*****************************************
|
|
*** mc3xxx_i2c_probe
|
|
*****************************************/
|
|
static int mc3xxx_i2c_probe(struct i2c_client *client,
|
|
const struct i2c_device_id *id)
|
|
{
|
|
struct i2c_client *new_client = NULL;
|
|
struct mc3xxx_i2c_data *obj = NULL;
|
|
struct acc_control_path ctl = { 0 };
|
|
struct acc_data_path data = { 0 };
|
|
int err = 0;
|
|
|
|
obj = kzalloc(sizeof(*obj), GFP_KERNEL);
|
|
if (!obj) {
|
|
err = -ENOMEM;
|
|
goto exit;
|
|
}
|
|
|
|
err = get_accel_dts_func(client->dev.of_node, &obj->hw);
|
|
if (err < 0) {
|
|
pr_err_ratelimited("[Gsensor]%s:get cust_baro dts info fail\n",
|
|
__func__);
|
|
goto exit_kfree;
|
|
}
|
|
err = hwmsen_get_convert(obj->hw.direction, &obj->cvt);
|
|
if (err) {
|
|
pr_err_ratelimited("[Gsensor]%s:invalid direction: %d\n",
|
|
__func__, obj->hw.direction);
|
|
goto exit_kfree;
|
|
}
|
|
|
|
mc3xxx_obj_i2c_data = obj;
|
|
obj->client = client;
|
|
new_client = obj->client;
|
|
i2c_set_clientdata(new_client, obj);
|
|
atomic_set(&obj->trace, 0);
|
|
atomic_set(&obj->suspend, 0);
|
|
|
|
#ifdef _MC3XXX_SUPPORT_LPF_
|
|
if (obj->hw.firlen > C_MAX_FIR_LENGTH)
|
|
atomic_set(&obj->firlen, C_MAX_FIR_LENGTH);
|
|
else
|
|
atomic_set(&obj->firlen, obj->hw.firlen);
|
|
|
|
if (atomic_read(&obj->firlen) > 0)
|
|
atomic_set(&obj->fir_en, 1);
|
|
#endif
|
|
mc3xxx_i2c_client = new_client;
|
|
MC3XXX_reset(new_client);
|
|
|
|
err = _mc3xxx_i2c_auto_probe(client);
|
|
if (err != MC3XXX_RETCODE_SUCCESS)
|
|
goto exit_init_failed;
|
|
|
|
MC3XXX_i2c_read_block(client, 0x21, offset_buf, 6);
|
|
err = MC3XXX_Init(new_client, 1);
|
|
if (err)
|
|
goto exit_init_failed;
|
|
mc3xxx_mutex_init();
|
|
|
|
ctl.is_use_common_factory = false;
|
|
/* factory */
|
|
err = accel_factory_device_register(&mc3410_factory_device);
|
|
if (err) {
|
|
pr_err_ratelimited("[Gsensor]%s:acc_factory register failed.\n",
|
|
__func__);
|
|
goto exit_misc_device_register_failed;
|
|
}
|
|
|
|
err =
|
|
mc3xxx_create_attr(&(mc3xxx_init_info.platform_diver_addr->driver));
|
|
if (err) {
|
|
pr_err_ratelimited("[Gsensor]%s:create attribute err = %d\n",
|
|
__func__, err);
|
|
goto exit_create_attr_failed;
|
|
}
|
|
|
|
ctl.open_report_data = mc3xxx_open_report_data;
|
|
ctl.enable_nodata = mc3xxx_enable_nodata;
|
|
ctl.batch = mc3410_batch;
|
|
ctl.flush = mc3410_flush;
|
|
ctl.set_delay = mc3xxx_set_delay;
|
|
ctl.is_report_input_direct = false;
|
|
ctl.is_support_batch = obj->hw.is_batch_supported;
|
|
err = acc_register_control_path(&ctl);
|
|
if (err) {
|
|
pr_err_ratelimited("[Gsensor]%s:acc_register_control_path(%d)\n",
|
|
__func__, err);
|
|
goto exit_kfree;
|
|
}
|
|
data.get_data = mc3xxx_get_data;
|
|
data.vender_div = 1000;
|
|
err = acc_register_data_path(&data);
|
|
if (err) {
|
|
pr_err_ratelimited("[Gsensor]%s:acc_register_data_path(%d)\n",
|
|
__func__, err);
|
|
goto exit_kfree;
|
|
}
|
|
s_nInitFlag = MC3XXX_INIT_SUCC;
|
|
return 0;
|
|
|
|
exit_create_attr_failed:
|
|
exit_init_failed:
|
|
exit_misc_device_register_failed:
|
|
exit_kfree:
|
|
kfree(obj);
|
|
exit:
|
|
pr_err_ratelimited("[Gsensor]%s:err = %d\n", __func__, err);
|
|
obj = NULL;
|
|
new_client = NULL;
|
|
s_nInitFlag = MC3XXX_INIT_FAIL;
|
|
mc3xxx_i2c_client = NULL;
|
|
mc3xxx_obj_i2c_data = NULL;
|
|
|
|
return err;
|
|
}
|
|
|
|
/*****************************************
|
|
*** mc3xxx_i2c_remove
|
|
*****************************************/
|
|
static int mc3xxx_i2c_remove(struct i2c_client *client)
|
|
{
|
|
int err = 0;
|
|
|
|
err =
|
|
mc3xxx_delete_attr(&(mc3xxx_init_info.platform_diver_addr->driver));
|
|
if (err) {
|
|
pr_err_ratelimited("[Gsensor]%s:mc3xxx_delete_attr fail: %d\n",
|
|
__func__, err);
|
|
}
|
|
|
|
#ifdef _MC3XXX_SUPPORT_VPROXIMITY_SENSOR_
|
|
misc_deregister(&mcube_psensor_device);
|
|
#endif
|
|
mc3xxx_i2c_client = NULL;
|
|
i2c_unregister_device(client);
|
|
accel_factory_device_deregister(&mc3410_factory_device);
|
|
kfree(i2c_get_clientdata(client));
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*****************************************
|
|
*** mc3xxx_remove
|
|
*****************************************/
|
|
static int mc3xxx_remove(void)
|
|
{
|
|
i2c_del_driver(&mc3xxx_i2c_driver);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*****************************************
|
|
*** mc3xxx_local_init
|
|
*****************************************/
|
|
static int mc3xxx_local_init(void)
|
|
{
|
|
if (i2c_add_driver(&mc3xxx_i2c_driver)) {
|
|
pr_err_ratelimited("[Gsensor]%s:add driver error\n",
|
|
__func__);
|
|
return -1;
|
|
}
|
|
|
|
if (s_nInitFlag == MC3XXX_INIT_FAIL)
|
|
return -1;
|
|
return 0;
|
|
}
|
|
|
|
/*****************************************
|
|
*** mc3xxx_init
|
|
*****************************************/
|
|
static int __init mc3410_init(void)
|
|
{
|
|
acc_driver_add(&mc3xxx_init_info);
|
|
return 0;
|
|
}
|
|
|
|
/*****************************************
|
|
*** mc3xxx_exit
|
|
*****************************************/
|
|
static void __exit mc3410_exit(void)
|
|
{
|
|
#ifdef CONFIG_CUSTOM_KERNEL_ACCELEROMETER_MODULE
|
|
success_Flag = false;
|
|
#endif
|
|
}
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
module_init(mc3410_init);
|
|
module_exit(mc3410_exit);
|
|
/*----------------------------------------------------------------------------*/
|
|
MODULE_DESCRIPTION("mc3XXX G-Sensor Driver");
|
|
MODULE_AUTHOR("Mediatek");
|
|
MODULE_LICENSE("GPL");
|