blob: 3991ada4da626bb25be9ff048deae764d44f5e1c [file] [log] [blame]
/*!
* @section LICENSE
* (C) Copyright 2011~2016 Bosch Sensortec GmbH All Rights Reserved
*
* (C) Modification Copyright 2018 Robert Bosch Kft All Rights Reserved
*
* This software program is licensed subject to the GNU General
* Public License (GPL).Version 2,June 1991,
* available at http://www.fsf.org/copyleft/gpl.html
*
* Special: Description of the Software:
*
* This software module (hereinafter called "Software") and any
* information on application-sheets (hereinafter called "Information") is
* provided free of charge for the sole purpose to support your application
* work.
*
* As such, the Software is merely an experimental software, not tested for
* safety in the field and only intended for inspiration for further development
* and testing. Any usage in a safety-relevant field of use (like automotive,
* seafaring, spacefaring, industrial plants etc.) was not intended, so there are
* no precautions for such usage incorporated in the Software.
*
* The Software is specifically designed for the exclusive use for Bosch
* Sensortec products by personnel who have special experience and training. Do
* not use this Software if you do not have the proper experience or training.
*
* This Software package is provided as is and without any expressed or
* implied warranties, including without limitation, the implied warranties of
* merchantability and fitness for a particular purpose.
*
* Bosch Sensortec and their representatives and agents deny any liability for
* the functional impairment of this Software in terms of fitness, performance
* and safety. Bosch Sensortec and their representatives and agents shall not be
* liable for any direct or indirect damages or injury, except as otherwise
* stipulated in mandatory applicable law.
* The Information provided is believed to be accurate and reliable. Bosch
* Sensortec assumes no responsibility for the consequences of use of such
* Information nor for any infringement of patents or other rights of third
* parties which may result from its use.
*
*------------------------------------------------------------------------------
* The following Product Disclaimer does not apply to the BSX4-HAL-4.1NoFusion Software
* which is licensed under the Apache License, Version 2.0 as stated above.
* http://www.apache.org/licenses/LICENSE-2.0
*
* Product Disclaimer
*
* Common:
*
* Assessment of Products Returned from Field
*
* Returned products are considered good if they fulfill the specifications /
* test data for 0-mileage and field listed in this document.
*
* Engineering Samples
*
* Engineering samples are marked with (e) or (E). Samples may vary from the
* valid technical specifications of the series product contained in this
* data sheet. Therefore, they are not intended or fit for resale to
* third parties or for use in end products. Their sole purpose is internal
* client testing. The testing of an engineering sample may in no way replace
* the testing of a series product. Bosch assumes no liability for the use
* of engineering samples. The purchaser shall indemnify Bosch from all claims
* arising from the use of engineering samples.
*
* Intended use
*
* Provided that SMI130 is used within the conditions (environment, application,
* installation, loads) as described in this TCD and the corresponding
* agreed upon documents, Bosch ensures that the product complies with
* the agreed properties. Agreements beyond this require
* the written approval by Bosch. The product is considered fit for the intended
* use when the product successfully has passed the tests
* in accordance with the TCD and agreed upon documents.
*
* It is the responsibility of the customer to ensure the proper application
* of the product in the overall system/vehicle.
*
* Bosch does not assume any responsibility for changes to the environment
* of the product that deviate from the TCD and the agreed upon documents
* as well as all applications not released by Bosch
*
* The resale and/or use of products are at the purchaser’s own risk and
* responsibility. The examination and testing of the SMI130
* is the sole responsibility of the purchaser.
*
* The purchaser shall indemnify Bosch from all third party claims
* arising from any product use not covered by the parameters of
* this product data sheet or not approved by Bosch and reimburse Bosch
* for all costs and damages in connection with such claims.
*
* The purchaser must monitor the market for the purchased products,
* particularly with regard to product safety, and inform Bosch without delay
* of all security relevant incidents.
*
* Application Examples and Hints
*
* With respect to any application examples, advice, normal values
* and/or any information regarding the application of the device,
* Bosch hereby disclaims any and all warranties and liabilities of any kind,
* including without limitation warranties of
* non-infringement of intellectual property rights or copyrights
* of any third party.
* The information given in this document shall in no event be regarded
* as a guarantee of conditions or characteristics. They are provided
* for illustrative purposes only and no evaluation regarding infringement
* of intellectual property rights or copyrights or regarding functionality,
* performance or error has been made.
*
* @filename smi130_driver.c
* @date 2016/08/01 14:40
* @Modification Date 2018/08/28 18:20
* @id "b5ff23a"
* @version 1.3
*
* @brief
* The core code of SMI130 device driver
*
* @detail
* This file implements the core code of SMI130 device driver,
* which includes hardware related functions, input device register,
* device attribute files, etc.
*/
#include "smi130.h"
#include "smi130_driver.h"
#include <linux/device.h>
#include <linux/gpio.h>
#include <linux/of_gpio.h>
#include <linux/of_irq.h>
#define DRIVER_VERSION "0.0.53.0"
#define I2C_BURST_READ_MAX_LEN (256)
#define SMI130_STORE_COUNT (6000)
#define LMADA (1)
uint64_t g_current_apts_us_mbl;
enum SMI_SENSOR_INT_T {
/* Interrupt enable0*/
SMI_ANYMO_X_INT = 0,
SMI_ANYMO_Y_INT,
SMI_ANYMO_Z_INT,
SMI_D_TAP_INT,
SMI_S_TAP_INT,
SMI_ORIENT_INT,
SMI_FLAT_INT,
/* Interrupt enable1*/
SMI_HIGH_X_INT,
SMI_HIGH_Y_INT,
SMI_HIGH_Z_INT,
SMI_LOW_INT,
SMI_DRDY_INT,
SMI_FFULL_INT,
SMI_FWM_INT,
/* Interrupt enable2 */
SMI_NOMOTION_X_INT,
SMI_NOMOTION_Y_INT,
SMI_NOMOTION_Z_INT,
SMI_STEP_DETECTOR_INT,
INT_TYPE_MAX
};
/*smi fifo sensor type combination*/
enum SMI_SENSOR_FIFO_COMBINATION {
SMI_FIFO_A = 0,
SMI_FIFO_G,
SMI_FIFO_M,
SMI_FIFO_G_A,
SMI_FIFO_M_A,
SMI_FIFO_M_G,
SMI_FIFO_M_G_A,
SMI_FIFO_COM_MAX
};
/*smi fifo analyse return err status*/
enum SMI_FIFO_ANALYSE_RETURN_T {
FIFO_OVER_READ_RETURN = -10,
FIFO_SENSORTIME_RETURN = -9,
FIFO_SKIP_OVER_LEN = -8,
FIFO_M_G_A_OVER_LEN = -7,
FIFO_M_G_OVER_LEN = -6,
FIFO_M_A_OVER_LEN = -5,
FIFO_G_A_OVER_LEN = -4,
FIFO_M_OVER_LEN = -3,
FIFO_G_OVER_LEN = -2,
FIFO_A_OVER_LEN = -1
};
/*!smi sensor generic power mode enum */
enum SMI_DEV_OP_MODE {
SENSOR_PM_NORMAL = 0,
SENSOR_PM_LP1,
SENSOR_PM_SUSPEND,
SENSOR_PM_LP2
};
/*! smi acc sensor power mode enum */
enum SMI_ACC_PM_TYPE {
SMI_ACC_PM_NORMAL = 0,
SMI_ACC_PM_LP1,
SMI_ACC_PM_SUSPEND,
SMI_ACC_PM_LP2,
SMI_ACC_PM_MAX
};
/*! smi gyro sensor power mode enum */
enum SMI_GYRO_PM_TYPE {
SMI_GYRO_PM_NORMAL = 0,
SMI_GYRO_PM_FAST_START,
SMI_GYRO_PM_SUSPEND,
SMI_GYRO_PM_MAX
};
/*! smi mag sensor power mode enum */
enum SMI_MAG_PM_TYPE {
SMI_MAG_PM_NORMAL = 0,
SMI_MAG_PM_LP1,
SMI_MAG_PM_SUSPEND,
SMI_MAG_PM_LP2,
SMI_MAG_PM_MAX
};
/*! smi sensor support type*/
enum SMI_SENSOR_TYPE {
SMI_ACC_SENSOR,
SMI_GYRO_SENSOR,
SMI_MAG_SENSOR,
SMI_SENSOR_TYPE_MAX
};
/*!smi sensor generic power mode enum */
enum SMI_AXIS_TYPE {
X_AXIS = 0,
Y_AXIS,
Z_AXIS,
AXIS_MAX
};
/*!smi sensor generic intterrupt enum */
enum SMI_INT_TYPE {
SMI130_INT0 = 0,
SMI130_INT1,
SMI130_INT_MAX
};
/*! smi sensor time resolution definition*/
enum SMI_SENSOR_TIME_RS_TYPE {
TS_0_78_HZ = 1,/*0.78HZ*/
TS_1_56_HZ,/*1.56HZ*/
TS_3_125_HZ,/*3.125HZ*/
TS_6_25_HZ,/*6.25HZ*/
TS_12_5_HZ,/*12.5HZ*/
TS_25_HZ,/*25HZ, odr=6*/
TS_50_HZ,/*50HZ*/
TS_100_HZ,/*100HZ*/
TS_200_HZ,/*200HZ*/
TS_400_HZ,/*400HZ*/
TS_800_HZ,/*800HZ*/
TS_1600_HZ,/*1600HZ*/
TS_MAX_HZ
};
/*! smi sensor interface mode */
enum SMI_SENSOR_IF_MODE_TYPE {
/*primary interface:autoconfig/secondary interface off*/
P_AUTO_S_OFF = 0,
/*primary interface:I2C/secondary interface:OIS*/
P_I2C_S_OIS,
/*primary interface:autoconfig/secondary interface:Magnetometer*/
P_AUTO_S_MAG,
/*interface mode reseved*/
IF_MODE_RESEVED
};
/*! smi130 acc/gyro calibration status in H/W layer */
enum SMI_CALIBRATION_STATUS_TYPE {
/*SMI FAST Calibration ready x/y/z status*/
SMI_ACC_X_FAST_CALI_RDY = 0,
SMI_ACC_Y_FAST_CALI_RDY,
SMI_ACC_Z_FAST_CALI_RDY
};
unsigned int reg_op_addr_mbl;
static const int smi_pmu_cmd_acc_arr[SMI_ACC_PM_MAX] = {
/*!smi pmu for acc normal, low power1,
* suspend, low power2 mode command */
CMD_PMU_ACC_NORMAL,
CMD_PMU_ACC_LP1,
CMD_PMU_ACC_SUSPEND,
CMD_PMU_ACC_LP2
};
static const int smi_pmu_cmd_gyro_arr[SMI_GYRO_PM_MAX] = {
/*!smi pmu for gyro normal, fast startup,
* suspend mode command */
CMD_PMU_GYRO_NORMAL,
CMD_PMU_GYRO_FASTSTART,
CMD_PMU_GYRO_SUSPEND
};
static const int smi_pmu_cmd_mag_arr[SMI_MAG_PM_MAX] = {
/*!smi pmu for mag normal, low power1,
* suspend, low power2 mode command */
CMD_PMU_MAG_NORMAL,
CMD_PMU_MAG_LP1,
CMD_PMU_MAG_SUSPEND,
CMD_PMU_MAG_LP2
};
static const char *smi_axis_name[AXIS_MAX] = {"x", "y", "z"};
static const int smi_interrupt_type[] = {
/*!smi interrupt type */
/* Interrupt enable0 , index=0~6*/
SMI130_ANY_MOTION_X_ENABLE,
SMI130_ANY_MOTION_Y_ENABLE,
SMI130_ANY_MOTION_Z_ENABLE,
SMI130_DOUBLE_TAP_ENABLE,
SMI130_SINGLE_TAP_ENABLE,
SMI130_ORIENT_ENABLE,
SMI130_FLAT_ENABLE,
/* Interrupt enable1, index=7~13*/
SMI130_HIGH_G_X_ENABLE,
SMI130_HIGH_G_Y_ENABLE,
SMI130_HIGH_G_Z_ENABLE,
SMI130_LOW_G_ENABLE,
SMI130_DATA_RDY_ENABLE,
SMI130_FIFO_FULL_ENABLE,
SMI130_FIFO_WM_ENABLE,
/* Interrupt enable2, index = 14~17*/
SMI130_NOMOTION_X_ENABLE,
SMI130_NOMOTION_Y_ENABLE,
SMI130_NOMOTION_Z_ENABLE,
SMI130_STEP_DETECTOR_EN
};
/*! smi sensor time depend on ODR*/
struct smi_sensor_time_odr_tbl {
u32 ts_duration_lsb;
u32 ts_duration_us;
u32 ts_delat;/*sub current delat fifo_time*/
};
struct smi130_axis_data_t {
s16 x;
s16 y;
s16 z;
};
struct smi130_type_mapping_type {
/*! smi16x sensor chip id */
uint16_t chip_id;
/*! smi16x chip revision code */
uint16_t revision_id;
/*! smi130_acc sensor name */
const char *sensor_name;
};
struct smi130_store_info_t {
uint8_t current_frm_cnt;
uint64_t current_apts_us[2];
uint8_t fifo_ts_total_frmcnt;
uint64_t fifo_time;
};
uint64_t get_current_timestamp_mbl(void)
{
uint64_t ts_ap;
struct timespec tmp_time;
get_monotonic_boottime(&tmp_time);
ts_ap = (uint64_t)tmp_time.tv_sec * 1000000000 + tmp_time.tv_nsec;
return ts_ap;
}
/*! sensor support type map */
static const struct smi130_type_mapping_type sensor_type_map[] = {
{SENSOR_CHIP_ID_SMI, SENSOR_CHIP_REV_ID_SMI, "SMI130/162AB"},
{SENSOR_CHIP_ID_SMI_C2, SENSOR_CHIP_REV_ID_SMI, "SMI130C2"},
{SENSOR_CHIP_ID_SMI_C3, SENSOR_CHIP_REV_ID_SMI, "SMI130C3"},
};
/*!smi130 sensor time depends on ODR */
static const struct smi_sensor_time_odr_tbl
sensortime_duration_tbl[TS_MAX_HZ] = {
{0x010000, 2560000, 0x00ffff},/*2560ms, 0.39hz, odr=resver*/
{0x008000, 1280000, 0x007fff},/*1280ms, 0.78hz, odr_acc=1*/
{0x004000, 640000, 0x003fff},/*640ms, 1.56hz, odr_acc=2*/
{0x002000, 320000, 0x001fff},/*320ms, 3.125hz, odr_acc=3*/
{0x001000, 160000, 0x000fff},/*160ms, 6.25hz, odr_acc=4*/
{0x000800, 80000, 0x0007ff},/*80ms, 12.5hz*/
{0x000400, 40000, 0x0003ff},/*40ms, 25hz, odr_acc = odr_gyro =6*/
{0x000200, 20000, 0x0001ff},/*20ms, 50hz, odr = 7*/
{0x000100, 10000, 0x0000ff},/*10ms, 100hz, odr=8*/
{0x000080, 5000, 0x00007f},/*5ms, 200hz, odr=9*/
{0x000040, 2500, 0x00003f},/*2.5ms, 400hz, odr=10*/
{0x000020, 1250, 0x00001f},/*1.25ms, 800hz, odr=11*/
{0x000010, 625, 0x00000f},/*0.625ms, 1600hz, odr=12*/
};
#if defined(CONFIG_USE_QUALCOMM_HAL)
#define POLL_INTERVAL_MIN_MS 10
#define POLL_INTERVAL_MAX_MS 4000
#define POLL_DEFAULT_INTERVAL_MS 200
#define SMI130_ACCEL_MIN_VALUE -32768
#define SMI130_ACCEL_MAX_VALUE 32767
#define SMI130_GYRO_MIN_VALUE -32768
#define SMI130_GYRO_MAX_VALUE 32767
#define SMI130_ACCEL_DEFAULT_POLL_INTERVAL_MS 200
#define SMI130_GYRO_DEFAULT_POLL_INTERVAL_MS 200
#define SMI130_ACCEL_MIN_POLL_INTERVAL_MS 10
#define SMI130_ACCEL_MAX_POLL_INTERVAL_MS 5000
#define SMI130_GYRO_MIN_POLL_INTERVAL_MS 10
#define SMI130_GYRO_MAX_POLL_INTERVAL_MS 5000
static struct sensors_classdev smi130_accel_cdev = {
.name = "smi130-accel",
.vendor = "bosch",
.version = 1,
.handle = SENSORS_ACCELERATION_HANDLE,
.type = SENSOR_TYPE_ACCELEROMETER,
.max_range = "156.8", /* 16g */
.resolution = "0.153125", /* 15.6mg */
.sensor_power = "0.13", /* typical value */
.min_delay = POLL_INTERVAL_MIN_MS * 1000, /* in microseconds */
.max_delay = POLL_INTERVAL_MAX_MS,
.delay_msec = POLL_DEFAULT_INTERVAL_MS, /* in millisecond */
.fifo_reserved_event_count = 0,
.fifo_max_event_count = 0,
.enabled = 0,
.max_latency = 0,
.flags = 0,
.sensors_enable = NULL,
.sensors_poll_delay = NULL,
.sensors_set_latency = NULL,
.sensors_flush = NULL,
.sensors_self_test = NULL,
};
static struct sensors_classdev smi130_gyro_cdev = {
.name = "smi130-gyro",
.vendor = "bosch",
.version = 1,
.handle = SENSORS_GYROSCOPE_HANDLE,
.type = SENSOR_TYPE_GYROSCOPE,
.max_range = "34.906586", /* rad/s */
.resolution = "0.0010681152", /* rad/s */
.sensor_power = "3.6", /* 3.6 mA */
.min_delay = SMI130_GYRO_MIN_POLL_INTERVAL_MS * 1000,
.max_delay = SMI130_GYRO_MAX_POLL_INTERVAL_MS,
.delay_msec = SMI130_GYRO_DEFAULT_POLL_INTERVAL_MS,
.fifo_reserved_event_count = 0,
.fifo_max_event_count = 0,
.enabled = 0,
.max_latency = 0,
.flags = 0, /* SENSOR_FLAG_CONTINUOUS_MODE */
.sensors_enable = NULL,
.sensors_poll_delay = NULL,
.sensors_enable_wakeup = NULL,
.sensors_set_latency = NULL,
.sensors_flush = NULL,
};
#endif
static void smi_delay(u32 msec)
{
if (msec <= 20)
usleep_range(msec * 1000, msec * 1000);
else
msleep(msec);
}
static void smi_dump_reg(struct smi_client_data *client_data)
{
#define REG_MAX0 0x24
#define REG_MAX1 0x56
int i;
u8 dbg_buf0[REG_MAX0];
u8 dbg_buf1[REG_MAX1];
u8 dbg_buf_str0[REG_MAX0 * 3 + 1] = "";
u8 dbg_buf_str1[REG_MAX1 * 3 + 1] = "";
dev_notice(client_data->dev, "\nFrom 0x00:\n");
client_data->device.bus_read(client_data->device.dev_addr,
SMI_REG_NAME(USER_CHIP_ID), dbg_buf0, REG_MAX0);
for (i = 0; i < REG_MAX0; i++) {
snprintf(dbg_buf_str0 + i * 3, 16, "%02x%c", dbg_buf0[i],
(((i + 1) % BYTES_PER_LINE == 0) ? '\n' : ' '));
}
dev_notice(client_data->dev, "%s\n", dbg_buf_str0);
client_data->device.bus_read(client_data->device.dev_addr,
SMI130_USER_ACCEL_CONFIG_ADDR, dbg_buf1, REG_MAX1);
dev_notice(client_data->dev, "\nFrom 0x40:\n");
for (i = 0; i < REG_MAX1; i++) {
snprintf(dbg_buf_str1 + i * 3, 16, "%02x%c", dbg_buf1[i],
(((i + 1) % BYTES_PER_LINE == 0) ? '\n' : ' '));
}
dev_notice(client_data->dev, "\n%s\n", dbg_buf_str1);
}
void smi_fifo_frame_bytes_extend_calc(
struct smi_client_data *client_data,
unsigned int *fifo_frmbytes_extend)
{
switch (client_data->fifo_data_sel) {
case SMI_FIFO_A_SEL:
case SMI_FIFO_G_SEL:
*fifo_frmbytes_extend = 7;
break;
case SMI_FIFO_G_A_SEL:
*fifo_frmbytes_extend = 13;
break;
case SMI_FIFO_M_SEL:
*fifo_frmbytes_extend = 9;
break;
case SMI_FIFO_M_A_SEL:
case SMI_FIFO_M_G_SEL:
/*8(mag) + 6(gyro or acc) +1(head) = 15*/
*fifo_frmbytes_extend = 15;
break;
case SMI_FIFO_M_G_A_SEL:
/*8(mag) + 6(gyro or acc) + 6 + 1 = 21*/
*fifo_frmbytes_extend = 21;
break;
default:
*fifo_frmbytes_extend = 0;
break;
};
}
static int smi_input_init(struct smi_client_data *client_data)
{
struct input_dev *dev;
int err = 0;
dev = input_allocate_device();
if (NULL == dev)
return -ENOMEM;
#if defined(CONFIG_USE_QUALCOMM_HAL)
dev->name = "smi130-accel";
#else
dev->name = SENSOR_NAME;
#endif
dev->id.bustype = BUS_I2C;
input_set_capability(dev, EV_MSC, MSC_GESTURE);
input_set_capability(dev, EV_MSC, INPUT_EVENT_SGM);
input_set_capability(dev, EV_MSC, INPUT_EVENT_FAST_GYRO_CALIB_DONE);
input_set_capability(dev, EV_MSC, INPUT_EVENT_STEP_DETECTOR);
input_set_capability(dev, EV_MSC, INPUT_EVENT_FAST_ACC_CALIB_DONE);
input_set_capability(dev, EV_REL, REL_X);
input_set_capability(dev, EV_REL, REL_Y);
input_set_capability(dev, EV_REL, REL_Z);
#if defined(CONFIG_USE_QUALCOMM_HAL)
input_set_capability(dev, EV_ABS, ABS_MISC);
input_set_abs_params(dev, ABS_X,
SMI130_ACCEL_MIN_VALUE, SMI130_ACCEL_MAX_VALUE,
0, 0);
input_set_abs_params(dev, ABS_Y,
SMI130_ACCEL_MIN_VALUE, SMI130_ACCEL_MAX_VALUE,
0, 0);
input_set_abs_params(dev, ABS_Z,
SMI130_ACCEL_MIN_VALUE, SMI130_ACCEL_MAX_VALUE,
0, 0);
#endif
input_set_drvdata(dev, client_data);
err = input_register_device(dev);
if (err < 0) {
input_free_device(dev);
dev_notice(client_data->dev, "smi130 input free!\n");
return err;
}
client_data->input = dev;
dev_notice(client_data->dev,
"smi130 input register successfully, %s!\n",
client_data->input->name);
return err;
}
//#if defined(CONFIG_USE_QUALCOMM_HAL)
static int smi_gyro_input_init(struct smi_client_data *client_data)
{
struct input_dev *dev;
int err = 0;
dev = input_allocate_device();
if (NULL == dev)
return -ENOMEM;
dev->name = "smi130-gyro";
dev->id.bustype = BUS_I2C;
input_set_capability(dev, EV_ABS, ABS_MISC);
input_set_capability(dev, EV_MSC, MSC_GESTURE);
input_set_capability(dev, EV_MSC, INPUT_EVENT_SGM);
input_set_capability(dev, EV_MSC, INPUT_EVENT_FAST_GYRO_CALIB_DONE);
input_set_capability(dev, EV_MSC, INPUT_EVENT_STEP_DETECTOR);
input_set_capability(dev, EV_MSC, INPUT_EVENT_FAST_ACC_CALIB_DONE);
#if defined(CONFIG_USE_QUALCOMM_HAL)
input_set_abs_params(dev, ABS_RX,
SMI130_ACCEL_MIN_VALUE, SMI130_ACCEL_MAX_VALUE,
0, 0);
input_set_abs_params(dev, ABS_RY,
SMI130_ACCEL_MIN_VALUE, SMI130_ACCEL_MAX_VALUE,
0, 0);
input_set_abs_params(dev, ABS_RZ,
SMI130_ACCEL_MIN_VALUE, SMI130_ACCEL_MAX_VALUE,
0, 0);
#endif
input_set_drvdata(dev, client_data);
err = input_register_device(dev);
if (err < 0) {
input_free_device(dev);
dev_notice(client_data->dev, "smi130 input free!\n");
return err;
}
client_data->gyro_input = dev;
dev_notice(client_data->dev,
"smi130 input register successfully, %s!\n",
client_data->gyro_input->name);
return err;
}
//#endif
static void smi_input_destroy(struct smi_client_data *client_data)
{
struct input_dev *dev = client_data->input;
input_unregister_device(dev);
input_free_device(dev);
}
static int smi_check_chip_id(struct smi_client_data *client_data)
{
int8_t err = 0;
int8_t i = 0;
uint8_t chip_id = 0;
uint8_t read_count = 0;
u8 smi_sensor_cnt = sizeof(sensor_type_map)
/ sizeof(struct smi130_type_mapping_type);
/* read and check chip id */
while (read_count++ < CHECK_CHIP_ID_TIME_MAX) {
if (client_data->device.bus_read(client_data->device.dev_addr,
SMI_REG_NAME(USER_CHIP_ID), &chip_id, 1) < 0) {
dev_err(client_data->dev,
"Bosch Sensortec Device not found"
"read chip_id:%d\n", chip_id);
continue;
} else {
for (i = 0; i < smi_sensor_cnt; i++) {
if (sensor_type_map[i].chip_id == chip_id) {
client_data->chip_id = chip_id;
dev_notice(client_data->dev,
"Bosch Sensortec Device detected, "
"HW IC name: %s\n", sensor_type_map[i].sensor_name);
break;
}
}
if (i < smi_sensor_cnt)
break;
else {
if (read_count == CHECK_CHIP_ID_TIME_MAX) {
dev_err(client_data->dev,
"Failed!Bosch Sensortec Device not found"
" mismatch chip_id:%d\n", chip_id);
err = -ENODEV;
return err;
}
}
smi_delay(1);
}
}
return err;
}
static int smi_pmu_set_suspend(struct smi_client_data *client_data)
{
int err = 0;
if (client_data == NULL)
return -EINVAL;
else {
err += SMI_CALL_API(set_command_register)
(smi_pmu_cmd_acc_arr[SENSOR_PM_SUSPEND]);
err += SMI_CALL_API(set_command_register)
(smi_pmu_cmd_gyro_arr[SENSOR_PM_SUSPEND]);
err += SMI_CALL_API(set_command_register)
(smi_pmu_cmd_mag_arr[SENSOR_PM_SUSPEND]);
client_data->pw.acc_pm = SMI_ACC_PM_SUSPEND;
client_data->pw.gyro_pm = SMI_GYRO_PM_SUSPEND;
client_data->pw.mag_pm = SMI_MAG_PM_SUSPEND;
}
return err;
}
static int smi_get_err_status(struct smi_client_data *client_data)
{
int err = 0;
err = SMI_CALL_API(get_error_status)(&client_data->err_st.fatal_err,
&client_data->err_st.err_code, &client_data->err_st.i2c_fail,
&client_data->err_st.drop_cmd, &client_data->err_st.mag_drdy_err);
return err;
}
static void smi_work_func(struct work_struct *work)
{
struct smi_client_data *client_data =
container_of((struct delayed_work *)work,
struct smi_client_data, work);
unsigned long delay =
msecs_to_jiffies(atomic_read(&client_data->delay));
struct smi130_accel_t data;
int err;
err = SMI_CALL_API(read_accel_xyz)(&data);
if (err < 0)
return;
/*report current frame via input event*/
input_event(client_data->input, EV_REL, REL_X, data.x);
input_event(client_data->input, EV_REL, REL_Y, data.y);
input_event(client_data->input, EV_REL, REL_Z, data.z);
input_sync(client_data->input);
schedule_delayed_work(&client_data->work, delay);
}
static ssize_t smi130_chip_id_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct input_dev *input = to_input_dev(dev);
struct smi_client_data *client_data = input_get_drvdata(input);
return snprintf(buf, 16, "0x%x\n", client_data->chip_id);
}
static ssize_t smi130_err_st_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct input_dev *input = to_input_dev(dev);
struct smi_client_data *client_data = input_get_drvdata(input);
int err = 0;
err = smi_get_err_status(client_data);
if (err)
return err;
else {
return snprintf(buf, 128, "fatal_err:0x%x, err_code:%d,\n\n"
"i2c_fail_err:%d, drop_cmd_err:%d, mag_drdy_err:%d\n",
client_data->err_st.fatal_err,
client_data->err_st.err_code,
client_data->err_st.i2c_fail,
client_data->err_st.drop_cmd,
client_data->err_st.mag_drdy_err);
}
}
static ssize_t smi130_sensor_time_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
int err = 0;
u32 sensor_time;
err = SMI_CALL_API(get_sensor_time)(&sensor_time);
if (err)
return err;
else
return snprintf(buf, 16, "0x%x\n", (unsigned int)sensor_time);
}
static ssize_t smi130_fifo_flush_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
int err;
unsigned long enable;
struct input_dev *input = to_input_dev(dev);
struct smi_client_data *client_data = input_get_drvdata(input);
err = kstrtoul(buf, 10, &enable);
if (err)
return err;
if (enable)
err = SMI_CALL_API(set_command_register)(CMD_CLR_FIFO_DATA);
if (err)
dev_err(client_data->dev, "fifo flush failed!\n");
return count;
}
static ssize_t smi130_fifo_bytecount_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
int err;
unsigned int fifo_bytecount = 0;
SMI_CALL_API(fifo_length)(&fifo_bytecount);
err = snprintf(buf, 16, "%u\n", fifo_bytecount);
return err;
}
static ssize_t smi130_fifo_bytecount_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct input_dev *input = to_input_dev(dev);
struct smi_client_data *client_data = input_get_drvdata(input);
int err;
unsigned long data;
err = kstrtoul(buf, 10, &data);
if (err)
return err;
client_data->fifo_bytecount = (unsigned int) data;
return count;
}
int smi130_fifo_data_sel_get(struct smi_client_data *client_data)
{
int err = 0;
unsigned char fifo_acc_en, fifo_gyro_en, fifo_mag_en;
unsigned char fifo_datasel;
err += SMI_CALL_API(get_fifo_accel_enable)(&fifo_acc_en);
err += SMI_CALL_API(get_fifo_gyro_enable)(&fifo_gyro_en);
err += SMI_CALL_API(get_fifo_mag_enable)(&fifo_mag_en);
if (err)
return err;
fifo_datasel = (fifo_acc_en << SMI_ACC_SENSOR) |
(fifo_gyro_en << SMI_GYRO_SENSOR) |
(fifo_mag_en << SMI_MAG_SENSOR);
client_data->fifo_data_sel = fifo_datasel;
return err;
}
static ssize_t smi130_fifo_data_sel_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
int err = 0;
struct input_dev *input = to_input_dev(dev);
struct smi_client_data *client_data = input_get_drvdata(input);
err = smi130_fifo_data_sel_get(client_data);
if (err) {
dev_err(client_data->dev, "get fifo_sel failed!\n");
return -EINVAL;
}
return snprintf(buf, 16, "%d\n", client_data->fifo_data_sel);
}
/* write any value to clear all the fifo data. */
static ssize_t smi130_fifo_data_sel_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct input_dev *input = to_input_dev(dev);
struct smi_client_data *client_data = input_get_drvdata(input);
int err;
unsigned long data;
unsigned char fifo_datasel;
err = kstrtoul(buf, 10, &data);
if (err)
return err;
/* data format: aimed 0b0000 0x(m)x(g)x(a), x:1 enable, 0:disable*/
if (data > 7)
return -EINVAL;
fifo_datasel = (unsigned char)data;
err += SMI_CALL_API(set_fifo_accel_enable)
((fifo_datasel & (1 << SMI_ACC_SENSOR)) ? 1 : 0);
err += SMI_CALL_API(set_fifo_gyro_enable)
(fifo_datasel & (1 << SMI_GYRO_SENSOR) ? 1 : 0);
err += SMI_CALL_API(set_fifo_mag_enable)
((fifo_datasel & (1 << SMI_MAG_SENSOR)) ? 1 : 0);
err += SMI_CALL_API(set_command_register)(CMD_CLR_FIFO_DATA);
if (err)
return -EIO;
else {
dev_notice(client_data->dev, "FIFO A_en:%d, G_en:%d, M_en:%d\n",
(fifo_datasel & (1 << SMI_ACC_SENSOR)) ? 1 : 0,
(fifo_datasel & (1 << SMI_GYRO_SENSOR) ? 1 : 0),
((fifo_datasel & (1 << SMI_MAG_SENSOR)) ? 1 : 0));
client_data->fifo_data_sel = fifo_datasel;
}
return count;
}
static ssize_t smi130_fifo_data_out_frame_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct input_dev *input = to_input_dev(dev);
struct smi_client_data *client_data = input_get_drvdata(input);
int err = 0;
uint32_t fifo_bytecount = 0;
err = SMI_CALL_API(fifo_length)(&fifo_bytecount);
if (err < 0) {
dev_err(client_data->dev, "read fifo_length err");
return -EINVAL;
}
if (fifo_bytecount == 0)
return 0;
err = smi_burst_read_wrapper(client_data->device.dev_addr,
SMI130_USER_FIFO_DATA__REG, buf,
fifo_bytecount);
if (err) {
dev_err(client_data->dev, "read fifo err");
SMI_CALL_API(set_command_register)(CMD_CLR_FIFO_DATA);
return -EINVAL;
}
return fifo_bytecount;
}
static ssize_t smi130_fifo_watermark_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
int err;
unsigned char data = 0xff;
err = SMI_CALL_API(get_fifo_wm)(&data);
if (err)
return err;
return snprintf(buf, 16, "%d\n", data);
}
static ssize_t smi130_fifo_watermark_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
int err;
unsigned long data;
unsigned char fifo_watermark;
err = kstrtoul(buf, 10, &data);
if (err)
return err;
fifo_watermark = (unsigned char)data;
err = SMI_CALL_API(set_fifo_wm)(fifo_watermark);
if (err)
return -EIO;
return count;
}
static ssize_t smi130_fifo_header_en_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
int err;
unsigned char data = 0xff;
err = SMI_CALL_API(get_fifo_header_enable)(&data);
if (err)
return err;
return snprintf(buf, 16, "%d\n", data);
}
static ssize_t smi130_fifo_header_en_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct input_dev *input = to_input_dev(dev);
struct smi_client_data *client_data = input_get_drvdata(input);
int err;
unsigned long data;
unsigned char fifo_header_en;
err = kstrtoul(buf, 10, &data);
if (err)
return err;
if (data > 1)
return -ENOENT;
fifo_header_en = (unsigned char)data;
err = SMI_CALL_API(set_fifo_header_enable)(fifo_header_en);
if (err)
return -EIO;
client_data->fifo_head_en = fifo_header_en;
return count;
}
static ssize_t smi130_fifo_time_en_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
int err;
unsigned char data = 0;
err = SMI_CALL_API(get_fifo_time_enable)(&data);
if (!err)
err = snprintf(buf, 16, "%d\n", data);
return err;
}
static ssize_t smi130_fifo_time_en_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
int err;
unsigned long data;
unsigned char fifo_ts_en;
err = kstrtoul(buf, 10, &data);
if (err)
return err;
fifo_ts_en = (unsigned char)data;
err = SMI_CALL_API(set_fifo_time_enable)(fifo_ts_en);
if (err)
return -EIO;
return count;
}
static ssize_t smi130_fifo_int_tag_en_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
int err = 0;
unsigned char fifo_tag_int1 = 0;
unsigned char fifo_tag_int2 = 0;
unsigned char fifo_tag_int;
err += SMI_CALL_API(get_fifo_tag_intr1_enable)(&fifo_tag_int1);
err += SMI_CALL_API(get_fifo_tag_intr2_enable)(&fifo_tag_int2);
fifo_tag_int = (fifo_tag_int1 << SMI130_INT0) |
(fifo_tag_int2 << SMI130_INT1);
if (!err)
err = snprintf(buf, 16, "%d\n", fifo_tag_int);
return err;
}
static ssize_t smi130_fifo_int_tag_en_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct input_dev *input = to_input_dev(dev);
struct smi_client_data *client_data = input_get_drvdata(input);
int err;
unsigned long data;
unsigned char fifo_tag_int_en;
err = kstrtoul(buf, 10, &data);
if (err)
return err;
if (data > 3)
return -EINVAL;
fifo_tag_int_en = (unsigned char)data;
err += SMI_CALL_API(set_fifo_tag_intr1_enable)
((fifo_tag_int_en & (1 << SMI130_INT0)) ? 1 : 0);
err += SMI_CALL_API(set_fifo_tag_intr2_enable)
((fifo_tag_int_en & (1 << SMI130_INT1)) ? 1 : 0);
if (err) {
dev_err(client_data->dev, "fifo int tag en err:%d\n", err);
return -EIO;
}
client_data->fifo_int_tag_en = fifo_tag_int_en;
return count;
}
static int smi130_set_acc_op_mode(struct smi_client_data *client_data,
unsigned long op_mode)
{
int err = 0;
unsigned char stc_enable;
unsigned char std_enable;
mutex_lock(&client_data->mutex_op_mode);
if (op_mode < SMI_ACC_PM_MAX) {
switch (op_mode) {
case SMI_ACC_PM_NORMAL:
err = SMI_CALL_API(set_command_register)
(smi_pmu_cmd_acc_arr[SMI_ACC_PM_NORMAL]);
client_data->pw.acc_pm = SMI_ACC_PM_NORMAL;
smi_delay(10);
break;
case SMI_ACC_PM_LP1:
err = SMI_CALL_API(set_command_register)
(smi_pmu_cmd_acc_arr[SMI_ACC_PM_LP1]);
client_data->pw.acc_pm = SMI_ACC_PM_LP1;
smi_delay(3);
break;
case SMI_ACC_PM_SUSPEND:
SMI_CALL_API(get_step_counter_enable)(&stc_enable);
SMI_CALL_API(get_step_detector_enable)(&std_enable);
if ((stc_enable == 0) && (std_enable == 0) &&
(client_data->sig_flag == 0)) {
err = SMI_CALL_API(set_command_register)
(smi_pmu_cmd_acc_arr[SMI_ACC_PM_SUSPEND]);
client_data->pw.acc_pm = SMI_ACC_PM_SUSPEND;
smi_delay(10);
}
break;
case SMI_ACC_PM_LP2:
err = SMI_CALL_API(set_command_register)
(smi_pmu_cmd_acc_arr[SMI_ACC_PM_LP2]);
client_data->pw.acc_pm = SMI_ACC_PM_LP2;
smi_delay(3);
break;
default:
mutex_unlock(&client_data->mutex_op_mode);
return -EINVAL;
}
} else {
mutex_unlock(&client_data->mutex_op_mode);
return -EINVAL;
}
mutex_unlock(&client_data->mutex_op_mode);
return err;
}
static ssize_t smi130_temperature_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
int err;
s16 temp = 0xff;
err = SMI_CALL_API(get_temp)(&temp);
if (!err)
err = snprintf(buf, 16, "0x%x\n", temp);
return err;
}
static ssize_t smi130_place_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct input_dev *input = to_input_dev(dev);
struct smi_client_data *client_data = input_get_drvdata(input);
int place = BOSCH_SENSOR_PLACE_UNKNOWN;
if (NULL != client_data->bosch_pd)
place = client_data->bosch_pd->place;
return snprintf(buf, 16, "%d\n", place);
}
static ssize_t smi130_delay_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct input_dev *input = to_input_dev(dev);
struct smi_client_data *client_data = input_get_drvdata(input);
return snprintf(buf, 16, "%d\n", atomic_read(&client_data->delay));
}
static ssize_t smi130_delay_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct input_dev *input = to_input_dev(dev);
struct smi_client_data *client_data = input_get_drvdata(input);
int err;
unsigned long data;
err = kstrtoul(buf, 10, &data);
if (err)
return err;
if (data == 0) {
err = -EINVAL;
return err;
}
if (data < SMI_DELAY_MIN)
data = SMI_DELAY_MIN;
atomic_set(&client_data->delay, (unsigned int)data);
return count;
}
static ssize_t smi130_enable_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct input_dev *input = to_input_dev(dev);
struct smi_client_data *client_data = input_get_drvdata(input);
return snprintf(buf, 16, "%d\n", atomic_read(&client_data->wkqueue_en));
}
static ssize_t smi130_enable_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct input_dev *input = to_input_dev(dev);
struct smi_client_data *client_data = input_get_drvdata(input);
int err;
unsigned long enable;
int pre_enable = atomic_read(&client_data->wkqueue_en);
err = kstrtoul(buf, 10, &enable);
if (err)
return err;
enable = enable ? 1 : 0;
mutex_lock(&client_data->mutex_enable);
if (enable) {
if (pre_enable == 0) {
smi130_set_acc_op_mode(client_data,
SMI_ACC_PM_NORMAL);
schedule_delayed_work(&client_data->work,
msecs_to_jiffies(atomic_read(&client_data->delay)));
atomic_set(&client_data->wkqueue_en, 1);
}
} else {
if (pre_enable == 1) {
smi130_set_acc_op_mode(client_data,
SMI_ACC_PM_SUSPEND);
cancel_delayed_work_sync(&client_data->work);
atomic_set(&client_data->wkqueue_en, 0);
}
}
mutex_unlock(&client_data->mutex_enable);
return count;
}
#if defined(SMI130_ENABLE_INT1) || defined(SMI130_ENABLE_INT2)
/* accel sensor part */
static ssize_t smi130_anymot_duration_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
int err;
unsigned char data;
err = SMI_CALL_API(get_intr_any_motion_durn)(&data);
if (err < 0)
return err;
return snprintf(buf, 16, "%d\n", data);
}
static ssize_t smi130_anymot_duration_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
unsigned long data;
int err;
err = kstrtoul(buf, 10, &data);
if (err)
return err;
err = SMI_CALL_API(set_intr_any_motion_durn)((unsigned char)data);
if (err < 0)
return -EIO;
return count;
}
static ssize_t smi130_anymot_threshold_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
unsigned char data;
int err;
err = SMI_CALL_API(get_intr_any_motion_thres)(&data);
if (err < 0)
return err;
return snprintf(buf, 16, "%d\n", data);
}
static ssize_t smi130_anymot_threshold_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
unsigned long data;
int err;
err = kstrtoul(buf, 10, &data);
if (err)
return err;
err = SMI_CALL_API(set_intr_any_motion_thres)((unsigned char)data);
if (err < 0)
return -EIO;
return count;
}
static ssize_t smi130_step_detector_status_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
u8 data = 0;
u8 step_det;
int err;
struct input_dev *input = to_input_dev(dev);
struct smi_client_data *client_data = input_get_drvdata(input);
err = SMI_CALL_API(get_step_detector_enable)(&step_det);
/*smi130_get_status0_step_int*/
if (err < 0)
return err;
/*client_data->std will be updated in smi_stepdetector_interrupt_handle */
if ((step_det == 1) && (client_data->std == 1)) {
data = 1;
client_data->std = 0;
}
else {
data = 0;
}
return snprintf(buf, 16, "%d\n", data);
}
static ssize_t smi130_step_detector_enable_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
unsigned char data;
int err;
err = SMI_CALL_API(get_step_detector_enable)(&data);
if (err < 0)
return err;
return snprintf(buf, 16, "%d\n", data);
}
static ssize_t smi130_step_detector_enable_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
unsigned long data;
int err;
struct input_dev *input = to_input_dev(dev);
struct smi_client_data *client_data = input_get_drvdata(input);
err = kstrtoul(buf, 10, &data);
if (err)
return err;
err = SMI_CALL_API(set_step_detector_enable)((unsigned char)data);
if (err < 0)
return -EIO;
if (data == 0)
client_data->pedo_data.wkar_step_detector_status = 0;
return count;
}
static ssize_t smi130_signification_motion_enable_store(
struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
unsigned long data;
int err;
struct input_dev *input = to_input_dev(dev);
struct smi_client_data *client_data = input_get_drvdata(input);
err = kstrtoul(buf, 10, &data);
if (err)
return err;
/*0x62 (bit 1) INT_MOTION_3 int_sig_mot_sel*/
err = SMI_CALL_API(set_intr_significant_motion_select)(
(unsigned char)data);
if (err < 0)
return -EIO;
if (data == 1) {
err = SMI_CALL_API(set_intr_enable_0)
(SMI130_ANY_MOTION_X_ENABLE, 1);
err += SMI_CALL_API(set_intr_enable_0)
(SMI130_ANY_MOTION_Y_ENABLE, 1);
err += SMI_CALL_API(set_intr_enable_0)
(SMI130_ANY_MOTION_Z_ENABLE, 1);
if (err < 0)
return -EIO;
enable_irq_wake(client_data->IRQ);
client_data->sig_flag = 1;
} else {
err = SMI_CALL_API(set_intr_enable_0)
(SMI130_ANY_MOTION_X_ENABLE, 0);
err += SMI_CALL_API(set_intr_enable_0)
(SMI130_ANY_MOTION_Y_ENABLE, 0);
err += SMI_CALL_API(set_intr_enable_0)
(SMI130_ANY_MOTION_Z_ENABLE, 0);
if (err < 0)
return -EIO;
disable_irq_wake(client_data->IRQ);
client_data->sig_flag = 0;
}
return count;
}
static ssize_t smi130_signification_motion_enable_show(
struct device *dev, struct device_attribute *attr, char *buf)
{
unsigned char data;
int err;
/*0x62 (bit 1) INT_MOTION_3 int_sig_mot_sel*/
err = SMI_CALL_API(get_intr_significant_motion_select)(&data);
if (err < 0)
return err;
return snprintf(buf, 16, "%d\n", data);
}
static int sigmotion_init_interrupts(u8 sig_map_int_pin)
{
int ret = 0;
/*0x60 */
ret += smi130_set_intr_any_motion_thres(0x1e);
/* 0x62(bit 3~2) 0=1.5s */
ret += smi130_set_intr_significant_motion_skip(0);
/*0x62(bit 5~4) 1=0.5s*/
ret += smi130_set_intr_significant_motion_proof(1);
/*0x50 (bit 0, 1, 2) INT_EN_0 anymo x y z*/
ret += smi130_map_significant_motion_intr(sig_map_int_pin);
/*0x62 (bit 1) INT_MOTION_3 int_sig_mot_sel
close the signification_motion*/
ret += smi130_set_intr_significant_motion_select(0);
/*close the anymotion interrupt*/
ret += SMI_CALL_API(set_intr_enable_0)
(SMI130_ANY_MOTION_X_ENABLE, 0);
ret += SMI_CALL_API(set_intr_enable_0)
(SMI130_ANY_MOTION_Y_ENABLE, 0);
ret += SMI_CALL_API(set_intr_enable_0)
(SMI130_ANY_MOTION_Z_ENABLE, 0);
if (ret)
printk(KERN_ERR "smi130 sig motion failed setting,%d!\n", ret);
return ret;
}
#endif
static ssize_t smi130_acc_range_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
int err;
unsigned char range;
struct input_dev *input = to_input_dev(dev);
struct smi_client_data *client_data = input_get_drvdata(input);
err = SMI_CALL_API(get_accel_range)(&range);
if (err)
return err;
client_data->range.acc_range = range;
return snprintf(buf, 16, "%d\n", range);
}
static ssize_t smi130_acc_range_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
int err;
unsigned long range;
struct input_dev *input = to_input_dev(dev);
struct smi_client_data *client_data = input_get_drvdata(input);
err = kstrtoul(buf, 10, &range);
if (err)
return err;
err = SMI_CALL_API(set_accel_range)(range);
if (err)
return -EIO;
client_data->range.acc_range = range;
return count;
}
static ssize_t smi130_acc_odr_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
int err;
unsigned char acc_odr;
struct input_dev *input = to_input_dev(dev);
struct smi_client_data *client_data = input_get_drvdata(input);
err = SMI_CALL_API(get_accel_output_data_rate)(&acc_odr);
if (err)
return err;
client_data->odr.acc_odr = acc_odr;
return snprintf(buf, 16, "%d\n", acc_odr);
}
static ssize_t smi130_acc_odr_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
int err;
unsigned long acc_odr;
struct input_dev *input = to_input_dev(dev);
struct smi_client_data *client_data = input_get_drvdata(input);
err = kstrtoul(buf, 10, &acc_odr);
if (err)
return err;
if (acc_odr < 1 || acc_odr > 12)
return -EIO;
if (acc_odr < 5)
err = SMI_CALL_API(set_accel_under_sampling_parameter)(1);
else
err = SMI_CALL_API(set_accel_under_sampling_parameter)(0);
if (err)
return err;
err = SMI_CALL_API(set_accel_output_data_rate)(acc_odr);
if (err)
return -EIO;
client_data->odr.acc_odr = acc_odr;
return count;
}
static ssize_t smi130_acc_op_mode_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct input_dev *input = to_input_dev(dev);
struct smi_client_data *client_data = input_get_drvdata(input);
int err = 0;
u8 accel_pmu_status = 0;
err = SMI_CALL_API(get_accel_power_mode_stat)(
&accel_pmu_status);
if (err)
return err;
else
return snprintf(buf, 32, "reg:%d, val:%d\n", accel_pmu_status,
client_data->pw.acc_pm);
}
static ssize_t smi130_acc_op_mode_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct input_dev *input = to_input_dev(dev);
struct smi_client_data *client_data = input_get_drvdata(input);
int err;
unsigned long op_mode;
err = kstrtoul(buf, 10, &op_mode);
if (err)
return err;
err = smi130_set_acc_op_mode(client_data, op_mode);
if (err)
return err;
else
return count;
}
static ssize_t smi130_acc_value_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct smi130_accel_t data;
int err;
err = SMI_CALL_API(read_accel_xyz)(&data);
if (err < 0)
return err;
return snprintf(buf, 48, "%hd %hd %hd\n",
data.x, data.y, data.z);
}
static ssize_t smi130_acc_fast_calibration_x_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
unsigned char data;
int err;
err = SMI_CALL_API(get_foc_accel_x)(&data);
if (err < 0)
return err;
return snprintf(buf, 16, "%d\n", data);
}
static ssize_t smi130_acc_fast_calibration_x_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
unsigned long data;
int err;
s8 accel_offset_x = 0;
struct input_dev *input = to_input_dev(dev);
struct smi_client_data *client_data = input_get_drvdata(input);
err = kstrtoul(buf, 10, &data);
if (err)
return err;
/* 0: disable, 1: +1g, 2: -1g, 3: 0g */
if (data > 3)
return -EINVAL;
err = SMI_CALL_API(set_accel_foc_trigger)(X_AXIS,
data, &accel_offset_x);
if (err)
return -EIO;
else
client_data->calib_status |=
SMI_FAST_CALI_TRUE << SMI_ACC_X_FAST_CALI_RDY;
return count;
}
static ssize_t smi130_acc_fast_calibration_y_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
unsigned char data;
int err;
err = SMI_CALL_API(get_foc_accel_y)(&data);
if (err < 0)
return err;
return snprintf(buf, 16, "%d\n", data);
}
static ssize_t smi130_acc_fast_calibration_y_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
unsigned long data;
int err;
s8 accel_offset_y = 0;
struct input_dev *input = to_input_dev(dev);
struct smi_client_data *client_data = input_get_drvdata(input);
err = kstrtoul(buf, 10, &data);
if (err)
return err;
/* 0: disable, 1: +1g, 2: -1g, 3: 0g */
if (data > 3)
return -EINVAL;
err = SMI_CALL_API(set_accel_foc_trigger)(Y_AXIS,
data, &accel_offset_y);
if (err)
return -EIO;
else
client_data->calib_status |=
SMI_FAST_CALI_TRUE << SMI_ACC_Y_FAST_CALI_RDY;
return count;
}
static ssize_t smi130_acc_fast_calibration_z_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
unsigned char data;
int err;
err = SMI_CALL_API(get_foc_accel_z)(&data);
if (err < 0)
return err;
return snprintf(buf, 16, "%d\n", data);
}
static ssize_t smi130_acc_fast_calibration_z_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
unsigned long data;
int err;
s8 accel_offset_z = 0;
struct input_dev *input = to_input_dev(dev);
struct smi_client_data *client_data = input_get_drvdata(input);
unsigned char data1[3] = {0};
err = kstrtoul(buf, 10, &data);
if (err)
return err;
/* 0: disable, 1: +1g, 2: -1g, 3: 0g */
if (data > 3)
return -EINVAL;
err = SMI_CALL_API(set_accel_foc_trigger)(Z_AXIS,
data, &accel_offset_z);
if (err)
return -EIO;
else
client_data->calib_status |=
SMI_FAST_CALI_TRUE << SMI_ACC_Z_FAST_CALI_RDY;
if (client_data->calib_status == SMI_FAST_CALI_ALL_RDY) {
err = SMI_CALL_API(get_accel_offset_compensation_xaxis)(
&data1[0]);
err += SMI_CALL_API(get_accel_offset_compensation_yaxis)(
&data1[1]);
err += SMI_CALL_API(get_accel_offset_compensation_zaxis)(
&data1[2]);
dev_info(client_data->dev, "accx %d, accy %d, accz %d\n",
data1[0], data1[1], data1[2]);
if (err)
return -EIO;
input_event(client_data->input, EV_MSC,
INPUT_EVENT_FAST_ACC_CALIB_DONE,
(data1[0] | (data1[1] << 8) | (data1[2] << 16)));
input_sync(client_data->input);
client_data->calib_status = 0;
}
return count;
}
static ssize_t smi130_acc_offset_x_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
unsigned char data;
int err;
err = SMI_CALL_API(get_accel_offset_compensation_xaxis)(&data);
if (err < 0)
return err;
return snprintf(buf, 16, "%d\n", data);
}
static ssize_t smi130_acc_offset_x_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
unsigned long data;
int err;
err = kstrtoul(buf, 10, &data);
if (err)
return err;
err = SMI_CALL_API(set_accel_offset_compensation_xaxis)
((unsigned char)data);
if (err < 0)
return -EIO;
return count;
}
static ssize_t smi130_acc_offset_y_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
unsigned char data;
int err;
err = SMI_CALL_API(get_accel_offset_compensation_yaxis)(&data);
if (err < 0)
return err;
return snprintf(buf, 16, "%d\n", data);
}
static ssize_t smi130_acc_offset_y_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
unsigned long data;
int err;
err = kstrtoul(buf, 10, &data);
if (err)
return err;
err = SMI_CALL_API(set_accel_offset_compensation_yaxis)
((unsigned char)data);
if (err < 0)
return -EIO;
return count;
}
static ssize_t smi130_acc_offset_z_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
unsigned char data;
int err;
err = SMI_CALL_API(get_accel_offset_compensation_zaxis)(&data);
if (err < 0)
return err;
return snprintf(buf, 16, "%d\n", data);
}
static ssize_t smi130_acc_offset_z_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
unsigned long data;
int err;
err = kstrtoul(buf, 10, &data);
if (err)
return err;
err = SMI_CALL_API(set_accel_offset_compensation_zaxis)
((unsigned char)data);
if (err < 0)
return -EIO;
return count;
}
static ssize_t smi130_test_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct input_dev *input = to_input_dev(dev);
struct smi_client_data *client_data = input_get_drvdata(input);
u8 raw_data[15] = {0};
unsigned int sensor_time = 0;
int err;
memset(raw_data, 0, sizeof(raw_data));
err = client_data->device.bus_read(client_data->device.dev_addr,
SMI130_USER_DATA_8_GYRO_X_LSB__REG, raw_data, 15);
if (err)
return err;
udelay(10);
sensor_time = (u32)(raw_data[14] << 16 | raw_data[13] << 8
| raw_data[12]);
return snprintf(buf, 128, "%d %d %d %d %d %d %u",
(s16)(raw_data[1] << 8 | raw_data[0]),
(s16)(raw_data[3] << 8 | raw_data[2]),
(s16)(raw_data[5] << 8 | raw_data[4]),
(s16)(raw_data[7] << 8 | raw_data[6]),
(s16)(raw_data[9] << 8 | raw_data[8]),
(s16)(raw_data[11] << 8 | raw_data[10]),
sensor_time);
}
static ssize_t smi130_step_counter_enable_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
unsigned char data;
int err;
struct input_dev *input = to_input_dev(dev);
struct smi_client_data *client_data = input_get_drvdata(input);
err = SMI_CALL_API(get_step_counter_enable)(&data);
client_data->stc_enable = data;
if (err < 0)
return err;
return snprintf(buf, 16, "%d\n", data);
}
static ssize_t smi130_step_counter_enable_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
unsigned long data;
int err;
struct input_dev *input = to_input_dev(dev);
struct smi_client_data *client_data = input_get_drvdata(input);
err = kstrtoul(buf, 10, &data);
if (err)
return err;
err = SMI_CALL_API(set_step_counter_enable)((unsigned char)data);
client_data->stc_enable = data;
if (err < 0)
return -EIO;
return count;
}
static ssize_t smi130_step_counter_mode_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
unsigned long data;
int err;
err = kstrtoul(buf, 10, &data);
if (err)
return err;
err = SMI_CALL_API(set_step_mode)((unsigned char)data);
if (err < 0)
return -EIO;
return count;
}
static ssize_t smi130_step_counter_clc_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
unsigned long data;
int err;
err = kstrtoul(buf, 10, &data);
if (err)
return err;
err = smi130_clear_step_counter();
if (err < 0)
return -EIO;
return count;
}
static ssize_t smi130_step_counter_value_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
u16 data;
int err;
static u16 last_stc_value;
struct input_dev *input = to_input_dev(dev);
struct smi_client_data *client_data = input_get_drvdata(input);
err = SMI_CALL_API(read_step_count)(&data);
if (err < 0)
return err;
if (data >= last_stc_value) {
client_data->pedo_data.last_step_counter_value += (
data - last_stc_value);
last_stc_value = data;
} else
last_stc_value = data;
return snprintf(buf, 16, "%d\n",
client_data->pedo_data.last_step_counter_value);
}
static ssize_t smi130_smi_value_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct input_dev *input = to_input_dev(dev);
struct smi_client_data *client_data = input_get_drvdata(input);
u8 raw_data[12] = {0};
int err;
memset(raw_data, 0, sizeof(raw_data));
err = client_data->device.bus_read(client_data->device.dev_addr,
SMI130_USER_DATA_8_GYRO_X_LSB__REG, raw_data, 12);
if (err)
return err;
/*output:gyro x y z acc x y z*/
return snprintf(buf, 96, "%hd %d %hd %hd %hd %hd\n",
(s16)(raw_data[1] << 8 | raw_data[0]),
(s16)(raw_data[3] << 8 | raw_data[2]),
(s16)(raw_data[5] << 8 | raw_data[4]),
(s16)(raw_data[7] << 8 | raw_data[6]),
(s16)(raw_data[9] << 8 | raw_data[8]),
(s16)(raw_data[11] << 8 | raw_data[10]));
}
static ssize_t smi130_selftest_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct input_dev *input = to_input_dev(dev);
struct smi_client_data *client_data = input_get_drvdata(input);
return snprintf(buf, 16, "0x%x\n",
atomic_read(&client_data->selftest_result));
}
static int smi_restore_hw_cfg(struct smi_client_data *client);
/*!
* @brief store selftest result which make up of acc and gyro
* format: 0b 0000 xxxx x:1 failed, 0 success
* bit3: gyro_self
* bit2..0: acc_self z y x
*/
static ssize_t smi130_selftest_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct input_dev *input = to_input_dev(dev);
struct smi_client_data *client_data = input_get_drvdata(input);
int err = 0;
int i = 0;
u8 acc_selftest = 0;
u8 gyro_selftest = 0;
u8 smi_selftest = 0;
s16 axis_p_value, axis_n_value;
u16 diff_axis[3] = {0xff, 0xff, 0xff};
u8 acc_odr, range, acc_selftest_amp, acc_selftest_sign;
dev_notice(client_data->dev, "Selftest for SMI16x starting.\n");
client_data->selftest = 1;
/*soft reset*/
err = SMI_CALL_API(set_command_register)(CMD_RESET_USER_REG);
msleep(70);
err += SMI_CALL_API(set_command_register)
(smi_pmu_cmd_acc_arr[SMI_ACC_PM_NORMAL]);
err += SMI_CALL_API(set_command_register)
(smi_pmu_cmd_gyro_arr[SMI_GYRO_PM_NORMAL]);
err += SMI_CALL_API(set_accel_under_sampling_parameter)(0);
err += SMI_CALL_API(set_accel_output_data_rate)(
SMI130_ACCEL_OUTPUT_DATA_RATE_1600HZ);
/* set to 8G range*/
err += SMI_CALL_API(set_accel_range)(SMI130_ACCEL_RANGE_8G);
/* set to self amp high */
err += SMI_CALL_API(set_accel_selftest_amp)(SMI_SELFTEST_AMP_HIGH);
err += SMI_CALL_API(get_accel_output_data_rate)(&acc_odr);
err += SMI_CALL_API(get_accel_range)(&range);
err += SMI_CALL_API(get_accel_selftest_amp)(&acc_selftest_amp);
err += SMI_CALL_API(read_accel_x)(&axis_n_value);
dev_info(client_data->dev,
"acc_odr:%d, acc_range:%d, acc_selftest_amp:%d, acc_x:%d\n",
acc_odr, range, acc_selftest_amp, axis_n_value);
for (i = X_AXIS; i < AXIS_MAX; i++) {
axis_n_value = 0;
axis_p_value = 0;
/* set every selftest axis */
/*set_acc_selftest_axis(param),param x:1, y:2, z:3
* but X_AXIS:0, Y_AXIS:1, Z_AXIS:2
* so we need to +1*/
err += SMI_CALL_API(set_accel_selftest_axis)(i + 1);
msleep(50);
switch (i) {
case X_AXIS:
/* set negative sign */
err += SMI_CALL_API(set_accel_selftest_sign)(0);
err += SMI_CALL_API(get_accel_selftest_sign)(
&acc_selftest_sign);
msleep(60);
err += SMI_CALL_API(read_accel_x)(&axis_n_value);
dev_info(client_data->dev,
"acc_x_selftest_sign:%d, axis_n_value:%d\n",
acc_selftest_sign, axis_n_value);
/* set postive sign */
err += SMI_CALL_API(set_accel_selftest_sign)(1);
err += SMI_CALL_API(get_accel_selftest_sign)(
&acc_selftest_sign);
msleep(60);
err += SMI_CALL_API(read_accel_x)(&axis_p_value);
dev_info(client_data->dev,
"acc_x_selftest_sign:%d, axis_p_value:%d\n",
acc_selftest_sign, axis_p_value);
diff_axis[i] = abs(axis_p_value - axis_n_value);
break;
case Y_AXIS:
/* set negative sign */
err += SMI_CALL_API(set_accel_selftest_sign)(0);
msleep(60);
err += SMI_CALL_API(read_accel_y)(&axis_n_value);
/* set postive sign */
err += SMI_CALL_API(set_accel_selftest_sign)(1);
msleep(60);
err += SMI_CALL_API(read_accel_y)(&axis_p_value);
diff_axis[i] = abs(axis_p_value - axis_n_value);
break;
case Z_AXIS:
/* set negative sign */
err += SMI_CALL_API(set_accel_selftest_sign)(0);
msleep(60);
err += SMI_CALL_API(read_accel_z)(&axis_n_value);
/* set postive sign */
err += SMI_CALL_API(set_accel_selftest_sign)(1);
msleep(60);
err += SMI_CALL_API(read_accel_z)(&axis_p_value);
/* also start gyro self test */
err += SMI_CALL_API(set_gyro_selftest_start)(1);
msleep(60);
err += SMI_CALL_API(get_gyro_selftest)(&gyro_selftest);
diff_axis[i] = abs(axis_p_value - axis_n_value);
break;
default:
err += -EINVAL;
break;
}
if (err) {
dev_err(client_data->dev,
"Failed selftest axis:%s, p_val=%d, n_val=%d\n",
smi_axis_name[i], axis_p_value, axis_n_value);
client_data->selftest = 0;
return -EINVAL;
}
/*400mg for acc z axis*/
if (Z_AXIS == i) {
if (diff_axis[i] < 1639) {
acc_selftest |= 1 << i;
dev_err(client_data->dev,
"Over selftest minimum for "
"axis:%s,diff=%d,p_val=%d, n_val=%d\n",
smi_axis_name[i], diff_axis[i],
axis_p_value, axis_n_value);
}
} else {
/*800mg for x or y axis*/
if (diff_axis[i] < 3277) {
acc_selftest |= 1 << i;
if (smi_get_err_status(client_data) < 0)
return err;
dev_err(client_data->dev,
"Over selftest minimum for "
"axis:%s,diff=%d, p_val=%d, n_val=%d\n",
smi_axis_name[i], diff_axis[i],
axis_p_value, axis_n_value);
dev_err(client_data->dev, "err_st:0x%x\n",
client_data->err_st.err_st_all);
}
}
}
/* gyro_selftest==1,gyro selftest successfully,
* but smi_result bit4 0 is successful, 1 is failed*/
smi_selftest = (acc_selftest & 0x0f) | ((!gyro_selftest) << AXIS_MAX);
atomic_set(&client_data->selftest_result, smi_selftest);
/*soft reset*/
err = SMI_CALL_API(set_command_register)(CMD_RESET_USER_REG);
if (err) {
client_data->selftest = 0;
return err;
}
msleep(50);
smi_restore_hw_cfg(client_data);
client_data->selftest = 0;
dev_notice(client_data->dev, "Selftest for SMI16x finished\n");
return count;
}
/* gyro sensor part */
static ssize_t smi130_gyro_op_mode_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct input_dev *input = to_input_dev(dev);
struct smi_client_data *client_data = input_get_drvdata(input);
int err = 0;
u8 gyro_pmu_status = 0;
err = SMI_CALL_API(get_gyro_power_mode_stat)(
&gyro_pmu_status);
if (err)
return err;
else
return snprintf(buf, 32, "reg:%d, val:%d\n", gyro_pmu_status,
client_data->pw.gyro_pm);
}
static ssize_t smi130_gyro_op_mode_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct input_dev *input = to_input_dev(dev);
struct smi_client_data *client_data = input_get_drvdata(input);
unsigned long op_mode;
int err;
err = kstrtoul(buf, 10, &op_mode);
if (err)
return err;
mutex_lock(&client_data->mutex_op_mode);
if (op_mode < SMI_GYRO_PM_MAX) {
switch (op_mode) {
case SMI_GYRO_PM_NORMAL:
err = SMI_CALL_API(set_command_register)
(smi_pmu_cmd_gyro_arr[SMI_GYRO_PM_NORMAL]);
client_data->pw.gyro_pm = SMI_GYRO_PM_NORMAL;
smi_delay(60);
break;
case SMI_GYRO_PM_FAST_START:
err = SMI_CALL_API(set_command_register)
(smi_pmu_cmd_gyro_arr[SMI_GYRO_PM_FAST_START]);
client_data->pw.gyro_pm = SMI_GYRO_PM_FAST_START;
smi_delay(60);
break;
case SMI_GYRO_PM_SUSPEND:
err = SMI_CALL_API(set_command_register)
(smi_pmu_cmd_gyro_arr[SMI_GYRO_PM_SUSPEND]);
client_data->pw.gyro_pm = SMI_GYRO_PM_SUSPEND;
smi_delay(60);
break;
default:
mutex_unlock(&client_data->mutex_op_mode);
return -EINVAL;
}
} else {
mutex_unlock(&client_data->mutex_op_mode);
return -EINVAL;
}
mutex_unlock(&client_data->mutex_op_mode);
if (err)
return err;
else
return count;
}
static ssize_t smi130_gyro_value_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct smi130_gyro_t data;
int err;
err = SMI_CALL_API(read_gyro_xyz)(&data);
if (err < 0)
return err;
return snprintf(buf, 48, "%hd %hd %hd\n", data.x,
data.y, data.z);
}
static ssize_t smi130_gyro_range_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
int err;
unsigned char range;
struct input_dev *input = to_input_dev(dev);
struct smi_client_data *client_data = input_get_drvdata(input);
err = SMI_CALL_API(get_gyro_range)(&range);
if (err)
return err;
client_data->range.gyro_range = range;
return snprintf(buf, 16, "%d\n", range);
}
static ssize_t smi130_gyro_range_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
int err;
unsigned long range;
struct input_dev *input = to_input_dev(dev);
struct smi_client_data *client_data = input_get_drvdata(input);
err = kstrtoul(buf, 10, &range);
if (err)
return err;
err = SMI_CALL_API(set_gyro_range)(range);
if (err)
return -EIO;
client_data->range.gyro_range = range;
return count;
}
static ssize_t smi130_gyro_odr_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
int err;
unsigned char gyro_odr;
struct input_dev *input = to_input_dev(dev);
struct smi_client_data *client_data = input_get_drvdata(input);
err = SMI_CALL_API(get_gyro_output_data_rate)(&gyro_odr);
if (err)
return err;
client_data->odr.gyro_odr = gyro_odr;
return snprintf(buf, 16, "%d\n", gyro_odr);
}
static ssize_t smi130_gyro_odr_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
int err;
unsigned long gyro_odr;
struct input_dev *input = to_input_dev(dev);
struct smi_client_data *client_data = input_get_drvdata(input);
err = kstrtoul(buf, 10, &gyro_odr);
if (err)
return err;
if (gyro_odr < 6 || gyro_odr > 13)
return -EIO;
err = SMI_CALL_API(set_gyro_output_data_rate)(gyro_odr);
if (err)
return -EIO;
client_data->odr.gyro_odr = gyro_odr;
return count;
}
static ssize_t smi130_gyro_fast_calibration_en_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
unsigned char data;
int err;
err = SMI_CALL_API(get_foc_gyro_enable)(&data);
if (err < 0)
return err;
return snprintf(buf, 16, "%d\n", data);
}
static ssize_t smi130_gyro_fast_calibration_en_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
unsigned long enable;
s8 err;
s16 gyr_off_x;
s16 gyr_off_y;
s16 gyr_off_z;
struct input_dev *input = to_input_dev(dev);
struct smi_client_data *client_data = input_get_drvdata(input);
err = kstrtoul(buf, 10, &enable);
if (err)
return err;
err = SMI_CALL_API(set_foc_gyro_enable)((u8)enable,
&gyr_off_x, &gyr_off_y, &gyr_off_z);
if (err < 0)
return -EIO;
else {
input_event(client_data->input, EV_MSC,
INPUT_EVENT_FAST_GYRO_CALIB_DONE, 1);
input_sync(client_data->input);
}
return count;
}
static ssize_t smi130_gyro_offset_x_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
s16 data = 0;
s8 err = 0;
err = SMI_CALL_API(get_gyro_offset_compensation_xaxis)(&data);
if (err < 0)
return err;
return snprintf(buf, 16, "%d\n", data);
}
static ssize_t smi130_gyro_offset_x_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
unsigned long data;
s8 err;
err = kstrtoul(buf, 10, &data);
if (err)
return err;
err = SMI_CALL_API(set_gyro_offset_compensation_xaxis)((s16)data);
if (err < 0)
return -EIO;
return count;
}
static ssize_t smi130_gyro_offset_y_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
s16 data = 0;
s8 err = 0;
err = SMI_CALL_API(get_gyro_offset_compensation_yaxis)(&data);
if (err < 0)
return err;
return snprintf(buf, 16, "%d\n", data);
}
static ssize_t smi130_gyro_offset_y_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
unsigned long data;
s8 err;
err = kstrtoul(buf, 10, &data);
if (err)
return err;
err = SMI_CALL_API(set_gyro_offset_compensation_yaxis)((s16)data);
if (err < 0)
return -EIO;
return count;
}
static ssize_t smi130_gyro_offset_z_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
s16 data = 0;
int err = 0;
err = SMI_CALL_API(get_gyro_offset_compensation_zaxis)(&data);
if (err < 0)
return err;
return snprintf(buf, 16, "%d\n", data);
}
static ssize_t smi130_gyro_offset_z_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
unsigned long data;
int err;
err = kstrtoul(buf, 10, &data);
if (err)
return err;
err = SMI_CALL_API(set_gyro_offset_compensation_zaxis)((s16)data);
if (err < 0)
return -EIO;
return count;
}
/* mag sensor part */
#ifdef SMI130_MAG_INTERFACE_SUPPORT
static ssize_t smi130_mag_op_mode_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct input_dev *input = to_input_dev(dev);
struct smi_client_data *client_data = input_get_drvdata(input);
u8 mag_op_mode;
s8 err;
err = smi130_get_mag_power_mode_stat(&mag_op_mode);
if (err) {
dev_err(client_data->dev,
"Failed to get SMI130 mag power mode:%d\n", err);
return err;
} else
return snprintf(buf, 32, "%d, reg:%d\n",
client_data->pw.mag_pm, mag_op_mode);
}
static ssize_t smi130_mag_op_mode_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct input_dev *input = to_input_dev(dev);
struct smi_client_data *client_data = input_get_drvdata(input);
unsigned long op_mode;
int err;
err = kstrtoul(buf, 10, &op_mode);
if (err)
return err;
if (op_mode == client_data->pw.mag_pm)
return count;
mutex_lock(&client_data->mutex_op_mode);
if (op_mode < SMI_MAG_PM_MAX) {
switch (op_mode) {
case SMI_MAG_PM_NORMAL:
/* need to modify as mag sensor connected,
* set write address to 0x4c and triggers
* write operation
* 0x4c(op mode control reg)
* enables normal mode in magnetometer */
#if defined(SMI130_AKM09912_SUPPORT)
err = smi130_set_bosch_akm_and_secondary_if_powermode(
SMI130_MAG_FORCE_MODE);
#else
err = smi130_set_bmm150_mag_and_secondary_if_power_mode(
SMI130_MAG_FORCE_MODE);
#endif
client_data->pw.mag_pm = SMI_MAG_PM_NORMAL;
smi_delay(5);
break;
case SMI_MAG_PM_LP1:
/* need to modify as mag sensor connected,
* set write address to 0x4 band triggers
* write operation
* 0x4b(bmm150, power control reg, bit0)
* enables power in magnetometer*/
#if defined(SMI130_AKM09912_SUPPORT)
err = smi130_set_bosch_akm_and_secondary_if_powermode(
SMI130_MAG_FORCE_MODE);
#else
err = smi130_set_bmm150_mag_and_secondary_if_power_mode(
SMI130_MAG_FORCE_MODE);
#endif
client_data->pw.mag_pm = SMI_MAG_PM_LP1;
smi_delay(5);
break;
case SMI_MAG_PM_SUSPEND:
case SMI_MAG_PM_LP2:
#if defined(SMI130_AKM09912_SUPPORT)
err = smi130_set_bosch_akm_and_secondary_if_powermode(
SMI130_MAG_SUSPEND_MODE);
#else
err = smi130_set_bmm150_mag_and_secondary_if_power_mode(
SMI130_MAG_SUSPEND_MODE);
#endif
client_data->pw.mag_pm = op_mode;
smi_delay(5);
break;
default:
mutex_unlock(&client_data->mutex_op_mode);
return -EINVAL;
}
} else {
mutex_unlock(&client_data->mutex_op_mode);
return -EINVAL;
}
mutex_unlock(&client_data->mutex_op_mode);
if (err) {
dev_err(client_data->dev,
"Failed to switch SMI130 mag power mode:%d\n",
client_data->pw.mag_pm);
return err;
} else
return count;
}
static ssize_t smi130_mag_odr_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
int err = 0;
unsigned char mag_odr = 0;
struct input_dev *input = to_input_dev(dev);
struct smi_client_data *client_data = input_get_drvdata(input);
err = SMI_CALL_API(get_mag_output_data_rate)(&mag_odr);
if (err)
return err;
client_data->odr.mag_odr = mag_odr;
return snprintf(buf, 16, "%d\n", mag_odr);
}
static ssize_t smi130_mag_odr_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
int err;
unsigned long mag_odr;
struct input_dev *input = to_input_dev(dev);
struct smi_client_data *client_data = input_get_drvdata(input);
err = kstrtoul(buf, 10, &mag_odr);
if (err)
return err;
/*1~25/32hz,..6(25hz),7(50hz),... */
err = SMI_CALL_API(set_mag_output_data_rate)(mag_odr);
if (err)
return -EIO;
client_data->odr.mag_odr = mag_odr;
return count;
}
static ssize_t smi130_mag_i2c_address_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
u8 data;
s8 err;
err = SMI_CALL_API(set_mag_manual_enable)(1);
err += SMI_CALL_API(get_i2c_device_addr)(&data);
err += SMI_CALL_API(set_mag_manual_enable)(0);
if (err < 0)
return err;
return snprintf(buf, 16, "0x%x\n", data);
}
static ssize_t smi130_mag_i2c_address_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
unsigned long data;
int err;
err = kstrtoul(buf, 10, &data);
if (err)
return err;
err += SMI_CALL_API(set_mag_manual_enable)(1);
if (!err)
err += SMI_CALL_API(set_i2c_device_addr)((unsigned char)data);
err += SMI_CALL_API(set_mag_manual_enable)(0);
if (err < 0)
return -EIO;
return count;
}
static ssize_t smi130_mag_value_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct input_dev *input = to_input_dev(dev);
struct smi_client_data *client_data = input_get_drvdata(input);
struct smi130_mag_xyz_s32_t data;
int err;
/* raw data with compensation */
#if defined(SMI130_AKM09912_SUPPORT)
err = smi130_bosch_akm09912_compensate_xyz(&data);
#else
err = smi130_bmm150_mag_compensate_xyz(&data);
#endif
if (err < 0) {
memset(&data, 0, sizeof(data));
dev_err(client_data->dev, "mag not ready!\n");
}
return snprintf(buf, 48, "%hd %hd %hd\n", data.x,
data.y, data.z);
}
static ssize_t smi130_mag_offset_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
int err = 0;
unsigned char mag_offset;
err = SMI_CALL_API(get_mag_offset)(&mag_offset);
if (err)
return err;
return snprintf(buf, 16, "%d\n", mag_offset);
}
static ssize_t smi130_mag_offset_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
unsigned long data;
int err;
err = kstrtoul(buf, 10, &data);
if (err)
return err;
err += SMI_CALL_API(set_mag_manual_enable)(1);
if (err == 0)
err += SMI_CALL_API(set_mag_offset)((unsigned char)data);
err += SMI_CALL_API(set_mag_manual_enable)(0);
if (err < 0)
return -EIO;
return count;
}
static ssize_t smi130_mag_chip_id_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
s8 err = 0;
u8 mag_chipid;
err = smi130_set_mag_manual_enable(0x01);
/* read mag chip_id value */
#if defined(SMI130_AKM09912_SUPPORT)
err += smi130_set_mag_read_addr(AKM09912_CHIP_ID_REG);
/* 0x04 is mag_x lsb register */
err += smi130_read_reg(SMI130_USER_DATA_0_MAG_X_LSB__REG,
&mag_chipid, 1);
/* Must add this commands to re-set data register addr of mag sensor */
err += smi130_set_mag_read_addr(AKM_DATA_REGISTER);
#else
err += smi130_set_mag_read_addr(SMI130_BMM150_CHIP_ID);
/* 0x04 is mag_x lsb register */
err += smi130_read_reg(SMI130_USER_DATA_0_MAG_X_LSB__REG,
&mag_chipid, 1);
/* Must add this commands to re-set data register addr of mag sensor */
/* 0x42 is bmm150 data register address */
err += smi130_set_mag_read_addr(SMI130_BMM150_DATA_REG);
#endif
err += smi130_set_mag_manual_enable(0x00);
if (err)
return err;
return snprintf(buf, 16, "%x\n", mag_chipid);
}
static ssize_t smi130_mag_chip_name_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
u8 mag_chipid = 0;
#if defined(SMI130_AKM09912_SUPPORT)
mag_chipid = 15;
#else
mag_chipid = 150;
#endif
return snprintf(buf, 16, "%d\n", mag_chipid);
}
struct smi130_mag_xyz_s32_t mag_compensate;
static ssize_t smi130_mag_compensate_xyz_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
memcpy(buf, &mag_compensate, sizeof(mag_compensate));
return sizeof(mag_compensate);
}
static ssize_t smi130_mag_compensate_xyz_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
struct smi130_mag_xyzr_t mag_raw;
memset(&mag_compensate, 0, sizeof(mag_compensate));
memset(&mag_raw, 0, sizeof(mag_raw));
mag_raw.x = (buf[1] << 8 | buf[0]);
mag_raw.y = (buf[3] << 8 | buf[2]);
mag_raw.z = (buf[5] << 8 | buf[4]);
mag_raw.r = (buf[7] << 8 | buf[6]);
mag_raw.x = mag_raw.x >> 3;
mag_raw.y = mag_raw.y >> 3;
mag_raw.z = mag_raw.z >> 1;
mag_raw.r = mag_raw.r >> 2;
smi130_bmm150_mag_compensate_xyz_raw(
&mag_compensate, mag_raw);
return count;
}
#endif
#if defined(SMI130_ENABLE_INT1) || defined(SMI130_ENABLE_INT2)
static ssize_t smi_enable_int_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
int interrupt_type, value;
sscanf(buf, "%3d %3d", &interrupt_type, &value);
if (interrupt_type < 0 || interrupt_type > 16)
return -EINVAL;
if (interrupt_type <= SMI_FLAT_INT) {
if (SMI_CALL_API(set_intr_enable_0)
(smi_interrupt_type[interrupt_type], value) < 0)
return -EINVAL;
} else if (interrupt_type <= SMI_FWM_INT) {
if (SMI_CALL_API(set_intr_enable_1)
(smi_interrupt_type[interrupt_type], value) < 0)
return -EINVAL;
} else {
if (SMI_CALL_API(set_intr_enable_2)
(smi_interrupt_type[interrupt_type], value) < 0)
return -EINVAL;
}
return count;
}
#endif
static ssize_t smi130_show_reg_sel(struct device *dev
, struct device_attribute *attr, char *buf)
{
struct input_dev *input = to_input_dev(dev);
struct smi_client_data *client_data = input_get_drvdata(input);
if (client_data == NULL) {
printk(KERN_ERR "Invalid client_data pointer");
return -ENODEV;
}
return snprintf(buf, 64, "reg=0X%02X, len=%d\n",
client_data->reg_sel, client_data->reg_len);
}
static ssize_t smi130_store_reg_sel(struct device *dev
, struct device_attribute *attr,
const char *buf, size_t count)
{
struct input_dev *input = to_input_dev(dev);
struct smi_client_data *client_data = input_get_drvdata(input);
ssize_t ret;
if (client_data == NULL) {
printk(KERN_ERR "Invalid client_data pointer");
return -ENODEV;
}
ret = sscanf(buf, "%11X %11d",
&client_data->reg_sel, &client_data->reg_len);
if (ret != 2) {
dev_err(client_data->dev, "Invalid argument");
return -EINVAL;
}
return count;
}
static ssize_t smi130_show_reg_val(struct device *dev
, struct device_attribute *attr, char *buf)
{
struct input_dev *input = to_input_dev(dev);
struct smi_client_data *client_data = input_get_drvdata(input);
ssize_t ret;
u8 reg_data[128] = {0}, i;
int pos;
if (client_data == NULL) {
printk(KERN_ERR "Invalid client_data pointer");
return -ENODEV;
}
ret = smi_burst_read_wrapper(client_data->device.dev_addr,
client_data->reg_sel,
reg_data, client_data->reg_len);
if (ret < 0) {
dev_err(client_data->dev, "Reg op failed");
return ret;
}
pos = 0;
for (i = 0; i < client_data->reg_len; ++i) {
pos += snprintf(buf + pos, 16, "%02X", reg_data[i]);
buf[pos++] = (i + 1) % 16 == 0 ? '\n' : ' ';
}
if (buf[pos - 1] == ' ')
buf[pos - 1] = '\n';
return pos;
}
static ssize_t smi130_store_reg_val(struct device *dev
, struct device_attribute *attr,
const char *buf, size_t count)
{
struct input_dev *input = to_input_dev(dev);
struct smi_client_data *client_data = input_get_drvdata(input);
ssize_t ret;
u8 reg_data[32] = {0};
int i, j, status, digit;
if (client_data == NULL) {
printk(KERN_ERR "Invalid client_data pointer");
return -ENODEV;
}
status = 0;
for (i = j = 0; i < count && j < client_data->reg_len; ++i) {
if (buf[i] == ' ' || buf[i] == '\n' || buf[i] == '\t' ||
buf[i] == '\r') {
status = 0;
++j;
continue;
}
digit = buf[i] & 0x10 ? (buf[i] & 0xF) : ((buf[i] & 0xF) + 9);
printk(KERN_INFO "digit is %d", digit);
switch (status) {
case 2:
++j; /* Fall thru */
case 0:
reg_data[j] = digit;
status = 1;
break;
case 1:
reg_data[j] = reg_data[j] * 16 + digit;
status = 2;
break;
}
}
if (status > 0)
++j;
if (j > client_data->reg_len)
j = client_data->reg_len;
else if (j < client_data->reg_len) {
dev_err(client_data->dev, "Invalid argument");
return -EINVAL;
}
printk(KERN_INFO "Reg data read as");
for (i = 0; i < j; ++i)
printk(KERN_INFO "%d", reg_data[i]);
ret = SMI_CALL_API(write_reg)(
client_data->reg_sel,
reg_data, client_data->reg_len);
if (ret < 0) {
dev_err(client_data->dev, "Reg op failed");
return ret;
}
return count;
}
static ssize_t smi130_driver_version_show(struct device *dev
, struct device_attribute *attr, char *buf)
{
struct input_dev *input = to_input_dev(dev);
struct smi_client_data *client_data = input_get_drvdata(input);
int ret;
if (client_data == NULL) {
printk(KERN_ERR "Invalid client_data pointer");
return -ENODEV;
}
ret = snprintf(buf, 128, "Driver version: %s\n",
DRIVER_VERSION);
return ret;
}
static DEVICE_ATTR(chip_id, S_IRUGO,
smi130_chip_id_show, NULL);
static DEVICE_ATTR(err_st, S_IRUGO,
smi130_err_st_show, NULL);
static DEVICE_ATTR(sensor_time, S_IRUGO,
smi130_sensor_time_show, NULL);
static DEVICE_ATTR(selftest, S_IRUGO | S_IWUSR,
smi130_selftest_show, smi130_selftest_store);
static DEVICE_ATTR(fifo_flush, S_IRUGO | S_IWUSR,
NULL, smi130_fifo_flush_store);
static DEVICE_ATTR(fifo_bytecount, S_IRUGO | S_IWUSR,
smi130_fifo_bytecount_show, smi130_fifo_bytecount_store);
static DEVICE_ATTR(fifo_data_sel, S_IRUGO | S_IWUSR,
smi130_fifo_data_sel_show, smi130_fifo_data_sel_store);
static DEVICE_ATTR(fifo_data_frame, S_IRUGO,
smi130_fifo_data_out_frame_show, NULL);
static DEVICE_ATTR(fifo_watermark, S_IRUGO | S_IWUSR,
smi130_fifo_watermark_show, smi130_fifo_watermark_store);
static DEVICE_ATTR(fifo_header_en, S_IRUGO | S_IWUSR,
smi130_fifo_header_en_show, smi130_fifo_header_en_store);
static DEVICE_ATTR(fifo_time_en, S_IRUGO | S_IWUSR,
smi130_fifo_time_en_show, smi130_fifo_time_en_store);
static DEVICE_ATTR(fifo_int_tag_en, S_IRUGO | S_IWUSR,
smi130_fifo_int_tag_en_show, smi130_fifo_int_tag_en_store);
static DEVICE_ATTR(temperature, S_IRUGO,
smi130_temperature_show, NULL);
static DEVICE_ATTR(place, S_IRUGO,
smi130_place_show, NULL);
static DEVICE_ATTR(delay, S_IRUGO | S_IWUSR,
smi130_delay_show, smi130_delay_store);
static DEVICE_ATTR(enable, S_IRUGO | S_IWUSR,
smi130_enable_show, smi130_enable_store);
static DEVICE_ATTR(acc_range, S_IRUGO | S_IWUSR,
smi130_acc_range_show, smi130_acc_range_store);
static DEVICE_ATTR(acc_odr, S_IRUGO | S_IWUSR,
smi130_acc_odr_show, smi130_acc_odr_store);
static DEVICE_ATTR(acc_op_mode, S_IRUGO | S_IWUSR,
smi130_acc_op_mode_show, smi130_acc_op_mode_store);
static DEVICE_ATTR(acc_value, S_IRUGO,
smi130_acc_value_show, NULL);
static DEVICE_ATTR(acc_fast_calibration_x, S_IRUGO | S_IWUSR,
smi130_acc_fast_calibration_x_show,
smi130_acc_fast_calibration_x_store);
static DEVICE_ATTR(acc_fast_calibration_y, S_IRUGO | S_IWUSR,
smi130_acc_fast_calibration_y_show,
smi130_acc_fast_calibration_y_store);
static DEVICE_ATTR(acc_fast_calibration_z, S_IRUGO | S_IWUSR,
smi130_acc_fast_calibration_z_show,
smi130_acc_fast_calibration_z_store);
static DEVICE_ATTR(acc_offset_x, S_IRUGO | S_IWUSR,
smi130_acc_offset_x_show,
smi130_acc_offset_x_store);
static DEVICE_ATTR(acc_offset_y, S_IRUGO | S_IWUSR,
smi130_acc_offset_y_show,
smi130_acc_offset_y_store);
static DEVICE_ATTR(acc_offset_z, S_IRUGO | S_IWUSR,
smi130_acc_offset_z_show,
smi130_acc_offset_z_store);
static DEVICE_ATTR(test, S_IRUGO,
smi130_test_show, NULL);
static DEVICE_ATTR(stc_enable, S_IRUGO | S_IWUSR,
smi130_step_counter_enable_show,
smi130_step_counter_enable_store);
static DEVICE_ATTR(stc_mode, S_IRUGO | S_IWUSR,
NULL, smi130_step_counter_mode_store);
static DEVICE_ATTR(stc_clc, S_IRUGO | S_IWUSR,
NULL, smi130_step_counter_clc_store);
static DEVICE_ATTR(stc_value, S_IRUGO,
smi130_step_counter_value_show, NULL);
static DEVICE_ATTR(reg_sel, S_IRUGO | S_IWUSR,
smi130_show_reg_sel, smi130_store_reg_sel);
static DEVICE_ATTR(reg_val, S_IRUGO | S_IWUSR,
smi130_show_reg_val, smi130_store_reg_val);
static DEVICE_ATTR(driver_version, S_IRUGO,
smi130_driver_version_show, NULL);
/* gyro part */
static DEVICE_ATTR(gyro_op_mode, S_IRUGO | S_IWUSR,
smi130_gyro_op_mode_show, smi130_gyro_op_mode_store);
static DEVICE_ATTR(gyro_value, S_IRUGO,
smi130_gyro_value_show, NULL);
static DEVICE_ATTR(gyro_range, S_IRUGO | S_IWUSR,
smi130_gyro_range_show, smi130_gyro_range_store);
static DEVICE_ATTR(gyro_odr, S_IRUGO | S_IWUSR,
smi130_gyro_odr_show, smi130_gyro_odr_store);
static DEVICE_ATTR(gyro_fast_calibration_en, S_IRUGO | S_IWUSR,
smi130_gyro_fast_calibration_en_show, smi130_gyro_fast_calibration_en_store);
static DEVICE_ATTR(gyro_offset_x, S_IRUGO | S_IWUSR,
smi130_gyro_offset_x_show, smi130_gyro_offset_x_store);
static DEVICE_ATTR(gyro_offset_y, S_IRUGO | S_IWUSR,
smi130_gyro_offset_y_show, smi130_gyro_offset_y_store);
static DEVICE_ATTR(gyro_offset_z, S_IRUGO | S_IWUSR,
smi130_gyro_offset_z_show, smi130_gyro_offset_z_store);
#ifdef SMI130_MAG_INTERFACE_SUPPORT
static DEVICE_ATTR(mag_op_mode, S_IRUGO | S_IWUSR,
smi130_mag_op_mode_show, smi130_mag_op_mode_store);
static DEVICE_ATTR(mag_odr, S_IRUGO | S_IWUSR,
smi130_mag_odr_show, smi130_mag_odr_store);
static DEVICE_ATTR(mag_i2c_addr, S_IRUGO | S_IWUSR,
smi130_mag_i2c_address_show, smi130_mag_i2c_address_store);
static DEVICE_ATTR(mag_value, S_IRUGO,
smi130_mag_value_show, NULL);
static DEVICE_ATTR(mag_offset, S_IRUGO | S_IWUSR,
smi130_mag_offset_show, smi130_mag_offset_store);
static DEVICE_ATTR(mag_chip_id, S_IRUGO,
smi130_mag_chip_id_show, NULL);
static DEVICE_ATTR(mag_chip_name, S_IRUGO,
smi130_mag_chip_name_show, NULL);
static DEVICE_ATTR(mag_compensate, S_IRUGO | S_IWUSR,
smi130_mag_compensate_xyz_show,
smi130_mag_compensate_xyz_store);
#endif
#if defined(SMI130_ENABLE_INT1) || defined(SMI130_ENABLE_INT2)
static DEVICE_ATTR(enable_int, S_IRUGO | S_IWUSR,
NULL, smi_enable_int_store);
static DEVICE_ATTR(anymot_duration, S_IRUGO | S_IWUSR,
smi130_anymot_duration_show, smi130_anymot_duration_store);
static DEVICE_ATTR(anymot_threshold, S_IRUGO | S_IWUSR,
smi130_anymot_threshold_show, smi130_anymot_threshold_store);
static DEVICE_ATTR(std_stu, S_IRUGO,
smi130_step_detector_status_show, NULL);
static DEVICE_ATTR(std_en, S_IRUGO | S_IWUSR,
smi130_step_detector_enable_show,
smi130_step_detector_enable_store);
static DEVICE_ATTR(sig_en, S_IRUGO | S_IWUSR,
smi130_signification_motion_enable_show,
smi130_signification_motion_enable_store);
#endif
static DEVICE_ATTR(smi_value, S_IRUGO,
smi130_smi_value_show, NULL);
static struct attribute *smi130_attributes[] = {
&dev_attr_chip_id.attr,
&dev_attr_err_st.attr,
&dev_attr_sensor_time.attr,
&dev_attr_selftest.attr,
&dev_attr_driver_version.attr,
&dev_attr_test.attr,
&dev_attr_fifo_flush.attr,
&dev_attr_fifo_header_en.attr,
&dev_attr_fifo_time_en.attr,
&dev_attr_fifo_int_tag_en.attr,
&dev_attr_fifo_bytecount.attr,
&dev_attr_fifo_data_sel.attr,
&dev_attr_fifo_data_frame.attr,
&dev_attr_fifo_watermark.attr,
&dev_attr_enable.attr,
&dev_attr_delay.attr,
&dev_attr_temperature.attr,
&dev_attr_place.attr,
&dev_attr_acc_range.attr,
&dev_attr_acc_odr.attr,
&dev_attr_acc_op_mode.attr,
&dev_attr_acc_value.attr,
&dev_attr_acc_fast_calibration_x.attr,
&dev_attr_acc_fast_calibration_y.attr,
&dev_attr_acc_fast_calibration_z.attr,
&dev_attr_acc_offset_x.attr,
&dev_attr_acc_offset_y.attr,
&dev_attr_acc_offset_z.attr,
&dev_attr_stc_enable.attr,
&dev_attr_stc_mode.attr,
&dev_attr_stc_clc.attr,
&dev_attr_stc_value.attr,
&dev_attr_gyro_op_mode.attr,
&dev_attr_gyro_value.attr,
&dev_attr_gyro_range.attr,
&dev_attr_gyro_odr.attr,
&dev_attr_gyro_fast_calibration_en.attr,
&dev_attr_gyro_offset_x.attr,
&dev_attr_gyro_offset_y.attr,
&dev_attr_gyro_offset_z.attr,
#ifdef SMI130_MAG_INTERFACE_SUPPORT
&dev_attr_mag_chip_id.attr,
&dev_attr_mag_op_mode.attr,
&dev_attr_mag_odr.attr,
&dev_attr_mag_i2c_addr.attr,
&dev_attr_mag_chip_name.attr,
&dev_attr_mag_value.attr,
&dev_attr_mag_offset.attr,
&dev_attr_mag_compensate.attr,
#endif
#if defined(SMI130_ENABLE_INT1) || defined(SMI130_ENABLE_INT2)
&dev_attr_enable_int.attr,
&dev_attr_anymot_duration.attr,
&dev_attr_anymot_threshold.attr,
&dev_attr_std_stu.attr,
&dev_attr_std_en.attr,
&dev_attr_sig_en.attr,
#endif
&dev_attr_reg_sel.attr,
&dev_attr_reg_val.attr,
&dev_attr_smi_value.attr,
NULL
};
static struct attribute_group smi130_attribute_group = {
.attrs = smi130_attributes
};
#if defined(SMI130_ENABLE_INT1) || defined(SMI130_ENABLE_INT2)
static void smi_slope_interrupt_handle(struct smi_client_data *client_data)
{
/* anym_first[0..2]: x, y, z */
u8 anym_first[3] = {0};
u8 status2;
u8 anym_sign;
u8 i = 0;
client_data->device.bus_read(client_data->device.dev_addr,
SMI130_USER_INTR_STAT_2_ADDR, &status2, 1);
anym_first[0] = SMI130_GET_BITSLICE(status2,
SMI130_USER_INTR_STAT_2_ANY_MOTION_FIRST_X);
anym_first[1] = SMI130_GET_BITSLICE(status2,
SMI130_USER_INTR_STAT_2_ANY_MOTION_FIRST_Y);
anym_first[2] = SMI130_GET_BITSLICE(status2,
SMI130_USER_INTR_STAT_2_ANY_MOTION_FIRST_Z);
anym_sign = SMI130_GET_BITSLICE(status2,
SMI130_USER_INTR_STAT_2_ANY_MOTION_SIGN);
for (i = 0; i < 3; i++) {
if (anym_first[i]) {
/*1: negative*/
if (anym_sign)
dev_notice(client_data->dev,
"Anymotion interrupt happend!"
"%s axis, negative sign\n", smi_axis_name[i]);
else
dev_notice(client_data->dev,
"Anymotion interrupt happend!"
"%s axis, postive sign\n", smi_axis_name[i]);
}
}
}
static void smi_fifo_watermark_interrupt_handle
(struct smi_client_data *client_data)
{
int err = 0;
unsigned int fifo_len0 = 0;
unsigned int fifo_frmbytes_ext = 0;
unsigned char *fifo_data = NULL;
fifo_data = kzalloc(FIFO_DATA_BUFSIZE, GFP_KERNEL);
/*TO DO*/
if (NULL == fifo_data) {
dev_err(client_data->dev, "no memory available");
err = -ENOMEM;
}
smi_fifo_frame_bytes_extend_calc(client_data, &fifo_frmbytes_ext);
if (client_data->pw.acc_pm == 2 && client_data->pw.gyro_pm == 2
&& client_data->pw.mag_pm == 2)
printk(KERN_INFO "pw_acc: %d, pw_gyro: %d\n",
client_data->pw.acc_pm, client_data->pw.gyro_pm);
if (!client_data->fifo_data_sel)
printk(KERN_INFO "no selsect sensor fifo, fifo_data_sel:%d\n",
client_data->fifo_data_sel);
err = SMI_CALL_API(fifo_length)(&fifo_len0);
client_data->fifo_bytecount = fifo_len0;
if (client_data->fifo_bytecount == 0 || err)
return;
if (client_data->fifo_bytecount + fifo_frmbytes_ext > FIFO_DATA_BUFSIZE)
client_data->fifo_bytecount = FIFO_DATA_BUFSIZE;
/* need give attention for the time of burst read*/
if (!err) {
err = smi_burst_read_wrapper(client_data->device.dev_addr,
SMI130_USER_FIFO_DATA__REG, fifo_data,
client_data->fifo_bytecount + fifo_frmbytes_ext);
} else
dev_err(client_data->dev, "read fifo leght err");
if (err)
dev_err(client_data->dev, "brust read fifo err\n");
/*err = smi_fifo_analysis_handle(client_data, fifo_data,
client_data->fifo_bytecount + 20, fifo_out_data);*/
if (fifo_data != NULL) {
kfree(fifo_data);
fifo_data = NULL;
}
}
static void smi_data_ready_interrupt_handle(
struct smi_client_data *client_data, uint8_t status)
{
uint8_t data12[12] = {0};
struct smi130_accel_t accel;
struct smi130_gyro_t gyro;
struct timespec ts;
client_data->device.bus_read(client_data->device.dev_addr,
SMI130_USER_DATA_8_ADDR, data12, 12);
if (status & 0x80)
{
/*report acc data*/
/* Data X */
accel.x = (s16)((((s32)((s8)data12[7])) << SMI130_SHIFT_BIT_POSITION_BY_08_BITS) | (data12[6]));
/* Data Y */
accel.y = (s16)((((s32)((s8)data12[9])) << SMI130_SHIFT_BIT_POSITION_BY_08_BITS) | (data12[8]));
/* Data Z */
accel.z = (s16)((((s32)((s8)data12[11]))<< SMI130_SHIFT_BIT_POSITION_BY_08_BITS) | (data12[10]));
ts = ns_to_timespec(client_data->timestamp);
input_event(client_data->input, EV_MSC, 6, ts.tv_sec);
input_event(client_data->input, EV_MSC, 6, ts.tv_nsec);
input_event(client_data->input, EV_MSC, MSC_GESTURE, accel.x);
input_event(client_data->input, EV_MSC, MSC_RAW, accel.y);
input_event(client_data->input, EV_MSC, MSC_SCAN, accel.z);
input_sync(client_data->input);
}
if (status & 0x40)
{
/*report gyro data*/
/* Data X */
gyro.x = (s16)((((s32)((s8)data12[1])) << SMI130_SHIFT_BIT_POSITION_BY_08_BITS) | (data12[0]));
/* Data Y */
gyro.y = (s16)((((s32)((s8)data12[3])) << SMI130_SHIFT_BIT_POSITION_BY_08_BITS) | (data12[2]));
/* Data Z */
gyro.z = (s16)((((s32)((s8)data12[5]))<< SMI130_SHIFT_BIT_POSITION_BY_08_BITS) | (data12[4]));
ts = ns_to_timespec(client_data->timestamp);
input_event(client_data->gyro_input, EV_MSC, 6, ts.tv_sec);
input_event(client_data->gyro_input, EV_MSC, 6, ts.tv_nsec);
input_event(client_data->gyro_input, EV_MSC, MSC_GESTURE, gyro.x);
input_event(client_data->gyro_input, EV_MSC, MSC_RAW, gyro.y);
input_event(client_data->gyro_input, EV_MSC, MSC_SCAN, gyro.z);
input_sync(client_data->gyro_input);
}
}
static void smi_signification_motion_interrupt_handle(
struct smi_client_data *client_data)
{
printk(KERN_INFO "smi_signification_motion_interrupt_handle\n");
input_event(client_data->input, EV_MSC, INPUT_EVENT_SGM, 1);
/*input_report_rel(client_data->input,INPUT_EVENT_SGM,1);*/
input_sync(client_data->input);
smi130_set_command_register(CMD_RESET_INT_ENGINE);
}
static void smi_stepdetector_interrupt_handle(
struct smi_client_data *client_data)
{
u8 current_step_dector_st = 0;
client_data->pedo_data.wkar_step_detector_status++;
current_step_dector_st =
client_data->pedo_data.wkar_step_detector_status;
client_data->std = ((current_step_dector_st == 1) ? 0 : 1);
input_event(client_data->input, EV_MSC, INPUT_EVENT_STEP_DETECTOR, 1);
input_sync(client_data->input);
}
static void smi_irq_work_func(struct work_struct *work)
{
struct smi_client_data *client_data =
container_of((struct work_struct *)work,
struct smi_client_data, irq_work);
unsigned char int_status[4] = {0, 0, 0, 0};
uint8_t status = 0;
//client_data->device.bus_read(client_data->device.dev_addr,
// SMI130_USER_INTR_STAT_0_ADDR, int_status, 4);
client_data->device.bus_read(client_data->device.dev_addr,
SMI130_USER_STAT_ADDR, &status, 1);
printk("status = 0x%x", status);
if (SMI130_GET_BITSLICE(int_status[0],
SMI130_USER_INTR_STAT_0_ANY_MOTION))
smi_slope_interrupt_handle(client_data);
if (SMI130_GET_BITSLICE(int_status[0],
SMI130_USER_INTR_STAT_0_STEP_INTR))
smi_stepdetector_interrupt_handle(client_data);
if (SMI130_GET_BITSLICE(int_status[1],
SMI130_USER_INTR_STAT_1_FIFO_WM_INTR))
smi_fifo_watermark_interrupt_handle(client_data);
if ((status & 0x80) || (status & 0x40))
smi_data_ready_interrupt_handle(client_data, status);
/* Clear ALL inputerrupt status after handler sig mition*/
/* Put this commads intot the last one*/
if (SMI130_GET_BITSLICE(int_status[0],
SMI130_USER_INTR_STAT_0_SIGNIFICANT_INTR))
smi_signification_motion_interrupt_handle(client_data);
}
static void smi130_delay_sigmo_work_func(struct work_struct *work)
{
struct smi_client_data *client_data =
container_of(work, struct smi_client_data,
delay_work_sig.work);
unsigned char int_status[4] = {0, 0, 0, 0};
client_data->device.bus_read(client_data->device.dev_addr,
SMI130_USER_INTR_STAT_0_ADDR, int_status, 4);
if (SMI130_GET_BITSLICE(int_status[0],
SMI130_USER_INTR_STAT_0_SIGNIFICANT_INTR))
smi_signification_motion_interrupt_handle(client_data);
}
static irqreturn_t smi_irq_handler(int irq, void *handle)
{
struct smi_client_data *client_data = handle;
int in_suspend_copy;
in_suspend_copy = atomic_read(&client_data->in_suspend);
if (client_data == NULL)
return IRQ_HANDLED;
if (client_data->dev == NULL)
return IRQ_HANDLED;
/*this only deal with SIG_motion CTS test*/
if ((in_suspend_copy == 1) &&
(client_data->sig_flag == 1)) {
/*wake_lock_timeout(&client_data->wakelock, HZ);*/
schedule_delayed_work(&client_data->delay_work_sig,
msecs_to_jiffies(50));
}
schedule_work(&client_data->irq_work);
return IRQ_HANDLED;
}
#endif /* defined(SMI_ENABLE_INT1)||defined(SMI_ENABLE_INT2) */
static int smi_restore_hw_cfg(struct smi_client_data *client)
{
int err = 0;
if ((client->fifo_data_sel) & (1 << SMI_ACC_SENSOR)) {
err += SMI_CALL_API(set_accel_range)(client->range.acc_range);
err += SMI_CALL_API(set_accel_output_data_rate)
(client->odr.acc_odr);
err += SMI_CALL_API(set_fifo_accel_enable)(1);
}
if ((client->fifo_data_sel) & (1 << SMI_GYRO_SENSOR)) {
err += SMI_CALL_API(set_gyro_range)(client->range.gyro_range);
err += SMI_CALL_API(set_gyro_output_data_rate)
(client->odr.gyro_odr);
err += SMI_CALL_API(set_fifo_gyro_enable)(1);
}
if ((client->fifo_data_sel) & (1 << SMI_MAG_SENSOR)) {
err += SMI_CALL_API(set_mag_output_data_rate)
(client->odr.mag_odr);
err += SMI_CALL_API(set_fifo_mag_enable)(1);
}
err += SMI_CALL_API(set_command_register)(CMD_CLR_FIFO_DATA);
mutex_lock(&client->mutex_op_mode);
if (client->pw.acc_pm != SMI_ACC_PM_SUSPEND) {
err += SMI_CALL_API(set_command_register)
(smi_pmu_cmd_acc_arr[SMI_ACC_PM_NORMAL]);
smi_delay(3);
}
mutex_unlock(&client->mutex_op_mode);
mutex_lock(&client->mutex_op_mode);
if (client->pw.gyro_pm != SMI_GYRO_PM_SUSPEND) {
err += SMI_CALL_API(set_command_register)
(smi_pmu_cmd_gyro_arr[SMI_GYRO_PM_NORMAL]);
smi_delay(3);
}
mutex_unlock(&client->mutex_op_mode);
mutex_lock(&client->mutex_op_mode);
if (client->pw.mag_pm != SMI_MAG_PM_SUSPEND) {
#ifdef SMI130_AKM09912_SUPPORT
err += smi130_set_bosch_akm_and_secondary_if_powermode
(SMI130_MAG_FORCE_MODE);
#else
err += smi130_set_bmm150_mag_and_secondary_if_power_mode
(SMI130_MAG_FORCE_MODE);
#endif
smi_delay(3);
}
mutex_unlock(&client->mutex_op_mode);
return err;
}
#if defined(CONFIG_USE_QUALCOMM_HAL)
static void smi130_accel_work_fn(struct work_struct *work)
{
struct smi_client_data *sensor;
ktime_t timestamp;
struct smi130_accel_t data;
int err;
sensor = container_of((struct delayed_work *)work,
struct smi_client_data, accel_poll_work);
timestamp = ktime_get();
err = SMI_CALL_API(read_accel_xyz)(&data);
if (err)
dev_err(sensor->dev, "read data err");
input_report_abs(sensor->input, ABS_X,
(data.x));
input_report_abs(sensor->input, ABS_Y,
(data.y));
input_report_abs(sensor->input, ABS_Z,
(data.z));
input_event(sensor->input,
EV_SYN, SYN_TIME_SEC,
ktime_to_timespec(timestamp).tv_sec);
input_event(sensor->input, EV_SYN,
SYN_TIME_NSEC,
ktime_to_timespec(timestamp).tv_nsec);
input_sync(sensor->input);
if (atomic_read(&sensor->accel_en))
queue_delayed_work(sensor->data_wq,
&sensor->accel_poll_work,
msecs_to_jiffies(sensor->accel_poll_ms));
}
static void smi130_gyro_work_fn(struct work_struct *work)
{
struct smi_client_data *sensor;
ktime_t timestamp;
struct smi130_gyro_t data;
int err;
sensor = container_of((struct delayed_work *)work,
struct smi_client_data, gyro_poll_work);
timestamp = ktime_get();
err = SMI_CALL_API(read_gyro_xyz)(&data);
if (err)
dev_err(sensor->dev, "read data err");
input_report_abs(sensor->gyro_input, ABS_RX,
(data.x));
input_report_abs(sensor->gyro_input, ABS_RY,
(data.y));
input_report_abs(sensor->gyro_input, ABS_RZ,
(data.z));
input_event(sensor->gyro_input,
EV_SYN, SYN_TIME_SEC,
ktime_to_timespec(timestamp).tv_sec);
input_event(sensor->gyro_input, EV_SYN,
SYN_TIME_NSEC,
ktime_to_timespec(timestamp).tv_nsec);
input_sync(sensor->gyro_input);
if (atomic_read(&sensor->gyro_en))
queue_delayed_work(sensor->data_wq,
&sensor->gyro_poll_work,
msecs_to_jiffies(sensor->gyro_poll_ms));
}
static int smi130_set_gyro_op_mode(struct smi_client_data *client_data,
unsigned long op_mode)
{
int err = 0;
mutex_lock(&client_data->mutex_op_mode);
if (op_mode < SMI_GYRO_PM_MAX) {
switch (op_mode) {
case SMI_GYRO_PM_NORMAL:
err = SMI_CALL_API(set_command_register)
(smi_pmu_cmd_gyro_arr[SMI_GYRO_PM_NORMAL]);
client_data->pw.gyro_pm = SMI_GYRO_PM_NORMAL;
smi_delay(60);
break;
case SMI_GYRO_PM_FAST_START:
err = SMI_CALL_API(set_command_register)
(smi_pmu_cmd_gyro_arr[SMI_GYRO_PM_FAST_START]);
client_data->pw.gyro_pm = SMI_GYRO_PM_FAST_START;
smi_delay(60);
break;
case SMI_GYRO_PM_SUSPEND:
err = SMI_CALL_API(set_command_register)
(smi_pmu_cmd_gyro_arr[SMI_GYRO_PM_SUSPEND]);
client_data->pw.gyro_pm = SMI_GYRO_PM_SUSPEND;
smi_delay(60);
break;
default:
mutex_unlock(&client_data->mutex_op_mode);
return -EINVAL;
}
} else {
mutex_unlock(&client_data->mutex_op_mode);
return -EINVAL;
}
mutex_unlock(&client_data->mutex_op_mode);
return err;
}
static int smi130_accel_set_enable(
struct smi_client_data *client_data, bool enable)
{
int ret = 0;
dev_notice(client_data->dev,
"smi130_accel_set_enable enable=%d\n", enable);
if (enable) {
ret = smi130_set_acc_op_mode(client_data, 0);
if (ret) {
dev_err(client_data->dev,
"Fail to enable accel engine ret=%d\n", ret);
ret = -EBUSY;
goto exit;
}
queue_delayed_work(client_data->data_wq,
&client_data->accel_poll_work,
msecs_to_jiffies(client_data->accel_poll_ms));
atomic_set(&client_data->accel_en, 1);
} else {
atomic_set(&client_data->accel_en, 0);
cancel_delayed_work_sync(&client_data->accel_poll_work);
ret = smi130_set_acc_op_mode(client_data, 2);
if (ret) {
dev_err(client_data->dev,
"Fail to disable accel engine ret=%d\n", ret);
ret = -EBUSY;
goto exit;
}
}
exit:
return ret;
}
static int smi130_accel_set_poll_delay(struct smi_client_data *client_data,
unsigned long delay)
{
dev_info(client_data->dev,
"smi130_accel_set_poll_delay delay_ms=%ld\n", delay);
if (delay < SMI130_ACCEL_MIN_POLL_INTERVAL_MS)
delay = SMI130_ACCEL_MIN_POLL_INTERVAL_MS;
if (delay > SMI130_ACCEL_MAX_POLL_INTERVAL_MS)
delay = SMI130_ACCEL_MAX_POLL_INTERVAL_MS;
client_data->accel_poll_ms = delay;
if (!atomic_read(&client_data->accel_en))
goto exit;
cancel_delayed_work_sync(&client_data->accel_poll_work);
queue_delayed_work(client_data->data_wq,
&client_data->accel_poll_work,
msecs_to_jiffies(client_data->accel_poll_ms));
exit:
return 0;
}
static int smi130_gyro_set_enable(
struct smi_client_data *client_data, bool enable)
{
int ret = 0;
dev_notice(client_data->dev,
"smi130_gyro_set_enable enable=%d\n", enable);
if (enable) {
ret = smi130_set_gyro_op_mode(client_data, 0);
if (ret) {
dev_err(client_data->dev,
"Fail to enable gyro engine ret=%d\n", ret);
ret = -EBUSY;
goto exit;
}
queue_delayed_work(client_data->data_wq,
&client_data->gyro_poll_work,
msecs_to_jiffies(client_data->gyro_poll_ms));
atomic_set(&client_data->gyro_en, 1);
} else {
atomic_set(&client_data->gyro_en, 0);
cancel_delayed_work_sync(&client_data->gyro_poll_work);
ret = smi130_set_gyro_op_mode(client_data, 2);
if (ret) {
dev_err(client_data->dev,
"Fail to disable accel engine ret=%d\n", ret);
ret = -EBUSY;
goto exit;
}
}
exit:
return ret;
}
static int smi130_gyro_set_poll_delay(struct smi_client_data *client_data,
unsigned long delay)
{
dev_info(client_data->dev,
"smi130_accel_set_poll_delay delay_ms=%ld\n", delay);
if (delay < SMI130_GYRO_MIN_POLL_INTERVAL_MS)
delay = SMI130_GYRO_MIN_POLL_INTERVAL_MS;
if (delay > SMI130_GYRO_MAX_POLL_INTERVAL_MS)
delay = SMI130_GYRO_MAX_POLL_INTERVAL_MS;
client_data->gyro_poll_ms = delay;
if (!atomic_read(&client_data->gyro_en))
goto exit;
cancel_delayed_work_sync(&client_data->gyro_poll_work);
queue_delayed_work(client_data->data_wq,
&client_data->gyro_poll_work,
msecs_to_jiffies(client_data->gyro_poll_ms));
exit:
return 0;
}
static int smi130_accel_cdev_enable(struct sensors_classdev *sensors_cdev,
unsigned int enable)
{
struct smi_client_data *sensor = container_of(sensors_cdev,
struct smi_client_data, accel_cdev);
return smi130_accel_set_enable(sensor, enable);
}
static int smi130_accel_cdev_poll_delay(struct sensors_classdev *sensors_cdev,
unsigned int delay_ms)
{
struct smi_client_data *sensor = container_of(sensors_cdev,
struct smi_client_data, accel_cdev);
return smi130_accel_set_poll_delay(sensor, delay_ms);
}
static int smi130_gyro_cdev_enable(struct sensors_classdev *sensors_cdev,
unsigned int enable)
{
struct smi_client_data *sensor = container_of(sensors_cdev,
struct smi_client_data, gyro_cdev);
return smi130_gyro_set_enable(sensor, enable);
}
static int smi130_gyro_cdev_poll_delay(struct sensors_classdev *sensors_cdev,
unsigned int delay_ms)
{
struct smi_client_data *sensor = container_of(sensors_cdev,
struct smi_client_data, gyro_cdev);
return smi130_gyro_set_poll_delay(sensor, delay_ms);
}
#endif
int smi_probe(struct smi_client_data *client_data, struct device *dev)
{
int err = 0;
#ifdef SMI130_MAG_INTERFACE_SUPPORT
u8 mag_dev_addr;
u8 mag_urst_len;
u8 mag_op_mode;
#endif
/* check chip id */
err = smi_check_chip_id(client_data);
if (err)
goto exit_err_clean;
dev_set_drvdata(dev, client_data);
client_data->dev = dev;
mutex_init(&client_data->mutex_enable);
mutex_init(&client_data->mutex_op_mode);
/* input device init */
err = smi_input_init(client_data);
if (err < 0)
goto exit_err_clean;
/* sysfs node creation */
err = sysfs_create_group(&client_data->input->dev.kobj,
&smi130_attribute_group);
if (err < 0)
goto exit_err_sysfs;
if (NULL != dev->platform_data) {
client_data->bosch_pd = kzalloc(sizeof(*client_data->bosch_pd),
GFP_KERNEL);
if (NULL != client_data->bosch_pd) {
memcpy(client_data->bosch_pd, dev->platform_data,
sizeof(*client_data->bosch_pd));
dev_notice(dev, "%s sensor driver set place: p%d\n",
client_data->bosch_pd->name,
client_data->bosch_pd->place);
}
}
if (NULL != client_data->bosch_pd) {
memcpy(client_data->bosch_pd, dev->platform_data,
sizeof(*client_data->bosch_pd));
dev_notice(dev, "%s sensor driver set place: p%d\n",
client_data->bosch_pd->name,
client_data->bosch_pd->place);
}
/* workqueue init */
INIT_DELAYED_WORK(&client_data->work, smi_work_func);
atomic_set(&client_data->delay, SMI_DELAY_DEFAULT);
atomic_set(&client_data->wkqueue_en, 0);
/* h/w init */
client_data->device.delay_msec = smi_delay;
err = SMI_CALL_API(init)(&client_data->device);
smi_dump_reg(client_data);
/*power on detected*/
/*or softrest(cmd 0xB6) */
/*fatal err check*/
/*soft reset*/
err += SMI_CALL_API(set_command_register)(CMD_RESET_USER_REG);
smi_delay(3);
if (err)
dev_err(dev, "Failed soft reset, er=%d", err);
/*usr data config page*/
err += SMI_CALL_API(set_target_page)(USER_DAT_CFG_PAGE);
if (err)
dev_err(dev, "Failed cffg page, er=%d", err);
err += smi_get_err_status(client_data);
if (err) {
dev_err(dev, "Failed to smi16x init!err_st=0x%x\n",
client_data->err_st.err_st_all);
goto exit_err_sysfs;
}
#ifdef SMI130_MAG_INTERFACE_SUPPORT
err += smi130_set_command_register(MAG_MODE_NORMAL);
smi_delay(2);
err += smi130_get_mag_power_mode_stat(&mag_op_mode);
smi_delay(2);
err += SMI_CALL_API(get_i2c_device_addr)(&mag_dev_addr);
smi_delay(2);
#if defined(SMI130_AKM09912_SUPPORT)
err += SMI_CALL_API(set_i2c_device_addr)(SMI130_AKM09912_I2C_ADDRESS);
smi130_bosch_akm_mag_interface_init(SMI130_AKM09912_I2C_ADDRESS);
#else
err += SMI_CALL_API(set_i2c_device_addr)(
SMI130_AUX_BMM150_I2C_ADDRESS);
smi130_bmm150_mag_interface_init();
#endif
err += smi130_set_mag_burst(3);
err += smi130_get_mag_burst(&mag_urst_len);
if (err)
dev_err(client_data->dev, "Failed cffg mag, er=%d", err);
dev_info(client_data->dev,
"SMI130 mag_urst_len:%d, mag_add:0x%x, mag_op_mode:%d\n",
mag_urst_len, mag_dev_addr, mag_op_mode);
#endif
if (err < 0)
goto exit_err_sysfs;
#if defined(SMI130_ENABLE_INT1) || defined(SMI130_ENABLE_INT2)
/*wake_lock_init(&client_data->wakelock,
WAKE_LOCK_SUSPEND, "smi130");*/
client_data->gpio_pin = of_get_named_gpio_flags(dev->of_node,
"smi,gpio_irq", 0, NULL);
dev_info(client_data->dev, "SMI130 qpio number:%d\n",
client_data->gpio_pin);
err += gpio_request_one(client_data->gpio_pin,
GPIOF_IN, "smi130_int");
err += gpio_direction_input(client_data->gpio_pin);
client_data->IRQ = gpio_to_irq(client_data->gpio_pin);
if (err) {
dev_err(client_data->dev,
"can not request gpio to irq number\n");
client_data->gpio_pin = 0;
}
INIT_DELAYED_WORK(&client_data->delay_work_sig,
smi130_delay_sigmo_work_func);
#ifdef SMI130_ENABLE_INT1
/* maps interrupt to INT1/InT2 pin */
SMI_CALL_API(set_intr_any_motion)(SMI_INT0, ENABLE);
SMI_CALL_API(set_intr_fifo_wm)(SMI_INT0, ENABLE);
SMI_CALL_API(set_intr_data_rdy)(SMI_INT0, ENABLE);
/*Set interrupt trige level way */
SMI_CALL_API(set_intr_edge_ctrl)(SMI_INT0, SMI_INT_LEVEL);
smi130_set_intr_level(SMI_INT0, 1);
/*set interrupt latch temporary, 5 ms*/
/*smi130_set_latch_int(5);*/
SMI_CALL_API(set_output_enable)(
SMI130_INTR1_OUTPUT_ENABLE, ENABLE);
sigmotion_init_interrupts(SMI130_MAP_INTR1);
SMI_CALL_API(map_step_detector_intr)(SMI130_MAP_INTR1);
/*close step_detector in init function*/
SMI_CALL_API(set_step_detector_enable)(0);
#endif
#ifdef SMI130_ENABLE_INT2
/* maps interrupt to INT1/InT2 pin */
SMI_CALL_API(set_intr_any_motion)(SMI_INT1, ENABLE);
SMI_CALL_API(set_intr_fifo_wm)(SMI_INT1, ENABLE);
SMI_CALL_API(set_intr_data_rdy)(SMI_INT1, ENABLE);
/*Set interrupt trige level way */
SMI_CALL_API(set_intr_edge_ctrl)(SMI_INT1, SMI_INT_LEVEL);
smi130_set_intr_level(SMI_INT1, 1);
/*set interrupt latch temporary, 5 ms*/
/*smi130_set_latch_int(5);*/
SMI_CALL_API(set_output_enable)(
SMI130_INTR2_OUTPUT_ENABLE, ENABLE);
sigmotion_init_interrupts(SMI130_MAP_INTR2);
SMI_CALL_API(map_step_detector_intr)(SMI130_MAP_INTR2);
/*close step_detector in init function*/
SMI_CALL_API(set_step_detector_enable)(0);
#endif
err = request_irq(client_data->IRQ, smi_irq_handler,
IRQF_TRIGGER_RISING, "smi130", client_data);
if (err)
dev_err(client_data->dev, "could not request irq\n");
INIT_WORK(&client_data->irq_work, smi_irq_work_func);
#endif
client_data->selftest = 0;
client_data->fifo_data_sel = 0;
#if defined(CONFIG_USE_QUALCOMM_HAL)
SMI_CALL_API(set_accel_output_data_rate)(9);/*defalut odr 200HZ*/
SMI_CALL_API(set_gyro_output_data_rate)(9);/*defalut odr 200HZ*/
#endif
SMI_CALL_API(get_accel_output_data_rate)(&client_data->odr.acc_odr);
SMI_CALL_API(get_gyro_output_data_rate)(&client_data->odr.gyro_odr);
SMI_CALL_API(get_mag_output_data_rate)(&client_data->odr.mag_odr);
SMI_CALL_API(set_fifo_time_enable)(1);
SMI_CALL_API(get_accel_range)(&client_data->range.acc_range);
SMI_CALL_API(get_gyro_range)(&client_data->range.gyro_range);
/* now it's power on which is considered as resuming from suspend */
/* gyro input device init */
err = smi_gyro_input_init(client_data);
#if defined(CONFIG_USE_QUALCOMM_HAL)
/* gyro input device init */
err = smi_gyro_input_init(client_data);
if (err < 0)
goto exit_err_clean;
client_data->accel_poll_ms = SMI130_ACCEL_DEFAULT_POLL_INTERVAL_MS;
client_data->gyro_poll_ms = SMI130_GYRO_DEFAULT_POLL_INTERVAL_MS;
client_data->data_wq = create_freezable_workqueue("smi130_data_work");
if (!client_data->data_wq) {
dev_err(dev, "Cannot create workqueue!\n");
goto exit_err_clean;
}
INIT_DELAYED_WORK(&client_data->accel_poll_work,
smi130_accel_work_fn);
client_data->accel_cdev = smi130_accel_cdev;
client_data->accel_cdev.delay_msec = client_data->accel_poll_ms;
client_data->accel_cdev.sensors_enable = smi130_accel_cdev_enable;
client_data->accel_cdev.sensors_poll_delay =
smi130_accel_cdev_poll_delay;
err = sensors_classdev_register(dev, &client_data->accel_cdev);
if (err) {
dev_err(dev,
"create accel class device file failed!\n");
goto exit_err_clean;
}
INIT_DELAYED_WORK(&client_data->gyro_poll_work, smi130_gyro_work_fn);
client_data->gyro_cdev = smi130_gyro_cdev;
client_data->gyro_cdev.delay_msec = client_data->gyro_poll_ms;
client_data->gyro_cdev.sensors_enable = smi130_gyro_cdev_enable;
client_data->gyro_cdev.sensors_poll_delay = smi130_gyro_cdev_poll_delay;
err = sensors_classdev_register(dev, &client_data->gyro_cdev);
if (err) {
dev_err(dev,
"create accel class device file failed!\n");
goto exit_err_clean;
}
#endif
/* set sensor PMU into suspend power mode for all */
if (smi_pmu_set_suspend(client_data) < 0) {
dev_err(dev, "Failed to set SMI130 to suspend power mode\n");
goto exit_err_sysfs;
}
/*enable the data ready interrupt*/
SMI_CALL_API(set_intr_enable_1)(SMI130_DATA_RDY_ENABLE, 1);
dev_notice(dev, "sensor_time:%d, %d, %d",
sensortime_duration_tbl[0].ts_delat,
sensortime_duration_tbl[0].ts_duration_lsb,
sensortime_duration_tbl[0].ts_duration_us);
dev_notice(dev, "sensor %s probed successfully", SENSOR_NAME);
return 0;
exit_err_sysfs:
if (err)
smi_input_destroy(client_data);
exit_err_clean:
if (err) {
if (client_data != NULL) {
if (NULL != client_data->bosch_pd) {
kfree(client_data->bosch_pd);
client_data->bosch_pd = NULL;
}
}
}
return err;
}
EXPORT_SYMBOL(smi_probe);
/*!
* @brief remove smi client
*
* @param dev the pointer of device
*
* @return zero
* @retval zero
*/
int smi_remove(struct device *dev)
{
int err = 0;
struct smi_client_data *client_data = dev_get_drvdata(dev);
if (NULL != client_data) {
#ifdef CONFIG_HAS_EARLYSUSPEND
unregister_early_suspend(&client_data->early_suspend_handler);
#endif
mutex_lock(&client_data->mutex_enable);
if (SMI_ACC_PM_NORMAL == client_data->pw.acc_pm ||
SMI_GYRO_PM_NORMAL == client_data->pw.gyro_pm ||
SMI_MAG_PM_NORMAL == client_data->pw.mag_pm) {
cancel_delayed_work_sync(&client_data->work);
}
mutex_unlock(&client_data->mutex_enable);
err = smi_pmu_set_suspend(client_data);
smi_delay(5);
sysfs_remove_group(&client_data->input->dev.kobj,
&smi130_attribute_group);
smi_input_destroy(client_data);
if (NULL != client_data->bosch_pd) {
kfree(client_data->bosch_pd);
client_data->bosch_pd = NULL;
}
kfree(client_data);
}
return err;
}
EXPORT_SYMBOL(smi_remove);
static int smi_post_resume(struct smi_client_data *client_data)
{
int err = 0;
mutex_lock(&client_data->mutex_enable);
if (atomic_read(&client_data->wkqueue_en) == 1) {
smi130_set_acc_op_mode(client_data, SMI_ACC_PM_NORMAL);
schedule_delayed_work(&client_data->work,
msecs_to_jiffies(
atomic_read(&client_data->delay)));
}
mutex_unlock(&client_data->mutex_enable);
return err;
}
int smi_suspend(struct device *dev)
{
int err = 0;
struct smi_client_data *client_data = dev_get_drvdata(dev);
unsigned char stc_enable;
unsigned char std_enable;
dev_err(client_data->dev, "smi suspend function entrance");
atomic_set(&client_data->in_suspend, 1);
if (atomic_read(&client_data->wkqueue_en) == 1) {
smi130_set_acc_op_mode(client_data, SMI_ACC_PM_SUSPEND);
cancel_delayed_work_sync(&client_data->work);
}
SMI_CALL_API(get_step_counter_enable)(&stc_enable);
SMI_CALL_API(get_step_detector_enable)(&std_enable);
if (client_data->pw.acc_pm != SMI_ACC_PM_SUSPEND &&
(stc_enable != 1) && (std_enable != 1) &&
(client_data->sig_flag != 1)) {
err += SMI_CALL_API(set_command_register)
(smi_pmu_cmd_acc_arr[SMI_ACC_PM_SUSPEND]);
smi_delay(3);
}
if (client_data->pw.gyro_pm != SMI_GYRO_PM_SUSPEND) {
err += SMI_CALL_API(set_command_register)
(smi_pmu_cmd_gyro_arr[SMI_GYRO_PM_SUSPEND]);
smi_delay(3);
}
if (client_data->pw.mag_pm != SMI_MAG_PM_SUSPEND) {
#if defined(SMI130_AKM09912_SUPPORT)
err += smi130_set_bosch_akm_and_secondary_if_powermode(
SMI130_MAG_SUSPEND_MODE);
#else
err += smi130_set_bmm150_mag_and_secondary_if_power_mode(
SMI130_MAG_SUSPEND_MODE);
#endif
smi_delay(3);
}
return err;
}
EXPORT_SYMBOL(smi_suspend);
int smi_resume(struct device *dev)
{
int err = 0;
struct smi_client_data *client_data = dev_get_drvdata(dev);
atomic_set(&client_data->in_suspend, 0);
if (client_data->pw.acc_pm != SMI_ACC_PM_SUSPEND) {
err += SMI_CALL_API(set_command_register)
(smi_pmu_cmd_acc_arr[SMI_ACC_PM_NORMAL]);
smi_delay(3);
}
if (client_data->pw.gyro_pm != SMI_GYRO_PM_SUSPEND) {
err += SMI_CALL_API(set_command_register)
(smi_pmu_cmd_gyro_arr[SMI_GYRO_PM_NORMAL]);
smi_delay(3);
}
if (client_data->pw.mag_pm != SMI_MAG_PM_SUSPEND) {
#if defined(SMI130_AKM09912_SUPPORT)
err += smi130_set_bosch_akm_and_secondary_if_powermode
(SMI130_MAG_FORCE_MODE);
#else
err += smi130_set_bmm150_mag_and_secondary_if_power_mode
(SMI130_MAG_FORCE_MODE);
#endif
smi_delay(3);
}
/* post resume operation */
err += smi_post_resume(client_data);
return err;
}
EXPORT_SYMBOL(smi_resume);