958 lines
28 KiB
C
958 lines
28 KiB
C
/*
|
|
* stk8baxx.c - Linux kernel modules for sensortek stk8ba50 / stk8ba50-R /
|
|
* stk8ba53 accelerometer
|
|
*
|
|
* Copyright (C) 2012~2016 Lex Hsieh / Sensortek
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*/
|
|
#include <linux/i2c.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/input.h>
|
|
#include <linux/delay.h>
|
|
#include <linux/interrupt.h>
|
|
#include <linux/workqueue.h>
|
|
#include <linux/mutex.h>
|
|
#include <linux/uaccess.h>
|
|
#include <linux/fs.h>
|
|
#include <linux/module.h>
|
|
#include <linux/math64.h>
|
|
#include <linux/init.h>
|
|
#include <linux/sensor-dev.h>
|
|
|
|
#define STK_ACC_DRIVER_VERSION "3.7.1_rk_0425_0428"
|
|
|
|
/*------------------User-defined settings-------------------------*/
|
|
/* #define CONFIG_SENSORS_STK8BA53 */
|
|
#define CONFIG_SENSORS_STK8BA50
|
|
/* #define STK_DEBUG_PRINT */
|
|
/* #define STK_LOWPASS */
|
|
#define STK_FIR_LEN 4 /* 1~32 */
|
|
/* #define STK_TUNE */
|
|
/* #define STK_ZG_FILTER */
|
|
#define STK_HOLD_ODR
|
|
#define STK_DEBUG_CALI
|
|
#define STK8BAXX_DEF_PLACEMENT 7
|
|
|
|
/*------------------Miscellaneous settings-------------------------*/
|
|
#define STK8BAXX_I2C_NAME "stk8baxx"
|
|
#define ACC_IDEVICE_NAME "accelerometer"
|
|
|
|
#define STK8BAXX_INIT_ODR 0xD /* 0xB:125Hz, 0xA:62Hz */
|
|
|
|
#define STK8BAXX_RNG_2G 0x3
|
|
#define STK8BAXX_RNG_4G 0x5
|
|
#define STK8BAXX_RNG_8G 0x8
|
|
#define STK8BAXX_RNG_16G 0xC
|
|
|
|
#ifdef CONFIG_SENSORS_STK8BA53
|
|
/* Parameters under +-4g dynamic range */
|
|
#define STK_DEF_DYNAMIC_RANGE STK8BAXX_RNG_4G
|
|
|
|
#if (STK_DEF_DYNAMIC_RANGE == STK8BAXX_RNG_4G)
|
|
#define STK_LSB_1G 512
|
|
#define STK_DEF_RANGE 4
|
|
#elif (STK_DEF_DYNAMIC_RANGE == STK8BAXX_RNG_2G)
|
|
#define STK_LSB_1G 1024
|
|
#define STK_DEF_RANGE 2
|
|
#elif (STK_DEF_DYNAMIC_RANGE == STK8BAXX_RNG_8G)
|
|
#define STK_LSB_1G 256
|
|
#define STK_DEF_RANGE 8
|
|
#elif (STK_DEF_DYNAMIC_RANGE == STK8BAXX_RNG_16G)
|
|
#define STK_LSB_1G 128
|
|
#define STK_DEF_RANGE 16
|
|
#endif
|
|
|
|
#define STK_ZG_COUNT (STK_LSB_1G / 128)
|
|
#define STK_TUNE_XYOFFSET (STK_LSB_1G * 3 / 10)
|
|
#define STK_TUNE_ZOFFSET (STK_LSB_1G * 3 / 10) /* (STK_LSB_1G * 3 / 20) */
|
|
#define STK_TUNE_NOISE (STK_LSB_1G / 10)
|
|
#else
|
|
/* Parameters under +-2g dynamic range */
|
|
#define STK_DEF_DYNAMIC_RANGE STK8BAXX_RNG_2G
|
|
|
|
#if (STK_DEF_DYNAMIC_RANGE == STK8BAXX_RNG_2G)
|
|
#define STK_LSB_1G 256
|
|
#define STK_DEF_RANGE 2
|
|
#elif (STK_DEF_DYNAMIC_RANGE == STK8BAXX_RNG_4G)
|
|
#define STK_LSB_1G 128
|
|
#define STK_DEF_RANGE 4
|
|
#elif (STK_DEF_DYNAMIC_RANGE == STK8BAXX_RNG_8G)
|
|
#define STK_LSB_1G 64
|
|
#define STK_DEF_RANGE 8
|
|
#elif (STK_DEF_DYNAMIC_RANGE == STK8BAXX_RNG_16G)
|
|
#define STK_LSB_1G 32
|
|
#define STK_DEF_RANGE 16
|
|
#endif
|
|
#define STK_ZG_COUNT (STK_LSB_1G / 128 + 1)
|
|
#define STK_TUNE_XYOFFSET (STK_LSB_1G * 4 / 10)
|
|
#define STK_TUNE_ZOFFSET (STK_LSB_1G * 4 / 10) /* (STK_LSB_1G * 3 / 20) */
|
|
#define STK_TUNE_NOISE (STK_LSB_1G / 10)
|
|
#endif
|
|
|
|
#define STK8BAXX_RANGE_UG (STK_DEF_RANGE * 16384)
|
|
|
|
/* STK_OFFSET_REG_LSB_1G is fixed for all dynamic range */
|
|
#define STK_OFFSET_REG_LSB_1G 128
|
|
|
|
#define STK_TUNE_NUM 60
|
|
#define STK_TUNE_DELAY 30
|
|
|
|
#define STK_EVENT_SINCE_EN_LIMIT_DEF (1)
|
|
|
|
#define STK8BA50_ID 0x09
|
|
#define STK8BA50R_ID 0x86
|
|
#define STK8BA53_ID 0x87
|
|
|
|
/*------------------Calibration prameters-------------------------*/
|
|
#define STK_SAMPLE_NO 10
|
|
#define STK_ACC_CALI_VER0 0x18
|
|
#define STK_ACC_CALI_VER1 0x03
|
|
#define STK_ACC_CALI_END '\0'
|
|
#define STK_ACC_CALI_FILE "/data/misc/stkacccali.conf"
|
|
#define STK_ACC_CALI_FILE_SDCARD "/sdcard/.stkacccali.conf"
|
|
#define STK_ACC_CALI_FILE_SIZE 25
|
|
|
|
#define STK_K_SUCCESS_TUNE 0x04
|
|
#define STK_K_SUCCESS_FT2 0x03
|
|
#define STK_K_SUCCESS_FT1 0x02
|
|
#define STK_K_SUCCESS_FILE 0x01
|
|
#define STK_K_NO_CALI 0xFF
|
|
#define STK_K_RUNNING 0xFE
|
|
#define STK_K_FAIL_LRG_DIFF 0xFD
|
|
#define STK_K_FAIL_OPEN_FILE 0xFC
|
|
#define STK_K_FAIL_W_FILE 0xFB
|
|
#define STK_K_FAIL_R_BACK 0xFA
|
|
#define STK_K_FAIL_R_BACK_COMP 0xF9
|
|
#define STK_K_FAIL_I2C 0xF8
|
|
#define STK_K_FAIL_K_PARA 0xF7
|
|
#define STK_K_FAIL_OUT_RG 0xF6
|
|
#define STK_K_FAIL_ENG_I2C 0xF5
|
|
#define STK_K_FAIL_FT1_USD 0xF4
|
|
#define STK_K_FAIL_FT2_USD 0xF3
|
|
#define STK_K_FAIL_WRITE_NOFST 0xF2
|
|
#define STK_K_FAIL_OTP_5T 0xF1
|
|
#define STK_K_FAIL_PLACEMENT 0xF0
|
|
|
|
/*------------------stk8baxx registers-------------------------*/
|
|
#define STK8BAXX_XOUT1 0x02
|
|
#define STK8BAXX_XOUT2 0x03
|
|
#define STK8BAXX_YOUT1 0x04
|
|
#define STK8BAXX_YOUT2 0x05
|
|
#define STK8BAXX_ZOUT1 0x06
|
|
#define STK8BAXX_ZOUT2 0x07
|
|
#define STK8BAXX_INTSTS1 0x09
|
|
#define STK8BAXX_INTSTS2 0x0A
|
|
#define STK8BAXX_EVENTINFO1 0x0B
|
|
#define STK8BAXX_EVENTINFO2 0x0C
|
|
#define STK8BAXX_RANGESEL 0x0F
|
|
#define STK8BAXX_BWSEL 0x10
|
|
#define STK8BAXX_POWMODE 0x11
|
|
#define STK8BAXX_DATASETUP 0x13
|
|
#define STK8BAXX_SWRST 0x14
|
|
#define STK8BAXX_INTEN1 0x16
|
|
#define STK8BAXX_INTEN2 0x17
|
|
#define STK8BAXX_INTMAP1 0x19
|
|
#define STK8BAXX_INTMAP2 0x1A
|
|
#define STK8BAXX_INTMAP3 0x1B
|
|
#define STK8BAXX_DATASRC 0x1E
|
|
#define STK8BAXX_INTCFG1 0x20
|
|
#define STK8BAXX_INTCFG2 0x21
|
|
#define STK8BAXX_LGDLY 0x22
|
|
#define STK8BAXX_LGTHD 0x23
|
|
#define STK8BAXX_HLGCFG 0x24
|
|
#define STK8BAXX_HGDLY 0x25
|
|
#define STK8BAXX_HGTHD 0x26
|
|
#define STK8BAXX_SLOPEDLY 0x27
|
|
#define STK8BAXX_SLOPETHD 0x28
|
|
#define STK8BAXX_TAPTIME 0x2A
|
|
#define STK8BAXX_TAPCFG 0x2B
|
|
#define STK8BAXX_ORIENTCFG 0x2C
|
|
#define STK8BAXX_ORIENTTHETA 0x2D
|
|
#define STK8BAXX_FLATTHETA 0x2E
|
|
#define STK8BAXX_FLATHOLD 0x2F
|
|
#define STK8BAXX_SLFTST 0x32
|
|
#define STK8BAXX_INTFCFG 0x34
|
|
#define STK8BAXX_OFSTCOMP1 0x36
|
|
#define STK8BAXX_OFSTCOMP2 0x37
|
|
#define STK8BAXX_OFSTFILTX 0x38
|
|
#define STK8BAXX_OFSTFILTY 0x39
|
|
#define STK8BAXX_OFSTFILTZ 0x3A
|
|
#define STK8BAXX_OFSTUNFILTX 0x3B
|
|
#define STK8BAXX_OFSTUNFILTY 0x3C
|
|
#define STK8BAXX_OFSTUNFILTZ 0x3D
|
|
|
|
/* ZOUT1 register */
|
|
#define STK8BAXX_O_NEW 0x01
|
|
|
|
/* SWRST register */
|
|
#define STK8BAXX_SWRST_VAL 0xB6
|
|
|
|
/* STK8BAXX_POWMODE register */
|
|
#define STK8BAXX_MD_SUSPEND 0x80
|
|
#define STK8BAXX_MD_NORMAL 0x00
|
|
#define STK8BAXX_MD_SLP_MASK 0x1E
|
|
|
|
/* RANGESEL register */
|
|
#define STK8BAXX_RANGE_MASK 0x0F
|
|
|
|
/* OFSTCOMP1 register */
|
|
#define STK8BAXX_OF_CAL_DRY_MASK 0x10
|
|
#define CAL_AXIS_X_EN 0x20
|
|
#define CAL_AXIS_Y_EN 0x40
|
|
#define CAL_AXIS_Z_EN 0x60
|
|
#define CAL_OFST_RST 0x80
|
|
|
|
/* OFSTCOMP2 register */
|
|
#define CAL_TG_X0_Y0_ZPOS1 0x20
|
|
#define CAL_TG_X0_Y0_ZNEG1 0x40
|
|
|
|
/*no_create_attr:the initial is 1-->no create attr. if created, change no_create_att to 0.*/
|
|
static int no_create_att = 1;
|
|
static int enable_status = -1;
|
|
|
|
/*------------------Data structure-------------------------*/
|
|
struct stk8baxx_acc {
|
|
union {
|
|
struct {
|
|
s16 x;
|
|
s16 y;
|
|
s16 z;
|
|
};
|
|
s16 acc[3];
|
|
};
|
|
};
|
|
|
|
#if defined(STK_LOWPASS)
|
|
#define MAX_FIR_LEN 32
|
|
struct data_filter {
|
|
s16 raw[MAX_FIR_LEN][3];
|
|
int sum[3];
|
|
int num;
|
|
int idx;
|
|
};
|
|
#endif
|
|
|
|
struct stk8baxx_data {
|
|
struct i2c_client *client;
|
|
struct input_dev *input_dev;
|
|
int irq;
|
|
struct stk8baxx_acc acc_xyz;
|
|
atomic_t enabled;
|
|
bool first_enable;
|
|
struct work_struct stk_work;
|
|
struct hrtimer acc_timer;
|
|
struct workqueue_struct *stk_mems_work_queue;
|
|
unsigned char stk8baxx_placement;
|
|
atomic_t cali_status;
|
|
atomic_t recv_reg;
|
|
bool re_enable;
|
|
#if defined(STK_LOWPASS)
|
|
atomic_t firlength;
|
|
atomic_t fir_en;
|
|
struct data_filter fir;
|
|
#endif
|
|
int event_since_en;
|
|
int event_since_en_limit;
|
|
u8 stk_tune_offset_record[3];
|
|
#ifdef STK_TUNE
|
|
int stk_tune_offset[3];
|
|
int stk_tune_sum[3];
|
|
int stk_tune_max[3];
|
|
int stk_tune_min[3];
|
|
int stk_tune_index;
|
|
int stk_tune_done;
|
|
s64 stk_tune_square_sum[3];
|
|
u32 variance[3];
|
|
#endif
|
|
};
|
|
|
|
/*------------------Function prototype-------------------------*/
|
|
static int stk8baxx_set_enable(struct stk8baxx_data *stk, char en);
|
|
static int stk8baxx_read_sensor_data(struct stk8baxx_data *stk);
|
|
/*------------------Global variables-------------------------*/
|
|
static struct stk8baxx_data *stk8baxx_data_ptr;
|
|
static struct sensor_private_data *sensor_ptr;
|
|
/*------------------Main functions-------------------------*/
|
|
|
|
static s32 stk8baxx_smbus_write_byte_data(u8 command, u8 value)
|
|
{
|
|
return sensor_write_reg(stk8baxx_data_ptr->client, command, value);
|
|
}
|
|
|
|
static int stk8baxx_smbus_read_byte_data(u8 command)
|
|
{
|
|
return sensor_read_reg(stk8baxx_data_ptr->client, command);
|
|
}
|
|
|
|
static int stk8baxx_chk_for_addr(struct stk8baxx_data *stk, s32 org_address, unsigned short reset_address)
|
|
{
|
|
int result;
|
|
s32 expected_reg0 = 0x86;
|
|
|
|
if ((org_address & 0xFE) == 0x18)
|
|
expected_reg0 = 0x86;
|
|
else
|
|
expected_reg0 = 0x87;
|
|
|
|
stk->client->addr = reset_address;
|
|
result = stk8baxx_smbus_write_byte_data(STK8BAXX_SWRST, STK8BAXX_SWRST_VAL);
|
|
printk(KERN_INFO "%s:issue sw reset to 0x%x, result=%d\n", __func__, reset_address, result);
|
|
usleep_range(2000, 3000);
|
|
|
|
stk->client->addr = org_address;
|
|
printk(KERN_INFO "%s Revise I2C Address = 0x%x\n", __func__, org_address);
|
|
result = stk8baxx_smbus_write_byte_data(STK8BAXX_POWMODE, STK8BAXX_MD_NORMAL);
|
|
result = stk8baxx_smbus_read_byte_data(0x0);
|
|
if (result < 0) {
|
|
printk(KERN_INFO "%s: read 0x0, result=%d\n", __func__, result);
|
|
return result;
|
|
}
|
|
if (result == expected_reg0) {
|
|
printk(KERN_INFO "%s:passed, expected_reg0=0x%x\n", __func__, expected_reg0);
|
|
result = stk8baxx_smbus_write_byte_data(STK8BAXX_SWRST, STK8BAXX_SWRST_VAL);
|
|
if (result < 0) {
|
|
printk(KERN_ERR "%s:failed to issue software reset, error=%d\n", __func__, result);
|
|
return result;
|
|
}
|
|
usleep_range(2000, 3000);
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int stk8baxx_sw_reset(struct stk8baxx_data *stk)
|
|
{
|
|
unsigned short org_addr = 0;
|
|
int result;
|
|
|
|
org_addr = stk->client->addr;
|
|
printk(KERN_INFO "%s:org_addr=0x%x\n", __func__, org_addr);
|
|
|
|
if ((org_addr & 0xFE) == 0x18) {
|
|
result = stk8baxx_chk_for_addr(stk, org_addr, 0x18);
|
|
if (result == 1)
|
|
return 0;
|
|
result = stk8baxx_chk_for_addr(stk, org_addr, 0x19);
|
|
if (result == 1)
|
|
return 0;
|
|
result = stk8baxx_chk_for_addr(stk, org_addr, 0x08);
|
|
if (result == 1)
|
|
return 0;
|
|
result = stk8baxx_chk_for_addr(stk, org_addr, 0x28);
|
|
if (result == 1)
|
|
return 0;
|
|
} else if (org_addr == 0x28) {
|
|
result = stk8baxx_chk_for_addr(stk, org_addr, 0x28);
|
|
if (result == 1)
|
|
return 0;
|
|
result = stk8baxx_chk_for_addr(stk, org_addr, 0x18);
|
|
if (result == 1)
|
|
return 0;
|
|
result = stk8baxx_chk_for_addr(stk, org_addr, 0x08);
|
|
if (result == 1)
|
|
return 0;
|
|
}
|
|
result = stk8baxx_chk_for_addr(stk, org_addr, 0x0B);
|
|
return 0;
|
|
}
|
|
|
|
static int stk8baxx_reg_init(struct stk8baxx_data *stk, struct i2c_client *client, struct sensor_private_data *sensor)
|
|
{
|
|
int result;
|
|
int aa;
|
|
|
|
#ifdef CONFIG_SENSORS_STK8BA53
|
|
printk(KERN_INFO "%s: Initialize stk8ba53\n", __func__);
|
|
#else
|
|
printk(KERN_INFO "%s: Initialize stk8ba50/stk8ba50-r\n", __func__);
|
|
#endif
|
|
|
|
/* sw reset */
|
|
result = stk8baxx_sw_reset(stk);
|
|
if (result < 0) {
|
|
printk(KERN_ERR "%s:failed to stk8baxx_sw_reset, error=%d\n", __func__, result);
|
|
return result;
|
|
}
|
|
|
|
result = stk8baxx_smbus_write_byte_data(STK8BAXX_POWMODE, STK8BAXX_MD_NORMAL);
|
|
if (result < 0) {
|
|
printk(KERN_ERR "%s:failed to write reg 0x%x, error=%d\n", __func__, STK8BAXX_POWMODE, result);
|
|
return result;
|
|
}
|
|
|
|
result = stk8baxx_smbus_read_byte_data(STK8BAXX_LGDLY);
|
|
if (result < 0) {
|
|
printk(KERN_ERR "%s: failed to read acc data, error=%d\n", __func__, result);
|
|
return result;
|
|
}
|
|
|
|
if (result == STK8BA50_ID) {
|
|
printk(KERN_INFO "%s: chip is stk8ba50\n", __func__);
|
|
sensor->devid = STK8BA50_ID;
|
|
} else {
|
|
result = stk8baxx_smbus_read_byte_data(0x0);
|
|
if (result < 0) {
|
|
printk(KERN_ERR "%s: failed to read acc data, error=%d\n", __func__, result);
|
|
return result;
|
|
}
|
|
printk(KERN_INFO "%s: 0x0=0x%x\n", __func__, result);
|
|
if (result == STK8BA50R_ID) {
|
|
printk(KERN_INFO "%s: chip is stk8ba50-R\n", __func__);
|
|
sensor->devid = STK8BA50R_ID;
|
|
} else {
|
|
printk(KERN_INFO "%s: chip is stk8ba53\n", __func__);
|
|
sensor->devid = STK8BA53_ID;
|
|
}
|
|
}
|
|
#ifdef CONFIG_SENSORS_STK8BA53
|
|
if (sensor->devid != STK8BA53_ID) {
|
|
printk(KERN_ERR "%s: stk8ba53 is not attached, devid=0x%x\n", __func__, sensor->devid);
|
|
return -ENODEV;
|
|
}
|
|
#else
|
|
if (sensor->devid == STK8BA53_ID) {
|
|
printk(KERN_ERR "%s: stk8ba50/stk8ba50-R is not attached, devid=0x%x\n", __func__, sensor->devid);
|
|
return -ENODEV;
|
|
}
|
|
#endif
|
|
if (sensor->pdata->irq_enable) {
|
|
/* map new data int to int1 */
|
|
result = stk8baxx_smbus_write_byte_data(STK8BAXX_INTMAP2, 0x01);
|
|
if (result < 0) {
|
|
printk(KERN_ERR "%s:failed to write reg 0x%x, error=%d\n", __func__, STK8BAXX_INTMAP2, result);
|
|
return result;
|
|
}
|
|
/* enable new data in */
|
|
result = stk8baxx_smbus_write_byte_data(STK8BAXX_INTEN2, 0x10);
|
|
if (result < 0) {
|
|
printk(KERN_ERR "%s:failed to write reg 0x%x, error=%d\n", __func__, STK8BAXX_INTEN2, result);
|
|
return result;
|
|
}
|
|
/* non-latch int */
|
|
result = stk8baxx_smbus_write_byte_data(STK8BAXX_INTCFG2, 0x00);
|
|
if (result < 0) {
|
|
printk(KERN_ERR "%s:failed to write reg 0x%x, error=%d\n", __func__, STK8BAXX_INTCFG2, result);
|
|
return result;
|
|
}
|
|
/* filtered data source for new data int */
|
|
result = stk8baxx_smbus_write_byte_data(STK8BAXX_DATASRC, 0x00);
|
|
if (result < 0) {
|
|
printk(KERN_ERR "%s:failed to write reg 0x%x, error=%d\n", __func__, STK8BAXX_DATASRC, result);
|
|
return result;
|
|
}
|
|
/* int1, push-pull, active high */
|
|
result = stk8baxx_smbus_write_byte_data(STK8BAXX_INTCFG1, 0x01);
|
|
if (result < 0) {
|
|
printk(KERN_ERR "%s:failed to write reg 0x%x, error=%d\n", __func__, STK8BAXX_INTCFG1, result);
|
|
return result;
|
|
}
|
|
}
|
|
#ifdef CONFIG_SENSORS_STK8BA53
|
|
/* +- 4g */
|
|
result = stk8baxx_smbus_write_byte_data(STK8BAXX_RANGESEL, STK_DEF_DYNAMIC_RANGE);
|
|
if (result < 0) {
|
|
printk(KERN_ERR "%s:failed to write reg 0x%x, error=%d\n", __func__, STK8BAXX_RANGESEL, result);
|
|
return result;
|
|
}
|
|
#else
|
|
/* +- 2g */
|
|
result = stk8baxx_smbus_write_byte_data(STK8BAXX_RANGESEL, STK_DEF_DYNAMIC_RANGE);
|
|
if (result < 0) {
|
|
printk(KERN_ERR "%s:failed to write reg 0x%x, error=%d\n", __func__, STK8BAXX_RANGESEL, result);
|
|
return result;
|
|
}
|
|
#endif
|
|
/* ODR = 62 Hz */
|
|
result = stk8baxx_smbus_write_byte_data(STK8BAXX_BWSEL, STK8BAXX_INIT_ODR);
|
|
if (result < 0) {
|
|
printk(KERN_ERR "%s:failed to write reg 0x%x, error=%d\n", __func__, STK8BAXX_BWSEL, result);
|
|
return result;
|
|
}
|
|
|
|
/* i2c watchdog enable, 1 ms timer perios */
|
|
result = stk8baxx_smbus_write_byte_data(STK8BAXX_INTFCFG, 0x04);
|
|
if (result < 0) {
|
|
printk(KERN_ERR "%s:failed to write reg 0x%x, error=%d\n", __func__, STK8BAXX_INTFCFG, result);
|
|
return result;
|
|
}
|
|
|
|
result = stk8baxx_smbus_write_byte_data(STK8BAXX_POWMODE, STK8BAXX_MD_SUSPEND);
|
|
if (result < 0) {
|
|
printk(KERN_ERR "%s:failed to write reg 0x%x, error=%d\n", __func__, STK8BAXX_POWMODE, result);
|
|
return result;
|
|
}
|
|
atomic_set(&stk->enabled, 0);
|
|
stk->first_enable = true;
|
|
atomic_set(&stk->cali_status, STK_K_NO_CALI);
|
|
atomic_set(&stk->recv_reg, 0);
|
|
#ifdef STK_LOWPASS
|
|
memset(&stk->fir, 0x00, sizeof(stk->fir));
|
|
atomic_set(&stk->firlength, STK_FIR_LEN);
|
|
atomic_set(&stk->fir_en, 1);
|
|
#endif
|
|
|
|
for (aa = 0; aa < 3; aa++)
|
|
stk->stk_tune_offset_record[aa] = 0;
|
|
#ifdef STK_TUNE
|
|
for (aa = 0; aa < 3; aa++) {
|
|
stk->stk_tune_offset[aa] = 0;
|
|
stk->stk_tune_sum[aa] = 0;
|
|
stk->stk_tune_max[aa] = 0;
|
|
stk->stk_tune_min[aa] = 0;
|
|
stk->stk_tune_square_sum[aa] = 0LL;
|
|
stk->variance[aa] = 0;
|
|
}
|
|
stk->stk_tune_done = 0;
|
|
stk->stk_tune_index = 0;
|
|
#endif
|
|
stk->event_since_en_limit = STK_EVENT_SINCE_EN_LIMIT_DEF;
|
|
|
|
return 0;
|
|
}
|
|
|
|
#ifdef STK_LOWPASS
|
|
static void stk8baxx_low_pass(struct stk8baxx_data *stk, struct stk8baxx_acc *acc_lp)
|
|
{
|
|
int idx, firlength = atomic_read(&stk->firlength);
|
|
#ifdef STK_ZG_FILTER
|
|
s16 zero_fir = 0;
|
|
#endif
|
|
|
|
if (atomic_read(&stk->fir_en)) {
|
|
if (stk->fir.num < firlength) {
|
|
stk->fir.raw[stk->fir.num][0] = acc_lp->x;
|
|
stk->fir.raw[stk->fir.num][1] = acc_lp->y;
|
|
stk->fir.raw[stk->fir.num][2] = acc_lp->z;
|
|
stk->fir.sum[0] += acc_lp->x;
|
|
stk->fir.sum[1] += acc_lp->y;
|
|
stk->fir.sum[2] += acc_lp->z;
|
|
stk->fir.num++;
|
|
stk->fir.idx++;
|
|
} else {
|
|
idx = stk->fir.idx % firlength;
|
|
stk->fir.sum[0] -= stk->fir.raw[idx][0];
|
|
stk->fir.sum[1] -= stk->fir.raw[idx][1];
|
|
stk->fir.sum[2] -= stk->fir.raw[idx][2];
|
|
stk->fir.raw[idx][0] = acc_lp->x;
|
|
stk->fir.raw[idx][1] = acc_lp->y;
|
|
stk->fir.raw[idx][2] = acc_lp->z;
|
|
stk->fir.sum[0] += acc_lp->x;
|
|
stk->fir.sum[1] += acc_lp->y;
|
|
stk->fir.sum[2] += acc_lp->z;
|
|
stk->fir.idx++;
|
|
#ifdef STK_ZG_FILTER
|
|
if (abs(stk->fir.sum[0] / firlength) <= STK_ZG_COUNT)
|
|
acc_lp->x = (stk->fir.sum[0] * zero_fir) / firlength;
|
|
else
|
|
acc_lp->x = stk->fir.sum[0] / firlength;
|
|
if (abs(stk->fir.sum[1] / firlength) <= STK_ZG_COUNT)
|
|
acc_lp->y = (stk->fir.sum[1] * zero_fir) / firlength;
|
|
else
|
|
acc_lp->y = stk->fir.sum[1] / firlength;
|
|
if (abs(stk->fir.sum[2] / firlength) <= STK_ZG_COUNT)
|
|
acc_lp->z = (stk->fir.sum[2] * zero_fir) / firlength;
|
|
else
|
|
acc_lp->z = stk->fir.sum[2] / firlength;
|
|
#else
|
|
acc_lp->x = stk->fir.sum[0] / firlength;
|
|
acc_lp->y = stk->fir.sum[1] / firlength;
|
|
acc_lp->z = stk->fir.sum[2] / firlength;
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#ifdef STK_TUNE
|
|
static void stk8baxx_reset_para(struct stk8baxx_data *stk)
|
|
{
|
|
int ii;
|
|
|
|
for (ii = 0; ii < 3; ii++) {
|
|
stk->stk_tune_sum[ii] = 0;
|
|
stk->stk_tune_square_sum[ii] = 0LL;
|
|
stk->stk_tune_min[ii] = 4096;
|
|
stk->stk_tune_max[ii] = -4096;
|
|
stk->variance[ii] = 0;
|
|
}
|
|
}
|
|
|
|
static void stk8baxx_tune(struct stk8baxx_data *stk, struct stk8baxx_acc *acc_xyz)
|
|
{
|
|
int ii;
|
|
u8 offset[3];
|
|
s16 acc[3];
|
|
s64 s64_temp;
|
|
const s64 var_enlarge_scale = 64;
|
|
|
|
if (stk->stk_tune_done != 0)
|
|
return;
|
|
|
|
acc[0] = acc_xyz->x;
|
|
acc[1] = acc_xyz->y;
|
|
acc[2] = acc_xyz->z;
|
|
|
|
if (stk->event_since_en >= STK_TUNE_DELAY) {
|
|
if ((abs(acc[0]) <= STK_TUNE_XYOFFSET) && (abs(acc[1]) <= STK_TUNE_XYOFFSET) &&
|
|
(abs(abs(acc[2]) - STK_LSB_1G) <= STK_TUNE_ZOFFSET)) {
|
|
stk->stk_tune_index++;
|
|
/* printk("\n-qhy20161108--%s----acc[0]=0x%x,,acc[1]=0x%x,,acc[2]=0x%x\n",__func__,acc[0],acc[1],acc[2]); */
|
|
} else {
|
|
stk->stk_tune_index = 0;
|
|
}
|
|
|
|
if (stk->stk_tune_index == 0) {
|
|
stk8baxx_reset_para(stk);
|
|
/* printk("\n--qhy20161108--%s-- %d--\n",__func__,__LINE__); */
|
|
} else {
|
|
for (ii = 0; ii < 3; ii++) {
|
|
stk->stk_tune_sum[ii] += acc[ii];
|
|
stk->stk_tune_square_sum[ii] += acc[ii] * acc[ii];
|
|
if (acc[ii] > stk->stk_tune_max[ii])
|
|
stk->stk_tune_max[ii] = acc[ii];
|
|
if (acc[ii] < stk->stk_tune_min[ii])
|
|
stk->stk_tune_min[ii] = acc[ii];
|
|
}
|
|
}
|
|
|
|
if (stk->stk_tune_index == STK_TUNE_NUM) {
|
|
for (ii = 0; ii < 3; ii++) {
|
|
if ((stk->stk_tune_max[ii] - stk->stk_tune_min[ii]) > STK_TUNE_NOISE) {
|
|
stk->stk_tune_index = 0;
|
|
stk8baxx_reset_para(stk);
|
|
return;
|
|
}
|
|
}
|
|
|
|
stk->stk_tune_offset[0] = stk->stk_tune_sum[0] / STK_TUNE_NUM;
|
|
stk->stk_tune_offset[1] = stk->stk_tune_sum[1] / STK_TUNE_NUM;
|
|
if (acc[2] > 0)
|
|
stk->stk_tune_offset[2] = stk->stk_tune_sum[2] / STK_TUNE_NUM - STK_LSB_1G;
|
|
else
|
|
stk->stk_tune_offset[2] = stk->stk_tune_sum[2] / STK_TUNE_NUM - (-STK_LSB_1G);
|
|
|
|
offset[0] = (u8)(-stk->stk_tune_offset[0]);
|
|
offset[1] = (u8)(-stk->stk_tune_offset[1]);
|
|
offset[2] = (u8)(-stk->stk_tune_offset[2]);
|
|
stk->stk_tune_offset_record[0] = offset[0];
|
|
stk->stk_tune_offset_record[1] = offset[1];
|
|
stk->stk_tune_offset_record[2] = offset[2];
|
|
|
|
stk->stk_tune_done = 1;
|
|
atomic_set(&stk->cali_status, STK_K_SUCCESS_TUNE);
|
|
stk->event_since_en = 0;
|
|
printk(KERN_INFO "%s:TUNE done, %d,%d,%d\n", __func__, offset[0], offset[1], offset[2]);
|
|
printk(KERN_INFO "%s:TUNE done, var=%u,%u,%u\n", __func__, stk->variance[0], stk->variance[1], stk->variance[2]);
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
static void stk8baxx_sign_conv(struct stk8baxx_data *stk, s16 raw_acc_data[], u8 acc_reg_data[])
|
|
{
|
|
#ifdef CONFIG_SENSORS_STK8BA53
|
|
raw_acc_data[0] = acc_reg_data[1] << 8 | acc_reg_data[0];
|
|
raw_acc_data[0] >>= 4;
|
|
raw_acc_data[1] = acc_reg_data[3] << 8 | acc_reg_data[2];
|
|
raw_acc_data[1] >>= 4;
|
|
raw_acc_data[2] = acc_reg_data[5] << 8 | acc_reg_data[4];
|
|
raw_acc_data[2] >>= 4;
|
|
#else
|
|
raw_acc_data[0] = acc_reg_data[1] << 8 | acc_reg_data[0];
|
|
raw_acc_data[0] >>= 6;
|
|
raw_acc_data[1] = acc_reg_data[3] << 8 | acc_reg_data[2];
|
|
raw_acc_data[1] >>= 6;
|
|
raw_acc_data[2] = acc_reg_data[5] << 8 | acc_reg_data[4];
|
|
raw_acc_data[2] >>= 6;
|
|
#endif
|
|
}
|
|
|
|
static int stk8baxx_set_enable(struct stk8baxx_data *stk, char en)
|
|
{
|
|
s8 result;
|
|
s8 write_buffer = 0;
|
|
int new_enabled = (en) ? 1 : 0;
|
|
|
|
/*int k_status = atomic_read(&stk->cali_status);*/
|
|
#ifdef STK_DEBUG_PRINT
|
|
printk("%s:+++1+++--k_status=%d,first_enable=%d\n", __func__, k_status, stk->first_enable);
|
|
if (stk->first_enable && k_status != STK_K_RUNNING) {
|
|
stk->first_enable = false;
|
|
printk("%s:+++2+++first_enable=%d\n", __func__, stk->first_enable);
|
|
stk8baxx_load_cali(stk);
|
|
}
|
|
#endif
|
|
enable_status = new_enabled;
|
|
if (new_enabled == atomic_read(&stk->enabled))
|
|
return 0;
|
|
/* printk(KERN_INFO "%s:%x\n", __func__, en); */
|
|
|
|
if (en)
|
|
write_buffer = STK8BAXX_MD_NORMAL;
|
|
else
|
|
write_buffer = STK8BAXX_MD_SUSPEND;
|
|
|
|
result = stk8baxx_smbus_write_byte_data(STK8BAXX_POWMODE, write_buffer);
|
|
if (result < 0) {
|
|
printk(KERN_ERR "%s:failed to write reg 0x%x, error=%d\n", __func__, STK8BAXX_POWMODE, result);
|
|
goto error_enable;
|
|
}
|
|
|
|
if (en) {
|
|
stk->event_since_en = 0;
|
|
#ifdef STK_TUNE
|
|
if ((k_status & 0xF0) != 0 && stk->stk_tune_done == 0) {
|
|
stk->stk_tune_index = 0;
|
|
stk8baxx_reset_para(stk);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
atomic_set(&stk->enabled, new_enabled);
|
|
return 0;
|
|
|
|
error_enable:
|
|
return result;
|
|
}
|
|
|
|
static int gsensor_report_value(struct i2c_client *client, struct sensor_axis *axis)
|
|
{
|
|
struct sensor_private_data *sensor =
|
|
(struct sensor_private_data *)i2c_get_clientdata(client);
|
|
|
|
/* Report acceleration sensor information */
|
|
input_report_abs(sensor->input_dev, ABS_X, axis->x);
|
|
input_report_abs(sensor->input_dev, ABS_Y, axis->y);
|
|
input_report_abs(sensor->input_dev, ABS_Z, axis->z);
|
|
input_sync(sensor->input_dev);
|
|
#ifdef STK_DEBUG_PRINT
|
|
printk(KERN_INFO "Gsensor x==%d y==%d z==%d\n", axis->x, axis->y, axis->z);
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
static int stk8baxx_read_sensor_data(struct stk8baxx_data *stk)
|
|
{
|
|
int result;
|
|
u8 acc_reg[6];
|
|
int x, y, z;
|
|
struct stk8baxx_acc acc;
|
|
struct sensor_private_data *sensor =
|
|
(struct sensor_private_data *)i2c_get_clientdata(stk->client);
|
|
struct sensor_platform_data *pdata = sensor->pdata;
|
|
s16 raw_acc[3];
|
|
|
|
acc.x = 0;
|
|
acc.y = 0;
|
|
acc.z = 0;
|
|
|
|
*acc_reg = sensor->ops->read_reg;
|
|
result = sensor_rx_data(stk->client, (char *)acc_reg, sensor->ops->read_len);
|
|
if (result < 0) {
|
|
printk(KERN_ERR "%s: failed to read acc data, error=%d\n", __func__, result);
|
|
return result;
|
|
}
|
|
|
|
stk8baxx_sign_conv(stk, raw_acc, acc_reg);
|
|
#ifdef STK_DEBUG_PRINT
|
|
printk(KERN_INFO "%s: raw_acc=%4d,%4d,%4d\n", __func__, (int)raw_acc[0], (int)raw_acc[1], (int)raw_acc[2]);
|
|
#endif
|
|
acc.x = raw_acc[0];
|
|
acc.y = raw_acc[1];
|
|
acc.z = raw_acc[2];
|
|
#ifdef STK_TUNE
|
|
if ((k_status & 0xF0) != 0)
|
|
stk8baxx_tune(stk, &acc);
|
|
#endif
|
|
x = acc.x;
|
|
y = acc.y;
|
|
z = acc.z;
|
|
acc.x = (pdata->orientation[0]) * x + (pdata->orientation[1]) * y + (pdata->orientation[2]) * z;
|
|
acc.y = (pdata->orientation[3]) * x + (pdata->orientation[4]) * y + (pdata->orientation[5]) * z;
|
|
acc.z = (pdata->orientation[6]) * x + (pdata->orientation[7]) * y + (pdata->orientation[8]) * z;
|
|
|
|
#ifdef STK_LOWPASS
|
|
stk8baxx_low_pass(stk, &acc);
|
|
#endif
|
|
|
|
stk->acc_xyz.x = acc.x;
|
|
stk->acc_xyz.y = acc.y;
|
|
stk->acc_xyz.z = acc.z;
|
|
#ifdef STK_DEBUG_PRINT
|
|
printk(KERN_INFO "stk8baxx acc= %4d, %4d, %4d\n", (int)stk->acc_xyz.x, (int)stk->acc_xyz.y, (int)stk->acc_xyz.z);
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
static int sensor_report_value(struct i2c_client *client)
|
|
{
|
|
unsigned int xyz_adc_rang = 0;
|
|
struct sensor_axis axis;
|
|
struct sensor_private_data *sensor =
|
|
(struct sensor_private_data *)i2c_get_clientdata(client);
|
|
static int flag;
|
|
|
|
stk8baxx_read_sensor_data(stk8baxx_data_ptr);
|
|
|
|
xyz_adc_rang = STK_LSB_1G * STK_DEF_RANGE;
|
|
axis.x = stk8baxx_data_ptr->acc_xyz.x * (STK8BAXX_RANGE_UG / xyz_adc_rang);
|
|
axis.y = stk8baxx_data_ptr->acc_xyz.y * (STK8BAXX_RANGE_UG / xyz_adc_rang);
|
|
axis.z = stk8baxx_data_ptr->acc_xyz.z * (STK8BAXX_RANGE_UG / xyz_adc_rang);
|
|
|
|
/*
|
|
*input dev will ignore report data if data value is the same with last_value,
|
|
*sample rate will not enough by this way, so just avoid this case
|
|
*/
|
|
if ((sensor->axis.x == axis.x) && (sensor->axis.y == axis.y) && (sensor->axis.z == axis.z)) {
|
|
if (flag) {
|
|
flag = 0;
|
|
axis.x += 1;
|
|
axis.y += 1;
|
|
axis.z += 1;
|
|
} else {
|
|
flag = 1;
|
|
axis.x -= 1;
|
|
axis.y -= 1;
|
|
axis.z -= 1;
|
|
}
|
|
}
|
|
|
|
gsensor_report_value(client, &axis);
|
|
|
|
mutex_lock(&sensor->data_mutex);
|
|
sensor->axis = axis;
|
|
mutex_unlock(&sensor->data_mutex);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int sensor_active(struct i2c_client *client, int enable, int rate)
|
|
{
|
|
if (enable)
|
|
stk8baxx_set_enable(stk8baxx_data_ptr, 1);
|
|
else
|
|
stk8baxx_set_enable(stk8baxx_data_ptr, 0);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int sensor_init(struct i2c_client *client)
|
|
{
|
|
int ret = 0;
|
|
struct stk8baxx_data *stk;
|
|
struct sensor_private_data *sensor =
|
|
(struct sensor_private_data *)i2c_get_clientdata(client);
|
|
|
|
printk(KERN_INFO "driver version:%s\n", STK_ACC_DRIVER_VERSION);
|
|
if (!enable_status)
|
|
return 0;
|
|
stk = kzalloc(sizeof(*stk), GFP_KERNEL);
|
|
if (!stk) {
|
|
printk(KERN_ERR "%s:memory allocation error\n", __func__);
|
|
return -ENOMEM;
|
|
}
|
|
stk8baxx_data_ptr = stk;
|
|
sensor_ptr = sensor;
|
|
stk->stk8baxx_placement = STK8BAXX_DEF_PLACEMENT;
|
|
stk->client = client;
|
|
ret = stk8baxx_reg_init(stk, client, sensor);
|
|
if (ret) {
|
|
printk(KERN_ERR "%s:stk8baxx initialization failed\n", __func__);
|
|
return ret;
|
|
}
|
|
stk->re_enable = false;
|
|
sensor->status_cur = SENSOR_OFF;
|
|
|
|
/* Sys Attribute Register */
|
|
if (no_create_att) {
|
|
struct input_dev *p_input_dev = NULL;
|
|
|
|
p_input_dev = input_allocate_device();
|
|
if (!p_input_dev) {
|
|
dev_err(&client->dev,
|
|
"Failed to allocate input device\n");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
p_input_dev->name = "stk8baxx_attr";
|
|
set_bit(EV_ABS, p_input_dev->evbit);
|
|
dev_set_drvdata(&p_input_dev->dev, stk);
|
|
ret = input_register_device(p_input_dev);
|
|
if (ret) {
|
|
dev_err(&client->dev,
|
|
"Unable to register input device %s\n", p_input_dev->name);
|
|
return ret;
|
|
}
|
|
|
|
DBG("Sys Attribute Register here %s is called for stk8baxx.\n", __func__);
|
|
no_create_att = 0;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static struct sensor_operate gsensor_stk8baxx_ops = {
|
|
.name = "gs_stk8baxx",
|
|
.type = SENSOR_TYPE_ACCEL, /*sensor type and it should be correct*/
|
|
.id_i2c = ACCEL_ID_STK8BAXX, /*i2c id number*/
|
|
.read_reg = STK8BAXX_XOUT1, /*read data*/
|
|
.read_len = 6, /*data length*/
|
|
.id_reg = SENSOR_UNKNOW_DATA, /*read device id from this register*/
|
|
.id_data = SENSOR_UNKNOW_DATA, /*device id*/
|
|
.precision = SENSOR_UNKNOW_DATA, /*12 bit*/
|
|
.ctrl_reg = STK8BAXX_POWMODE, /*enable or disable*/
|
|
/*intterupt status register*/
|
|
.int_status_reg = STK8BAXX_INTSTS2,
|
|
.range = {-STK8BAXX_RANGE_UG, STK8BAXX_RANGE_UG}, /*range*/
|
|
.trig = IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
|
|
.active = sensor_active,
|
|
.init = sensor_init,
|
|
.report = sensor_report_value,
|
|
};
|
|
|
|
static int gsensor_stk8baxx_probe(struct i2c_client *client,
|
|
const struct i2c_device_id *devid)
|
|
{
|
|
return sensor_register_device(client, NULL, devid, &gsensor_stk8baxx_ops);
|
|
}
|
|
|
|
static void gsensor_stk8baxx_remove(struct i2c_client *client)
|
|
{
|
|
sensor_unregister_device(client, NULL, &gsensor_stk8baxx_ops);
|
|
}
|
|
|
|
static const struct i2c_device_id gsensor_stk8baxx_id[] = {
|
|
{"gs_stk8baxx", ACCEL_ID_STK8BAXX},
|
|
{}
|
|
};
|
|
|
|
static struct i2c_driver gsensor_stk8baxx_driver = {
|
|
.probe = gsensor_stk8baxx_probe,
|
|
.remove = (void *)gsensor_stk8baxx_remove,
|
|
.shutdown = sensor_shutdown,
|
|
.id_table = gsensor_stk8baxx_id,
|
|
.driver = {
|
|
.name = "gsensor_stk8baxx",
|
|
#ifdef CONFIG_PM
|
|
.pm = &sensor_pm_ops,
|
|
#endif
|
|
},
|
|
};
|
|
|
|
module_i2c_driver(gsensor_stk8baxx_driver);
|
|
|
|
MODULE_AUTHOR("Lex Hsieh, Sensortek");
|
|
MODULE_DESCRIPTION("stk8baxx 3-Axis accelerometer driver");
|
|
MODULE_LICENSE("GPL");
|
|
MODULE_VERSION(STK_ACC_DRIVER_VERSION);
|