Files
threeeye bc1d038eac REVVL 6 Pro kernel bring-up: full working tree snapshot (2026-09-12)
Squashed backup commit (no shallow-clone history issues) of
xiaomi-mt6833-dev/kernel_xiaomi_mt6833 (lineage-23.2 branch, upstream
commit 9eece662e) plus all fixes applied to get everpal_defconfig building
clean against a modern (GCC 15.2.0) toolchain -- see the previous commit
message on the lineage-23.2 branch (also in this repo history is not
available here since this is a squashed snapshot; full incremental history
lives in the local DL380 checkout and the upstream GitHub remote).

Upstream: https://github.com/xiaomi-mt6833-dev/kernel_xiaomi_mt6833 (lineage-23.2)
2026-09-12 13:36:46 +00:00

1834 lines
46 KiB
C

/*
* Copyright (C) 2017 MediaTek Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
/* History: V1.0 --- [2013.01.29]Driver creation
* V1.1 --- [2013.03.28]
* 1.Instead late_resume, use resume to make sure
* driver resume is ealier than processes resume.
* 2.Add power mode setting in read data.
* V1.2 --- [2013.06.28]Add self test function.
* V1.3 --- [2013.07.26]Fix the bug of wrong axis remapping
* in rawdata inode.
*/
#define pr_fmt(fmt) "[bmi160_gyro] " fmt
#include <linux/atomic.h>
#include <linux/delay.h>
#include <linux/i2c.h>
#include <linux/input.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/kobject.h>
#include <linux/miscdevice.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/suspend.h>
#include <linux/uaccess.h>
#include <linux/workqueue.h>
#include <cust_gyro.h>
#include <gyroscope.h>
#include <hwmsensor.h>
/* #include <hwmsen_dev.h> */
#include "bmi160_gyro.h"
#include <hwmsen_helper.h>
#include <sensors_io.h>
/* sensor type */
enum SENSOR_TYPE_ENUM { BMI160_GYRO_TYPE = 0x0, INVALID_TYPE = 0xff };
/* range */
enum BMG_RANGE_ENUM {
BMG_RANGE_2000 = 0x0, /* +/- 2000 degree/s */
BMG_RANGE_1000, /* +/- 1000 degree/s */
BMG_RANGE_500, /* +/- 500 degree/s */
BMG_RANGE_250, /* +/- 250 degree/s */
BMG_RANGE_125, /* +/- 125 degree/s */
BMG_UNDEFINED_RANGE = 0xff
};
/* power mode */
enum BMG_POWERMODE_ENUM {
BMG_SUSPEND_MODE = 0x0,
BMG_NORMAL_MODE,
BMG_UNDEFINED_POWERMODE = 0xff
};
/* debug information flags */
enum GYRO_TRC {
GYRO_TRC_FILTER = 0x01,
GYRO_TRC_RAWDATA = 0x02,
GYRO_TRC_IOCTL = 0x04,
GYRO_TRC_CALI = 0x08,
GYRO_TRC_INFO = 0x10,
};
/* s/w data filter */
struct data_filter {
s16 raw[C_MAX_FIR_LENGTH][BMG_AXES_NUM];
int sum[BMG_AXES_NUM];
int num;
int idx;
};
/* bmg i2c client data */
struct bmg_i2c_data {
struct i2c_client *client;
struct gyro_hw *hw;
struct hwmsen_convert cvt;
/* sensor info */
u8 sensor_name[MAX_SENSOR_NAME];
enum SENSOR_TYPE_ENUM sensor_type;
enum BMG_POWERMODE_ENUM power_mode;
enum BMG_RANGE_ENUM range;
int datarate;
/* sensitivity = 2^bitnum/range
* [+/-2000 = 4000; +/-1000 = 2000;
* +/-500 = 1000; +/-250 = 500;
* +/-125 = 250 ]
*/
u16 sensitivity;
/*misc */
struct mutex lock;
atomic_t trace;
atomic_t suspend;
atomic_t filter;
atomic_t gyro_debounce; /*debounce time after enabling gyro */
atomic_t gyro_deb_on; /*indicates if the debounce is on */
atomic_t gyro_deb_end; /*the jiffies representing the end of debounce */
/* unmapped axis value */
s16 cali_sw[BMG_AXES_NUM + 1];
/* hw offset */
s8 offset[BMG_AXES_NUM + 1]; /* +1:for 4-byte alignment */
#if defined(CONFIG_BMG_LOWPASS)
atomic_t firlen;
atomic_t fir_en;
struct data_filter fir;
#endif
};
struct gyro_hw gyro_cust;
static struct gyro_hw *hw = &gyro_cust;
static struct gyro_init_info bmi160_gyro_init_info;
/* 0=OK, -1=fail */
static int bmi160_gyro_init_flag = -1;
static struct i2c_driver bmg_i2c_driver;
static struct bmg_i2c_data *obj_i2c_data;
static int bmg_set_powermode(struct i2c_client *client,
enum BMG_POWERMODE_ENUM power_mode);
static const struct i2c_device_id bmg_i2c_id[] = {{BMG_DEV_NAME, 0}, {} };
#ifndef BMI160_ACCESS_BY_GSE_I2C
/* I2C operation functions */
static int bmg_i2c_read_block(struct i2c_client *client, u8 addr, u8 *data,
u8 len)
{
int err = 0;
u8 beg = addr;
struct i2c_msg msgs[2] = {{0}, {0} };
mutex_lock(&lsm6ds3h_init_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;
if (!client)
return -EINVAL;
else if (len > C_I2C_FIFO_SIZE) {
pr_err_ratelimited("len %d ex %d\n", len, C_I2C_FIFO_SIZE);
return -EINVAL;
}
err = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
if (err != 2) {
pr_err_ratelimited("i2c_tr err: (%d %p %d) %d\n",
addr, data, len, err);
err = -EIO;
} else {
err = 0;
}
return err;
}
static int bmg_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 = 0;
int idx = 0;
int num = 0;
char buf[C_I2C_FIFO_SIZE];
if (!client)
return -EINVAL;
else if (len >= C_I2C_FIFO_SIZE) {
pr_err_ratelimited("len %d ex %d\n", len, C_I2C_FIFO_SIZE);
return -EINVAL;
}
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("send command error.\n");
return -EFAULT;
}
err = 0;
return err;
}
#endif
static int bmg_read_raw_data(struct i2c_client *client, s16 data[BMG_AXES_NUM])
{
int err = 0;
struct bmg_i2c_data *priv = obj_i2c_data;
u8 data_1 = 0;
if (priv->power_mode == BMG_SUSPEND_MODE) {
err = bmg_set_powermode(
client, (enum BMG_POWERMODE_ENUM)BMG_NORMAL_MODE);
if (err < 0) {
pr_err_ratelimited("set power fail, err = %d\n", err);
return err;
}
}
if (priv->sensor_type == BMI160_GYRO_TYPE) {
u8 buf_tmp[BMG_DATA_LEN] = {0};
#ifdef BMI160_ACCESS_BY_GSE_I2C
err = bmi_i2c_read_wrapper(0, BMI160_USER_DATA_8_GYR_X_LSB__REG,
buf_tmp, 6);
#else
err = bmg_i2c_read_block(
client, BMI160_USER_DATA_8_GYR_X_LSB__REG, buf_tmp, 6);
#endif
if (err) {
pr_err_ratelimited("read gyro raw data failed.\n");
return err;
}
/* Data X */
data[BMG_AXIS_X] = (s16)(
(((s32)((s8)buf_tmp[1])) << BMI160_SHIFT_8_POSITION) |
(buf_tmp[0]));
/* Data Y */
data[BMG_AXIS_Y] = (s16)(
(((s32)((s8)buf_tmp[3])) << BMI160_SHIFT_8_POSITION) |
(buf_tmp[2]));
/* Data Z */
data[BMG_AXIS_Z] = (s16)(
(((s32)((s8)buf_tmp[5])) << BMI160_SHIFT_8_POSITION) |
(buf_tmp[4]));
if (atomic_read(&priv->trace) & GYRO_TRC_RAWDATA) {
pr_debug(
"[%s][16bit raw][%08X %08X %08X] => [%5d %5d %5d]\n",
priv->sensor_name, data[BMG_AXIS_X],
data[BMG_AXIS_Y], data[BMG_AXIS_Z],
data[BMG_AXIS_X], data[BMG_AXIS_Y],
data[BMG_AXIS_Z]);
}
}
if (atomic_read(&priv->trace) & GYRO_TRC_INFO) {
err = bmg_get_powermode(client, &data_1);
if (err < 0) {
pr_err_ratelimited("bmg_get_powermode failed.\n");
/* return err; */
}
}
#ifdef CONFIG_BMG_LOWPASS
/*
*Example: firlen = 16, filter buffer = [0] ... [15],
*when 17th data come, replace [0] with this new data.
*Then, average this filter buffer and report average value to upper
*layer.
*/
if (atomic_read(&priv->filter)) {
if (atomic_read(&priv->fir_en) &&
!atomic_read(&priv->suspend)) {
int idx, firlen = atomic_read(&priv->firlen);
if (priv->fir.num < firlen) {
priv->fir.raw[priv->fir.num][BMG_AXIS_X] =
data[BMG_AXIS_X];
priv->fir.raw[priv->fir.num][BMG_AXIS_Y] =
data[BMG_AXIS_Y];
priv->fir.raw[priv->fir.num][BMG_AXIS_Z] =
data[BMG_AXIS_Z];
priv->fir.sum[BMG_AXIS_X] += data[BMG_AXIS_X];
priv->fir.sum[BMG_AXIS_Y] += data[BMG_AXIS_Y];
priv->fir.sum[BMG_AXIS_Z] += data[BMG_AXIS_Z];
if (atomic_read(&priv->trace) &
GYRO_TRC_FILTER) {
pr_debug(
"add [%2d][%5d %5d %5d] => [%5d %5d %5d]\n",
priv->fir.num,
priv->fir.raw[priv->fir.num]
[BMG_AXIS_X],
priv->fir.raw[priv->fir.num]
[BMG_AXIS_Y],
priv->fir.raw[priv->fir.num]
[BMG_AXIS_Z],
priv->fir.sum[BMG_AXIS_X],
priv->fir.sum[BMG_AXIS_Y],
priv->fir.sum[BMG_AXIS_Z]);
}
priv->fir.num++;
priv->fir.idx++;
} else {
idx = priv->fir.idx % firlen;
priv->fir.sum[BMG_AXIS_X] -=
priv->fir.raw[idx][BMG_AXIS_X];
priv->fir.sum[BMG_AXIS_Y] -=
priv->fir.raw[idx][BMG_AXIS_Y];
priv->fir.sum[BMG_AXIS_Z] -=
priv->fir.raw[idx][BMG_AXIS_Z];
priv->fir.raw[idx][BMG_AXIS_X] =
data[BMG_AXIS_X];
priv->fir.raw[idx][BMG_AXIS_Y] =
data[BMG_AXIS_Y];
priv->fir.raw[idx][BMG_AXIS_Z] =
data[BMG_AXIS_Z];
priv->fir.sum[BMG_AXIS_X] += data[BMG_AXIS_X];
priv->fir.sum[BMG_AXIS_Y] += data[BMG_AXIS_Y];
priv->fir.sum[BMG_AXIS_Z] += data[BMG_AXIS_Z];
priv->fir.idx++;
data[BMG_AXIS_X] =
priv->fir.sum[BMG_AXIS_X] / firlen;
data[BMG_AXIS_Y] =
priv->fir.sum[BMG_AXIS_Y] / firlen;
data[BMG_AXIS_Z] =
priv->fir.sum[BMG_AXIS_Z] / firlen;
if (atomic_read(&priv->trace) &
GYRO_TRC_FILTER) {
pr_debug(
"add [%2d][%5d %5d %5d] => [%5d %5d %5d] : [%5d %5d %5d]\n",
idx,
priv->fir.raw[idx][BMG_AXIS_X],
priv->fir.raw[idx][BMG_AXIS_Y],
priv->fir.raw[idx][BMG_AXIS_Z],
priv->fir.sum[BMG_AXIS_X],
priv->fir.sum[BMG_AXIS_Y],
priv->fir.sum[BMG_AXIS_Z],
data[BMG_AXIS_X],
data[BMG_AXIS_Y],
data[BMG_AXIS_Z]);
}
}
}
}
#endif
return err;
}
#ifndef SW_CALIBRATION
/* get hardware offset value from chip register */
static int bmg_get_hw_offset(struct i2c_client *client,
s8 offset[BMG_AXES_NUM + 1])
{
int err = 0;
/* HW calibration is under construction */
pr_debug("hw offset x=%x, y=%x, z=%x\n", offset[BMG_AXIS_X],
offset[BMG_AXIS_Y], offset[BMG_AXIS_Z]);
return err;
}
#endif
#ifndef SW_CALIBRATION
/* set hardware offset value to chip register*/
static int bmg_set_hw_offset(struct i2c_client *client,
s8 offset[BMG_AXES_NUM + 1])
{
/* HW calibration is under construction */
pr_debug("hw offset x=%x, y=%x, z=%x\n", offset[BMG_AXIS_X],
offset[BMG_AXIS_Y], offset[BMG_AXIS_Z]);
return 0;
}
#endif
static int bmg_reset_calibration(struct i2c_client *client)
{
struct bmg_i2c_data *obj = obj_i2c_data;
#ifdef SW_CALIBRATION
#else
bmg_set_hw_offset(client, obj->offset);
#endif
memset(obj->cali_sw, 0x00, sizeof(obj->cali_sw));
memset(obj->offset, 0x00, sizeof(obj->offset));
return 0;
}
static int bmg_read_calibration(struct i2c_client *client,
int act[BMG_AXES_NUM], int raw[BMG_AXES_NUM])
{
/*
*raw: the raw calibration data, unmapped;
*act: the actual calibration data, mapped
*/
int err = 0;
int mul;
struct bmg_i2c_data *obj = obj_i2c_data;
#ifdef SW_CALIBRATION
/* only sw calibration, disable hw calibration */
mul = 0;
#else
err = bmg_get_hw_offset(client, obj->offset);
if (err) {
pr_err_ratelimited("read cali err, %d\n", err);
return err;
}
mul = 1; /* mul = sensor sensitivity / offset sensitivity */
#endif
raw[BMG_AXIS_X] =
obj->offset[BMG_AXIS_X] * mul + obj->cali_sw[BMG_AXIS_X];
raw[BMG_AXIS_Y] =
obj->offset[BMG_AXIS_Y] * mul + obj->cali_sw[BMG_AXIS_Y];
raw[BMG_AXIS_Z] =
obj->offset[BMG_AXIS_Z] * mul + obj->cali_sw[BMG_AXIS_Z];
act[obj->cvt.map[BMG_AXIS_X]] =
obj->cvt.sign[BMG_AXIS_X] * raw[BMG_AXIS_X];
act[obj->cvt.map[BMG_AXIS_Y]] =
obj->cvt.sign[BMG_AXIS_Y] * raw[BMG_AXIS_Y];
act[obj->cvt.map[BMG_AXIS_Z]] =
obj->cvt.sign[BMG_AXIS_Z] * raw[BMG_AXIS_Z];
return err;
}
static int bmg_write_calibration(struct i2c_client *client,
int dat[BMG_AXES_NUM])
{
/* dat array : Android coordinate system, mapped, unit:LSB */
int err = 0;
int cali[BMG_AXES_NUM] = {0};
int raw[BMG_AXES_NUM] = {0};
struct bmg_i2c_data *obj = obj_i2c_data;
/*offset will be updated in obj->offset */
err = bmg_read_calibration(client, cali, raw);
if (err) {
pr_err_ratelimited("read cali fail, %d\n", err);
return err;
}
/* calculate the real offset expected by caller */
cali[BMG_AXIS_X] += dat[BMG_AXIS_X];
cali[BMG_AXIS_Y] += dat[BMG_AXIS_Y];
cali[BMG_AXIS_Z] += dat[BMG_AXIS_Z];
pr_debug("UPDATE: add mapped data(%+3d %+3d %+3d)\n", dat[BMG_AXIS_X],
dat[BMG_AXIS_Y], dat[BMG_AXIS_Z]);
#ifdef SW_CALIBRATION
/* obj->cali_sw array : chip coordinate system, unmapped,unit:LSB */
obj->cali_sw[BMG_AXIS_X] =
obj->cvt.sign[BMG_AXIS_X] * (cali[obj->cvt.map[BMG_AXIS_X]]);
obj->cali_sw[BMG_AXIS_Y] =
obj->cvt.sign[BMG_AXIS_Y] * (cali[obj->cvt.map[BMG_AXIS_Y]]);
obj->cali_sw[BMG_AXIS_Z] =
obj->cvt.sign[BMG_AXIS_Z] * (cali[obj->cvt.map[BMG_AXIS_Z]]);
#else
/* divisor = sensor sensitivity / offset sensitivity */
int divisor = 1;
obj->offset[BMG_AXIS_X] =
(s8)(obj->cvt.sign[BMG_AXIS_X] *
(cali[obj->cvt.map[BMG_AXIS_X]]) / (divisor));
obj->offset[BMG_AXIS_Y] =
(s8)(obj->cvt.sign[BMG_AXIS_Y] *
(cali[obj->cvt.map[BMG_AXIS_Y]]) / (divisor));
obj->offset[BMG_AXIS_Z] =
(s8)(obj->cvt.sign[BMG_AXIS_Z] *
(cali[obj->cvt.map[BMG_AXIS_Z]]) / (divisor));
/*convert software calibration using standard calibration */
obj->cali_sw[BMG_AXIS_X] = obj->cvt.sign[BMG_AXIS_X] *
(cali[obj->cvt.map[BMG_AXIS_X]]) % (divisor);
obj->cali_sw[BMG_AXIS_Y] = obj->cvt.sign[BMG_AXIS_Y] *
(cali[obj->cvt.map[BMG_AXIS_Y]]) % (divisor);
obj->cali_sw[BMG_AXIS_Z] = obj->cvt.sign[BMG_AXIS_Z] *
(cali[obj->cvt.map[BMG_AXIS_Z]]) % (divisor);
/* HW calibration is under construction */
err = bmg_set_hw_offset(client, obj->offset);
if (err) {
pr_err_ratelimited("read hw offset failed.\n");
return err;
}
#endif
return err;
}
/* get chip type */
static int bmg_get_chip_type(struct i2c_client *client)
{
int err = 0;
u8 chip_id = 0;
struct bmg_i2c_data *obj = obj_i2c_data;
/* twice */
#ifdef BMI160_ACCESS_BY_GSE_I2C
err = bmi_i2c_read_wrapper(0, BMI160_USER_CHIP_ID__REG, &chip_id, 1);
err = bmi_i2c_read_wrapper(0, BMI160_USER_CHIP_ID__REG, &chip_id, 1);
#else
err = bmg_i2c_read_block(client, BMI160_USER_CHIP_ID__REG, &chip_id, 1);
err = bmg_i2c_read_block(client, BMI160_USER_CHIP_ID__REG, &chip_id, 1);
#endif
if (err != 0) {
pr_err_ratelimited("read chip id failed.\n");
return err;
}
switch (chip_id) {
case SENSOR_CHIP_ID_BMI:
case SENSOR_CHIP_ID_BMI_C2:
case SENSOR_CHIP_ID_BMI_C3:
obj->sensor_type = BMI160_GYRO_TYPE;
strlcpy(obj->sensor_name, BMG_DEV_NAME,
sizeof(obj->sensor_name));
break;
default:
obj->sensor_type = INVALID_TYPE;
strlcpy(obj->sensor_name, UNKNOWN_DEV,
sizeof(obj->sensor_name));
break;
}
if (obj->sensor_type == INVALID_TYPE) {
pr_err("unknown gyroscope.\n");
return -1;
}
return err;
}
int bmg_get_drop_cmd_err(struct i2c_client *client, unsigned char *drop_cmd_err)
{
int comres = 0;
u8 v_data_u8r = ((u8)0);
#ifdef BMI160_ACCESS_BY_GSE_I2C
comres = bmi_i2c_read_wrapper(0, BMI160_USER_DROP_CMD_ERR__REG,
&v_data_u8r, 1);
#else
comres = bmg_i2c_read_block(client, BMI160_USER_DROP_CMD_ERR__REG,
&v_data_u8r, 1);
#endif
/* drop command error*/
*drop_cmd_err =
BMI160_GET_BITSLICE(v_data_u8r, BMI160_USER_DROP_CMD_ERR);
return comres;
}
int bmg_get_powermode(struct i2c_client *client, unsigned char *mode)
{
int comres = 0;
u8 v_data_u8r = 0;
#ifdef BMI160_ACCESS_BY_GSE_I2C
comres = bmi_i2c_read_wrapper(0, BMI160_USER_GYRO_POWER_MODE_STAT__REG,
&v_data_u8r, 1);
#else
comres = bmg_i2c_read_block(
client, BMI160_USER_GYRO_POWER_MODE_STAT__REG, &v_data_u8r, 1);
#endif
*mode = BMI160_GET_BITSLICE(v_data_u8r,
BMI160_USER_GYRO_POWER_MODE_STAT);
return comres;
}
static int bmg_set_powermode(struct i2c_client *client,
enum BMG_POWERMODE_ENUM power_mode)
{
int err = 0;
u8 data = 0;
u8 actual_power_mode = 0;
struct bmg_i2c_data *obj = obj_i2c_data;
u8 data_1 = 0;
u8 drop_cmd_err = 0;
int cnt = 0;
mutex_lock(&obj->lock);
if (power_mode == obj->power_mode) {
pr_debug("power status is newest!\n");
mutex_unlock(&obj->lock);
return 0;
}
if (obj->sensor_type == BMI160_GYRO_TYPE) {
if (power_mode == BMG_SUSPEND_MODE) {
actual_power_mode = CMD_PMU_GYRO_SUSPEND;
} else if (power_mode == BMG_NORMAL_MODE) {
actual_power_mode = CMD_PMU_GYRO_NORMAL;
} else {
err = -EINVAL;
pr_err("inv power mode = %d\n", power_mode);
mutex_unlock(&obj->lock);
return err;
}
data = actual_power_mode;
do {
#ifdef BMI160_ACCESS_BY_GSE_I2C
err += bmi_i2c_write_wrapper(
0, BMI160_CMD_COMMANDS__REG, &data, 1);
#else
err += bmg_i2c_write_block(
client, BMI160_CMD_COMMANDS__REG, &data, 1);
#endif
if (err < 0) {
pr_err_ratelimited(
"set power failed, err = %d, sensor name = %s\n",
err, obj->sensor_name);
mutex_unlock(&obj->lock);
return err;
}
mdelay(1);
err = bmg_get_drop_cmd_err(client, &drop_cmd_err);
if (err < 0) {
pr_err_ratelimited(
"get_drop_cmd_err failed.\n");
mutex_unlock(&obj->lock);
return err;
}
cnt++;
} while (drop_cmd_err == 0x1 && cnt < 500);
if ((cnt == 500) || (drop_cmd_err == 0x1)) {
pr_err("drop_cmd!,cmd=%x, cnt=%d,m=%d\n",
drop_cmd_err, cnt, (int)data);
mutex_unlock(&obj->lock);
return -EINVAL;
}
/* mdelay(55); */
}
obj->power_mode = power_mode;
/* set debounce */
if (power_mode == BMG_SUSPEND_MODE) {
atomic_set(&obj->gyro_deb_on, 0);
} else if (power_mode == BMG_NORMAL_MODE) {
atomic_set(&obj->gyro_deb_on, 1);
atomic_set(&obj->gyro_deb_end,
jiffies +
atomic_read(&obj->gyro_debounce) /
(1000 / HZ));
} else {
err = -EINVAL;
pr_err("invalid power mode = %d\n", power_mode);
mutex_unlock(&obj->lock);
return err;
}
/* */
mutex_unlock(&obj->lock);
pr_debug("set power mode = %d ok.\n", (int)data);
err = bmg_get_powermode(client, &data_1);
if (err < 0) {
pr_err_ratelimited("bmg_get_powermode failed.\n");
/* return err; */
}
pr_debug("[Lomen] gyro_pmu_status=%x\n", data_1);
return err;
}
static int bmg_set_range(struct i2c_client *client, enum BMG_RANGE_ENUM range)
{
u8 err = 0;
u8 data = 0;
u8 actual_range = 0;
struct bmg_i2c_data *obj = obj_i2c_data;
if (range == obj->range)
return 0;
mutex_lock(&obj->lock);
if (obj->sensor_type == BMI160_GYRO_TYPE) {
if (range == BMG_RANGE_2000)
actual_range = BMI160_RANGE_2000;
else if (range == BMG_RANGE_1000)
actual_range = BMI160_RANGE_1000;
else if (range == BMG_RANGE_500)
actual_range = BMI160_RANGE_500;
else if (range == BMG_RANGE_250) /*ALPS02962852*/
actual_range = BMI160_RANGE_250;
else if (range == BMG_RANGE_125) /*ALPS02962852*/
actual_range = BMI160_RANGE_125;
else {
err = -EINVAL;
pr_err("invalid range = %d\n", range);
mutex_unlock(&obj->lock);
return err;
}
#ifdef BMI160_ACCESS_BY_GSE_I2C
err = bmi_i2c_read_wrapper(0, BMI160_USER_GYR_RANGE__REG, &data,
1);
#else
err = bmg_i2c_read_block(client, BMI160_USER_GYR_RANGE__REG,
&data, 1);
#endif
data = BMG_SET_BITSLICE(data, BMI160_USER_GYR_RANGE,
actual_range);
#ifdef BMI160_ACCESS_BY_GSE_I2C
err += bmi_i2c_write_wrapper(0, BMI160_USER_GYR_RANGE__REG,
&data, 1);
#else
err += bmg_i2c_write_block(client, BMI160_USER_GYR_RANGE__REG,
&data, 1);
#endif
mdelay(1);
if (err < 0) {
pr_err_ratelimited("set range failed.\n");
} else {
obj->range = range;
/* bitnum: 16bit */
switch (range) {
case BMG_RANGE_2000:
obj->sensitivity = BMI160_FS_2000_LSB; /* 16; */
break;
case BMG_RANGE_1000:
obj->sensitivity = BMI160_FS_1000_LSB; /* 33; */
break;
case BMG_RANGE_500:
obj->sensitivity = BMI160_FS_500_LSB; /* 66; */
break;
case BMG_RANGE_250:
obj->sensitivity = BMI160_FS_250_LSB; /* 131; */
break;
case BMG_RANGE_125:
obj->sensitivity = BMI160_FS_125_LSB; /* 262; */
break;
default:
obj->sensitivity = BMI160_FS_2000_LSB; /* 16; */
break;
}
}
}
mutex_unlock(&obj->lock);
pr_debug("set range ok.\n");
return err;
}
static int bmg_set_datarate(struct i2c_client *client, int datarate)
{
int err = 0;
u8 data = 0;
struct bmg_i2c_data *obj = obj_i2c_data;
if (datarate == obj->datarate) {
pr_debug("set new data rate = %d, old data rate = %d\n",
datarate, obj->datarate);
return 0;
}
mutex_lock(&obj->lock);
if (obj->sensor_type == BMI160_GYRO_TYPE) {
#ifdef BMI160_ACCESS_BY_GSE_I2C
err = bmi_i2c_read_wrapper(0, BMI160_USER_GYR_CONF_ODR__REG,
&data, 1);
#else
err = bmg_i2c_read_block(client, BMI160_USER_GYR_CONF_ODR__REG,
&data, 1);
#endif
data = BMG_SET_BITSLICE(data, BMI160_USER_GYR_CONF_ODR,
datarate);
#ifdef CONFIG_MTK_GPS_SUPPORT
data &= 0x0F; /* set filter mode to OSR4 */
#endif
#ifdef BMI160_ACCESS_BY_GSE_I2C
err += bmi_i2c_write_wrapper(0, BMI160_USER_GYR_CONF_ODR__REG,
&data, 1);
#else
err += bmg_i2c_write_block(
client, BMI160_USER_GYR_CONF_ODR__REG, &data, 1);
#endif
}
if (err < 0)
pr_err_ratelimited("set data rate failed.\n");
else
obj->datarate = datarate;
mutex_unlock(&obj->lock);
pr_debug("set data rate = %d ok.\n", datarate);
return err;
}
static int bmg_init_client(struct i2c_client *client, int reset_cali)
{
#ifdef CONFIG_BMG_LOWPASS
struct bmg_i2c_data *obj = (struct bmg_i2c_data *)obj_i2c_data;
#endif
int err = 0;
err = bmg_get_chip_type(client);
if (err < 0)
return err;
err = bmg_set_datarate(client, BMI160_GYRO_ODR_100HZ);
if (err < 0)
return err;
err = bmg_set_range(client, (enum BMG_RANGE_ENUM)BMG_RANGE_2000);
if (err < 0)
return err;
err = bmg_set_powermode(client,
(enum BMG_POWERMODE_ENUM)BMG_SUSPEND_MODE);
if (err < 0)
return err;
if (reset_cali != 0)
/*reset calibration only in power on */
bmg_reset_calibration(client);
#ifdef CONFIG_BMG_LOWPASS
memset(&obj->fir, 0x00, sizeof(obj->fir));
#endif
return 0;
}
/*!
* @brief This API reads the temperature of the sensor
* from the register 0x21 bit 0 to 7
*
*
*
* @param v_temp_s16 : The value of temperature
*
*
*
* @return results of bus communication function
* @retval 0 -> Success
* @retval -1 -> Error
*
*
*/
static int bmi160_get_temp(struct i2c_client *client, s16 *v_temp_s16)
{
/* variable used to return the status of communication result*/
int err = 0;
struct bmg_i2c_data *priv = obj_i2c_data;
/* Array contains the temperature LSB and MSB data
* v_data_u8[0] - LSB
* v_data_u8[1] - MSB
*/
u8 v_data_u8[BMI160_TEMP_DATA_SIZE] = {0, 0};
#ifdef BMI160_ACCESS_BY_GSE_I2C
err = bmi_i2c_read_wrapper(0, BMI160_USER_TEMP_LSB_VALUE__REG,
v_data_u8, 2);
#else
err = bmg_i2c_read_block(client, BMI160_USER_TEMP_LSB_VALUE__REG,
v_data_u8, 2);
#endif
if (err) {
pr_err_ratelimited("read gyro temperature data failed.\n");
return err;
}
*v_temp_s16 =
(s16)(((s32)((s8)(v_data_u8[1]) << BMI160_SHIFT_8_POSITION)) |
v_data_u8[0]);
if (atomic_read(&priv->trace) & GYRO_TRC_RAWDATA) {
pr_debug("[%s][16bit temperature][%08X] => [%5d]\n",
priv->sensor_name, *v_temp_s16, *v_temp_s16);
}
return err;
}
/*
*Returns compensated and mapped value. unit is :degree/second
*/
static int bmg_read_sensor_data(struct i2c_client *client, char *buf,
int bufsize)
{
s16 databuf[BMG_AXES_NUM] = {0};
int gyro[BMG_AXES_NUM] = {0};
int err = 0;
struct bmg_i2c_data *obj = obj_i2c_data;
if (atomic_read(&obj->gyro_deb_on) == 1) {
unsigned long endt = atomic_read(&obj->gyro_deb_end);
if (time_after(jiffies, endt))
atomic_set(&obj->gyro_deb_on, 0);
if (atomic_read(&obj->gyro_deb_on) == 1) {
err = -1;
return err;
}
}
err = bmg_read_raw_data(client, databuf);
if (err) {
pr_err_ratelimited("bmg read raw data failed.\n");
return err;
}
/* compensate data */
databuf[BMG_AXIS_X] += obj->cali_sw[BMG_AXIS_X];
databuf[BMG_AXIS_Y] += obj->cali_sw[BMG_AXIS_Y];
databuf[BMG_AXIS_Z] += obj->cali_sw[BMG_AXIS_Z];
/* remap coordinate */
gyro[obj->cvt.map[BMG_AXIS_X]] =
obj->cvt.sign[BMG_AXIS_X] * databuf[BMG_AXIS_X];
gyro[obj->cvt.map[BMG_AXIS_Y]] =
obj->cvt.sign[BMG_AXIS_Y] * databuf[BMG_AXIS_Y];
gyro[obj->cvt.map[BMG_AXIS_Z]] =
obj->cvt.sign[BMG_AXIS_Z] * databuf[BMG_AXIS_Z];
/* convert: LSB -> degree/second(o/s) */
gyro[BMG_AXIS_X] =
gyro[BMG_AXIS_X] * BMI160_FS_250_LSB / obj->sensitivity;
gyro[BMG_AXIS_Y] =
gyro[BMG_AXIS_Y] * BMI160_FS_250_LSB / obj->sensitivity;
gyro[BMG_AXIS_Z] =
gyro[BMG_AXIS_Z] * BMI160_FS_250_LSB / obj->sensitivity;
sprintf(buf, "%04x %04x %04x", gyro[BMG_AXIS_X], gyro[BMG_AXIS_Y],
gyro[BMG_AXIS_Z]);
if (atomic_read(&obj->trace) & GYRO_TRC_IOCTL)
pr_debug("gyroscope data: %s\n", buf);
return 0;
}
static ssize_t show_chipinfo_value(struct device_driver *ddri, char *buf)
{
struct bmg_i2c_data *obj = obj_i2c_data;
return snprintf(buf, PAGE_SIZE, "%s\n", obj->sensor_name);
}
/*
* sensor data format is hex, unit:degree/second
*/
static ssize_t show_sensordata_value(struct device_driver *ddri, char *buf)
{
struct bmg_i2c_data *obj = obj_i2c_data;
char strbuf[BMG_BUFSIZE] = {0};
bmg_read_sensor_data(obj->client, strbuf, BMG_BUFSIZE);
return snprintf(buf, PAGE_SIZE, "%s\n", strbuf);
}
/*
* raw data format is s16, unit:LSB, axis mapped
*/
static ssize_t show_rawdata_value(struct device_driver *ddri, char *buf)
{
s16 databuf[BMG_AXES_NUM] = {0};
s16 dataraw[BMG_AXES_NUM] = {0};
struct bmg_i2c_data *obj = obj_i2c_data;
bmg_read_raw_data(obj->client, dataraw);
/*remap coordinate */
databuf[obj->cvt.map[BMG_AXIS_X]] =
obj->cvt.sign[BMG_AXIS_X] * dataraw[BMG_AXIS_X];
databuf[obj->cvt.map[BMG_AXIS_Y]] =
obj->cvt.sign[BMG_AXIS_Y] * dataraw[BMG_AXIS_Y];
databuf[obj->cvt.map[BMG_AXIS_Z]] =
obj->cvt.sign[BMG_AXIS_Z] * dataraw[BMG_AXIS_Z];
return snprintf(buf, PAGE_SIZE, "%hd %hd %hd\n", databuf[BMG_AXIS_X],
databuf[BMG_AXIS_Y], databuf[BMG_AXIS_Z]);
}
static ssize_t show_cali_value(struct device_driver *ddri, char *buf)
{
int err = 0;
int len = 0;
int mul;
int act[BMG_AXES_NUM] = {0};
int raw[BMG_AXES_NUM] = {0};
struct bmg_i2c_data *obj = obj_i2c_data;
err = bmg_read_calibration(obj->client, act, raw);
if (err)
return -EINVAL;
mul = 1; /* mul = sensor sensitivity / offset sensitivity */
len += snprintf(
buf + len, PAGE_SIZE - len,
"[HW ][%d] (%+3d, %+3d, %+3d) : (0x%02X, 0x%02X, 0x%02X)\n",
mul, obj->offset[BMG_AXIS_X], obj->offset[BMG_AXIS_Y],
obj->offset[BMG_AXIS_Z], obj->offset[BMG_AXIS_X],
obj->offset[BMG_AXIS_Y], obj->offset[BMG_AXIS_Z]);
len += snprintf(buf + len, PAGE_SIZE - len,
"[SW ][%d] (%+3d, %+3d, %+3d)\n", 1,
obj->cali_sw[BMG_AXIS_X], obj->cali_sw[BMG_AXIS_Y],
obj->cali_sw[BMG_AXIS_Z]);
len += snprintf(
buf + len, PAGE_SIZE - len,
"[ALL]unmapped(%+3d, %+3d, %+3d), mapped(%+3d, %+3d, %+3d)\n",
raw[BMG_AXIS_X], raw[BMG_AXIS_Y], raw[BMG_AXIS_Z],
act[BMG_AXIS_X], act[BMG_AXIS_Y], act[BMG_AXIS_Z]);
return len;
}
static ssize_t store_cali_value(struct device_driver *ddri, const char *buf,
size_t count)
{
int err = 0;
int dat[BMG_AXES_NUM] = {0};
struct bmg_i2c_data *obj = obj_i2c_data;
if (!strncmp(buf, "rst", 3)) {
bmg_reset_calibration(obj->client);
} else if (sscanf(buf, "0x%02X 0x%02X 0x%02X", &dat[BMG_AXIS_X],
&dat[BMG_AXIS_Y], &dat[BMG_AXIS_Z]) == BMG_AXES_NUM) {
err = bmg_write_calibration(obj->client, dat);
if (err)
pr_err_ratelimited("bmg write cali err = %d\n", err);
} else {
pr_err("invalid format\n");
}
return count;
}
static ssize_t show_firlen_value(struct device_driver *ddri, char *buf)
{
#ifdef CONFIG_BMG_LOWPASS
struct i2c_client *client = bmg222_i2c_client;
struct bmg_i2c_data *obj = obj_i2c_data;
if (atomic_read(&obj->firlen)) {
int idx, len = atomic_read(&obj->firlen);
pr_debug("len = %2d, idx = %2d\n", obj->fir.num, obj->fir.idx);
for (idx = 0; idx < len; idx++) {
pr_debug("[%5d %5d %5d]\n",
obj->fir.raw[idx][BMG_AXIS_X],
obj->fir.raw[idx][BMG_AXIS_Y],
obj->fir.raw[idx][BMG_AXIS_Z]);
}
pr_debug("sum = [%5d %5d %5d]\n", obj->fir.sum[BMG_AXIS_X],
obj->fir.sum[BMG_AXIS_Y], obj->fir.sum[BMG_AXIS_Z]);
pr_debug("avg = [%5d %5d %5d]\n",
obj->fir.sum[BMG_AXIS_X] / len,
obj->fir.sum[BMG_AXIS_Y] / len,
obj->fir.sum[BMG_AXIS_Z] / len);
}
return snprintf(buf, PAGE_SIZE, "%d\n", atomic_read(&obj->firlen));
#else
return snprintf(buf, PAGE_SIZE, "not support\n");
#endif
}
static ssize_t store_firlen_value(struct device_driver *ddri, const char *buf,
size_t count)
{
#ifdef CONFIG_BMG_LOWPASS
struct i2c_client *client = bmg222_i2c_client;
struct bmg_i2c_data *obj = obj_i2c_data;
int firlen;
if (kstrtos32(buf, 10, &firlen) != 0) {
pr_err("invallid format\n");
} else if (firlen > C_MAX_FIR_LENGTH) {
pr_err("exceeds maximum filter length\n");
} else {
atomic_set(&obj->firlen, firlen);
if (firlen == NULL) {
atomic_set(&obj->fir_en, 0);
} else {
memset(&obj->fir, 0x00, sizeof(obj->fir));
atomic_set(&obj->fir_en, 1);
}
}
#endif
return count;
}
static ssize_t show_trace_value(struct device_driver *ddri, char *buf)
{
ssize_t res;
struct bmg_i2c_data *obj = obj_i2c_data;
res = snprintf(buf, PAGE_SIZE, "0x%04X\n", atomic_read(&obj->trace));
return res;
}
static ssize_t store_trace_value(struct device_driver *ddri, const char *buf,
size_t count)
{
int trace;
struct bmg_i2c_data *obj = obj_i2c_data;
if (sscanf(buf, "0x%x", &trace) == 1)
atomic_set(&obj->trace, trace);
else
pr_err("invalid content: '%s'\n", buf);
return count;
}
static ssize_t show_status_value(struct device_driver *ddri, char *buf)
{
ssize_t len = 0;
struct bmg_i2c_data *obj = obj_i2c_data;
if (obj->hw)
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);
else
len += snprintf(buf + len, PAGE_SIZE - len, "CUST: NULL\n");
len += snprintf(buf + len, PAGE_SIZE - len, "i2c addr:%#x,ver:%s\n",
obj->client->addr, BMG_DRIVER_VERSION);
return len;
}
static ssize_t show_power_mode_value(struct device_driver *ddri, char *buf)
{
ssize_t len = 0;
struct bmg_i2c_data *obj = obj_i2c_data;
len += snprintf(buf + len, PAGE_SIZE - len, "%s mode\n",
obj->power_mode == BMG_NORMAL_MODE ? "normal"
: "suspend");
return len;
}
static ssize_t store_power_mode_value(struct device_driver *ddri,
const char *buf, size_t count)
{
int err;
unsigned long power_mode;
struct bmg_i2c_data *obj = obj_i2c_data;
err = kstrtoul(buf, 10, &power_mode);
if (err < 0)
return err;
if (power_mode == BMI_GYRO_PM_NORMAL)
err = bmg_set_powermode(obj->client, BMG_NORMAL_MODE);
else
err = bmg_set_powermode(obj->client, BMG_SUSPEND_MODE);
if (err < 0)
pr_err_ratelimited("set power mode failed.\n");
return err;
}
static ssize_t show_range_value(struct device_driver *ddri, char *buf)
{
ssize_t len = 0;
struct bmg_i2c_data *obj = obj_i2c_data;
len += snprintf(buf + len, PAGE_SIZE - len, "%d\n", obj->range);
return len;
}
static ssize_t store_range_value(struct device_driver *ddri, const char *buf,
size_t count)
{
struct bmg_i2c_data *obj = obj_i2c_data;
unsigned long range;
int err;
err = kstrtoul(buf, 10, &range);
if (err == 0) {
if ((range == BMG_RANGE_2000) || (range == BMG_RANGE_1000) ||
(range == BMG_RANGE_500) || (range == BMG_RANGE_250) ||
(range == BMG_RANGE_125)) {
err = bmg_set_range(obj->client, range);
if (err) {
pr_err_ratelimited("set range value failed.\n");
return err;
}
}
}
return count;
}
static ssize_t show_datarate_value(struct device_driver *ddri, char *buf)
{
ssize_t len = 0;
struct bmg_i2c_data *obj = obj_i2c_data;
len += snprintf(buf + len, PAGE_SIZE - len, "%d\n", obj->datarate);
return len;
}
static ssize_t store_datarate_value(struct device_driver *ddri, const char *buf,
size_t count)
{
int err;
unsigned long datarate;
struct bmg_i2c_data *obj = obj_i2c_data;
err = kstrtoul(buf, 10, &datarate);
if (err < 0)
return err;
err = bmg_set_datarate(obj->client, datarate);
if (err < 0) {
pr_err_ratelimited("set data rate failed.\n");
return err;
}
return count;
}
static DRIVER_ATTR(chipinfo, 0644, show_chipinfo_value, NULL);
static DRIVER_ATTR(sensordata, 0644, show_sensordata_value, NULL);
static DRIVER_ATTR(rawdata, 0644, show_rawdata_value, NULL);
static DRIVER_ATTR(cali, 0644, show_cali_value, store_cali_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(gyro_op_mode, 0644, show_power_mode_value,
store_power_mode_value);
static DRIVER_ATTR(gyro_range, 0644, show_range_value, store_range_value);
static DRIVER_ATTR(gyro_odr, 0644, show_datarate_value, store_datarate_value);
static struct driver_attribute *bmg_attr_list[] = {
/* chip information */
&driver_attr_chipinfo,
/* dump sensor data */
&driver_attr_sensordata,
/* dump raw data */
&driver_attr_rawdata,
/* show calibration data */
&driver_attr_cali,
/* filter length: 0: disable, others: enable */
&driver_attr_firlen,
/* trace flag */
&driver_attr_trace,
/* get hw configuration */
&driver_attr_status,
/* get power mode */
&driver_attr_gyro_op_mode,
/* get range */
&driver_attr_gyro_range,
/* get data rate */
&driver_attr_gyro_odr,
};
static int bmg_create_attr(struct device_driver *driver)
{
int idx = 0;
int err = 0;
int num = ARRAY_SIZE(bmg_attr_list);
if (driver == NULL)
return -EINVAL;
for (idx = 0; idx < num; idx++) {
err = driver_create_file(driver, bmg_attr_list[idx]);
if (err) {
pr_err("driver_create_file (%s) = %d\n",
bmg_attr_list[idx]->attr.name, err);
break;
}
}
return err;
}
static int bmg_delete_attr(struct device_driver *driver)
{
int idx, err = 0;
int num = ARRAY_SIZE(bmg_attr_list);
for (idx = 0; idx < num; idx++)
driver_remove_file(driver, bmg_attr_list[idx]);
return err;
}
int gyroscope_operate(void *self, uint32_t command, void *buff_in, int size_in,
void *buff_out, int size_out, int *actualout)
{
int err = 0;
int value, sample_delay;
char buff[BMG_BUFSIZE] = {0};
struct bmg_i2c_data *priv = (struct bmg_i2c_data *)self;
struct hwm_sensor_data *gyroscope_data;
switch (command) {
case SENSOR_DELAY:
if ((buff_in == NULL) || (size_in < sizeof(int))) {
pr_err("set delay parameter error\n");
err = -EINVAL;
} else {
value = *(int *)buff_in;
/*
*Currently, fix data rate to 100Hz.
*/
sample_delay = BMI160_GYRO_ODR_100HZ;
pr_debug(
"sensor delay command: %d, sample_delay = %d\n",
value, sample_delay);
err = bmg_set_datarate(priv->client, sample_delay);
if (err < 0)
pr_err_ratelimited(
"set delay para error\n");
if (value >= 40)
atomic_set(&priv->filter, 0);
else {
#if defined(CONFIG_BMG_LOWPASS)
priv->fir.num = 0;
priv->fir.idx = 0;
priv->fir.sum[BMG_AXIS_X] = 0;
priv->fir.sum[BMG_AXIS_Y] = 0;
priv->fir.sum[BMG_AXIS_Z] = 0;
atomic_set(&priv->filter, 1);
#endif
}
}
break;
case SENSOR_ENABLE:
if ((buff_in == NULL) || (size_in < sizeof(int))) {
pr_err("enable sensor parameter error\n");
err = -EINVAL;
} else {
/* value:[0--->suspend, 1--->normal] */
value = *(int *)buff_in;
pr_debug("sensor enable/disable command: %s\n",
value ? "enable" : "disable");
err = bmg_set_powermode(
priv->client,
(enum BMG_POWERMODE_ENUM)(!!value));
if (err)
pr_err_ratelimited("set power err = %d\n",
err);
}
break;
case SENSOR_GET_DATA:
if ((buff_out == NULL) ||
(size_out < sizeof(struct hwm_sensor_data))) {
pr_err("get sensor data parameter error\n");
err = -EINVAL;
} else {
gyroscope_data = (struct hwm_sensor_data *)buff_out;
bmg_read_sensor_data(priv->client, buff, BMG_BUFSIZE);
err = sscanf(buff, "%x %x %x",
&gyroscope_data->values[0],
&gyroscope_data->values[1],
&gyroscope_data->values[2]);
if (err)
pr_info("get data failed, err = %d\n", err);
gyroscope_data->status = SENSOR_STATUS_ACCURACY_MEDIUM;
gyroscope_data->value_divide = DEGREE_TO_RAD;
}
break;
default:
pr_err("gyroscope operate function no this parameter %d\n",
command);
err = -1;
break;
}
return err;
}
#ifdef CONFIG_PM
static int bmg_suspend(void)
{
struct bmg_i2c_data *obj = obj_i2c_data;
int err = 0;
if (obj == NULL) {
pr_err("null pointer\n");
return -EINVAL;
}
atomic_set(&obj->suspend, 1);
err = bmg_set_powermode(obj->client, BMG_SUSPEND_MODE);
if (err)
pr_err_ratelimited("bmg set suspend mode failed.\n");
return err;
}
static int bmg_resume(void)
{
int err;
struct bmg_i2c_data *obj = obj_i2c_data;
err = bmg_init_client(obj->client, 0);
if (err) {
pr_err_ratelimited("init client failed, err = %d\n", err);
return err;
}
atomic_set(&obj->suspend, 0);
return 0;
}
static int pm_event_handler(struct notifier_block *notifier,
unsigned long pm_event, void *unused)
{
switch (pm_event) {
case PM_SUSPEND_PREPARE:
bmg_suspend();
return NOTIFY_DONE;
case PM_POST_SUSPEND:
bmg_resume();
return NOTIFY_DONE;
}
return NOTIFY_DONE;
}
static struct notifier_block pm_notifier_func = {
.notifier_call = pm_event_handler, .priority = 0,
};
#endif /* CONFIG_PM */
static int bmg_i2c_detect(struct i2c_client *client,
struct i2c_board_info *info)
{
strncpy(info->type, BMG_DEV_NAME, sizeof(info->type)); /*ALPS03195530*/
return 0;
}
static int bmi160_gyro_open_report_data(int open)
{
/* should queuq work to report event if is_report_input_direct=true */
return 0;
}
static int bmi160_gyro_enable_nodata(int en)
{
int err = 0;
int retry = 0;
bool power = false;
if (en == 1)
power = true;
else
power = false;
for (retry = 0; retry < 3; retry++) {
err = bmg_set_powermode(obj_i2c_data->client, power);
if (err == 0) {
pr_debug("bmi160_gyro_SetPowerMode ok.\n");
break;
}
}
if (err < 0)
pr_debug("bmi160_gyro_SetPowerMode fail!\n");
return err;
}
static int bmi160_gyro_batch(int flag, int64_t samplingPeriodNs,
int64_t maxBatchReportLatencyNs)
{
int err;
int value = (int)samplingPeriodNs / 1000 / 1000;
#ifdef CONFIG_MTK_GPS_SUPPORT
int sample_delay = BMI160_GYRO_ODR_3200HZ;
#else
/* Currently, fix data rate to 100Hz. */
int sample_delay = BMI160_GYRO_ODR_100HZ;
#endif
struct bmg_i2c_data *priv = obj_i2c_data;
err = bmg_set_datarate(priv->client, sample_delay);
if (err < 0)
pr_err_ratelimited("set data rate failed.\n");
if (value >= 40)
atomic_set(&priv->filter, 0);
else {
#if defined(CONFIG_BMG_LOWPASS)
priv->fir.num = 0;
priv->fir.idx = 0;
priv->fir.sum[BMG_AXIS_X] = 0;
priv->fir.sum[BMG_AXIS_Y] = 0;
priv->fir.sum[BMG_AXIS_Z] = 0;
atomic_set(&priv->filter, 1);
#endif
}
pr_debug("set gyro delay = %d\n", sample_delay);
return 0;
}
static int bmi160_gyro_flush(void)
{
return gyro_flush_report();
}
#if 0
static int bmi160_gyro_set_delay(u64 ns)
{
int err;
int value = (int)ns / 1000 / 1000;
#ifdef CONFIG_MTK_GPS_SUPPORT
int sample_delay = BMI160_GYRO_ODR_3200HZ;
#else
/* Currently, fix data rate to 100Hz. */
int sample_delay = BMI160_GYRO_ODR_100HZ;
#endif
struct bmg_i2c_data *priv = obj_i2c_data;
err = bmg_set_datarate(priv->client, sample_delay);
if (err < 0)
pr_err_ratelimited("set data rate failed.\n");
if (value >= 40)
atomic_set(&priv->filter, 0);
else {
#if defined(CONFIG_BMG_LOWPASS)
priv->fir.num = 0;
priv->fir.idx = 0;
priv->fir.sum[BMG_AXIS_X] = 0;
priv->fir.sum[BMG_AXIS_Y] = 0;
priv->fir.sum[BMG_AXIS_Z] = 0;
atomic_set(&priv->filter, 1);
#endif
}
pr_debug("set gyro delay = %d\n", sample_delay);
return 0;
}
#endif
static int bmi160_gyro_get_data(int *x, int *y, int *z, int *status)
{
int err = 0;
char buff[BMG_BUFSIZE] = {0};
err = bmg_read_sensor_data(obj_i2c_data->client, buff, BMG_BUFSIZE);
if (err)
return err;
if (sscanf(buff, "%x %x %x", x, y, z) != 3)
pr_err_ratelimited("%s failed\n", __func__);
*status = SENSOR_STATUS_ACCURACY_MEDIUM;
return 0;
}
static int bmi160_gyro_get_temperature(int *temperature)
{
int err = 0;
s16 data = 0;
err = bmi160_get_temp(obj_i2c_data->client, &data);
if (err)
return err;
*temperature = (int)data;
return 0;
}
static int bmg_factory_enable_sensor(bool enabledisable,
int64_t sample_periods_ms)
{
int err = 0;
err = bmi160_gyro_enable_nodata(enabledisable == true ? 1 : 0);
if (err) {
pr_err_ratelimited("%s en failed!\n", __func__);
return -1;
}
err = bmi160_gyro_batch(0, sample_periods_ms * 1000000, 0);
if (err) {
pr_err_ratelimited("%s set batch failed!\n", __func__);
return -1;
}
return 0;
}
static int bmg_factory_get_data(int32_t data[3], int *status)
{
struct bmg_i2c_data *obj = obj_i2c_data;
if (atomic_read(&obj->gyro_deb_on) == 1) {
unsigned long endt = atomic_read(&obj->gyro_deb_end);
if (time_before_eq(jiffies, endt))
mdelay(55); /* 55ms */
}
return bmi160_gyro_get_data(&data[0], &data[1], &data[2], status);
}
static int bmg_factory_get_raw_data(int32_t data[3])
{
pr_debug("%s don't support!\n", __func__);
return 0;
}
static int bmg_factory_enable_calibration(void)
{
return 0;
}
static int bmg_factory_clear_cali(void)
{
bmg_reset_calibration(bmi160_acc_i2c_client);
return 0;
}
static int bmg_factory_set_cali(int32_t data[3])
{
struct bmg_i2c_data *obj = obj_i2c_data;
int err = 0;
int cali[3] = {0};
cali[BMG_AXIS_X] = data[0] * obj->sensitivity / BMI160_FS_250_LSB;
cali[BMG_AXIS_Y] = data[1] * obj->sensitivity / BMI160_FS_250_LSB;
cali[BMG_AXIS_Z] = data[2] * obj->sensitivity / BMI160_FS_250_LSB;
err = bmg_write_calibration(bmi160_acc_i2c_client, cali);
if (err) {
pr_info("bmg_WriteCalibration failed!\n");
return -1;
}
return 0;
}
static int bmg_factory_get_cali(int32_t data[3])
{
struct bmg_i2c_data *obj = obj_i2c_data;
int err = 0;
int cali[3] = {0};
int raw_offset[BMG_BUFSIZE] = {0};
err = bmg_read_calibration(bmi160_acc_i2c_client, cali, raw_offset);
if (err) {
pr_info("bmg_ReadCalibration failed!\n");
return -1;
}
data[0] = cali[BMG_AXIS_X] * BMI160_FS_250_LSB / obj->sensitivity;
data[1] = cali[BMG_AXIS_Y] * BMI160_FS_250_LSB / obj->sensitivity;
data[2] = cali[BMG_AXIS_Z] * BMI160_FS_250_LSB / obj->sensitivity;
return 0;
}
static int bmg_factory_do_self_test(void)
{
return 0;
}
static struct gyro_factory_fops bmg_factory_fops = {
.enable_sensor = bmg_factory_enable_sensor,
.get_data = bmg_factory_get_data,
.get_raw_data = bmg_factory_get_raw_data,
.enable_calibration = bmg_factory_enable_calibration,
.clear_cali = bmg_factory_clear_cali,
.set_cali = bmg_factory_set_cali,
.get_cali = bmg_factory_get_cali,
.do_self_test = bmg_factory_do_self_test,
};
static struct gyro_factory_public bmg_factory_device = {
.gain = 1, .sensitivity = 1, .fops = &bmg_factory_fops,
};
static int bmg_i2c_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct bmg_i2c_data *obj = NULL;
struct gyro_control_path ctl = {0};
struct gyro_data_path data = {0};
int err = 0;
pr_debug("%s\n", __func__);
err = get_gyro_dts_func(client->dev.of_node, hw);
if (err) {
pr_err("get dts info fail\n");
err = -EFAULT;
goto exit;
}
obj = kzalloc(sizeof(*obj), GFP_KERNEL);
if (!obj) {
err = -ENOMEM;
goto exit;
}
obj->hw = hw;
err = hwmsen_get_convert(obj->hw->direction, &obj->cvt);
if (err) {
pr_err("inv dir: %d\n", obj->hw->direction);
goto exit_hwmsen_get_convert_failed;
}
obj_i2c_data = obj;
if (!bmi160_acc_i2c_client)
goto exit_init_client_failed;
obj->client = bmi160_acc_i2c_client;
i2c_set_clientdata(obj->client, obj);
/* bmi160 gyro typical start up time 55ms */
atomic_set(&obj->gyro_debounce, 55);
atomic_set(&obj->gyro_deb_on, 0);
atomic_set(&obj->gyro_deb_end, 0);
atomic_set(&obj->trace, 0);
atomic_set(&obj->suspend, 0);
obj->power_mode = BMG_UNDEFINED_POWERMODE;
obj->range = BMG_UNDEFINED_RANGE;
obj->datarate = BMI160_GYRO_ODR_RESERVED;
mutex_init(&obj->lock);
#ifdef CONFIG_BMG_LOWPASS
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
err = bmg_init_client(obj->client, 1);
if (err)
goto exit_init_client_failed;
/* err = misc_register(&bmg_device); */
err = gyro_factory_device_register(&bmg_factory_device);
if (err) {
pr_err("misc dev reg failed, err = %d\n", err);
goto exit_misc_device_register_failed;
}
err = bmg_create_attr(
&bmi160_gyro_init_info.platform_diver_addr->driver);
if (err) {
pr_err("create attri failed, err = %d\n", err);
goto exit_create_attr_failed;
}
ctl.open_report_data = bmi160_gyro_open_report_data;
ctl.enable_nodata = bmi160_gyro_enable_nodata;
ctl.batch = bmi160_gyro_batch;
ctl.flush = bmi160_gyro_flush;
ctl.is_report_input_direct = false;
ctl.is_support_batch = obj->hw->is_batch_supported;
err = gyro_register_control_path(&ctl);
if (err) {
pr_err("register gyro control path err\n");
goto exit_create_attr_failed;
}
data.get_data = bmi160_gyro_get_data;
data.get_temperature = bmi160_gyro_get_temperature;
data.vender_div = DEGREE_TO_RAD;
err = gyro_register_data_path(&data);
if (err) {
pr_err("gyro_register_data_path fail = %d\n", err);
goto exit_create_attr_failed;
}
#ifdef CONFIG_PM
err = register_pm_notifier(&pm_notifier_func);
if (err) {
pr_err("Failed to register PM notifier.\n");
goto exit_create_attr_failed;
}
#endif /* CONFIG_PM */
bmi160_gyro_init_flag = 0;
pr_debug("%s: OK\n", __func__);
return 0;
exit_create_attr_failed:
/* misc_deregister(&bmg_device); */
exit_misc_device_register_failed:
exit_init_client_failed:
exit_hwmsen_get_convert_failed:
kfree(obj);
exit:
obj = NULL;
bmi160_gyro_init_flag = -1;
obj_i2c_data = NULL;
pr_err("err = %d\n", err);
return err;
}
static int bmg_i2c_remove(struct i2c_client *client)
{
int err = 0;
err = bmg_delete_attr(
&bmi160_gyro_init_info.platform_diver_addr->driver);
if (err)
pr_err("bmg_delete_attr failed, err = %d\n", err);
/* misc_deregister(&bmg_device); */
gyro_factory_device_deregister(&bmg_factory_device);
pr_debug("bmg_factory_device deregister\n");
obj_i2c_data = NULL;
i2c_unregister_device(client);
kfree(i2c_get_clientdata(client));
return 0;
}
#ifdef CONFIG_OF
static const struct of_device_id gyro_of_match[] = {
{.compatible = "mediatek,gyro"}, {},
};
#endif
static struct i2c_driver bmg_i2c_driver = {
.driver = {
.name = BMG_DEV_NAME,
#ifdef CONFIG_OF
.of_match_table = gyro_of_match,
#endif
},
.probe = bmg_i2c_probe,
.remove = bmg_i2c_remove,
.detect = bmg_i2c_detect,
.id_table = bmg_i2c_id,
};
static int bmi160_gyro_remove(void)
{
i2c_del_driver(&bmg_i2c_driver);
return 0;
}
static int bmi160_gyro_local_init(struct platform_device *pdev)
{
if (i2c_add_driver(&bmg_i2c_driver)) {
pr_err("add gyro driver error.\n");
return -1;
}
if (-1 == bmi160_gyro_init_flag)
return -1;
pr_debug("bmi160 gyro init ok.\n");
return 0;
}
static struct gyro_init_info bmi160_gyro_init_info = {
.name = BMG_DEV_NAME,
.init = bmi160_gyro_local_init,
.uninit = bmi160_gyro_remove,
};
static int __init bmg_init(void)
{
pr_debug("%s: bosch gyroscope driver version: %s\n", __func__,
BMG_DRIVER_VERSION);
gyro_driver_add(&bmi160_gyro_init_info);
return 0;
}
static void __exit bmg_exit(void)
{
pr_debug("%s\n", __func__);
}
module_init(bmg_init);
module_exit(bmg_exit);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("BMG I2C Driver");
MODULE_AUTHOR("xiaogang.fan@cn.bosch.com");
MODULE_VERSION(BMG_DRIVER_VERSION);