blob: 159b21f649b3af8031c996b826052ae57a1f9cc6 [file] [log] [blame]
/*
* STMicroelectronics lsm6ds3h core driver
*
* Copyright 2016 STMicroelectronics Inc.
*
* Denis Ciocca <denis.ciocca@st.com>
*
* Licensed under the GPL-2.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/errno.h>
#include <linux/types.h>
#include <linux/mutex.h>
#include <linux/interrupt.h>
#include <linux/gpio.h>
#include <linux/delay.h>
#include <linux/of.h>
#include <linux/irq.h>
#include <linux/firmware.h>
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
#include <linux/iio/trigger.h>
#include <linux/iio/buffer.h>
#include <linux/iio/events.h>
#include <asm/unaligned.h>
#include <linux/iio/common/st_sensors.h>
#include <linux/pm_runtime.h>
#include "st_lsm6ds3h.h"
#ifdef CONFIG_ST_LSM6DS3H_IIO_ALGO_UPLOAD_WRIST_TILT
#define ST_LSM6DS3H_DATA_FW "st_lsm6ds3h_wrist_tilt_data.fw"
#define ST_LSM6DS3H_WRIST_TILT_THRESHOLD 0x10
static const u8 st_lsm6ds3h_fw[] = {
#include "st_lsm6ds3h_wrist_tilt_data.fw"
};
DECLARE_BUILTIN_FIRMWARE(ST_LSM6DS3H_DATA_FW, st_lsm6ds3h_fw);
#endif /* CONFIG_ST_LSM6DS3H_IIO_ALGO_UPLOAD_WRIST_TILT */
#define MS_TO_NS(msec) ((msec) * 1000 * 1000)
#ifndef MAX
#define MAX(a, b) (((a) > (b)) ? (a) : (b))
#endif
#ifndef MIN
#define MIN(a, b) (((a) < (b)) ? (a) : (b))
#endif
#define MIN_BNZ(a, b) (((a) < (b)) ? ((a == 0) ? \
(b) : (a)) : ((b == 0) ? \
(a) : (b)))
/* COMMON VALUES FOR ACCEL-GYRO SENSORS */
#define ST_LSM6DS3H_WAI_ADDRESS 0x0f
#define ST_LSM6DS3H_WAI_EXP 0x69
#define ST_LSM6DS3H_INT1_ADDR 0x0d
#define ST_LSM6DS3H_INT2_ADDR 0x0e
#define ST_LSM6DS3H_ACCEL_DRDY_IRQ_MASK 0x01
#define ST_LSM6DS3H_GYRO_DRDY_IRQ_MASK 0x02
#define ST_LSM6DS3H_MD1_ADDR 0x5e
#define ST_LSM6DS3H_MD2_ADDR 0x5f
#define ST_LSM6DS3H_ODR_LIST_NUM 6
#define ST_LSM6DS3H_ODR_POWER_OFF_VAL 0x00
#define ST_LSM6DS3H_ODR_13HZ_VAL 0x01
#define ST_LSM6DS3H_ODR_26HZ_VAL 0x02
#define ST_LSM6DS3H_ODR_52HZ_VAL 0x03
#define ST_LSM6DS3H_ODR_104HZ_VAL 0x04
#define ST_LSM6DS3H_ODR_208HZ_VAL 0x05
#define ST_LSM6DS3H_ODR_416HZ_VAL 0x06
#define ST_LSM6DS3H_FS_LIST_NUM 4
#define ST_LSM6DS3H_BDU_ADDR 0x12
#define ST_LSM6DS3H_BDU_MASK 0x40
#define ST_LSM6DS3H_EN_BIT 0x01
#define ST_LSM6DS3H_DIS_BIT 0x00
#define ST_LSM6DS3H_FUNC_EN_ADDR 0x19
#define ST_LSM6DS3H_FUNC_EN_MASK 0x04
#define ST_LSM6DS3H_FUNC_CFG_ACCESS_ADDR 0x01
#define ST_LSM6DS3H_FUNC_CFG_ACCESS_MASK 0x01
#define ST_LSM6DS3H_FUNC_CFG_ACCESS_MASK2 0x04
#define ST_LSM6DS3H_FUNC_CFG_REG2_MASK 0x80
#define ST_LSM6DS3H_FUNC_CFG_START1_ADDR 0x62
#define ST_LSM6DS3H_FUNC_CFG_START2_ADDR 0x63
#define ST_LSM6DS3H_FUNC_CFG_DATA_WRITE_ADDR 0x64
#define ST_LSM6DS3H_FUNC_CFG_DATA_READ_ADDR 0x65
#define ST_LSM6DS3H_SENSORHUB_ADDR 0x1a
#define ST_LSM6DS3H_SENSORHUB_MASK 0x01
#define ST_LSM6DS3H_SENSORHUB_TRIG_MASK 0x10
#define ST_LSM6DS3H_TRIG_INTERNAL 0x00
#define ST_LSM6DS3H_TRIG_EXTERNAL 0x01
#define ST_LSM6DS3H_SELFTEST_ADDR 0x14
#define ST_LSM6DS3H_SELFTEST_ACCEL_MASK 0x03
#define ST_LSM6DS3H_SELFTEST_GYRO_MASK 0x0c
#define ST_LSM6DS3H_SELF_TEST_DISABLED_VAL 0x00
#define ST_LSM6DS3H_SELF_TEST_POS_SIGN_VAL 0x01
#define ST_LSM6DS3H_SELF_TEST_NEG_ACCEL_SIGN_VAL 0x02
#define ST_LSM6DS3H_SELF_TEST_NEG_GYRO_SIGN_VAL 0x03
#define ST_LSM6DS3H_LIR_ADDR 0x58
#define ST_LSM6DS3H_LIR_MASK 0x01
#define ST_LSM6DS3H_TIMER_EN_ADDR 0x58
#define ST_LSM6DS3H_TIMER_EN_MASK 0x80
#define ST_LSM6DS3H_PEDOMETER_EN_ADDR 0x58
#define ST_LSM6DS3H_PEDOMETER_EN_MASK 0x40
#define ST_LSM6DS3H_INT2_ON_INT1_ADDR 0x13
#define ST_LSM6DS3H_INT2_ON_INT1_MASK 0x20
#define ST_LSM6DS3H_MIN_DURATION_MS 1638
#define ST_LSM6DS3H_XL_HM_MODE_ADDR 0x15
#define ST_LSM6DS3H_XL_HM_MODE_MASK 0x01
#define ST_LSM6DS3H_XL_HM_MODE_DISABLE 0x01
#define ST_LSM6DS3H_ROUNDING_ADDR 0x16
#define ST_LSM6DS3H_G_HM_MODE_DISABLE 0x80
#define ST_LSM6DS3H_ROUNDING_MASK 0x84
#define ST_LSM6DS3H_FIFO_MODE_ADDR 0x0a
#define ST_LSM6DS3H_FIFO_MODE_MASK 0x07
#define ST_LSM6DS3H_FIFO_MODE_BYPASS 0x00
#define ST_LSM6DS3H_FIFO_MODE_CONTINUOS 0x06
#define ST_LSM6DS3H_FIFO_THRESHOLD_IRQ_MASK 0x08
#define ST_LSM6DS3H_FIFO_ODR_MAX 0x40
#define ST_LSM6DS3H_FIFO_DECIMATOR_ADDR 0x08
#define ST_LSM6DS3H_FIFO_ACCEL_DECIMATOR_MASK 0x07
#define ST_LSM6DS3H_FIFO_GYRO_DECIMATOR_MASK 0x38
#define ST_LSM6DS3H_FIFO_DECIMATOR2_ADDR 0x09
#define ST_LSM6DS3H_FIFO_THR_L_ADDR 0x06
#define ST_LSM6DS3H_FIFO_THR_H_ADDR 0x07
#define ST_LSM6DS3H_FIFO_THR_MASK 0x0fff
#define ST_LSM6DS3H_FIFO_THR_IRQ_MASK 0x08
#define ST_LSM6DS3H_RESET_ADDR 0x12
#define ST_LSM6DS3H_RESET_MASK 0x01
#define ST_LSM6DS3H_TEST_REG_ADDR 0x00
#define ST_LSM6DS3H_START_INJECT_XL_MASK 0x08
#define ST_LSM6DS3H_INJECT_XL_X_ADDR 0x06
#define ST_LSM6DS3H_NS_AT_25HZ 40000000LL
#define ST_LSM6DS3H_26HZ_NS (38461538LL)
#define ST_LSM6DS3H_SELFTEST_NA_MS "na"
#define ST_LSM6DS3H_SELFTEST_FAIL_MS "fail"
#define ST_LSM6DS3H_SELFTEST_PASS_MS "pass"
/* VALUES TO UPLOAD FIRMWARE */
#define ST_LSM6DS3H_FIFO_CTRL4_ADDR 0x09
#define ST_LSM6DS3H_RESERVE_HALF_FIFO 0x80
/* CUSTOM VALUES FOR ACCEL SENSOR */
#define ST_LSM6DS3H_ACCEL_ODR_ADDR 0x10
#define ST_LSM6DS3H_ACCEL_ODR_MASK 0xf0
#define ST_LSM6DS3H_ACCEL_FS_ADDR 0x10
#define ST_LSM6DS3H_ACCEL_FS_MASK 0x0c
#define ST_LSM6DS3H_ACCEL_FS_2G_VAL 0x00
#define ST_LSM6DS3H_ACCEL_FS_4G_VAL 0x02
#define ST_LSM6DS3H_ACCEL_FS_8G_VAL 0x03
#define ST_LSM6DS3H_ACCEL_FS_16G_VAL 0x01
#define ST_LSM6DS3H_ACCEL_FS_2G_SENSITIVITY 61 /* ug/LSB */
#define ST_LSM6DS3H_ACCEL_FS_4G_SENSITIVITY 122 /* ug/LSB */
#define ST_LSM6DS3H_ACCEL_FS_8G_SENSITIVITY 244 /* ug/LSB */
#define ST_LSM6DS3H_ACCEL_FS_16G_SENSITIVITY 488 /* ug/LSB */
#define ST_LSM6DS3H_ACCEL_FS_2G_GAIN IIO_G_TO_M_S_2(ST_LSM6DS3H_ACCEL_FS_2G_SENSITIVITY)
#define ST_LSM6DS3H_ACCEL_FS_4G_GAIN IIO_G_TO_M_S_2(ST_LSM6DS3H_ACCEL_FS_4G_SENSITIVITY)
#define ST_LSM6DS3H_ACCEL_FS_8G_GAIN IIO_G_TO_M_S_2(ST_LSM6DS3H_ACCEL_FS_8G_SENSITIVITY)
#define ST_LSM6DS3H_ACCEL_FS_16G_GAIN IIO_G_TO_M_S_2(ST_LSM6DS3H_ACCEL_FS_16G_SENSITIVITY)
#define ST_LSM6DS3H_ACCEL_OUT_X_L_ADDR 0x28
#define ST_LSM6DS3H_ACCEL_OUT_Y_L_ADDR 0x2a
#define ST_LSM6DS3H_ACCEL_OUT_Z_L_ADDR 0x2c
#define ST_LSM6DS3H_ACCEL_STD_52HZ 1
#define ST_LSM6DS3H_ACCEL_STD_104HZ 2
#define ST_LSM6DS3H_ACCEL_STD_208HZ 3
#define ST_LSM6DS3H_SELFTEST_ACCEL_ADDR 0x10
#define ST_LSM6DS3H_SELFTEST_ACCEL_REG_VALUE 0x40
#define ST_LSM6DS3H_SELFTEST_ACCEL_MIN 1492
#define ST_LSM6DS3H_SELFTEST_ACCEL_MAX 27868
/* CUSTOM VALUES FOR GYRO SENSOR */
#define ST_LSM6DS3H_GYRO_ODR_ADDR 0x11
#define ST_LSM6DS3H_GYRO_ODR_MASK 0xf0
#define ST_LSM6DS3H_GYRO_FS_ADDR 0x11
#define ST_LSM6DS3H_GYRO_FS_MASK 0x0c
#define ST_LSM6DS3H_GYRO_FS_245_VAL 0x00
#define ST_LSM6DS3H_GYRO_FS_500_VAL 0x01
#define ST_LSM6DS3H_GYRO_FS_1000_VAL 0x02
#define ST_LSM6DS3H_GYRO_FS_2000_VAL 0x03
#define ST_LSM6DS3H_GYRO_FS_125_SENSITIVITY 4375 /* mdps/LSB */
#define ST_LSM6DS3H_GYRO_FS_245_SENSITIVITY 8750 /* mdps/LSB */
#define ST_LSM6DS3H_GYRO_FS_500_SENSITIVITY 17500 /* mdps/LSB */
#define ST_LSM6DS3H_GYRO_FS_1000_SENSITIVITY 35000 /* mdps/LSB */
#define ST_LSM6DS3H_GYRO_FS_2000_SENSITIVITY 70000 /* mdps/LSB */
#define ST_LSM6DS3H_GYRO_FS_125_GAIN IIO_DEGREE_TO_RAD(ST_LSM6DS3H_GYRO_FS_125_SENSITIVITY)
#define ST_LSM6DS3H_GYRO_FS_245_GAIN IIO_DEGREE_TO_RAD(ST_LSM6DS3H_GYRO_FS_245_SENSITIVITY)
#define ST_LSM6DS3H_GYRO_FS_500_GAIN IIO_DEGREE_TO_RAD(ST_LSM6DS3H_GYRO_FS_500_SENSITIVITY)
#define ST_LSM6DS3H_GYRO_FS_1000_GAIN IIO_DEGREE_TO_RAD(ST_LSM6DS3H_GYRO_FS_1000_SENSITIVITY)
#define ST_LSM6DS3H_GYRO_FS_2000_GAIN IIO_DEGREE_TO_RAD(ST_LSM6DS3H_GYRO_FS_2000_SENSITIVITY)
#define ST_LSM6DS3H_GYRO_OUT_X_L_ADDR 0x22
#define ST_LSM6DS3H_GYRO_OUT_Y_L_ADDR 0x24
#define ST_LSM6DS3H_GYRO_OUT_Z_L_ADDR 0x26
#define ST_LSM6DS3H_GYRO_STD_13HZ 2
#define ST_LSM6DS3H_GYRO_STD_52HZ 3
#define ST_LSM6DS3H_GYRO_STD_104HZ 5
#define ST_LSM6DS3H_GYRO_STD_208HZ 8
#define ST_LSM6DS3H_SELFTEST_GYRO_ADDR 0x11
#define ST_LSM6DS3H_SELFTEST_GYRO_REG_VALUE 0x4c
#define ST_LSM6DS3H_SELFTEST_GYRO_MIN 2142
#define ST_LSM6DS3H_SELFTEST_GYRO_MAX 10000
#define CALIBRATE_ODR_SET_VALUE 0X1C
/* CUSTOM VALUES FOR SIGNIFICANT MOTION SENSOR */
#define ST_LSM6DS3H_SIGN_MOTION_EN_ADDR 0x19
#define ST_LSM6DS3H_SIGN_MOTION_EN_MASK 0x01
#define ST_LSM6DS3H_SIGN_MOTION_DRDY_IRQ_MASK 0x40
/* CUSTOM VALUES FOR STEP DETECTOR SENSOR */
#define ST_LSM6DS3H_STEP_DETECTOR_DRDY_IRQ_MASK 0x80
/* CUSTOM VALUES FOR STEP COUNTER SENSOR */
#define ST_LSM6DS3H_STEP_COUNTER_DRDY_IRQ_MASK 0x80
#define ST_LSM6DS3H_STEP_COUNTER_OUT_L_ADDR 0x4b
#define ST_LSM6DS3H_STEP_COUNTER_RES_ADDR 0x19
#define ST_LSM6DS3H_STEP_COUNTER_RES_MASK 0x06
#define ST_LSM6DS3H_STEP_COUNTER_RES_ALL_EN 0x03
#define ST_LSM6DS3H_STEP_COUNTER_RES_FUNC_EN 0x02
#define ST_LSM6DS3H_STEP_COUNTER_DURATION_ADDR 0x15
/* CUSTOM VALUES FOR TILT SENSOR */
#define ST_LSM6DS3H_TILT_EN_ADDR 0x58
#define ST_LSM6DS3H_TILT_EN_MASK 0x20
#define ST_LSM6DS3H_TILT_DRDY_IRQ_MASK 0x02
/* CUSTOM VALUES FOR WRIST TILT SENSOR */
#define ST_LSM6DS3H_WRIST_TILT_EN_ADDR 0x11
#define ST_LSM6DS3H_WRIST_TILT_EN_MASK 0x01
#define ST_LSM6DS3H_WRIST_TILT_DRDY_IRQ_MASK 0x01
#define ST_LSM6DS3H_WRIST_TILT_FILTER_ADDR 0x27
#define ST_LSM6DS3H_WRIST_TILT_LATENCY_ADDR 0x28
#define ST_LSM6DS3H_WRIST_TILT_THS1_ADDR 0x29
#define ST_LSM6DS3H_WRIST_TILT_AXES_ADDR 0x2b
#define ST_LSM6DS3H_WRIST_TILT_AXES_XPOS_EN 0x80
#define ST_LSM6DS3H_WRIST_TILT_AXES_XNEG_EN 0x40
#define ST_LSM6DS3H_WRIST_TILT_AXES_YPOS_EN 0x20
#define ST_LSM6DS3H_WRIST_TILT_AXES_YNEG_EN 0x10
#define ST_LSM6DS3H_WRIST_TILT_AXES_ZPOS_EN 0x08
#define ST_LSM6DS3H_WRIST_TILT_AXES_ZNEG_EN 0x04
#define ST_LSM6DS3H_STEP_COUNTER_THS_MIN_MASK 0x1f
#define ST_LSM6DS3H_STEP_COUNTER_THS_MIN_DEF_VAL 0x19
#define ST_LSM6DS3H_STEP_COUNTER_PEDO_THS_ADDR 0x0f
#define ST_LSM6DS3H_STEP_COUNTER_DEB_STEP_MASK 0x07
#define ST_LSM6DS3H_STEP_COUNTER_DEB_STEP_DEF_VAL 0x07
#define ST_LSM6DS3H_STEP_COUNTER_PEDO_DEB_ADDR 0x14
#ifdef CONFIG_ST_LSM6DS3H_IIO_TAP_TAP_ENABLED
/* CUSTOM VALUES FOR TAP_TAP SENSOR */
#define ST_LSM6DS3H_TAP_CFG_ADDR 0x58
#define ST_LSM6DS3H_TAP_CFG_TAP_X_EN_MASK 0x08
#define ST_LSM6DS3H_TAP_CFG_TAP_Y_EN_MASK 0x04
#define ST_LSM6DS3H_TAP_CFG_TAP_Z_EN_MASK 0x02
#define ST_LSM6DS3H_TAP_THS_6D_ADDR 0x59
#define ST_LSM6DS3H_TAP_THS_MASK 0x1F
#define ST_LSM6DS3H_TAP_THS_DEF_VAL 0x0C /* 750 mg */
#define ST_LSM6DS3H_WAKE_UP_THS_ADDR 0x5B
#define ST_LSM6DS3H_WAKE_UP_THS_SINGLE_DOUBLE_TAP_MASK 0x80
#define ST_LSM6DS3H_INT_DUR2_ADDR 0x5A
#define ST_LSM6DS3H_INT_DUR2_MASK 0xFF
#define ST_LSM6DS3H_INT_DUR2_DEF_VAL 0x7F /* Duration, Quiet and Shock time */
#define ST_LSM6DS3H_MD1_CFG_INT1_DOUBLE_TAP_MASK 0x08
#define ST_LSM6DS3H_TAP_TAP_EN_ADDR ST_LSM6DS3H_TAP_CFG_ADDR
#define ST_LSM6DS3H_TAP_TAP_EN_MASK 0x0E
#define ST_LSM6DS3H_TAP_TAP_DRDY_IRQ_MASK ST_LSM6DS3H_MD1_CFG_INT1_DOUBLE_TAP_MASK
#endif /* CONFIG_ST_LSM6DS3H_IIO_TAP_TAP_ENABLED */
/* STATUS REGISTER */
#define ST_LSM6DS3H_STAT_REG_ADDR 0x1e
#define ST_LSM6DS3H_XLDA_MASK 0x01
#define ST_LSM6DS3H_GDA_MASK 0x02
#define ST_LSM6DS3H_ACCEL_SUFFIX_NAME "accel"
#define ST_LSM6DS3H_GYRO_SUFFIX_NAME "gyro"
#define ST_LSM6DS3H_STEP_COUNTER_SUFFIX_NAME "step_c"
#define ST_LSM6DS3H_STEP_DETECTOR_SUFFIX_NAME "step_d"
#define ST_LSM6DS3H_SIGN_MOTION_SUFFIX_NAME "sign_motion"
#define ST_LSM6DS3H_TILT_SUFFIX_NAME "tilt"
#define ST_LSM6DS3H_WRIST_TILT_SUFFIX_NAME "wrist"
#define ST_LSM6DS3H_TAP_TAP_SUFFIX_NAME "tap_tap"
#define ST_LSM6DS3H_WAKEUP_SUFFIX_NAME "wk"
#define DELAY_FOR_OUT_STABLE 200/* 200ms */
#define MAX_WHILE_COUNTER 150
#define SAMPLE_WAIT_DELAY 39
/* Macro for calibrate */
#define CALIBRATE_SAMPLE_COUNT 50
#define GRAVITY_ACCEL_LSB_2G (1000000 / ST_LSM6DS3H_ACCEL_FS_2G_SENSITIVITY)
#define SIGN_X_A 1
#define SIGN_Y_A 1
#define SIGN_Z_A 1
#define SIGN_X_G 1
#define SIGN_Y_G 1
#define SIGN_Z_G 1
#define ACCEL_SCOPE_SCALE 4
#define ST_LSM6DS3H_ACCEL_FS_2G_SENSITIVITY 61 /* ug/LSB */
#define ST_LSM6DS3H_DEV_ATTR_SAMP_FREQ() \
IIO_DEV_ATTR_SAMP_FREQ(S_IWUSR | S_IRUGO, \
st_lsm6ds3h_sysfs_get_sampling_frequency, \
st_lsm6ds3h_sysfs_set_sampling_frequency)
#define ST_LSM6DS3H_DEV_ATTR_SAMP_FREQ_AVAIL() \
IIO_DEV_ATTR_SAMP_FREQ_AVAIL( \
st_lsm6ds3h_sysfs_sampling_frequency_avail)
#define ST_LSM6DS3H_DEV_ATTR_SCALE_AVAIL(name) \
IIO_DEVICE_ATTR(name, S_IRUGO, \
st_lsm6ds3h_sysfs_scale_avail, NULL , 0);
u8 threshold_value = ST_LSM6DS3H_STEP_COUNTER_THS_MIN_DEF_VAL;
#define RETRY_COUNTER_LIMITATION 10
static bool stay_wake;
static int accel_cal_data[3], gyro_cal_data[3];
static struct st_lsm6ds3h_selftest_table {
char *string_mode;
u8 accel_value;
u8 gyro_value;
u8 gyro_mask;
} st_lsm6ds3h_selftest_table[] = {
[0] = {
.string_mode = "disabled",
.accel_value = ST_LSM6DS3H_SELF_TEST_DISABLED_VAL,
.gyro_value = ST_LSM6DS3H_SELF_TEST_DISABLED_VAL,
},
[1] = {
.string_mode = "positive-sign",
.accel_value = ST_LSM6DS3H_SELF_TEST_POS_SIGN_VAL,
.gyro_value = ST_LSM6DS3H_SELF_TEST_POS_SIGN_VAL
},
[2] = {
.string_mode = "negative-sign",
.accel_value = ST_LSM6DS3H_SELF_TEST_NEG_ACCEL_SIGN_VAL,
.gyro_value = ST_LSM6DS3H_SELF_TEST_NEG_GYRO_SIGN_VAL
},
};
struct st_lsm6ds3h_odr_reg {
unsigned int hz;
u8 value;
};
static struct st_lsm6ds3h_odr_table {
u8 addr[4];
u8 mask[4];
struct st_lsm6ds3h_odr_reg odr_avl[ST_LSM6DS3H_ODR_LIST_NUM];
} st_lsm6ds3h_odr_table = {
.addr[ST_MASK_ID_ACCEL] = ST_LSM6DS3H_ACCEL_ODR_ADDR,
.mask[ST_MASK_ID_ACCEL] = ST_LSM6DS3H_ACCEL_ODR_MASK,
.addr[ST_MASK_ID_ACCEL_WK] = ST_LSM6DS3H_ACCEL_ODR_ADDR,
.mask[ST_MASK_ID_ACCEL_WK] = ST_LSM6DS3H_ACCEL_ODR_MASK,
.addr[ST_MASK_ID_GYRO] = ST_LSM6DS3H_GYRO_ODR_ADDR,
.mask[ST_MASK_ID_GYRO] = ST_LSM6DS3H_GYRO_ODR_MASK,
.addr[ST_MASK_ID_GYRO_WK] = ST_LSM6DS3H_GYRO_ODR_ADDR,
.mask[ST_MASK_ID_GYRO_WK] = ST_LSM6DS3H_GYRO_ODR_MASK,
.odr_avl[0] = { .hz = 13, .value = ST_LSM6DS3H_ODR_13HZ_VAL },
.odr_avl[1] = { .hz = 26, .value = ST_LSM6DS3H_ODR_26HZ_VAL },
.odr_avl[2] = { .hz = 52, .value = ST_LSM6DS3H_ODR_52HZ_VAL },
.odr_avl[3] = { .hz = 104, .value = ST_LSM6DS3H_ODR_104HZ_VAL },
.odr_avl[4] = { .hz = 208, .value = ST_LSM6DS3H_ODR_208HZ_VAL },
.odr_avl[5] = { .hz = 416, .value = ST_LSM6DS3H_ODR_416HZ_VAL },
};
struct st_lsm6ds3h_fs_reg {
unsigned int gain;
u8 value;
};
static struct st_lsm6ds3h_fs_table {
u8 addr;
u8 mask;
struct st_lsm6ds3h_fs_reg fs_avl[ST_LSM6DS3H_FS_LIST_NUM];
} st_lsm6ds3h_fs_table[ST_INDIO_DEV_NUM] = {
[ST_MASK_ID_ACCEL] = {
.addr = ST_LSM6DS3H_ACCEL_FS_ADDR,
.mask = ST_LSM6DS3H_ACCEL_FS_MASK,
.fs_avl[0] = { .gain = ST_LSM6DS3H_ACCEL_FS_2G_GAIN,
.value = ST_LSM6DS3H_ACCEL_FS_2G_VAL },
.fs_avl[1] = { .gain = ST_LSM6DS3H_ACCEL_FS_4G_GAIN,
.value = ST_LSM6DS3H_ACCEL_FS_4G_VAL },
.fs_avl[2] = { .gain = ST_LSM6DS3H_ACCEL_FS_8G_GAIN,
.value = ST_LSM6DS3H_ACCEL_FS_8G_VAL },
.fs_avl[3] = { .gain = ST_LSM6DS3H_ACCEL_FS_16G_GAIN,
.value = ST_LSM6DS3H_ACCEL_FS_16G_VAL },
},
[ST_MASK_ID_ACCEL_WK] = {
.addr = ST_LSM6DS3H_ACCEL_FS_ADDR,
.mask = ST_LSM6DS3H_ACCEL_FS_MASK,
.fs_avl[0] = { .gain = ST_LSM6DS3H_ACCEL_FS_2G_GAIN,
.value = ST_LSM6DS3H_ACCEL_FS_2G_VAL },
.fs_avl[1] = { .gain = ST_LSM6DS3H_ACCEL_FS_4G_GAIN,
.value = ST_LSM6DS3H_ACCEL_FS_4G_VAL },
.fs_avl[2] = { .gain = ST_LSM6DS3H_ACCEL_FS_8G_GAIN,
.value = ST_LSM6DS3H_ACCEL_FS_8G_VAL },
.fs_avl[3] = { .gain = ST_LSM6DS3H_ACCEL_FS_16G_GAIN,
.value = ST_LSM6DS3H_ACCEL_FS_16G_VAL },
},
[ST_MASK_ID_GYRO] = {
.addr = ST_LSM6DS3H_GYRO_FS_ADDR,
.mask = ST_LSM6DS3H_GYRO_FS_MASK,
.fs_avl[0] = { .gain = ST_LSM6DS3H_GYRO_FS_245_GAIN,
.value = ST_LSM6DS3H_GYRO_FS_245_VAL },
.fs_avl[1] = { .gain = ST_LSM6DS3H_GYRO_FS_500_GAIN,
.value = ST_LSM6DS3H_GYRO_FS_500_VAL },
.fs_avl[2] = { .gain = ST_LSM6DS3H_GYRO_FS_1000_GAIN,
.value = ST_LSM6DS3H_GYRO_FS_1000_VAL },
.fs_avl[3] = { .gain = ST_LSM6DS3H_GYRO_FS_2000_GAIN,
.value = ST_LSM6DS3H_GYRO_FS_2000_VAL },
},
[ST_MASK_ID_GYRO_WK] = {
.addr = ST_LSM6DS3H_GYRO_FS_ADDR,
.mask = ST_LSM6DS3H_GYRO_FS_MASK,
.fs_avl[0] = { .gain = ST_LSM6DS3H_GYRO_FS_245_GAIN,
.value = ST_LSM6DS3H_GYRO_FS_245_VAL },
.fs_avl[1] = { .gain = ST_LSM6DS3H_GYRO_FS_500_GAIN,
.value = ST_LSM6DS3H_GYRO_FS_500_VAL },
.fs_avl[2] = { .gain = ST_LSM6DS3H_GYRO_FS_1000_GAIN,
.value = ST_LSM6DS3H_GYRO_FS_1000_VAL },
.fs_avl[3] = { .gain = ST_LSM6DS3H_GYRO_FS_2000_GAIN,
.value = ST_LSM6DS3H_GYRO_FS_2000_VAL },
}
};
static const struct iio_event_spec st_lsm6ds3h_event_spec[] = {
{}
};
static const struct iio_chan_spec st_lsm6ds3h_accel_ch[] = {
ST_LSM6DS3H_LSM_CHANNELS(IIO_ACCEL, 1, 0, IIO_MOD_X, IIO_LE,
16, 16, ST_LSM6DS3H_ACCEL_OUT_X_L_ADDR, 's'),
ST_LSM6DS3H_LSM_CHANNELS(IIO_ACCEL, 1, 1, IIO_MOD_Y, IIO_LE,
16, 16, ST_LSM6DS3H_ACCEL_OUT_Y_L_ADDR, 's'),
ST_LSM6DS3H_LSM_CHANNELS(IIO_ACCEL, 1, 2, IIO_MOD_Z, IIO_LE,
16, 16, ST_LSM6DS3H_ACCEL_OUT_Z_L_ADDR, 's'),
ST_LSM6DS3H_FLUSH_CHANNEL(IIO_ACCEL),
IIO_CHAN_SOFT_TIMESTAMP(3)
};
static const struct iio_chan_spec st_lsm6ds3h_gyro_ch[] = {
ST_LSM6DS3H_LSM_CHANNELS(IIO_ANGL_VEL, 1, 0, IIO_MOD_X, IIO_LE,
16, 16, ST_LSM6DS3H_GYRO_OUT_X_L_ADDR, 's'),
ST_LSM6DS3H_LSM_CHANNELS(IIO_ANGL_VEL, 1, 1, IIO_MOD_Y, IIO_LE,
16, 16, ST_LSM6DS3H_GYRO_OUT_Y_L_ADDR, 's'),
ST_LSM6DS3H_LSM_CHANNELS(IIO_ANGL_VEL, 1, 2, IIO_MOD_Z, IIO_LE,
16, 16, ST_LSM6DS3H_GYRO_OUT_Z_L_ADDR, 's'),
ST_LSM6DS3H_FLUSH_CHANNEL(IIO_ANGL_VEL),
IIO_CHAN_SOFT_TIMESTAMP(3)
};
static const struct iio_chan_spec st_lsm6ds3h_sign_motion_ch[] = {
ST_LSM6DS3H_EVENT_CHANNEL_WITH_MASK(IIO_SIGN_MOTION, 0),
IIO_CHAN_SOFT_TIMESTAMP(1)
};
static const struct iio_chan_spec st_lsm6ds3h_step_c_ch[] = {
{
.type = IIO_STEP_COUNTER,
.modified = 0,
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
.address = ST_LSM6DS3H_STEP_COUNTER_OUT_L_ADDR,
.scan_type = {
.sign = 'u',
.realbits = 16,
.storagebits = 16,
.endianness = IIO_LE,
},
},
IIO_CHAN_SOFT_TIMESTAMP(1)
};
static const struct iio_chan_spec st_lsm6ds3h_step_d_ch[] = {
ST_LSM6DS3H_EVENT_CHANNEL_WITH_MASK(IIO_STEP_DETECTOR, 0),
IIO_CHAN_SOFT_TIMESTAMP(1)
};
static const struct iio_chan_spec st_lsm6ds3h_tilt_ch[] = {
ST_LSM6DS3H_EVENT_CHANNEL_WITH_MASK(IIO_TILT, 0),
IIO_CHAN_SOFT_TIMESTAMP(1)
};
#ifdef CONFIG_ST_LSM6DS3H_IIO_ALGO_UPLOAD_WRIST_TILT
static const struct iio_chan_spec st_lsm6ds3h_wrist_tilt_ch[] = {
ST_LSM6DS3H_EVENT_CHANNEL_WITH_MASK(IIO_WRIST_TILT_GESTURE, 0),
IIO_CHAN_SOFT_TIMESTAMP(1)
};
#endif /* CONFIG_ST_LSM6DS3H_IIO_ALGO_UPLOAD_WRIST_TILT */
#ifdef CONFIG_ST_LSM6DS3H_IIO_TAP_TAP_ENABLED
static const struct iio_chan_spec st_lsm6ds3h_tap_tap_ch[] = {
ST_LSM6DS3H_EVENT_CHANNEL_WITH_MASK(IIO_TAP_TAP, 0),
IIO_CHAN_SOFT_TIMESTAMP(1)
};
#endif /* CONFIG_ST_LSM6DS3H_IIO_TAP_TAP_ENABLED */
int st_lsm6ds3h_write_data_with_mask(struct lsm6ds3h_data *cdata,
u8 reg_addr, u8 mask, u8 data, bool b_lock)
{
int err;
u8 new_data = 0x00, old_data = 0x00;
err = cdata->tf->read(cdata, reg_addr, 1, &old_data, b_lock);
if (err < 0)
return err;
new_data = ((old_data & (~mask)) | ((data << __ffs(mask)) & mask));
if (new_data == old_data)
return 1;
return cdata->tf->write(cdata, reg_addr, 1, &new_data, b_lock);
}
EXPORT_SYMBOL(st_lsm6ds3h_write_data_with_mask);
static inline int st_lsm6ds3h_enable_embedded_page_regs(struct lsm6ds3h_data *cdata, bool enable)
{
u8 value = 0x00;
if (enable)
value = ST_LSM6DS3H_FUNC_CFG_REG2_MASK;
#ifndef CONFIG_ST_LSM6DS3H_IIO_ALGO_DISABLED
if (cdata->fifo2_algo_available)
value |= ST_LSM6DS3H_FUNC_CFG_ACCESS_MASK2;
#endif /* CONFIG_ST_LSM6DS3H_IIO_ALGO_DISABLED */
return cdata->tf->write(cdata, ST_LSM6DS3H_FUNC_CFG_ACCESS_ADDR, 1, &value, false);
}
int st_lsm6ds3h_read_embedded_registers(struct lsm6ds3h_data *cdata,
u8 reg_addr, u8 *data, int len)
{
int err = 0, err2;
int retry_counter = 0;
mutex_lock(&cdata->bank_registers_lock);
if (cdata->enable_digfunc_mask) {
err = st_lsm6ds3h_write_data_with_mask(cdata,
ST_LSM6DS3H_FUNC_EN_ADDR,
ST_LSM6DS3H_FUNC_EN_MASK,
ST_LSM6DS3H_DIS_BIT, false);
if (err < 0) {
mutex_unlock(&cdata->bank_registers_lock);
return err;
}
}
udelay(100);
err = st_lsm6ds3h_enable_embedded_page_regs(cdata, true);
if (err < 0)
goto restore_digfunc;
udelay(100);
err = cdata->tf->read(cdata, reg_addr, len, data, false);
if (err < 0)
goto restore_bank_regs;
err = st_lsm6ds3h_enable_embedded_page_regs(cdata, false);
if (err < 0)
goto restore_bank_regs;
udelay(100);
if (cdata->enable_digfunc_mask) {
err = st_lsm6ds3h_write_data_with_mask(cdata,
ST_LSM6DS3H_FUNC_EN_ADDR,
ST_LSM6DS3H_FUNC_EN_MASK,
ST_LSM6DS3H_EN_BIT, false);
if (err < 0)
goto restore_digfunc;
}
mutex_unlock(&cdata->bank_registers_lock);
return 0;
restore_bank_regs:
do {
msleep(200);
err2 = st_lsm6ds3h_enable_embedded_page_regs(cdata, false);
} while (err2 < 0 && retry_counter++ < RETRY_COUNTER_LIMITATION);
restore_digfunc:
if (!cdata->enable_digfunc_mask) {
err2 = st_lsm6ds3h_write_data_with_mask(cdata,
ST_LSM6DS3H_FUNC_EN_ADDR,
ST_LSM6DS3H_FUNC_EN_MASK,
ST_LSM6DS3H_EN_BIT, false);
}
mutex_unlock(&cdata->bank_registers_lock);
return err;
}
int st_lsm6ds3h_write_embedded_registers(struct lsm6ds3h_data *cdata,
u8 reg_addr, u8 *data, int len)
{
int err = 0, err2;
int retry_counter = 0;
mutex_lock(&cdata->bank_registers_lock);
if (cdata->enable_digfunc_mask) {
err = st_lsm6ds3h_write_data_with_mask(cdata,
ST_LSM6DS3H_FUNC_EN_ADDR,
ST_LSM6DS3H_FUNC_EN_MASK,
ST_LSM6DS3H_DIS_BIT, false);
if (err < 0) {
mutex_unlock(&cdata->bank_registers_lock);
return err;
}
}
udelay(100);
err = st_lsm6ds3h_enable_embedded_page_regs(cdata, true);
if (err < 0)
goto restore_digfunc;
udelay(100);
err = cdata->tf->write(cdata, reg_addr, len, data, false);
if (err < 0)
goto restore_bank_regs;
err = st_lsm6ds3h_enable_embedded_page_regs(cdata, false);
if (err < 0)
goto restore_bank_regs;
udelay(100);
if (cdata->enable_digfunc_mask) {
err = st_lsm6ds3h_write_data_with_mask(cdata,
ST_LSM6DS3H_FUNC_EN_ADDR,
ST_LSM6DS3H_FUNC_EN_MASK,
ST_LSM6DS3H_EN_BIT, false);
if (err < 0)
goto restore_digfunc;
}
mutex_unlock(&cdata->bank_registers_lock);
return 0;
restore_bank_regs:
do {
msleep(200);
err2 = st_lsm6ds3h_enable_embedded_page_regs(cdata, false);
} while (err2 < 0 && retry_counter++ < RETRY_COUNTER_LIMITATION);
restore_digfunc:
if (!cdata->enable_digfunc_mask) {
err2 = st_lsm6ds3h_write_data_with_mask(cdata,
ST_LSM6DS3H_FUNC_EN_ADDR,
ST_LSM6DS3H_FUNC_EN_MASK,
ST_LSM6DS3H_EN_BIT, false);
}
mutex_unlock(&cdata->bank_registers_lock);
return err;
}
static int lsm6ds3h_set_watermark(struct lsm6ds3h_data *cdata)
{
int err;
u8 reg_value = 0;
u16 fifo_watermark;
unsigned int fifo_len, sip = 0, min_pattern = UINT_MAX;
u16 hwfifo_watermark_accel = 0, hwfifo_watermark_gyro = 0;
if (cdata->accel_on || cdata->accel_wk_on) {
if (cdata->accel_on) {
if (cdata->accel_wk_on) {
hwfifo_watermark_accel = MIN(cdata->hwfifo_watermark[ST_MASK_ID_ACCEL],
cdata->hwfifo_watermark[ST_MASK_ID_ACCEL_WK]);
} else {
hwfifo_watermark_accel = cdata->hwfifo_watermark[ST_MASK_ID_ACCEL];
}
} else {
hwfifo_watermark_accel = cdata->hwfifo_watermark[ST_MASK_ID_ACCEL_WK];
}
}
if (cdata->gyro_on || cdata->gyro_wk_on) {
if (cdata->gyro_on) {
if (cdata->gyro_wk_on) {
hwfifo_watermark_gyro = MIN(cdata->hwfifo_watermark[ST_MASK_ID_GYRO],
cdata->hwfifo_watermark[ST_MASK_ID_GYRO_WK]);
} else {
hwfifo_watermark_gyro = cdata->hwfifo_watermark[ST_MASK_ID_GYRO];
}
} else {
hwfifo_watermark_gyro = cdata->hwfifo_watermark[ST_MASK_ID_GYRO_WK];
}
}
if (cdata->fifo_output[ST_MASK_ID_ACCEL].sip > 0) {
sip += cdata->fifo_output[ST_MASK_ID_ACCEL].sip;
min_pattern = MIN(min_pattern,
hwfifo_watermark_accel /
cdata->fifo_output[ST_MASK_ID_ACCEL].sip);
}
if (cdata->fifo_output[ST_MASK_ID_GYRO].sip > 0) {
sip += cdata->fifo_output[ST_MASK_ID_GYRO].sip;
min_pattern = MIN(min_pattern,
hwfifo_watermark_gyro /
cdata->fifo_output[ST_MASK_ID_GYRO].sip);
}
#ifdef CONFIG_ST_LSM6DS3H_IIO_MASTER_SUPPORT
if (cdata->fifo_output[ST_MASK_ID_EXT0].sip > 0) {
sip += cdata->fifo_output[ST_MASK_ID_EXT0].sip;
min_pattern = MIN(min_pattern,
cdata->hwfifo_watermark[ST_MASK_ID_EXT0] /
cdata->fifo_output[ST_MASK_ID_EXT0].sip);
}
#endif /* CONFIG_ST_LSM6DS3H_IIO_MASTER_SUPPORT */
if (sip == 0)
return 0;
if (min_pattern == 0)
min_pattern = 1;
min_pattern = MIN(min_pattern, ((unsigned int)ST_LSM6DS3H_MAX_FIFO_THRESHOLD / sip));
fifo_len = min_pattern * sip * ST_LSM6DS3H_FIFO_ELEMENT_LEN_BYTE;
fifo_watermark = (fifo_len / 2);
if (fifo_watermark < (ST_LSM6DS3H_FIFO_ELEMENT_LEN_BYTE / 2))
fifo_watermark = ST_LSM6DS3H_FIFO_ELEMENT_LEN_BYTE / 2;
if (fifo_watermark != cdata->fifo_watermark) {
err = cdata->tf->read(cdata, ST_LSM6DS3H_FIFO_THR_H_ADDR, 1, &reg_value, true);
if (err < 0)
return err;
fifo_watermark = (fifo_watermark & ST_LSM6DS3H_FIFO_THR_MASK) |
((reg_value & ~ST_LSM6DS3H_FIFO_THR_MASK) << 8);
err = cdata->tf->write(cdata, ST_LSM6DS3H_FIFO_THR_L_ADDR, 2,
(u8 *)&fifo_watermark, true);
if (err < 0)
return err;
cdata->fifo_watermark = fifo_watermark;
}
return 0;
}
#ifdef CONFIG_ST_LSM6DS3H_IIO_TAP_TAP_ENABLED
/*
* Configure Threshold, Duration, Quiet and Shock time.
*/
static int lsm6ds3h_config_tap_tap(struct lsm6ds3h_data *cdata)
{
int err;
err = st_lsm6ds3h_write_data_with_mask(cdata,
ST_LSM6DS3H_TAP_THS_6D_ADDR,
ST_LSM6DS3H_TAP_THS_MASK,
ST_LSM6DS3H_TAP_THS_DEF_VAL, false);
if (err < 0)
return err;
err = st_lsm6ds3h_write_data_with_mask(cdata,
ST_LSM6DS3H_INT_DUR2_ADDR,
ST_LSM6DS3H_INT_DUR2_MASK,
ST_LSM6DS3H_INT_DUR2_DEF_VAL, false);
if (err < 0)
return err;
err = st_lsm6ds3h_write_data_with_mask(cdata,
ST_LSM6DS3H_WAKE_UP_THS_ADDR,
ST_LSM6DS3H_WAKE_UP_THS_SINGLE_DOUBLE_TAP_MASK,
ST_LSM6DS3H_EN_BIT, false);
if (err < 0)
return err;
return 0;
}
int lsm6ds3h_get_fifo_odr_value(struct lsm6ds3h_data *cdata)
{
int i, fifo_odr = 0, odr_value = 0;
fifo_odr = cdata->fifo_odr;
if (fifo_odr <= 0)
return ST_LSM6DS3H_ODR_POWER_OFF_VAL;
for (i = 0; i < ST_LSM6DS3H_ODR_LIST_NUM; i++) {
if (st_lsm6ds3h_odr_table.odr_avl[i].hz == fifo_odr)
break;
}
if (i == ST_LSM6DS3H_ODR_LIST_NUM)
return -EINVAL;
odr_value = st_lsm6ds3h_odr_table.odr_avl[i].value;
return odr_value;
}
#endif /* CONFIG_ST_LSM6DS3H_IIO_TAP_TAP_ENABLED */
int st_lsm6ds3h_set_fifo_mode(struct lsm6ds3h_data *cdata, enum fifo_mode fm)
{
int err;
int fifo_odr = 0;
u8 reg_value;
bool enable_fifo;
struct timespec ts;
switch (fm) {
case BYPASS:
reg_value = ST_LSM6DS3H_FIFO_MODE_BYPASS;
enable_fifo = false;
break;
case CONTINUOS:
if (cdata->sensors_enabled & BIT(ST_MASK_ID_TAP_TAP)) {
fifo_odr = lsm6ds3h_get_fifo_odr_value(cdata);
if (fifo_odr < 0)
return -EINVAL;
reg_value = (ST_LSM6DS3H_FIFO_MODE_CONTINUOS | (fifo_odr << 3));
} else {
reg_value = ST_LSM6DS3H_FIFO_MODE_CONTINUOS | ST_LSM6DS3H_FIFO_ODR_MAX;
}
enable_fifo = true;
break;
default:
return -EINVAL;
}
err = cdata->tf->write(cdata, ST_LSM6DS3H_FIFO_MODE_ADDR, 1, &reg_value, true);
if (err < 0)
return err;
if (enable_fifo) {
get_monotonic_boottime(&ts);
cdata->timestamp = cdata->last_timestamp = timespec_to_ns(&ts);
cdata->fifo_output[ST_MASK_ID_GYRO].timestamp = 0;
cdata->fifo_output[ST_MASK_ID_ACCEL].timestamp = 0;
#ifdef CONFIG_ST_LSM6DS3H_IIO_MASTER_SUPPORT
cdata->fifo_output[ST_MASK_ID_EXT0].timestamp = 0;
#endif /* CONFIG_ST_LSM6DS3H_IIO_MASTER_SUPPORT */
}
cdata->fifo_status = fm;
return 0;
}
EXPORT_SYMBOL(st_lsm6ds3h_set_fifo_mode);
static int lsm6ds3h_write_decimators(struct lsm6ds3h_data *cdata,
u8 decimators[3])
{
int i;
u8 value[3], decimators_reg[2];
for (i = 0; i < 3; i++) {
switch (decimators[i]) {
case 0:
case 1:
case 2:
case 3:
case 4:
value[i] = decimators[i];
break;
case 8:
value[i] = 0x05;
break;
case 16:
value[i] = 0x06;
break;
case 32:
value[i] = 0x07;
break;
default:
return -EINVAL;
}
}
decimators_reg[0] = value[0] | (value[1] << 3);
decimators_reg[1] = value[2];
#ifndef CONFIG_ST_LSM6DS3H_IIO_ALGO_DISABLED
if (cdata->fifo2_algo_available)
decimators_reg[1] |= ST_LSM6DS3H_RESERVE_HALF_FIFO;
#endif /* CONFIG_ST_LSM6DS3H_IIO_ALGO_DISABLED */
return cdata->tf->write(cdata, ST_LSM6DS3H_FIFO_DECIMATOR_ADDR,
ARRAY_SIZE(decimators_reg), decimators_reg, true);
}
static bool lsm6ds3h_calculate_fifo_decimators(struct lsm6ds3h_data *cdata,
u8 decimators[3], u8 samples_in_pattern[3],
unsigned int new_v_odr[ST_INDIO_DEV_NUM + 1],
unsigned int new_hw_odr[ST_INDIO_DEV_NUM + 1],
int64_t new_deltatime[ST_INDIO_DEV_NUM + 1],
short new_fifo_decimator[ST_INDIO_DEV_NUM + 1])
{
unsigned int trigger_odr;
u8 min_decimator, max_decimator = 0;
u8 accel_decimator = 0, gyro_decimator = 0;
#ifdef CONFIG_ST_LSM6DS3H_IIO_MASTER_SUPPORT
u8 ext_decimator = 0;
#endif /* CONFIG_ST_LSM6DS3H_IIO_MASTER_SUPPORT */
trigger_odr = new_hw_odr[ST_MASK_ID_ACCEL];
if (trigger_odr < new_hw_odr[ST_MASK_ID_GYRO])
trigger_odr = new_hw_odr[ST_MASK_ID_GYRO];
if ((cdata->sensors_use_fifo & BIT(ST_MASK_ID_ACCEL)) &&
(new_v_odr[ST_MASK_ID_ACCEL] != 0) && (cdata->accel_on || cdata->accel_wk_on))
accel_decimator = trigger_odr / new_v_odr[ST_MASK_ID_ACCEL];
if ((cdata->sensors_use_fifo & BIT(ST_MASK_ID_GYRO)) &&
(new_v_odr[ST_MASK_ID_GYRO] != 0) &&
(new_hw_odr[ST_MASK_ID_GYRO] > 0))
gyro_decimator = trigger_odr / new_v_odr[ST_MASK_ID_GYRO];
#ifdef CONFIG_ST_LSM6DS3H_IIO_MASTER_SUPPORT
if ((cdata->sensors_use_fifo & BIT(ST_MASK_ID_EXT0)) &&
(new_v_odr[ST_MASK_ID_EXT0] != 0) && cdata->magn_on)
ext_decimator = trigger_odr / new_v_odr[ST_MASK_ID_EXT0];
new_fifo_decimator[ST_MASK_ID_EXT0] = 1;
#endif /* CONFIG_ST_LSM6DS3H_IIO_MASTER_SUPPORT */
new_fifo_decimator[ST_MASK_ID_ACCEL] = 1;
new_fifo_decimator[ST_MASK_ID_GYRO] = 1;
#ifdef CONFIG_ST_LSM6DS3H_IIO_MASTER_SUPPORT
if ((accel_decimator != 0) || (gyro_decimator != 0) || (ext_decimator != 0)) {
min_decimator = MIN_BNZ(MIN_BNZ(accel_decimator, gyro_decimator), ext_decimator);
max_decimator = MAX(MAX(accel_decimator, gyro_decimator), ext_decimator);
#else
if ((accel_decimator != 0) || (gyro_decimator != 0)) {
min_decimator = MIN_BNZ(accel_decimator, gyro_decimator);
max_decimator = MAX(accel_decimator, gyro_decimator);
#endif
if (min_decimator != 1) {
if ((accel_decimator / min_decimator) == 1) {
accel_decimator = 1;
new_fifo_decimator[ST_MASK_ID_ACCEL] = min_decimator;
} else if ((gyro_decimator / min_decimator) == 1) {
gyro_decimator = 1;
new_fifo_decimator[ST_MASK_ID_GYRO] = min_decimator;
#ifdef CONFIG_ST_LSM6DS3H_IIO_MASTER_SUPPORT
} else if ((ext_decimator / min_decimator) == 1) {
ext_decimator = 1;
new_fifo_decimator[ST_MASK_ID_EXT0] = min_decimator;
#endif /* CONFIG_ST_LSM6DS3H_IIO_MASTER_SUPPORT */
}
min_decimator = 1;
}
if ((accel_decimator > 4) && (accel_decimator < 8)) {
new_fifo_decimator[ST_MASK_ID_ACCEL] = accel_decimator - 3;
accel_decimator = 4;
} else if ((accel_decimator > 8) && (accel_decimator < 16)) {
new_fifo_decimator[ST_MASK_ID_ACCEL] = accel_decimator - 7;
accel_decimator = 8;
}
if ((gyro_decimator > 4) && (gyro_decimator < 8)) {
new_fifo_decimator[ST_MASK_ID_GYRO] = gyro_decimator - 3;
gyro_decimator = 4;
} else if ((gyro_decimator > 8) && (gyro_decimator < 16)) {
new_fifo_decimator[ST_MASK_ID_GYRO] = gyro_decimator - 7;
gyro_decimator = 8;
}
#ifdef CONFIG_ST_LSM6DS3H_IIO_MASTER_SUPPORT
if ((ext_decimator > 4) && (ext_decimator < 8)) {
new_fifo_decimator[ST_MASK_ID_EXT0] = ext_decimator - 3;
ext_decimator = 4;
} else if ((ext_decimator > 8) && (ext_decimator < 16)) {
new_fifo_decimator[ST_MASK_ID_EXT0] = ext_decimator - 7;
ext_decimator = 8;
}
max_decimator = MAX(MAX(accel_decimator, gyro_decimator), ext_decimator);
#else
max_decimator = MAX(accel_decimator, gyro_decimator);
#endif /* CONFIG_ST_LSM6DS3H_IIO_MASTER_SUPPORT */
}
decimators[0] = accel_decimator;
if (accel_decimator > 0) {
new_deltatime[ST_MASK_ID_ACCEL] = accel_decimator *
(1000000000U / trigger_odr);
samples_in_pattern[0] = max_decimator / accel_decimator;
} else
samples_in_pattern[0] = 0;
decimators[1] = gyro_decimator;
if (gyro_decimator > 0) {
new_deltatime[ST_MASK_ID_GYRO] = gyro_decimator *
(1000000000U / trigger_odr);
samples_in_pattern[1] = max_decimator / gyro_decimator;
} else
samples_in_pattern[1] = 0;
#ifdef CONFIG_ST_LSM6DS3H_IIO_MASTER_SUPPORT
decimators[2] = ext_decimator;
if (ext_decimator > 0) {
new_deltatime[ST_MASK_ID_EXT0] = ext_decimator *
(1000000000U / trigger_odr);
samples_in_pattern[2] = max_decimator / ext_decimator;
} else
samples_in_pattern[2] = 0;
#endif /* CONFIG_ST_LSM6DS3H_IIO_MASTER_SUPPORT */
#ifdef CONFIG_ST_LSM6DS3H_IIO_MASTER_SUPPORT
if ((accel_decimator == cdata->hwfifo_decimator[ST_MASK_ID_ACCEL]) &&
(ext_decimator == cdata->hwfifo_decimator[ST_MASK_ID_EXT0]) &&
(gyro_decimator == cdata->hwfifo_decimator[ST_MASK_ID_GYRO])) {
#else /* CONFIG_ST_LSM6DS3H_IIO_MASTER_SUPPORT */
if ((accel_decimator == cdata->hwfifo_decimator[ST_MASK_ID_ACCEL]) &&
(gyro_decimator == cdata->hwfifo_decimator[ST_MASK_ID_GYRO])) {
#endif /* CONFIG_ST_LSM6DS3_IIO_MASTER_SUPPORT */
return false;
}
return true;
}
int st_lsm6ds3h_flush_work_fifo(struct lsm6ds3h_data *cdata,
bool disable_irq_and_flush)
{
int err;
dev_dbg(cdata->dev, "st_lsm6ds3h_flush_work_fifo!\n");
if (disable_irq_and_flush) {
disable_irq(cdata->irq);
st_lsm6ds3h_flush_workqueue(cdata);
}
mutex_lock(&cdata->fifo_lock);
err = st_lsm6ds3h_read_fifo(cdata, READ_FIFO_IN_COF_FIFO);
if (err < 0)
dev_warn(cdata->dev,"Read_fifo error in st_lsm6ds3h_flush_work_fifo!\n");
mutex_unlock(&cdata->fifo_lock);
if (disable_irq_and_flush)
enable_irq(cdata->irq);
return err;
}
int st_lsm6ds3h_set_drdy_irq(struct lsm6ds3h_sensor_data *sdata, bool state)
{
int err;
u16 *irq_mask = NULL;
u8 reg_addr, mask = 0, value;
u16 tmp_irq_enable_fifo_mask, tmp_irq_enable_accel_ext_mask;
if (state)
value = ST_LSM6DS3H_EN_BIT;
else
value = ST_LSM6DS3H_DIS_BIT;
tmp_irq_enable_fifo_mask =
sdata->cdata->irq_enable_fifo_mask & ~sdata->sindex;
tmp_irq_enable_accel_ext_mask =
sdata->cdata->irq_enable_accel_ext_mask & ~sdata->sindex;
switch (sdata->sindex) {
case ST_MASK_ID_ACCEL:
case ST_MASK_ID_ACCEL_WK:
reg_addr = ST_LSM6DS3H_INT1_ADDR;
if (sdata->cdata->hwfifo_enabled[ST_MASK_ID_ACCEL]) {
if (tmp_irq_enable_fifo_mask == 0)
mask = ST_LSM6DS3H_FIFO_THR_IRQ_MASK;
irq_mask = &sdata->cdata->irq_enable_fifo_mask;
} else {
if (tmp_irq_enable_accel_ext_mask == 0)
mask = ST_LSM6DS3H_ACCEL_DRDY_IRQ_MASK;
irq_mask = &sdata->cdata->irq_enable_accel_ext_mask;
}
break;
case ST_MASK_ID_GYRO:
case ST_MASK_ID_GYRO_WK:
reg_addr = ST_LSM6DS3H_INT1_ADDR;
if (sdata->cdata->hwfifo_enabled[ST_MASK_ID_GYRO]) {
if (tmp_irq_enable_fifo_mask == 0)
mask = ST_LSM6DS3H_FIFO_THR_IRQ_MASK;
irq_mask = &sdata->cdata->irq_enable_fifo_mask;
} else
mask = ST_LSM6DS3H_GYRO_DRDY_IRQ_MASK;
break;
case ST_MASK_ID_SIGN_MOTION:
reg_addr = ST_LSM6DS3H_INT1_ADDR;
mask = ST_LSM6DS3H_SIGN_MOTION_DRDY_IRQ_MASK;
break;
case ST_MASK_ID_STEP_COUNTER:
reg_addr = ST_LSM6DS3H_INT2_ADDR;
mask = ST_LSM6DS3H_STEP_COUNTER_DRDY_IRQ_MASK;
break;
case ST_MASK_ID_STEP_DETECTOR:
reg_addr = ST_LSM6DS3H_INT1_ADDR;
mask = ST_LSM6DS3H_STEP_DETECTOR_DRDY_IRQ_MASK;
break;
case ST_MASK_ID_TILT:
reg_addr = ST_LSM6DS3H_MD1_ADDR;
mask = ST_LSM6DS3H_TILT_DRDY_IRQ_MASK;
break;
#ifdef CONFIG_ST_LSM6DS3H_IIO_TAP_TAP_ENABLED
case ST_MASK_ID_TAP_TAP:
reg_addr = ST_LSM6DS3H_MD1_ADDR;
mask = ST_LSM6DS3H_TAP_TAP_DRDY_IRQ_MASK;
break;
#endif /* CONFIG_ST_LSM6DS3H_IIO_TAP_TAP_ENABLED */
#ifdef CONFIG_ST_LSM6DS3H_IIO_ALGO_UPLOAD_WRIST_TILT
case ST_MASK_ID_WRIST_TILT:
reg_addr = ST_LSM6DS3H_MD2_ADDR;
mask = ST_LSM6DS3H_WRIST_TILT_DRDY_IRQ_MASK;
break;
#endif /* CONFIG_ST_LSM6DS3H_IIO_ALGO_UPLOAD_WRIST_TILT */
#ifdef CONFIG_ST_LSM6DS3H_IIO_MASTER_SUPPORT
case ST_MASK_ID_EXT0:
reg_addr = ST_LSM6DS3H_INT1_ADDR;
if (sdata->cdata->hwfifo_enabled[ST_MASK_ID_EXT0]) {
if (tmp_irq_enable_fifo_mask == 0)
mask = ST_LSM6DS3H_FIFO_THR_IRQ_MASK;
irq_mask = &sdata->cdata->irq_enable_fifo_mask;
} else {
if (tmp_irq_enable_accel_ext_mask == 0)
mask = ST_LSM6DS3H_ACCEL_DRDY_IRQ_MASK;
irq_mask = &sdata->cdata->irq_enable_accel_ext_mask;
}
break;
#endif /* CONFIG_ST_LSM6DS3H_IIO_MASTER_SUPPORT */
default:
return -EINVAL;
}
if (mask > 0) {
err = st_lsm6ds3h_write_data_with_mask(sdata->cdata,
reg_addr, mask, value, true);
if (err < 0)
return err;
}
if (irq_mask != NULL) {
if (state)
*irq_mask |= BIT(sdata->sindex);
else
*irq_mask &= ~BIT(sdata->sindex);
}
return 0;
}
EXPORT_SYMBOL(st_lsm6ds3h_set_drdy_irq);
static int st_lsm6ds3h_set_odr(struct lsm6ds3h_sensor_data *sdata,
unsigned int odr, bool force)
{
u8 reg_value;
int err, i = 0;
bool scan_odr = true, fifo_conf_changed;
u8 fifo_decimator[3] = { 0 }, samples_in_pattern[3] = { 0 };
unsigned int temp_v_odr[ST_INDIO_DEV_NUM + 1];
unsigned int temp_hw_odr[ST_INDIO_DEV_NUM + 1];
int64_t new_deltatime[ST_INDIO_DEV_NUM + 1] = { 0 };
short new_fifo_decimator[ST_INDIO_DEV_NUM + 1] = { 0 };
dev_dbg(sdata->cdata->dev, "st_lsm6ds3h_set_odr: index=%d, odr=%d, force=%d\n",
sdata->sindex, odr, force);
if (odr == 0) {
if (force)
scan_odr = false;
else
return -EINVAL;
}
if (scan_odr) {
for (i = 0; i < ST_LSM6DS3H_ODR_LIST_NUM; i++) {
if (st_lsm6ds3h_odr_table.odr_avl[i].hz == odr)
break;
}
if (i == ST_LSM6DS3H_ODR_LIST_NUM)
return -EINVAL;
if (sdata->cdata->hw_odr[sdata->sindex] == st_lsm6ds3h_odr_table.odr_avl[i].hz)
reg_value = 0xff;
else
reg_value = st_lsm6ds3h_odr_table.odr_avl[i].value;
} else {
reg_value = ST_LSM6DS3H_ODR_POWER_OFF_VAL;
}
#ifdef CONFIG_ST_LSM6DS3H_IIO_TAP_TAP_ENABLED
if (((sdata->sindex == ST_MASK_ID_ACCEL)
|| (sdata->sindex == ST_MASK_ID_ACCEL_WK))
&& (reg_value != 0xff))
{
if (sdata->cdata->sensors_enabled & BIT(ST_MASK_ID_TAP_TAP)) {
reg_value = ST_LSM6DS3H_ODR_416HZ_VAL;
}
}
#endif /* CONFIG_ST_LSM6DS3H_IIO_TAP_TAP_ENABLED */
if (sdata->cdata->sensors_use_fifo > 0) {
/* someone is using fifo */
if ((sdata->sindex == ST_MASK_ID_ACCEL) ||
(sdata->sindex == ST_MASK_ID_ACCEL_WK)) {
temp_v_odr[ST_MASK_ID_ACCEL] = odr;
temp_v_odr[ST_MASK_ID_GYRO] = sdata->cdata->v_odr[ST_MASK_ID_GYRO];
temp_hw_odr[ST_MASK_ID_ACCEL] = odr;
temp_hw_odr[ST_MASK_ID_GYRO] = sdata->cdata->hw_odr[ST_MASK_ID_GYRO];
} else {
temp_v_odr[ST_MASK_ID_GYRO] = odr;
temp_v_odr[ST_MASK_ID_ACCEL] = sdata->cdata->v_odr[ST_MASK_ID_ACCEL];
temp_hw_odr[ST_MASK_ID_GYRO] = odr;
temp_hw_odr[ST_MASK_ID_ACCEL] = sdata->cdata->hw_odr[ST_MASK_ID_ACCEL];
}
sdata->cdata->fifo_odr = MAX(temp_hw_odr[ST_MASK_ID_ACCEL], temp_hw_odr[ST_MASK_ID_GYRO]);
#ifdef CONFIG_ST_LSM6DS3H_IIO_MASTER_SUPPORT
temp_v_odr[ST_MASK_ID_EXT0] = sdata->cdata->v_odr[ST_MASK_ID_EXT0];
#endif /* CONFIG_ST_LSM6DS3H_IIO_MASTER_SUPPORT */
fifo_conf_changed = lsm6ds3h_calculate_fifo_decimators(sdata->cdata,
fifo_decimator, samples_in_pattern, temp_v_odr,
temp_hw_odr, new_deltatime, new_fifo_decimator);
if (fifo_conf_changed) {
/* FIFO configuration changed, needs to write new decimators */
disable_irq(sdata->cdata->irq);
if (sdata->cdata->fifo_status != BYPASS) {
mutex_lock(&sdata->cdata->fifo_lock);
st_lsm6ds3h_read_fifo(sdata->cdata, READ_FIFO_IN_COF_FIFO);
mutex_unlock(&sdata->cdata->fifo_lock);
err = st_lsm6ds3h_set_fifo_mode(sdata->cdata, BYPASS);
if (err < 0)
goto reenable_fifo_irq;
}
err = lsm6ds3h_write_decimators(sdata->cdata, fifo_decimator);
if (err < 0)
goto reenable_fifo_irq;
if (reg_value != 0xff) {
err = st_lsm6ds3h_write_data_with_mask(sdata->cdata,
st_lsm6ds3h_odr_table.addr[sdata->sindex],
st_lsm6ds3h_odr_table.mask[sdata->sindex],
reg_value, true);
if (err < 0)
goto reenable_fifo_irq;
if ((sdata->sindex == ST_MASK_ID_ACCEL) ||
(sdata->sindex == ST_MASK_ID_ACCEL_WK)) {
switch (temp_hw_odr[ST_MASK_ID_ACCEL]) {
case 13:
case 26:
case 52:
sdata->cdata->samples_to_discard[ST_MASK_ID_ACCEL] = ST_LSM6DS3H_ACCEL_STD_52HZ;
break;
case 104:
sdata->cdata->samples_to_discard[ST_MASK_ID_ACCEL] = ST_LSM6DS3H_ACCEL_STD_104HZ;
break;
default:
sdata->cdata->samples_to_discard[ST_MASK_ID_ACCEL] = ST_LSM6DS3H_ACCEL_STD_208HZ;
break;
}
}
switch (temp_hw_odr[ST_MASK_ID_GYRO]) {
case 13:
sdata->cdata->samples_to_discard[ST_MASK_ID_GYRO] = ST_LSM6DS3H_GYRO_STD_13HZ;
break;
case 26:
case 52:
sdata->cdata->samples_to_discard[ST_MASK_ID_GYRO] = ST_LSM6DS3H_GYRO_STD_52HZ;
break;
case 104:
sdata->cdata->samples_to_discard[ST_MASK_ID_GYRO] = ST_LSM6DS3H_GYRO_STD_104HZ;
break;
default:
sdata->cdata->samples_to_discard[ST_MASK_ID_GYRO] = ST_LSM6DS3H_GYRO_STD_208HZ;
break;
}
}
sdata->cdata->hwfifo_decimator[ST_MASK_ID_ACCEL] = fifo_decimator[0];
sdata->cdata->hwfifo_decimator[ST_MASK_ID_GYRO] = fifo_decimator[1];
sdata->cdata->fifo_output[ST_MASK_ID_ACCEL].deltatime = new_deltatime[ST_MASK_ID_ACCEL];
sdata->cdata->fifo_output[ST_MASK_ID_GYRO].deltatime = new_deltatime[ST_MASK_ID_GYRO];
sdata->cdata->fifo_output[ST_MASK_ID_ACCEL].decimator = new_fifo_decimator[ST_MASK_ID_ACCEL];
sdata->cdata->fifo_output[ST_MASK_ID_GYRO].decimator = new_fifo_decimator[ST_MASK_ID_GYRO];
sdata->cdata->fifo_output[ST_MASK_ID_ACCEL].num_samples = new_fifo_decimator[ST_MASK_ID_ACCEL] - 1;
sdata->cdata->fifo_output[ST_MASK_ID_GYRO].num_samples = new_fifo_decimator[ST_MASK_ID_GYRO] - 1;
sdata->cdata->fifo_output[ST_MASK_ID_ACCEL].sip = samples_in_pattern[0];
sdata->cdata->fifo_output[ST_MASK_ID_GYRO].sip = samples_in_pattern[1];
#ifdef CONFIG_ST_LSM6DS3H_IIO_MASTER_SUPPORT
sdata->cdata->hwfifo_decimator[ST_MASK_ID_EXT0] = fifo_decimator[2];
sdata->cdata->fifo_output[ST_MASK_ID_EXT0].deltatime = new_deltatime[ST_MASK_ID_EXT0];
sdata->cdata->fifo_output[ST_MASK_ID_EXT0].decimator = new_fifo_decimator[ST_MASK_ID_EXT0];
sdata->cdata->fifo_output[ST_MASK_ID_EXT0].num_samples = new_fifo_decimator[ST_MASK_ID_EXT0] - 1;
sdata->cdata->fifo_output[ST_MASK_ID_EXT0].sip = samples_in_pattern[2];
#endif /* CONFIG_ST_LSM6DS3H_IIO_MASTER_SUPPORT */
#ifdef CONFIG_ST_LSM6DS3H_IIO_MASTER_SUPPORT
sdata->cdata->byte_in_pattern = (sdata->cdata->fifo_output[ST_MASK_ID_ACCEL].sip+sdata->cdata->fifo_output[ST_MASK_ID_GYRO].sip+sdata->cdata->fifo_output[ST_MASK_ID_EXT0].sip)*ST_LSM6DS3H_FIFO_ELEMENT_LEN_BYTE;
#else
sdata->cdata->byte_in_pattern = (sdata->cdata->fifo_output[ST_MASK_ID_ACCEL].sip+sdata->cdata->fifo_output[ST_MASK_ID_GYRO].sip)*ST_LSM6DS3H_FIFO_ELEMENT_LEN_BYTE;
#endif
err = lsm6ds3h_set_watermark(sdata->cdata);
if (err < 0)
goto reenable_fifo_irq;
if ((samples_in_pattern[0] > 0) || (samples_in_pattern[1] > 0) || (samples_in_pattern[2] > 0)) {
err = st_lsm6ds3h_set_fifo_mode(sdata->cdata, CONTINUOS);
if (err < 0)
goto reenable_fifo_irq;
}
enable_irq(sdata->cdata->irq);
} else {
/* FIFO configuration not changed */
if (reg_value == 0xff)
return 0;
disable_irq(sdata->cdata->irq);
if (sdata->cdata->fifo_status != BYPASS) {
mutex_lock(&sdata->cdata->fifo_lock);
st_lsm6ds3h_read_fifo(sdata->cdata, READ_FIFO_IN_COF_FIFO);
mutex_unlock(&sdata->cdata->fifo_lock);
err = st_lsm6ds3h_set_fifo_mode(sdata->cdata, BYPASS);
if (err < 0)
goto reenable_fifo_irq;
}
err = st_lsm6ds3h_write_data_with_mask(sdata->cdata,
st_lsm6ds3h_odr_table.addr[sdata->sindex],
st_lsm6ds3h_odr_table.mask[sdata->sindex],
reg_value, true);
if (err < 0)
goto reenable_fifo_irq;
if ((sdata->sindex == ST_MASK_ID_ACCEL) ||
(sdata->sindex == ST_MASK_ID_ACCEL_WK)) {
switch (temp_hw_odr[ST_MASK_ID_ACCEL]) {
case 13:
case 26:
case 52:
sdata->cdata->samples_to_discard[ST_MASK_ID_ACCEL] = ST_LSM6DS3H_ACCEL_STD_52HZ;
break;
case 104:
sdata->cdata->samples_to_discard[ST_MASK_ID_ACCEL] = ST_LSM6DS3H_ACCEL_STD_104HZ;
break;
default:
sdata->cdata->samples_to_discard[ST_MASK_ID_ACCEL] = ST_LSM6DS3H_ACCEL_STD_208HZ;
break;
}
}
switch (temp_hw_odr[ST_MASK_ID_GYRO]) {
case 13:
sdata->cdata->samples_to_discard[ST_MASK_ID_GYRO] = ST_LSM6DS3H_GYRO_STD_13HZ;
break;
case 26:
case 52:
sdata->cdata->samples_to_discard[ST_MASK_ID_GYRO] = ST_LSM6DS3H_GYRO_STD_52HZ;
break;
case 104:
sdata->cdata->samples_to_discard[ST_MASK_ID_GYRO] = ST_LSM6DS3H_GYRO_STD_104HZ;
break;
default:
sdata->cdata->samples_to_discard[ST_MASK_ID_GYRO] = ST_LSM6DS3H_GYRO_STD_208HZ;
break;
}
sdata->cdata->fifo_output[ST_MASK_ID_ACCEL].deltatime = new_deltatime[ST_MASK_ID_ACCEL];
sdata->cdata->fifo_output[ST_MASK_ID_GYRO].deltatime = new_deltatime[ST_MASK_ID_GYRO];
#ifdef CONFIG_ST_LSM6DS3H_IIO_MASTER_SUPPORT
sdata->cdata->fifo_output[ST_MASK_ID_EXT0].deltatime = new_deltatime[ST_MASK_ID_EXT0];
#endif /* CONFIG_ST_LSM6DS3H_IIO_MASTER_SUPPORT */
#ifdef CONFIG_ST_LSM6DS3H_IIO_MASTER_SUPPORT
sdata->cdata->byte_in_pattern = (sdata->cdata->fifo_output[ST_MASK_ID_ACCEL].sip+sdata->cdata->fifo_output[ST_MASK_ID_GYRO].sip+sdata->cdata->fifo_output[ST_MASK_ID_EXT0].sip)*ST_LSM6DS3H_FIFO_ELEMENT_LEN_BYTE;
#else
sdata->cdata->byte_in_pattern = (sdata->cdata->fifo_output[ST_MASK_ID_ACCEL].sip+sdata->cdata->fifo_output[ST_MASK_ID_GYRO].sip)*ST_LSM6DS3H_FIFO_ELEMENT_LEN_BYTE;
#endif
if ((sdata->cdata->fifo_output[ST_MASK_ID_ACCEL].sip > 0) ||
(sdata->cdata->fifo_output[ST_MASK_ID_GYRO].sip > 0)
#ifdef CONFIG_ST_LSM6DS3H_IIO_MASTER_SUPPORT
|| (sdata->cdata->fifo_output[ST_MASK_ID_EXT0].sip > 0)
#endif /* CONFIG_ST_LSM6DS3H_IIO_MASTER_SUPPORT */
) {
err = st_lsm6ds3h_set_fifo_mode(sdata->cdata, CONTINUOS);
if (err < 0)
goto reenable_fifo_irq;
}
enable_irq(sdata->cdata->irq);
}
if (!force) {
sdata->cdata->v_odr[sdata->sindex] = st_lsm6ds3h_odr_table.odr_avl[i].hz;
sdata->cdata->accel_odr_dependency[0] = sdata->cdata->v_odr[sdata->sindex];
}
switch (sdata->sindex) {
case ST_MASK_ID_ACCEL:
case ST_MASK_ID_ACCEL_WK:
if (odr == 0)
sdata->cdata->hw_odr[ST_MASK_ID_ACCEL] = sdata->cdata->hw_odr[ST_MASK_ID_ACCEL_WK] = 0;
else
sdata->cdata->hw_odr[ST_MASK_ID_ACCEL] = sdata->cdata->hw_odr[ST_MASK_ID_ACCEL_WK] = st_lsm6ds3h_odr_table.odr_avl[i].hz;
break;
case ST_MASK_ID_GYRO:
case ST_MASK_ID_GYRO_WK:
if (odr == 0)
sdata->cdata->hw_odr[ST_MASK_ID_GYRO] = sdata->cdata->hw_odr[ST_MASK_ID_GYRO_WK] = 0;
else
sdata->cdata->hw_odr[ST_MASK_ID_GYRO] = sdata->cdata->hw_odr[ST_MASK_ID_GYRO_WK] = st_lsm6ds3h_odr_table.odr_avl[i].hz;
break;
default:
if (odr == 0)
sdata->cdata->hw_odr[sdata->sindex] = 0;
else
sdata->cdata->hw_odr[sdata->sindex] = st_lsm6ds3h_odr_table.odr_avl[i].hz;
break;
}
} else {
/* no one is using FIFO */
disable_irq(sdata->cdata->irq);
if ((odr != 0) && (sdata->cdata->hw_odr[sdata->sindex] == st_lsm6ds3h_odr_table.odr_avl[i].hz)) {
if ((sdata->sindex == ST_MASK_ID_ACCEL) ||
(sdata->sindex == ST_MASK_ID_ACCEL_WK)) {
sdata->cdata->nofifo_decimation[ST_MASK_ID_ACCEL].decimator =
sdata->cdata->hw_odr[ST_MASK_ID_ACCEL] / sdata->cdata->v_odr[ST_MASK_ID_ACCEL];
sdata->cdata->nofifo_decimation[ST_MASK_ID_ACCEL].num_samples =
sdata->cdata->nofifo_decimation[ST_MASK_ID_ACCEL].decimator - 1;
#ifdef CONFIG_ST_LSM6DS3H_IIO_MASTER_SUPPORT
sdata->cdata->nofifo_decimation[ST_MASK_ID_EXT0].decimator =
sdata->cdata->hw_odr[ST_MASK_ID_ACCEL] / sdata->cdata->v_odr[ST_MASK_ID_EXT0];
sdata->cdata->nofifo_decimation[ST_MASK_ID_EXT0].num_samples =
sdata->cdata->nofifo_decimation[ST_MASK_ID_EXT0].decimator - 1;
#endif /* CONFIG_ST_LSM6DS3H_IIO_MASTER_SUPPORT */
}
enable_irq(sdata->cdata->irq);
return 0;
}
err = st_lsm6ds3h_write_data_with_mask(sdata->cdata,
st_lsm6ds3h_odr_table.addr[sdata->sindex],
st_lsm6ds3h_odr_table.mask[sdata->sindex],
reg_value, true);
if (err < 0) {
enable_irq(sdata->cdata->irq);
return err;
}
if (!force) {
sdata->cdata->v_odr[sdata->sindex] = st_lsm6ds3h_odr_table.odr_avl[i].hz;
sdata->cdata->accel_odr_dependency[0] = st_lsm6ds3h_odr_table.odr_avl[i].hz;
}
switch (sdata->sindex) {
case ST_MASK_ID_ACCEL:
case ST_MASK_ID_ACCEL_WK:
if (odr == 0)
sdata->cdata->hw_odr[ST_MASK_ID_ACCEL] = sdata->cdata->hw_odr[ST_MASK_ID_ACCEL_WK] = 0;
else
sdata->cdata->hw_odr[ST_MASK_ID_ACCEL] = sdata->cdata->hw_odr[ST_MASK_ID_ACCEL_WK] = st_lsm6ds3h_odr_table.odr_avl[i].hz;
break;
case ST_MASK_ID_GYRO:
case ST_MASK_ID_GYRO_WK:
if (odr == 0)
sdata->cdata->hw_odr[ST_MASK_ID_GYRO] = sdata->cdata->hw_odr[ST_MASK_ID_GYRO_WK] = 0;
else
sdata->cdata->hw_odr[ST_MASK_ID_GYRO] = sdata->cdata->hw_odr[ST_MASK_ID_GYRO_WK] = st_lsm6ds3h_odr_table.odr_avl[i].hz;
break;
default:
if (odr == 0)
sdata->cdata->hw_odr[sdata->sindex] = 0;
else
sdata->cdata->hw_odr[sdata->sindex] = st_lsm6ds3h_odr_table.odr_avl[i].hz;
break;
}
if ((sdata->sindex == ST_MASK_ID_ACCEL) ||
(sdata->sindex == ST_MASK_ID_ACCEL_WK)) {
switch (sdata->cdata->hw_odr[sdata->sindex]) {
case 13:
case 26:
case 52:
sdata->cdata->samples_to_discard[ST_MASK_ID_ACCEL] = ST_LSM6DS3H_ACCEL_STD_52HZ;
break;
case 104:
sdata->cdata->samples_to_discard[ST_MASK_ID_ACCEL] = ST_LSM6DS3H_ACCEL_STD_104HZ;
break;
default:
sdata->cdata->samples_to_discard[ST_MASK_ID_ACCEL] = ST_LSM6DS3H_ACCEL_STD_208HZ;
break;
}
}
switch (sdata->cdata->hw_odr[ST_MASK_ID_GYRO]) {
case 13:
sdata->cdata->samples_to_discard[ST_MASK_ID_GYRO] = ST_LSM6DS3H_GYRO_STD_13HZ;
break;
case 26:
case 52:
sdata->cdata->samples_to_discard[ST_MASK_ID_GYRO] = ST_LSM6DS3H_GYRO_STD_52HZ;
break;
case 104:
sdata->cdata->samples_to_discard[ST_MASK_ID_GYRO] = ST_LSM6DS3H_GYRO_STD_104HZ;
break;
default:
sdata->cdata->samples_to_discard[ST_MASK_ID_GYRO] = ST_LSM6DS3H_GYRO_STD_208HZ;
break;
}
if ((sdata->sindex == ST_MASK_ID_ACCEL) ||
(sdata->sindex == ST_MASK_ID_ACCEL_WK)) {
if (sdata->cdata->hw_odr[sdata->sindex] > 0) {
sdata->cdata->nofifo_decimation[ST_MASK_ID_ACCEL].decimator =
sdata->cdata->hw_odr[ST_MASK_ID_ACCEL] / sdata->cdata->v_odr[ST_MASK_ID_ACCEL];
#ifdef CONFIG_ST_LSM6DS3H_IIO_MASTER_SUPPORT
sdata->cdata->nofifo_decimation[ST_MASK_ID_EXT0].decimator =
sdata->cdata->hw_odr[ST_MASK_ID_ACCEL] / sdata->cdata->v_odr[ST_MASK_ID_EXT0];
#endif /* CONFIG_ST_LSM6DS3H_IIO_MASTER_SUPPORT */
} else {
sdata->cdata->nofifo_decimation[ST_MASK_ID_ACCEL].decimator = 1;
#ifdef CONFIG_ST_LSM6DS3H_IIO_MASTER_SUPPORT
sdata->cdata->nofifo_decimation[ST_MASK_ID_EXT0].decimator = 1;
#endif /* CONFIG_ST_LSM6DS3H_IIO_MASTER_SUPPORT */
}
sdata->cdata->nofifo_decimation[ST_MASK_ID_ACCEL].num_samples =
sdata->cdata->nofifo_decimation[ST_MASK_ID_ACCEL].decimator - 1;
#ifdef CONFIG_ST_LSM6DS3H_IIO_MASTER_SUPPORT
sdata->cdata->nofifo_decimation[ST_MASK_ID_EXT0].num_samples =
sdata->cdata->nofifo_decimation[ST_MASK_ID_EXT0].decimator - 1;
#endif /* CONFIG_ST_LSM6DS3H_IIO_MASTER_SUPPORT */
}
enable_irq(sdata->cdata->irq);
}
sdata->cdata->trigger_odr = sdata->cdata->hw_odr[0] > sdata->cdata->hw_odr[1] ? sdata->cdata->hw_odr[0] : sdata->cdata->hw_odr[1];
return 0;
reenable_fifo_irq:
enable_irq(sdata->cdata->irq);
return err;
}
/*
* Enable / disable accelerometer
*/
static int lsm6ds3h_enable_accel(struct lsm6ds3h_data *cdata, enum st_mask_id id, int min_odr, bool enable)
{
int odr, err;
struct lsm6ds3h_sensor_data *sdata_accel = iio_priv(cdata->indio_dev[ST_MASK_ID_ACCEL]);
struct lsm6ds3h_sensor_data *sdata_accel_wk = iio_priv(cdata->indio_dev[ST_MASK_ID_ACCEL_WK]);
switch (id) {
case ST_MASK_ID_ACCEL:
cdata->accel_odr_dependency[0] = min_odr;
cdata->accel_on = enable;
break;
case ST_MASK_ID_ACCEL_WK:
cdata->accel_odr_dependency[0] = min_odr;
cdata->accel_wk_on = enable;
break;
case ST_MASK_ID_SENSOR_HUB:
cdata->accel_odr_dependency[1] = min_odr;
cdata->magn_on = enable;
break;
case ST_MASK_ID_DIGITAL_FUNC:
cdata->accel_odr_dependency[2] = min_odr;
break;
default:
return -EINVAL;
}
odr = cdata->accel_odr_dependency[0];
if (!cdata->accel_on && !cdata->accel_wk_on)
odr = 0;
if (MAX(cdata->accel_odr_dependency[1], cdata->accel_odr_dependency[2]) > odr)
odr = MAX(cdata->accel_odr_dependency[1], cdata->accel_odr_dependency[2]);
#ifdef CONFIG_ST_LSM6DS3H_XL_DATA_INJECTION
if (cdata->injection_mode)
return 0;
#endif /* CONFIG_ST_LSM6DS3H_XL_DATA_INJECTION */
if (id == ST_MASK_ID_ACCEL_WK) {
err = st_lsm6ds3h_set_odr(sdata_accel_wk, odr, true);
if (err < 0)
return err;
} else {
err = st_lsm6ds3h_set_odr(sdata_accel, odr, true);
if (err < 0)
return err;
}
return 0;
}
/*
* Enable / disable digital func
*/
static int lsm6ds3h_enable_digital_func(struct lsm6ds3h_data *cdata,
bool enable, enum st_mask_id id)
{
int err;
if (enable) {
if (cdata->enable_digfunc_mask == 0) {
err = lsm6ds3h_enable_accel(cdata,
ST_MASK_ID_DIGITAL_FUNC, 104, enable);
if (err < 0)
return err;
err = st_lsm6ds3h_write_data_with_mask(cdata,
ST_LSM6DS3H_FUNC_EN_ADDR,
ST_LSM6DS3H_FUNC_EN_MASK,
ST_LSM6DS3H_EN_BIT, true);
if (err < 0)
return err;
}
cdata->enable_digfunc_mask |= BIT(id);
} else {
if ((cdata->enable_digfunc_mask & ~BIT(id)) == 0) {
err = st_lsm6ds3h_write_data_with_mask(cdata,
ST_LSM6DS3H_FUNC_EN_ADDR,
ST_LSM6DS3H_FUNC_EN_MASK,
ST_LSM6DS3H_DIS_BIT, true);
if (err < 0)
return err;
err = lsm6ds3h_enable_accel(cdata,
ST_MASK_ID_DIGITAL_FUNC, 0, enable);
if (err < 0)
return err;
}
cdata->enable_digfunc_mask &= ~BIT(id);
}
return 0;
}
/*
* Enable / disable HW pedometer
*/
static int lsm6ds3h_enable_pedometer(struct lsm6ds3h_data *cdata,
bool enable, enum st_mask_id id)
{
int err;
if (enable) {
if (cdata->enable_pedometer_mask == 0) {
err = st_lsm6ds3h_write_data_with_mask(cdata,
ST_LSM6DS3H_PEDOMETER_EN_ADDR,
ST_LSM6DS3H_PEDOMETER_EN_MASK,
ST_LSM6DS3H_EN_BIT, true);
if (err < 0)
return err;
err = lsm6ds3h_enable_digital_func(cdata,
true, ST_MASK_ID_HW_PEDOMETER);
if (err < 0)
return err;
}
/* timestamp for step detector should be updated on
* system resume. Otherwise, event will be considered invalid.
*/
cdata->timestamp = ktime_to_ns(ktime_get_boottime());
cdata->enable_pedometer_mask |= BIT(id);
} else {
if ((cdata->enable_pedometer_mask & ~BIT(id)) == 0) {
err = st_lsm6ds3h_write_data_with_mask(cdata,
ST_LSM6DS3H_PEDOMETER_EN_ADDR,
ST_LSM6DS3H_PEDOMETER_EN_MASK,
ST_LSM6DS3H_DIS_BIT, true);
if (err < 0)
return err;
err = lsm6ds3h_enable_digital_func(cdata,
false, ST_MASK_ID_HW_PEDOMETER);
if (err < 0)
return err;
}
cdata->enable_pedometer_mask &= ~BIT(id);
}
return 0;
}
#ifdef CONFIG_ST_LSM6DS3H_IIO_ALGO_UPLOAD_WRIST_TILT
static int st_lsm6ds3h_set_wrist_tilt_threshold(struct lsm6ds3h_data *cdata)
{
int err;
u8 default_tilt_angle = ST_LSM6DS3H_WRIST_TILT_THRESHOLD;
err = st_lsm6ds3h_write_embedded_registers(cdata, ST_LSM6DS3H_WRIST_TILT_THS1_ADDR,
&default_tilt_angle, 1);
if (err < 0)
return err;
return 0;
}
#endif
#ifdef CONFIG_ST_LSM6DS3H_IIO_MASTER_SUPPORT
int st_lsm6ds3h_enable_sensor_hub(struct lsm6ds3h_data *cdata,
bool enable, enum st_mask_id id)
{
int err;
if (enable) {
if (cdata->enable_sensorhub_mask == 0) {
err = lsm6ds3h_enable_digital_func(cdata,
true, ST_MASK_ID_SENSOR_HUB);
if (err < 0)
return err;
err = lsm6ds3h_enable_accel(cdata, ST_MASK_ID_SENSOR_HUB,
cdata->v_odr[ST_MASK_ID_EXT0], enable);
if (err < 0)
return err;
err = st_lsm6ds3h_write_data_with_mask(cdata,
ST_LSM6DS3H_SENSORHUB_ADDR,
ST_LSM6DS3H_SENSORHUB_MASK,
ST_LSM6DS3H_EN_BIT, true);
if (err < 0)
return err;
} else
err = lsm6ds3h_enable_accel(cdata, ST_MASK_ID_SENSOR_HUB,
cdata->v_odr[ST_MASK_ID_EXT0], enable);
cdata->enable_sensorhub_mask |= BIT(id);
} else {
if ((cdata->enable_sensorhub_mask & ~BIT(id)) == 0) {
err = st_lsm6ds3h_write_data_with_mask(cdata,
ST_LSM6DS3H_SENSORHUB_ADDR,
ST_LSM6DS3H_SENSORHUB_MASK,
ST_LSM6DS3H_DIS_BIT, true);
if (err < 0)
return err;
err = lsm6ds3h_enable_accel(cdata,
ST_MASK_ID_SENSOR_HUB, 0, enable);
if (err < 0)
return err;
err = lsm6ds3h_enable_digital_func(cdata,
false, ST_MASK_ID_SENSOR_HUB);
if (err < 0)
return err;
} else
err = lsm6ds3h_enable_accel(cdata, ST_MASK_ID_SENSOR_HUB,
cdata->v_odr[ST_MASK_ID_EXT0], enable);
cdata->enable_sensorhub_mask &= ~BIT(id);
}
return err < 0 ? err : 0;
}
#endif /* CONFIG_ST_LSM6DS3H_IIO_MASTER_SUPPORT */
#ifdef CONFIG_ST_LSM6DS3H_IIO_TAP_TAP_ENABLED
/*
* Enable tap_tap event in all directions (X,Y,Z) for testing.
* In production code only one direction will be enabled.
*/
static int lsm6ds3h_enable_tap_tap(struct lsm6ds3h_data *cdata, bool enable)
{
int err;
if (enable) {
err = st_lsm6ds3h_write_data_with_mask(cdata,
ST_LSM6DS3H_TAP_TAP_EN_ADDR,
ST_LSM6DS3H_TAP_CFG_TAP_X_EN_MASK,
ST_LSM6DS3H_EN_BIT, true);
if (err < 0)
return err;
err = st_lsm6ds3h_write_data_with_mask(cdata,
ST_LSM6DS3H_TAP_TAP_EN_ADDR,
ST_LSM6DS3H_TAP_CFG_TAP_Y_EN_MASK,
ST_LSM6DS3H_EN_BIT, true);
if (err < 0)
return err;
err = st_lsm6ds3h_write_data_with_mask(cdata,
ST_LSM6DS3H_TAP_TAP_EN_ADDR,
ST_LSM6DS3H_TAP_CFG_TAP_Z_EN_MASK,
ST_LSM6DS3H_EN_BIT, true);
if (err < 0)
return err;
cdata->sensors_enabled |= BIT(ST_MASK_ID_TAP_TAP);
err = lsm6ds3h_enable_accel(cdata, ST_MASK_ID_ACCEL,
cdata->hw_odr[ST_MASK_ID_ACCEL], enable);
if (err < 0)
return err;
} else {
err = st_lsm6ds3h_write_data_with_mask(cdata,
ST_LSM6DS3H_TAP_TAP_EN_ADDR,
ST_LSM6DS3H_TAP_TAP_EN_MASK,
ST_LSM6DS3H_DIS_BIT, true);
if (err < 0)
return err;
cdata->sensors_enabled &= ~BIT(ST_MASK_ID_TAP_TAP);
err = lsm6ds3h_enable_accel(cdata, ST_MASK_ID_ACCEL,
cdata->hw_odr[ST_MASK_ID_ACCEL], enable);
if (err < 0)
return err;
}
return 0;
}
#endif /* CONFIG_ST_LSM6DS3H_IIO_TAP_TAP_ENABLED */
int st_lsm6ds3h_set_enable(struct lsm6ds3h_sensor_data *sdata, bool enable)
{
int err;
u8 reg_value;
int en_odr, dis_odr;
dev_dbg(sdata->cdata->dev, "st_lsm6ds3h_set_enable: index=%d, enable=%d\n",
sdata->sindex, enable);
switch (sdata->sindex) {
case ST_MASK_ID_ACCEL:
if (sdata->cdata->sensors_enabled & (1 << ST_MASK_ID_ACCEL_WK)) {
en_odr = MAX(sdata->cdata->v_odr[ST_MASK_ID_ACCEL],
sdata->cdata->v_odr[ST_MASK_ID_ACCEL_WK]);
dis_odr = sdata->cdata->v_odr[ST_MASK_ID_ACCEL_WK];
} else {
en_odr = sdata->cdata->v_odr[ST_MASK_ID_ACCEL];
dis_odr = 0;
}
err = lsm6ds3h_enable_accel(sdata->cdata, ST_MASK_ID_ACCEL,
enable ? en_odr : dis_odr, enable);
if (err < 0)
return err;
break;
case ST_MASK_ID_ACCEL_WK:
if (sdata->cdata->sensors_enabled & (1 << ST_MASK_ID_ACCEL)) {
en_odr = MAX(sdata->cdata->v_odr[ST_MASK_ID_ACCEL_WK],
sdata->cdata->v_odr[ST_MASK_ID_ACCEL]);
dis_odr = sdata->cdata->v_odr[ST_MASK_ID_ACCEL];
} else {
en_odr = sdata->cdata->v_odr[ST_MASK_ID_ACCEL_WK];
dis_odr = 0;
}
err = lsm6ds3h_enable_accel(sdata->cdata, ST_MASK_ID_ACCEL_WK,
enable ? en_odr : dis_odr, enable);
if (err < 0)
return err;
if (enable)
stay_wake = true;
/* both A_WK and G_WK disabled, the sensor can be suspended */
else if (!(sdata->cdata->sensors_enabled & (1 << ST_MASK_ID_GYRO_WK)))
stay_wake = false;
break;
case ST_MASK_ID_GYRO:
if (sdata->cdata->sensors_enabled & (1 << ST_MASK_ID_GYRO_WK)) {
en_odr = MAX(sdata->cdata->v_odr[ST_MASK_ID_GYRO],
sdata->cdata->v_odr[ST_MASK_ID_GYRO_WK]);
dis_odr = sdata->cdata->v_odr[ST_MASK_ID_GYRO_WK];
} else {
en_odr = sdata->cdata->v_odr[ST_MASK_ID_GYRO];
dis_odr = 0;
}
if (enable)
sdata->cdata->gyro_on = true;
else
sdata->cdata->gyro_on = false;
err = st_lsm6ds3h_set_odr(sdata, enable ?
en_odr : dis_odr, true);
if (err < 0)
return err;
break;
case ST_MASK_ID_GYRO_WK:
if (sdata->cdata->sensors_enabled & (1 << ST_MASK_ID_GYRO)) {
en_odr = MAX(sdata->cdata->v_odr[ST_MASK_ID_GYRO_WK],
sdata->cdata->v_odr[ST_MASK_ID_GYRO]);
dis_odr = sdata->cdata->v_odr[ST_MASK_ID_GYRO];
} else {
en_odr = sdata->cdata->v_odr[ST_MASK_ID_GYRO_WK];
dis_odr = 0;
}
if (enable)
sdata->cdata->gyro_wk_on= true;
else
sdata->cdata->gyro_wk_on = false;
err = st_lsm6ds3h_set_odr(sdata, enable ?
en_odr : dis_odr, true);
if (err < 0)
return err;
if (enable)
stay_wake = true;
/* both A_WK and G_WK disabled, the sensor can be suspended */
else if (!(sdata->cdata->sensors_enabled & (1 << ST_MASK_ID_ACCEL_WK)))
stay_wake = false;
break;
case ST_MASK_ID_SIGN_MOTION:
if (enable)
reg_value = ST_LSM6DS3H_EN_BIT;
else
reg_value = ST_LSM6DS3H_DIS_BIT;
err = st_lsm6ds3h_write_data_with_mask(sdata->cdata,
ST_LSM6DS3H_SIGN_MOTION_EN_ADDR,
ST_LSM6DS3H_SIGN_MOTION_EN_MASK,
reg_value, true);
if (err < 0)
return err;
err = lsm6ds3h_enable_pedometer(sdata->cdata,
enable, ST_MASK_ID_SIGN_MOTION);
if (err < 0)
return err;
break;
case ST_MASK_ID_STEP_COUNTER:
if (enable)
reg_value = ST_LSM6DS3H_EN_BIT;
else
reg_value = ST_LSM6DS3H_DIS_BIT;
err = st_lsm6ds3h_write_data_with_mask(sdata->cdata,
ST_LSM6DS3H_TIMER_EN_ADDR,
ST_LSM6DS3H_TIMER_EN_MASK,
reg_value, true);
if (err < 0)
return err;
err = lsm6ds3h_enable_pedometer(sdata->cdata,
enable, ST_MASK_ID_STEP_COUNTER);
if (err < 0)
return err;
break;
case ST_MASK_ID_STEP_DETECTOR:
err = lsm6ds3h_enable_pedometer(sdata->cdata,
enable, ST_MASK_ID_STEP_DETECTOR);
if (err < 0)
return err;
break;
case ST_MASK_ID_TILT:
if (enable)
reg_value = ST_LSM6DS3H_EN_BIT;
else
reg_value = ST_LSM6DS3H_DIS_BIT;
err = st_lsm6ds3h_write_data_with_mask(sdata->cdata,
ST_LSM6DS3H_TILT_EN_ADDR,
ST_LSM6DS3H_TILT_EN_MASK,
reg_value, true);
if (err < 0)
return err;
err = lsm6ds3h_enable_digital_func(sdata->cdata,
enable, ST_MASK_ID_TILT);
if (err < 0)
return err;
break;
case ST_MASK_ID_WRIST_TILT:
if (enable)
reg_value = ST_LSM6DS3H_EN_BIT;
else
reg_value = ST_LSM6DS3H_DIS_BIT;
err = st_lsm6ds3h_write_data_with_mask(sdata->cdata,
ST_LSM6DS3H_WRIST_TILT_EN_ADDR,
ST_LSM6DS3H_WRIST_TILT_EN_MASK,
reg_value, true);
if (err < 0)
return err;
err = lsm6ds3h_enable_digital_func(sdata->cdata,
enable, ST_MASK_ID_WRIST_TILT);
if (err < 0)
return err;
#ifdef CONFIG_ST_LSM6DS3H_IIO_ALGO_UPLOAD_WRIST_TILT
if (enable) {
err = st_lsm6ds3h_set_wrist_tilt_threshold(sdata->cdata);
if (err < 0)
return err;
}
#endif
break;
#ifdef CONFIG_ST_LSM6DS3H_IIO_TAP_TAP_ENABLED
case ST_MASK_ID_TAP_TAP:
err = lsm6ds3h_enable_tap_tap(sdata->cdata, enable);
if (err < 0)
return err;
break;
#endif /* CONFIG_ST_LSM6DS3H_IIO_TAP_TAP_ENABLED */
#ifdef CONFIG_ST_LSM6DS3H_IIO_MASTER_SUPPORT
case ST_MASK_ID_EXT0:
break;
#endif /* CONFIG_ST_LSM6DS3H_IIO_MASTER_SUPPORT */
default:
return -EINVAL;
}
err = st_lsm6ds3h_set_drdy_irq(sdata, enable);
if (err < 0)
return err;
if (enable)
sdata->cdata->sensors_enabled |= BIT(sdata->sindex);
else
sdata->cdata->sensors_enabled &= ~BIT(sdata->sindex);
return 0;
}
static int st_lsm6ds3h_set_fs(struct lsm6ds3h_sensor_data *sdata,
unsigned int gain)
{
int err, i;
for (i = 0; i < ST_LSM6DS3H_FS_LIST_NUM; i++) {
if (st_lsm6ds3h_fs_table[sdata->sindex].fs_avl[i].gain == gain)
break;
}
if (i == ST_LSM6DS3H_FS_LIST_NUM)
return -EINVAL;
err = st_lsm6ds3h_write_data_with_mask(sdata->cdata,
st_lsm6ds3h_fs_table[sdata->sindex].addr,
st_lsm6ds3h_fs_table[sdata->sindex].mask,
st_lsm6ds3h_fs_table[sdata->sindex].fs_avl[i].value,
true);
if (err < 0)
return err;
sdata->c_gain[0] = gain;
return 0;
}
static int st_lsm6ds3h_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *ch, int *val,
int *val2, long mask)
{
int err;
u8 outdata[ST_LSM6DS3H_BYTE_FOR_CHANNEL];
struct lsm6ds3h_sensor_data *sdata = iio_priv(indio_dev);
switch (mask) {
case IIO_CHAN_INFO_RAW:
mutex_lock(&indio_dev->mlock);
if ((sdata->sindex == ST_MASK_ID_ACCEL) ||
(sdata->sindex == ST_MASK_ID_ACCEL_WK))
msleep(40);
if ((sdata->sindex == ST_MASK_ID_GYRO) ||
(sdata->sindex == ST_MASK_ID_GYRO_WK))
msleep(120);
err = sdata->cdata->tf->read(sdata->cdata, ch->address,
ST_LSM6DS3H_BYTE_FOR_CHANNEL, outdata, true);
if (err < 0) {
mutex_unlock(&indio_dev->mlock);
return err;
}
*val = (s16)get_unaligned_le16(outdata);
*val = *val >> ch->scan_type.shift;
mutex_unlock(&indio_dev->mlock);
return IIO_VAL_INT;
case IIO_CHAN_INFO_OFFSET:
if ((sdata->sindex == ST_MASK_ID_ACCEL) ||
(sdata->sindex == ST_MASK_ID_ACCEL_WK)) {
*val = accel_cal_data[ch->scan_index];
return IIO_VAL_INT;
}
if ((sdata->sindex == ST_MASK_ID_GYRO) ||
(sdata->sindex == ST_MASK_ID_GYRO_WK)) {
*val = gyro_cal_data[ch->scan_index];
return IIO_VAL_INT;
}
break;
case IIO_CHAN_INFO_SCALE:
*val = 0;
*val2 = sdata->c_gain[0];
return IIO_VAL_INT_PLUS_MICRO;
default:
return -EINVAL;
}
return 0;
}
static int st_lsm6ds3h_write_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan, int val, int val2, long mask)
{
int err;
struct lsm6ds3h_sensor_data *sdata = iio_priv(indio_dev);
switch (mask) {
case IIO_CHAN_INFO_SCALE:
mutex_lock(&indio_dev->mlock);
if (indio_dev->currentmode == INDIO_BUFFER_TRIGGERED) {
mutex_unlock(&indio_dev->mlock);
return -EBUSY;
}
err = st_lsm6ds3h_set_fs(sdata, val2);
mutex_unlock(&indio_dev->mlock);
break;
case IIO_CHAN_INFO_OFFSET:
err = 0;
if ((sdata->sindex == ST_MASK_ID_ACCEL) ||
(sdata->sindex == ST_MASK_ID_ACCEL_WK))
accel_cal_data[chan->scan_index] = val;
else if ((sdata->sindex == ST_MASK_ID_GYRO) ||
(sdata->sindex == ST_MASK_ID_GYRO_WK))
gyro_cal_data[chan->scan_index] = val;
else
err = -EINVAL;
break;
default:
return -EINVAL;
}
return err < 0 ? err : 0;
}
static int st_lsm6ds3h_reset_steps(struct lsm6ds3h_data *cdata)
{
int err;
u8 reg_value = 0x00;
err = cdata->tf->read(cdata,
ST_LSM6DS3H_STEP_COUNTER_RES_ADDR, 1, &reg_value, true);
if (err < 0)
return err;
if (reg_value & ST_LSM6DS3H_FUNC_EN_MASK)
reg_value = ST_LSM6DS3H_STEP_COUNTER_RES_FUNC_EN;
else
reg_value = ST_LSM6DS3H_DIS_BIT;
err = st_lsm6ds3h_write_data_with_mask(cdata,
ST_LSM6DS3H_STEP_COUNTER_RES_ADDR,
ST_LSM6DS3H_STEP_COUNTER_RES_MASK,
ST_LSM6DS3H_STEP_COUNTER_RES_ALL_EN, true);
if (err < 0)
return err;
err = st_lsm6ds3h_write_data_with_mask(cdata,
ST_LSM6DS3H_STEP_COUNTER_RES_ADDR,
ST_LSM6DS3H_STEP_COUNTER_RES_MASK,
reg_value, true);
if (err < 0)
return err;
cdata->reset_steps = true;
return 0;
}
static int st_lsm6ds3h_init_sensor(struct lsm6ds3h_data *cdata)
{
int err;
u8 default_reg_value = 0x00;
/* Latch interrupts */
err = st_lsm6ds3h_write_data_with_mask(cdata, ST_LSM6DS3H_LIR_ADDR,
ST_LSM6DS3H_LIR_MASK, ST_LSM6DS3H_EN_BIT, true);
if (err < 0)
return err;
/* Enable BDU for sensors data */
err = st_lsm6ds3h_write_data_with_mask(cdata, ST_LSM6DS3H_BDU_ADDR,
ST_LSM6DS3H_BDU_MASK, ST_LSM6DS3H_EN_BIT, true);
if (err < 0)
return err;
err = st_lsm6ds3h_write_data_with_mask(cdata,
ST_LSM6DS3H_ROUNDING_ADDR,
ST_LSM6DS3H_ROUNDING_MASK,
ST_LSM6DS3H_G_HM_MODE_DISABLE | ST_LSM6DS3H_EN_BIT,
true);
if (err < 0)
return err;
err = st_lsm6ds3h_write_data_with_mask(cdata,
ST_LSM6DS3H_XL_HM_MODE_ADDR,
ST_LSM6DS3H_XL_HM_MODE_MASK,
ST_LSM6DS3H_XL_HM_MODE_DISABLE,
true);
if (err < 0)
return err;
/* Redirect INT2 on INT1, all interrupt will be available on INT1 */
err = st_lsm6ds3h_write_data_with_mask(cdata,
ST_LSM6DS3H_INT2_ON_INT1_ADDR,
ST_LSM6DS3H_INT2_ON_INT1_MASK,
ST_LSM6DS3H_EN_BIT, true);
if (err < 0)
return err;
err = st_lsm6ds3h_reset_steps(cdata);
if (err < 0)
return err;
#ifdef CONFIG_ST_LSM6DS3H_IIO_TAP_TAP_ENABLED
err = lsm6ds3h_config_tap_tap(cdata);
if (err < 0)
return err;
#endif /* CONFIG_ST_LSM6DS3H_IIO_TAP_TAP_ENABLED */
err = st_lsm6ds3h_write_embedded_registers(cdata,
ST_LSM6DS3H_STEP_COUNTER_DURATION_ADDR,
&default_reg_value, 1);
if (err < 0)
return err;
mutex_lock(&cdata->bank_registers_lock);
err = st_lsm6ds3h_write_data_with_mask(cdata,
ST_LSM6DS3H_FUNC_CFG_ACCESS_ADDR,
ST_LSM6DS3H_FUNC_CFG_REG2_MASK,
ST_LSM6DS3H_EN_BIT, false);
if (err < 0)
goto st_lsm6ds3h_init_sensor_mutex_unlock;
err = st_lsm6ds3h_write_data_with_mask(cdata,
ST_LSM6DS3H_STEP_COUNTER_PEDO_THS_ADDR,
ST_LSM6DS3H_STEP_COUNTER_THS_MIN_MASK,
ST_LSM6DS3H_STEP_COUNTER_THS_MIN_DEF_VAL, false);
if (err < 0)
goto st_lsm6ds3h_init_sensor_mutex_unlock;
err = st_lsm6ds3h_write_data_with_mask(cdata,
ST_LSM6DS3H_STEP_COUNTER_PEDO_DEB_ADDR,
ST_LSM6DS3H_STEP_COUNTER_DEB_STEP_MASK,
ST_LSM6DS3H_STEP_COUNTER_DEB_STEP_DEF_VAL, false);
if (err < 0)
goto st_lsm6ds3h_init_sensor_mutex_unlock;
err = st_lsm6ds3h_write_data_with_mask(cdata,
ST_LSM6DS3H_FUNC_CFG_ACCESS_ADDR,
ST_LSM6DS3H_FUNC_CFG_REG2_MASK,
ST_LSM6DS3H_DIS_BIT, false);
if (err < 0)
goto st_lsm6ds3h_init_sensor_mutex_unlock;
mutex_unlock(&cdata->bank_registers_lock);
return 0;
st_lsm6ds3h_init_sensor_mutex_unlock:
mutex_unlock(&cdata->bank_registers_lock);
return err;
}
static int st_lsm6ds3h_set_selftest(struct lsm6ds3h_sensor_data *sdata, int index)
{
u8 mode, mask;
switch (sdata->sindex) {
case ST_MASK_ID_ACCEL:
mask = ST_LSM6DS3H_SELFTEST_ACCEL_MASK;
mode = st_lsm6ds3h_selftest_table[index].accel_value;
break;
case ST_MASK_ID_GYRO:
mask = ST_LSM6DS3H_SELFTEST_GYRO_MASK;
mode = st_lsm6ds3h_selftest_table[index].gyro_value;
break;
default:
return -EINVAL;
}
return st_lsm6ds3h_write_data_with_mask(sdata->cdata,
ST_LSM6DS3H_SELFTEST_ADDR, mask, mode, true);
}
static ssize_t st_lsm6ds3h_sysfs_set_max_delivery_rate(struct device *dev,
struct device_attribute *attr, const char *buf, size_t size)
{
u8 duration;
int err;
unsigned int max_delivery_rate;
struct iio_dev *indio_dev = dev_get_drvdata(dev);
struct lsm6ds3h_sensor_data *sdata = iio_priv(indio_dev);
err = kstrtouint(buf, 10, &max_delivery_rate);
if (err < 0)
return -EINVAL;
if (max_delivery_rate == sdata->cdata->v_odr[ST_MASK_ID_STEP_COUNTER])
return size;
duration = max_delivery_rate / ST_LSM6DS3H_MIN_DURATION_MS;
err = st_lsm6ds3h_write_embedded_registers(sdata->cdata,
ST_LSM6DS3H_STEP_COUNTER_DURATION_ADDR,
&duration, 1);
if (err < 0)
return err;
sdata->cdata->v_odr[ST_MASK_ID_STEP_COUNTER] = max_delivery_rate;
return size;
}
static ssize_t st_lsm6ds3h_sysfs_get_max_delivery_rate(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct lsm6ds3h_sensor_data *sdata = iio_priv(dev_get_drvdata(dev));
return sprintf(buf, "%d\n",
sdata->cdata->v_odr[ST_MASK_ID_STEP_COUNTER]);
}
static ssize_t st_lsm6ds3h_sysfs_reset_counter(struct device *dev,
struct device_attribute *attr, const char *buf, size_t size)
{
int err;
struct lsm6ds3h_sensor_data *sdata = iio_priv(dev_get_drvdata(dev));
err = st_lsm6ds3h_reset_steps(sdata->cdata);
if (err < 0)
return err;
return size;
}
static ssize_t st_lsm6ds3h_sysfs_get_sampling_frequency(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct lsm6ds3h_sensor_data *sdata = iio_priv(dev_get_drvdata(dev));
return sprintf(buf, "%d\n", sdata->cdata->v_odr[sdata->sindex]);
}
static ssize_t st_lsm6ds3h_sysfs_set_sampling_frequency(struct device *dev,
struct device_attribute *attr, const char *buf, size_t size)
{
int err, i;
unsigned int odr;
struct iio_dev *indio_dev = dev_get_drvdata(dev);
struct lsm6ds3h_sensor_data *sdata = iio_priv(indio_dev);
err = kstrtoint(buf, 10, &odr);
if (err < 0)
return err;
dev_dbg(sdata->cdata->dev, "st_lsm6ds3h_sysfs_set_sampling_frequency: odr=%d\n", odr);
mutex_lock(&indio_dev->mlock);
mutex_lock(&sdata->cdata->odr_lock);
#ifdef CONFIG_ST_LSM6DS3H_XL_DATA_INJECTION
if (!((sdata->sindex & ST_MASK_ID_ACCEL) &&
sdata->cdata->injection_mode)) {
if (sdata->cdata->v_odr[sdata->sindex] != odr)
err = st_lsm6ds3h_set_odr(sdata, odr, false);
}
#else /* CONFIG_ST_LSM6DS3H_XL_DATA_INJECTION */
if (sdata->cdata->v_odr[sdata->sindex] != odr) {
for (i = 0; i < ST_LSM6DS3H_ODR_LIST_NUM; i++) {
if (st_lsm6ds3h_odr_table.odr_avl[i].hz == odr)
break;
}
if (i == ST_LSM6DS3H_ODR_LIST_NUM) {
err = -EINVAL;
goto sampling_freq_set_out;
}
sdata->cdata->v_odr[sdata->sindex] = st_lsm6ds3h_odr_table.odr_avl[i].hz;
if ((sdata->cdata->sensors_enabled & BIT(sdata->sindex)) == 0) {
err = size;
goto sampling_freq_set_out;
}
}
if (sdata->cdata->hw_odr[sdata->sindex] != odr) {
switch (sdata->sindex) {
case ST_MASK_ID_ACCEL:
if (sdata->cdata->sensors_enabled & BIT(ST_MASK_ID_ACCEL)) {
if (sdata->cdata->sensors_enabled & BIT(ST_MASK_ID_ACCEL_WK)) {
if (odr > sdata->cdata->hw_odr[ST_MASK_ID_ACCEL])
err = st_lsm6ds3h_set_odr(sdata, odr, false);
} else
err = st_lsm6ds3h_set_odr(sdata, odr, false);
}
break;
case ST_MASK_ID_ACCEL_WK:
if (sdata->cdata->sensors_enabled & BIT(ST_MASK_ID_ACCEL_WK)) {
if (sdata->cdata->sensors_enabled & BIT(ST_MASK_ID_ACCEL)) {
if (odr > sdata->cdata->hw_odr[ST_MASK_ID_ACCEL_WK])
err = st_lsm6ds3h_set_odr(sdata, odr, false);
} else
err = st_lsm6ds3h_set_odr(sdata, odr, false);
}
break;
case ST_MASK_ID_GYRO:
if (sdata->cdata->sensors_enabled & BIT(ST_MASK_ID_GYRO)) {
if (sdata->cdata->sensors_enabled & BIT(ST_MASK_ID_GYRO_WK)) {
if (odr > sdata->cdata->hw_odr[ST_MASK_ID_GYRO])
err = st_lsm6ds3h_set_odr(sdata, odr, false);
} else
err = st_lsm6ds3h_set_odr(sdata, odr, false);
}
break;
case ST_MASK_ID_GYRO_WK:
if (sdata->cdata->sensors_enabled & BIT(ST_MASK_ID_GYRO_WK)) {
if (sdata->cdata->sensors_enabled & BIT(ST_MASK_ID_GYRO)) {
if (odr > sdata->cdata->hw_odr[ST_MASK_ID_GYRO_WK])
err = st_lsm6ds3h_set_odr(sdata, odr, false);
} else
err = st_lsm6ds3h_set_odr(sdata, odr, false);
}
break;
default:
err = st_lsm6ds3h_set_odr(sdata, odr, false);
break;
}
}
#endif /* CONFIG_ST_LSM6DS3H_XL_DATA_INJECTION */
sampling_freq_set_out:
mutex_unlock(&sdata->cdata->odr_lock);
mutex_unlock(&indio_dev->mlock);
return err < 0 ? err : size;
}
static ssize_t st_lsm6ds3h_sysfs_sampling_frequency_avail(struct device *dev,
struct device_attribute *attr, char *buf)
{
int i, len = 0;
for (i = 0; i < ST_LSM6DS3H_ODR_LIST_NUM; i++) {
len += scnprintf(buf + len, PAGE_SIZE - len, "%d ",
st_lsm6ds3h_odr_table.odr_avl[i].hz);
}
buf[len - 1] = '\n';
return len;
}
static ssize_t st_lsm6ds3h_sysfs_scale_avail(struct device *dev,
struct device_attribute *attr, char *buf)
{
int i, len = 0;
struct lsm6ds3h_sensor_data *sdata = iio_priv(dev_get_drvdata(dev));
for (i = 0; i < ST_LSM6DS3H_FS_LIST_NUM; i++) {
len += scnprintf(buf + len, PAGE_SIZE - len, "0.%06u ",
st_lsm6ds3h_fs_table[sdata->sindex].fs_avl[i].gain);
}
buf[len - 1] = '\n';
return len;
}
static ssize_t st_lsm6ds3h_sysfs_get_selftest_available(struct device *dev,
struct device_attribute *attr, char *buf)
{
return sprintf(buf, "%s, %s\n",
st_lsm6ds3h_selftest_table[1].string_mode,
st_lsm6ds3h_selftest_table[2].string_mode);
}
static ssize_t st_lsm6ds3h_sysfs_get_selftest_status(struct device *dev,
struct device_attribute *attr, char *buf)
{
int8_t result;
char *message;
struct iio_dev *indio_dev = dev_get_drvdata(dev);
struct lsm6ds3h_sensor_data *sdata = iio_priv(indio_dev);
mutex_lock(&sdata->cdata->odr_lock);
switch (sdata->sindex) {
case ST_MASK_ID_ACCEL:
result = sdata->cdata->accel_selftest_status;
break;
case ST_MASK_ID_GYRO:
result = sdata->cdata->gyro_selftest_status;
break;
default:
mutex_unlock(&sdata->cdata->odr_lock);
return -EINVAL;
}
mutex_unlock(&sdata->cdata->odr_lock);
if (result == 0)
message = ST_LSM6DS3H_SELFTEST_NA_MS;
else if (result < 0)
message = ST_LSM6DS3H_SELFTEST_FAIL_MS;
else if (result > 0)
message = ST_LSM6DS3H_SELFTEST_PASS_MS;
return sprintf(buf, "%s\n", message);
}
#if CONFIG_ST_LSM6DS3H_IIO_ALGO_UPLOAD_WRIST_TILT
int st_lsm6ds3h_wrist_tilt_conf_read(struct device *dev, u8 *value, char type)
{
int err = 0;
struct iio_dev *indio_dev = dev_get_drvdata(dev);
struct lsm6ds3h_sensor_data *sdata = iio_priv(indio_dev);
struct lsm6ds3h_data *cdata = sdata->cdata;
switch (type) {
case 'l':
err = st_lsm6ds3h_read_embedded_registers(cdata, ST_LSM6DS3H_WRIST_TILT_LATENCY_ADDR, value, 1);
break;
case 'a':
err = st_lsm6ds3h_read_embedded_registers(cdata, ST_LSM6DS3H_WRIST_TILT_AXES_ADDR, value, 1);
break;
case 't':
err = st_lsm6ds3h_read_embedded_registers(cdata, ST_LSM6DS3H_WRIST_TILT_THS1_ADDR, value, 1);
break;
case 'f':
err = st_lsm6ds3h_read_embedded_registers(cdata, ST_LSM6DS3H_WRIST_TILT_FILTER_ADDR, value, 1);
break;
default:
dev_err(dev, "Unknown command, failed to read embedded register for wrist tilt");
err = -EINVAL;
}
return err;
}
int st_lsm6ds3h_wrist_tilt_conf(struct device *dev, u8 *value, char type)
{
int err = 0;
struct iio_dev *indio_dev = dev_get_drvdata(dev);
struct lsm6ds3h_sensor_data *sdata = iio_priv(indio_dev);
struct lsm6ds3h_data *cdata = sdata->cdata;
switch (type) {
case 'l':
err = st_lsm6ds3h_write_embedded_registers(cdata, ST_LSM6DS3H_WRIST_TILT_LATENCY_ADDR, value, 1);
break;
case 'a':
err = st_lsm6ds3h_write_embedded_registers(cdata, ST_LSM6DS3H_WRIST_TILT_AXES_ADDR, value, 1);
break;
case 't':
err = st_lsm6ds3h_write_embedded_registers(cdata, ST_LSM6DS3H_WRIST_TILT_THS1_ADDR, value, 1);
break;
case 'f':
err = st_lsm6ds3h_write_embedded_registers(cdata, ST_LSM6DS3H_WRIST_TILT_FILTER_ADDR, value, 1);
break;
default:
dev_err(dev, "Unknown command, failed to set embedded register for wrist tilt");
err = -EINVAL;
}
return err;
}
static ssize_t st_lsm6ds3h_sysfs_get_wrist_tilt_latency(struct device *dev,
struct device_attribute *attr, char *buf)
{
int err;
u8 latency_value;
err = st_lsm6ds3h_wrist_tilt_conf_read(dev, &latency_value, 'l');
if (err < 0)
return err;
return sprintf(buf, "%d\n", latency_value);
}
static ssize_t st_lsm6ds3h_sysfs_set_wrist_tilt_latency(struct device *dev,
struct device_attribute *attr, const char *buf, size_t size)
{
int err;
u8 latency_value;
err = kstrtou8(buf, 10, &latency_value);
if (err < 0)
return -EINVAL;
err = st_lsm6ds3h_wrist_tilt_conf(dev, &latency_value, 'l');
if (err < 0)
return err;
return size;
}
static ssize_t st_lsm6ds3h_sysfs_get_wrist_tilt_axes(struct device *dev,
struct device_attribute *attr, char *buf)
{
int err;
u8 axes_value;
err = st_lsm6ds3h_wrist_tilt_conf_read(dev, &axes_value, 'a');
if (err < 0)
return err;
return sprintf(buf, "%d\n", axes_value);
}
static ssize_t st_lsm6ds3h_sysfs_set_wrist_tilt_axes(struct device *dev,
struct device_attribute *attr, const char *buf, size_t size)
{
int err;
u8 axes_value;
err = kstrtou8(buf, 10, &axes_value);
if (err < 0)
return -EINVAL;
err = st_lsm6ds3h_wrist_tilt_conf(dev, &axes_value, 'a');
if (err < 0)
return err;
return size;
}
static ssize_t st_lsm6ds3h_sysfs_get_wrist_tilt_threshold(struct device *dev,
struct device_attribute *attr, char *buf)
{
int err;
u8 threshold_value;
err = st_lsm6ds3h_wrist_tilt_conf_read(dev, &threshold_value, 't');
if (err < 0)
return err;
return sprintf(buf, "%d\n", threshold_value);
}
static ssize_t st_lsm6ds3h_sysfs_set_wrist_tilt_threshold(struct device *dev,
struct device_attribute *attr, const char *buf, size_t size)
{
int err;
u8 threshold_value;
err = kstrtou8(buf, 10, &threshold_value);
if (err < 0)
return -EINVAL;
err = st_lsm6ds3h_wrist_tilt_conf(dev, &threshold_value, 't');
if (err < 0)
return err;
return size;
}
static ssize_t st_lsm6ds3h_sysfs_get_wrist_tilt_filter_timer(struct device *dev,
struct device_attribute *attr, char *buf)
{
int err;
u8 filter_timer_value;
err = st_lsm6ds3h_wrist_tilt_conf_read(dev, &filter_timer_value, 'f');
if (err < 0)
return err;
return sprintf(buf, "%d\n", filter_timer_value);
}
static ssize_t st_lsm6ds3h_sysfs_set_wrist_tilt_filter_timer(struct device *dev,
struct device_attribute *attr, const char *buf, size_t size)
{
int err;
u8 filter_value;
err = kstrtou8(buf, 10, &filter_value);
if (err < 0)
return -EINVAL;
err = st_lsm6ds3h_wrist_tilt_conf(dev, &filter_value, 'f');
if (err < 0)
return err;
return size;
}
#endif
static ssize_t st_lsm6ds3h_sysfs_start_selftest_status(struct device *dev,
struct device_attribute *attr, const char *buf, size_t size)
{
int err, i, n;
struct iio_dev *indio_dev = dev_get_drvdata(dev);
struct lsm6ds3h_sensor_data *sdata = iio_priv(indio_dev);
u8 reg_status, reg_addr, temp_reg_status, outdata[6];
int x = 0, y = 0, z = 0, x_selftest = 0, y_selftest = 0, z_selftest = 0;
mutex_lock(&sdata->cdata->odr_lock);
switch (sdata->sindex) {
case ST_MASK_ID_ACCEL:
sdata->cdata->accel_selftest_status = 0;
break;
case ST_MASK_ID_GYRO:
sdata->cdata->gyro_selftest_status = 0;
break;
default:
mutex_unlock(&sdata->cdata->odr_lock);
return -EINVAL;
}
if (sdata->cdata->sensors_enabled > 0) {
mutex_unlock(&sdata->cdata->odr_lock);
return -EBUSY;
}
for (n = 0; n < ARRAY_SIZE(st_lsm6ds3h_selftest_table); n++) {
if (strncmp(buf, st_lsm6ds3h_selftest_table[n].string_mode,
size - 2) == 0)
break;
}
if (n == ARRAY_SIZE(st_lsm6ds3h_selftest_table)) {
mutex_unlock(&sdata->cdata->odr_lock);
return -EINVAL;
}
switch (sdata->sindex) {
case ST_MASK_ID_ACCEL:
reg_addr = ST_LSM6DS3H_SELFTEST_ACCEL_ADDR;
temp_reg_status = ST_LSM6DS3H_SELFTEST_ACCEL_REG_VALUE;
break;
case ST_MASK_ID_GYRO:
reg_addr = ST_LSM6DS3H_SELFTEST_GYRO_ADDR;
temp_reg_status = ST_LSM6DS3H_SELFTEST_GYRO_REG_VALUE;
break;
default:
mutex_unlock(&sdata->cdata->odr_lock);
return -EINVAL;
}
err = sdata->cdata->tf->read(sdata->cdata,
reg_addr, 1, &reg_status, true);
if (err < 0) {
mutex_unlock(&sdata->cdata->odr_lock);
return err;
}
err = sdata->cdata->tf->write(sdata->cdata,
reg_addr, 1, &temp_reg_status, false);
if (err < 0) {
mutex_unlock(&sdata->cdata->odr_lock);
return err;
}
/* get data with selftest disabled */
msleep(100);
for (i = 0; i < 20; i++) {
err = sdata->cdata->tf->read(sdata->cdata,
sdata->data_out_reg, 6, outdata, true);
if (err < 0) {
i--;
continue;
}
x += ((s16)*(u16 *)&outdata[0]) / 20;
y += ((s16)*(u16 *)&outdata[2]) / 20;
z += ((s16)*(u16 *)&outdata[4]) / 20;
mdelay(10);
}
err = st_lsm6ds3h_set_selftest(sdata, n);
if (err < 0) {
mutex_unlock(&sdata->cdata->odr_lock);
return err;
}
/* get data with selftest enabled */
msleep(100);
for (i = 0; i < 20; i++) {
err = sdata->cdata->tf->read(sdata->cdata,
sdata->data_out_reg, 6, outdata, true);
if (err < 0) {
i--;
continue;
}
x_selftest += ((s16)*(u16 *)&outdata[0]) / 20;
y_selftest += ((s16)*(u16 *)&outdata[2]) / 20;
z_selftest += ((s16)*(u16 *)&outdata[4]) / 20;
mdelay(10);
}
err = sdata->cdata->tf->write(sdata->cdata,
reg_addr, 1, &reg_status, false);
if (err < 0) {
mutex_unlock(&sdata->cdata->odr_lock);
return err;
}
err = st_lsm6ds3h_set_selftest(sdata, 0);
if (err < 0) {
mutex_unlock(&sdata->cdata->odr_lock);
return err;
}
switch (sdata->sindex) {
case ST_MASK_ID_ACCEL:
if ((abs(x_selftest - x) < ST_LSM6DS3H_SELFTEST_ACCEL_MIN) ||
(abs(x_selftest - x) > ST_LSM6DS3H_SELFTEST_ACCEL_MAX)) {
sdata->cdata->accel_selftest_status = -1;
goto selftest_failure;
}
if ((abs(y_selftest - y) < ST_LSM6DS3H_SELFTEST_ACCEL_MIN) ||
(abs(y_selftest - y) > ST_LSM6DS3H_SELFTEST_ACCEL_MAX)) {
sdata->cdata->accel_selftest_status = -1;
goto selftest_failure;
}
if ((abs(z_selftest - z) < ST_LSM6DS3H_SELFTEST_ACCEL_MIN) ||
(abs(z_selftest - z) > ST_LSM6DS3H_SELFTEST_ACCEL_MAX)) {
sdata->cdata->accel_selftest_status = -1;
goto selftest_failure;
}
sdata->cdata->accel_selftest_status = 1;
break;
case ST_MASK_ID_GYRO:
if ((abs(x_selftest - x) < ST_LSM6DS3H_SELFTEST_GYRO_MIN) ||
(abs(x_selftest - x) > ST_LSM6DS3H_SELFTEST_GYRO_MAX)) {
sdata->cdata->gyro_selftest_status = -1;
goto selftest_failure;
}
if ((abs(y_selftest - y) < ST_LSM6DS3H_SELFTEST_GYRO_MIN) ||
(abs(y_selftest - y) > ST_LSM6DS3H_SELFTEST_GYRO_MAX)) {
sdata->cdata->gyro_selftest_status = -1;
goto selftest_failure;
}
if ((abs(z_selftest - z) < ST_LSM6DS3H_SELFTEST_GYRO_MIN) ||
(abs(z_selftest - z) > ST_LSM6DS3H_SELFTEST_GYRO_MAX)) {
sdata->cdata->gyro_selftest_status = -1;
goto selftest_failure;
}
sdata->cdata->gyro_selftest_status = 1;
break;
default:
mutex_unlock(&sdata->cdata->odr_lock);
return -EINVAL;
}
selftest_failure:
mutex_unlock(&sdata->cdata->odr_lock);
return size;
}
ssize_t st_lsm6ds3h_sysfs_flush_fifo(struct device *dev,
struct device_attribute *attr, const char *buf, size_t size)
{
u64 sensor_last_timestamp, event_type = 0;
int stype = 0;
int flags = READ_FIFO_IN_FLUSH;
u64 timestamp_flush = 0;
struct iio_dev *indio_dev = dev_get_drvdata(dev);
struct lsm6ds3h_sensor_data *sdata = iio_priv(indio_dev);
int err;
unsigned int sensor_handle;
mutex_lock(&indio_dev->mlock);
err = kstrtouint(buf, 10, &sensor_handle);
if (err < 0) {
mutex_unlock(&indio_dev->mlock);
return -EINVAL;
}
dev_info(dev, "sensor_handle = %d", sensor_handle);
if (indio_dev->currentmode == INDIO_BUFFER_TRIGGERED) {
mutex_lock(&sdata->cdata->odr_lock);
disable_irq(sdata->cdata->irq);
} else {
mutex_unlock(&indio_dev->mlock);
return -EINVAL;
}
if (sdata->sindex == ST_MASK_ID_ACCEL_WK)
sensor_last_timestamp =
sdata->cdata->fifo_output[ST_MASK_ID_ACCEL].timestamp_p;
else if (sdata->sindex == ST_MASK_ID_GYRO_WK)
sensor_last_timestamp =
sdata->cdata->fifo_output[ST_MASK_ID_GYRO].timestamp_p;
else
sensor_last_timestamp =
sdata->cdata->fifo_output[sdata->sindex].timestamp_p;
mutex_lock(&sdata->cdata->fifo_lock);
if (sdata->cdata->system_state & SF_RESUME)
flags |= READ_FIFO_DISCARD_DATA;
sdata->cdata->system_state = SF_NORMAL;
st_lsm6ds3h_read_fifo(sdata->cdata, flags);
mutex_unlock(&sdata->cdata->fifo_lock);
if (sensor_last_timestamp ==
sdata->cdata->fifo_output[sdata->sindex].timestamp_p)
event_type = IIO_EV_DIR_FIFO_EMPTY;
else
event_type = IIO_EV_DIR_FIFO_DATA;
timestamp_flush = sdata->cdata->fifo_output[sdata->sindex].timestamp_p;
enable_irq(sdata->cdata->irq);
switch (sdata->sindex) {
case ST_MASK_ID_ACCEL:
case ST_MASK_ID_ACCEL_WK:
stype = IIO_ACCEL;
break;
case ST_MASK_ID_GYRO:
case ST_MASK_ID_GYRO_WK:
stype = IIO_ANGL_VEL;
break;
#ifdef CONFIG_ST_LSM6DS3H_IIO_MASTER_SUPPORT
case ST_MASK_ID_EXT0:
stype = IIO_MAGN;
break;
#endif /* CONFIG_ST_LSM6DS3H_IIO_MASTER_SUPPORT */
}
iio_push_event(indio_dev, IIO_UNMOD_EVENT_CODE(stype,
sensor_handle, IIO_EV_TYPE_FIFO_FLUSH, event_type),
timestamp_flush);
mutex_unlock(&sdata->cdata->odr_lock);
mutex_unlock(&indio_dev->mlock);
return size;
}
ssize_t st_lsm6ds3h_sysfs_get_hwfifo_enabled(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct iio_dev *indio_dev = dev_get_drvdata(dev);
struct lsm6ds3h_sensor_data *sdata = iio_priv(indio_dev);
return sprintf(buf, "%d\n",
sdata->cdata->hwfifo_enabled[sdata->sindex]);
}
ssize_t st_lsm6ds3h_sysfs_set_hwfifo_enabled(struct device *dev,
struct device_attribute *attr, const char *buf, size_t size)
{
int err;
bool enable = false;
struct iio_dev *indio_dev = dev_get_drvdata(dev);
struct lsm6ds3h_sensor_data *sdata = iio_priv(indio_dev);
mutex_lock(&indio_dev->mlock);
if (indio_dev->currentmode == INDIO_BUFFER_TRIGGERED) {
err = -EBUSY;
goto set_hwfifo_enabled_unlock_mutex;
}
err = strtobool(buf, &enable);
if (err < 0)
goto set_hwfifo_enabled_unlock_mutex;
mutex_lock(&sdata->cdata->odr_lock);
sdata->cdata->hwfifo_enabled[sdata->sindex] = enable;
if (enable)
sdata->cdata->sensors_use_fifo |= BIT(sdata->sindex);
else
sdata->cdata->sensors_use_fifo &= ~BIT(sdata->sindex);
mutex_unlock(&sdata->cdata->odr_lock);
mutex_unlock(&indio_dev->mlock);
return size;
set_hwfifo_enabled_unlock_mutex:
mutex_unlock(&indio_dev->mlock);
return err;
}
ssize_t st_lsm6ds3h_sysfs_get_hwfifo_watermark(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct iio_dev *indio_dev = dev_get_drvdata(dev);
struct lsm6ds3h_sensor_data *sdata = iio_priv(indio_dev);
return sprintf(buf, "%d\n",
sdata->cdata->hwfifo_watermark[sdata->sindex]);
}
ssize_t st_lsm6ds3h_sysfs_set_hwfifo_watermark(struct device *dev,
struct device_attribute *attr, const char *buf, size_t size)
{
int err = 0, watermark = 0, old_watermark;
struct iio_dev *indio_dev = dev_get_drvdata(dev);
struct lsm6ds3h_sensor_data *sdata = iio_priv(indio_dev);
err = kstrtoint(buf, 10, &watermark);
if (err < 0)
return err;
if ((watermark < 1) || (watermark > ST_LSM6DS3H_MAX_FIFO_LENGHT))
return -EINVAL;
mutex_lock(&sdata->cdata->odr_lock);
if ((sdata->cdata->sensors_enabled & BIT(sdata->sindex)) &&
(sdata->cdata->sensors_use_fifo & BIT(sdata->sindex))) {
disable_irq(sdata->cdata->irq);
if (sdata->cdata->fifo_status != BYPASS) {
mutex_lock(&sdata->cdata->fifo_lock);
st_lsm6ds3h_read_fifo(sdata->cdata, READ_FIFO_IN_COF_FIFO);
mutex_unlock(&sdata->cdata->fifo_lock);
}
old_watermark = sdata->cdata->hwfifo_watermark[sdata->sindex];
sdata->cdata->hwfifo_watermark[sdata->sindex] = watermark;
err = lsm6ds3h_set_watermark(sdata->cdata);
if (err < 0)
sdata->cdata->hwfifo_watermark[sdata->sindex] = old_watermark;
enable_irq(sdata->cdata->irq);
} else
sdata->cdata->hwfifo_watermark[sdata->sindex] = watermark;
mutex_unlock(&sdata->cdata->odr_lock);
return err < 0 ? err : size;
}
ssize_t st_lsm6ds3h_sysfs_get_hwfifo_watermark_max(struct device *dev,
struct device_attribute *attr, char *buf)
{
return sprintf(buf, "%d\n", ST_LSM6DS3H_MAX_FIFO_LENGHT);
}
ssize_t st_lsm6ds3h_sysfs_get_hwfifo_watermark_min(struct device *dev,
struct device_attribute *attr, char *buf)
{
return sprintf(buf, "%d\n", 1);
}
static ssize_t st_lsm6ds3h_sysfs_set_pedo_threshold(struct device *dev,
struct device_attribute *attr, const char *buf, size_t size)
{
int err;
struct iio_dev *indio_dev = dev_get_drvdata(dev);
struct lsm6ds3h_sensor_data *sdata = iio_priv(indio_dev);
err = kstrtou8(buf, 16, &threshold_value);
if (err < 0)
return -EINVAL;
pr_info("pedo_threshold: threshold_value=0x%x", threshold_value);
err = st_lsm6ds3h_write_data_with_mask(sdata->cdata,
ST_LSM6DS3H_STEP_COUNTER_PEDO_THS_ADDR,
ST_LSM6DS3H_STEP_COUNTER_THS_MIN_MASK,
threshold_value, false);
if (err < 0)
pr_info("pedo_threshold: err=%d", err);
return size;
}
static ssize_t st_lsm6ds3h_sysfs_get_pedo_threshold(struct device *dev,
struct device_attribute *attr, char *buf)
{
return sprintf(buf, "%x\n", threshold_value);
}
#ifdef CONFIG_ST_LSM6DS3H_XL_DATA_INJECTION
static ssize_t st_lsm6ds3h_sysfs_set_injection_mode(struct device *dev,
struct device_attribute *attr, const char *buf, size_t size)
{
int err, start;
struct iio_dev *indio_dev = dev_get_drvdata(dev);
struct lsm6ds3h_sensor_data *sdata = iio_priv(indio_dev);
mutex_lock(&indio_dev->mlock);
if (indio_dev->currentmode == INDIO_BUFFER_TRIGGERED) {
mutex_unlock(&indio_dev->mlock);
return -EBUSY;
}
err = kstrtoint(buf, 10, &start);
if (err < 0) {
mutex_unlock(&indio_dev->mlock);
return err;
}
mutex_lock(&sdata->cdata->odr_lock);
if (start == 0) {
hrtimer_cancel(&sdata->cdata->injection_timer);
/* End injection */
err = st_lsm6ds3h_write_data_with_mask(sdata->cdata,
ST_LSM6DS3H_TEST_REG_ADDR,
ST_LSM6DS3H_START_INJECT_XL_MASK, 0, true);
if (err < 0) {
mutex_unlock(&sdata->cdata->odr_lock);
mutex_unlock(&indio_dev->mlock);
return err;
}
/* Force accel ODR to 26Hz if dependencies are enabled */
if (sdata->cdata->sensors_enabled > 0) {
err = st_lsm6ds3h_write_data_with_mask(sdata->cdata,
st_lsm6ds3h_odr_table.addr[sdata->sindex],
st_lsm6ds3h_odr_table.mask[sdata->sindex],
st_lsm6ds3h_odr_table.odr_avl[1].value, true);
if (err < 0) {
mutex_unlock(&sdata->cdata->odr_lock);
mutex_unlock(&indio_dev->mlock);
return err;
}
}
sdata->cdata->injection_mode = false;
} else {
sdata->cdata->last_injection_timestamp = 0;
sdata->cdata->injection_samples = 0;
/* Force accel ODR to 26Hz */
err = st_lsm6ds3h_write_data_with_mask(sdata->cdata,
st_lsm6ds3h_odr_table.addr[sdata->sindex],
st_lsm6ds3h_odr_table.mask[sdata->sindex],
st_lsm6ds3h_odr_table.odr_avl[1].value, true);
if (err < 0) {
mutex_unlock(&sdata->cdata->odr_lock);
mutex_unlock(&indio_dev->mlock);
return err;
}
/* Set start injection */
err = st_lsm6ds3h_write_data_with_mask(sdata->cdata,
ST_LSM6DS3H_TEST_REG_ADDR,
ST_LSM6DS3H_START_INJECT_XL_MASK, 1, true);
if (err < 0) {
mutex_unlock(&sdata->cdata->odr_lock);
mutex_unlock(&indio_dev->mlock);
return err;
}
sdata->cdata->injection_mode = true;
}
mutex_unlock(&sdata->cdata->odr_lock);
mutex_unlock(&indio_dev->mlock);
return size;
}
static ssize_t st_lsm6ds3h_sysfs_get_injection_mode(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct iio_dev *indio_dev = dev_get_drvdata(dev);
struct lsm6ds3h_sensor_data *sdata = iio_priv(indio_dev);
return sprintf(buf, "%d\n", sdata->cdata->injection_mode);
}
static ssize_t st_lsm6ds3h_sysfs_upload_xl_data(struct device *dev,
struct device_attribute *attr, const char *buf, size_t size)
{
int i;
u8 sample[3];
s64 timestamp;
struct iio_dev *indio_dev = dev_get_drvdata(dev);
struct lsm6ds3h_sensor_data *sdata = iio_priv(indio_dev);
mutex_lock(&indio_dev->mlock);
if (!sdata->cdata->injection_mode) {
mutex_unlock(&indio_dev->mlock);
return -EINVAL;
}
for (i = 0; i < 3; i++)
sample[i] = *(s16 *)(&buf[i * 2]) >> 8;
timestamp = *(s64 *)(buf + ALIGN(6, sizeof(s64)));
if (timestamp < sdata->cdata->last_injection_timestamp +
ST_LSM6DS3H_NS_AT_25HZ) {
mutex_unlock(&indio_dev->mlock);
return size;
}
while (sdata->cdata->injection_samples >= 10)
msleep(200);
spin_lock(&sdata->cdata->injection_spinlock);
memcpy(&sdata->cdata->injection_data[
sdata->cdata->injection_samples * 3], sample, 3);
sdata->cdata->injection_samples++;
spin_unlock(&sdata->cdata->injection_spinlock);
sdata->cdata->last_injection_timestamp = timestamp;
if (sdata->cdata->injection_samples >= 8)
hrtimer_start(&sdata->cdata->injection_timer,
ktime_set(0, ST_LSM6DS3H_26HZ_NS), HRTIMER_MODE_REL);
mutex_unlock(&indio_dev->mlock);
return size;
}
static void st_lsm6ds3h_injection_work(struct work_struct *work)
{
int i, err;
struct lsm6ds3h_data *cdata;
cdata = container_of(work, struct lsm6ds3h_data, injection_work);
if (cdata->injection_samples == 0)
return;
err = cdata->tf->write(cdata, ST_LSM6DS3H_INJECT_XL_X_ADDR,
3, cdata->injection_data, false);
if (err < 0)
return;
spin_lock(&cdata->injection_spinlock);
for (i = 0; i < cdata->injection_samples - 1; i++)
memcpy(&cdata->injection_data[i * 3],
&cdata->injection_data[(i + 1) * 3], 3);
cdata->injection_samples--;
spin_unlock(&cdata->injection_spinlock);
}
static enum hrtimer_restart st_lsm6ds3h_injection_timer_func(
struct hrtimer *timer)
{
ktime_t now;
struct lsm6ds3h_data *cdata;
cdata = container_of(timer, struct lsm6ds3h_data, injection_timer);
now = hrtimer_cb_get_time(timer);
hrtimer_forward(timer, now, ktime_set(0, ST_LSM6DS3H_26HZ_NS));
schedule_work(&cdata->injection_work);
return HRTIMER_RESTART;
}
static ssize_t st_lsm6ds3h_sysfs_get_injection_sensors(struct device *dev,
struct device_attribute *attr, char *buf)
{
return sprintf(buf, "%s\n", "lsm6ds3h_accel");
}
#endif /* CONFIG_ST_LSM6DS3H_XL_DATA_INJECTION */
int st_lsm6ds3h_average_sample(struct lsm6ds3h_sensor_data *sdata,
s32 *out_data, int sample_count)
{
int i, err = 0;
u8 hw_data[ST_LSM6DS3H_FIFO_ELEMENT_LEN_BYTE];
struct lsm6ds3h_data *cdata = sdata->cdata;
if (sample_count <= 0) {
return -ERANGE;
}
msleep(DELAY_FOR_OUT_STABLE);
/* first sample will be discarded */
for (i = 0; i < (sample_count + 1); i++) {
msleep(SAMPLE_WAIT_DELAY);
/* Now data is ready */
err = cdata->tf->read(cdata, cdata->indio_dev[sdata->sindex]->channels->address,
ST_LSM6DS3H_FIFO_ELEMENT_LEN_BYTE, hw_data, true);
if (err < 0) {
dev_err(cdata->dev, "failed to read out data.\n");
return err;
}
dev_dbg(cdata->dev, "hw_data[x]=%x,hw_data[y]=%x, hw_data[z]=%x\n",
(s16)((hw_data[1] << 8 | hw_data[0])),
(s16)((hw_data[3] << 8 | hw_data[2])),
(s16)((hw_data[5] << 8 | hw_data[4])));
if (i != 0) {
out_data[0] += (s16)((hw_data[1] << 8 | hw_data[0]));
out_data[1] += (s16)((hw_data[3] << 8 | hw_data[2]));
out_data[2] += (s16)((hw_data[5] << 8 | hw_data[4]));
}
}
out_data[0] /= sample_count;
out_data[1] /= sample_count;
out_data[2] /= sample_count;
dev_dbg(cdata->dev, "out_data[x]=%d, out_data[y]=%d, out_data[z]=%d\n", out_data[0], out_data[1], out_data[2]);
return 0;
}
ssize_t st_lsm6ds3h_sysfs_do_calibrate(struct device *dev,
struct device_attribute *attr, char *buf)
{
int err;
s32 no_cali[3] = {0};
u8 calibrate_odr_reg = CALIBRATE_ODR_SET_VALUE;
struct iio_dev *indio_dev = dev_get_drvdata(dev);
struct lsm6ds3h_sensor_data *sdata = iio_priv(indio_dev);
err = sdata->cdata->tf->write(sdata->cdata, ST_LSM6DS3H_GYRO_ODR_ADDR, 1, &calibrate_odr_reg, true);
if (err < 0) {
dev_err(sdata->cdata,"failed to write ST_LSM6DS3H_GYRO_ODR_ADDR\n");
}
err = st_lsm6ds3h_average_sample(sdata, no_cali, CALIBRATE_SAMPLE_COUNT);
if (err < 0)
return err;
/* The 4th parameter used for data sum check */
if (sdata->sindex == ST_MASK_ID_ACCEL)
return sprintf(buf, "%d %d %d %d\n",
0 * SIGN_X_A - ACCEL_SCOPE_SCALE * no_cali[0],
0 * SIGN_Y_A - ACCEL_SCOPE_SCALE * no_cali[1],
GRAVITY_ACCEL_LSB_2G * SIGN_Z_A - ACCEL_SCOPE_SCALE * no_cali[2],
0 * SIGN_X_A + 0 * SIGN_Y_A + GRAVITY_ACCEL_LSB_2G * SIGN_Z_A -
ACCEL_SCOPE_SCALE * (no_cali[0] + no_cali[1] + no_cali[2]));
else
return sprintf(buf, "%d %d %d %d\n",
0 * SIGN_X_G - no_cali[0],
0 * SIGN_Y_G - no_cali[1],
0 * SIGN_Z_G - no_cali[2],
0 * SIGN_X_G + 0 * SIGN_Y_G + 0 * SIGN_Z_G -
no_cali[0] - no_cali[1] - no_cali[2]);
}
static IIO_DEVICE_ATTR(do_calibrate, S_IRUGO,
st_lsm6ds3h_sysfs_do_calibrate,
NULL, 0);
#ifdef CONFIG_ST_LSM6DS3H_IIO_ALGO_DISABLED
static inline int st_lsm6ds3h_upload_algo(struct lsm6ds3h_data *cdata)
{
return 0;
}
#else /* CONFIG_ST_LSM6DS3H_IIO_ALGO_DISABLED */
static int st_lsm6ds3h_upload_algo(struct lsm6ds3h_data *cdata)
{
int err, err2, i;
u8 data = 0x00, *fw_check_data;
const struct firmware *fw;
#if (CONFIG_ST_LSM6DS3H_IIO_LIMIT_FIFO > 0)
u16 address = 0;
int remaining_byte, read_size = CONFIG_ST_LSM6DS3H_IIO_LIMIT_FIFO;
#endif /* CONFIG_ST_LSM6DS3H_IIO_LIMIT_FIFO */
int retry_counter = 0;
err = request_firmware(&fw, ST_LSM6DS3H_DATA_FW, cdata->dev);
if (err < 0)
return err;
fw_check_data = kmalloc(fw->size, GFP_KERNEL);
if (!fw_check_data) {
err = -ENOMEM;
goto release_firmware;
}
/* Stop current algo */
err = cdata->tf->write(cdata,
ST_LSM6DS3H_FUNC_CFG_ACCESS_ADDR, 1, &data, true);
if (err < 0)
goto free_fw_check_data;
/* Reserve HALF FIFO for algo to be uploaded */
err = st_lsm6ds3h_write_data_with_mask(cdata,
ST_LSM6DS3H_FIFO_CTRL4_ADDR,
ST_LSM6DS3H_RESERVE_HALF_FIFO,
ST_LSM6DS3H_EN_BIT, true);
if (err < 0)
goto free_fw_check_data;
mutex_lock(&cdata->bank_registers_lock);
/* Start the upload algo procedure */
err = st_lsm6ds3h_write_data_with_mask(cdata,
ST_LSM6DS3H_FUNC_CFG_ACCESS_ADDR,
ST_LSM6DS3H_FUNC_CFG_ACCESS_MASK,
ST_LSM6DS3H_EN_BIT, false);
if (err < 0)
goto close_upload_procedure;
/* Set upload start address (LSB) */
err = cdata->tf->write(cdata,
ST_LSM6DS3H_FUNC_CFG_START1_ADDR, 1, &data, false);
if (err < 0)
goto close_upload_procedure;
/* Set upload start address (MSB) */
err = cdata->tf->write(cdata,
ST_LSM6DS3H_FUNC_CFG_START2_ADDR, 1, &data, false);
if (err < 0)
goto close_upload_procedure;
/* upload algo */
err = cdata->tf->write(cdata, ST_LSM6DS3H_FUNC_CFG_DATA_WRITE_ADDR,
fw->size, (u8 *)fw->data, false);
if (err < 0)
goto close_upload_procedure;
#if (CONFIG_ST_LSM6DS3H_IIO_LIMIT_FIFO == 0)
/* Set upload start address (LSB) */
err = cdata->tf->write(cdata,
ST_LSM6DS3H_FUNC_CFG_START1_ADDR, 1, &data, false);
if (err < 0)
goto close_upload_procedure;
/* Set upload start address (MSB) */
err = cdata->tf->write(cdata,
ST_LSM6DS3H_FUNC_CFG_START2_ADDR, 1, &data, false);
if (err < 0)
goto close_upload_procedure;
err = cdata->tf->read(cdata, ST_LSM6DS3H_WAI_ADDRESS,
fw->size, fw_check_data, false);
if (err < 0)
goto close_upload_procedure;
#else /* CONFIG_ST_LSM6DS3H_IIO_LIMIT_FIFO */
remaining_byte = fw->size;
do {
data = *(u8 *)&address;
/* Set upload start address (LSB) */
err = cdata->tf->write(cdata,
ST_LSM6DS3H_FUNC_CFG_START1_ADDR,
1, &data, false);
if (err < 0)
goto close_upload_procedure;
data = *((u8 *)&address + 1);
/* Set upload start address (MSB) */
err = cdata->tf->write(cdata,
ST_LSM6DS3H_FUNC_CFG_START2_ADDR,
1, &data, false);
if (err < 0)
goto close_upload_procedure;
if (remaining_byte < read_size)
read_size = remaining_byte;
err = cdata->tf->read(cdata,
ST_LSM6DS3H_FUNC_CFG_DATA_READ_ADDR, read_size,
&fw_check_data[fw->size - remaining_byte],
false);
if (err < 0)
goto close_upload_procedure;
remaining_byte -= read_size;
address += read_size;
} while (remaining_byte > 0);
#endif /* CONFIG_ST_LSM6DS3H_IIO_LIMIT_FIFO */
/* Verify fw sent is same */
for (i = 0; i < fw->size; i++) {
if (fw_check_data[i] != fw->data[i]) {
dev_err(cdata->dev, "uploaded fw not valid.\n");
err = -EINVAL;
goto close_upload_procedure;
}
}
/* End the upload algo procedure */
err = st_lsm6ds3h_write_data_with_mask(cdata,
ST_LSM6DS3H_FUNC_CFG_ACCESS_ADDR,
ST_LSM6DS3H_FUNC_CFG_ACCESS_MASK,
ST_LSM6DS3H_DIS_BIT, false);
if (err < 0)
goto close_upload_procedure;
mutex_unlock(&cdata->bank_registers_lock);
/* Run the algo */
err = st_lsm6ds3h_write_data_with_mask(cdata,
ST_LSM6DS3H_FUNC_CFG_ACCESS_ADDR,
ST_LSM6DS3H_FUNC_CFG_ACCESS_MASK2,
ST_LSM6DS3H_EN_BIT, true);
if (err < 0)
goto free_fw_check_data;
err = st_lsm6ds3h_write_data_with_mask(cdata,
st_lsm6ds3h_odr_table.addr[ST_MASK_ID_ACCEL],
st_lsm6ds3h_odr_table.mask[ST_MASK_ID_ACCEL],
st_lsm6ds3h_odr_table.odr_avl[3].value, false);
if (err < 0)
goto free_fw_check_data;
err = st_lsm6ds3h_write_data_with_mask(cdata,
ST_LSM6DS3H_FUNC_EN_ADDR,
ST_LSM6DS3H_FUNC_EN_MASK,
ST_LSM6DS3H_EN_BIT, true);
if (err < 0)
goto free_fw_check_data;
err = st_lsm6ds3h_write_data_with_mask(cdata,
ST_LSM6DS3H_WRIST_TILT_EN_ADDR,
ST_LSM6DS3H_WRIST_TILT_EN_MASK,
ST_LSM6DS3H_EN_BIT, true);
if (err < 0)
goto free_fw_check_data;
msleep(50);
err = st_lsm6ds3h_write_data_with_mask(cdata,
ST_LSM6DS3H_WRIST_TILT_EN_ADDR,
ST_LSM6DS3H_WRIST_TILT_EN_MASK,
ST_LSM6DS3H_DIS_BIT, true);
if (err < 0)
goto free_fw_check_data;
err = st_lsm6ds3h_write_data_with_mask(cdata,
ST_LSM6DS3H_FUNC_EN_ADDR,
ST_LSM6DS3H_FUNC_EN_MASK,
ST_LSM6DS3H_DIS_BIT, true);
if (err < 0)
goto free_fw_check_data;
err = st_lsm6ds3h_write_data_with_mask(cdata,
st_lsm6ds3h_odr_table.addr[ST_MASK_ID_ACCEL],
st_lsm6ds3h_odr_table.mask[ST_MASK_ID_ACCEL],
ST_LSM6DS3H_ODR_POWER_OFF_VAL, false);
if (err < 0)
goto free_fw_check_data;
#ifndef CONFIG_ST_LSM6DS3H_IIO_ALGO_DISABLED
cdata->fifo2_algo_available = true;
#endif /* CONFIG_ST_LSM6DS3H_IIO_ALGO_DISABLED */
#ifdef CONFIG_ST_LSM6DS3H_IIO_ALGO_UPLOAD_WRIST_TILT
cdata->wrist_tilt_available = true;
#endif /* CONFIG_ST_LSM6DS3H_IIO_ALGO_UPLOAD_WRIST_TILT */
dev_info(cdata->dev, "algo upload completed\n");
kfree(fw_check_data);
release_firmware(fw);
return 0;
close_upload_procedure:
mutex_unlock(&cdata->bank_registers_lock);
do {
err2 = st_lsm6ds3h_write_data_with_mask(cdata,
ST_LSM6DS3H_FUNC_CFG_ACCESS_ADDR,
ST_LSM6DS3H_FUNC_CFG_ACCESS_MASK,
ST_LSM6DS3H_DIS_BIT, true);
msleep(200);
} while (err2 < 0 && retry_counter++ < RETRY_COUNTER_LIMITATION);
free_fw_check_data:
kfree(fw_check_data);
release_firmware:
release_firmware(fw);
return err;
}
#endif /* CONFIG_ST_LSM6DS3H_IIO_ALGO_DISABLED */
static ST_LSM6DS3H_DEV_ATTR_SAMP_FREQ();
static ST_LSM6DS3H_DEV_ATTR_SAMP_FREQ_AVAIL();
static ST_LSM6DS3H_DEV_ATTR_SCALE_AVAIL(in_accel_scale_available);
static ST_LSM6DS3H_DEV_ATTR_SCALE_AVAIL(in_anglvel_scale_available);
static ST_LSM6DS3H_HWFIFO_ENABLED();
static ST_LSM6DS3H_HWFIFO_WATERMARK();
static ST_LSM6DS3H_HWFIFO_WATERMARK_MIN();
static ST_LSM6DS3H_HWFIFO_WATERMARK_MAX();
static ST_LSM6DS3H_HWFIFO_FLUSH();
static IIO_DEVICE_ATTR(reset_counter, S_IWUSR,
NULL, st_lsm6ds3h_sysfs_reset_counter, 0);
static IIO_DEVICE_ATTR(pedo_threshold, S_IWUSR | S_IRUGO,
st_lsm6ds3h_sysfs_get_pedo_threshold,
st_lsm6ds3h_sysfs_set_pedo_threshold, 0);
static IIO_DEVICE_ATTR(max_delivery_rate, S_IWUSR | S_IRUGO,
st_lsm6ds3h_sysfs_get_max_delivery_rate,
st_lsm6ds3h_sysfs_set_max_delivery_rate, 0);
static IIO_DEVICE_ATTR(selftest_available, S_IRUGO,
st_lsm6ds3h_sysfs_get_selftest_available,
NULL, 0);
static IIO_DEVICE_ATTR(selftest, S_IWUSR | S_IRUGO,
st_lsm6ds3h_sysfs_get_selftest_status,
st_lsm6ds3h_sysfs_start_selftest_status, 0);
#ifdef CONFIG_ST_LSM6DS3H_IIO_ALGO_UPLOAD_WRIST_TILT
static IIO_DEVICE_ATTR(wrist_tilt_latency, S_IWUSR | S_IRUGO,
st_lsm6ds3h_sysfs_get_wrist_tilt_latency, st_lsm6ds3h_sysfs_set_wrist_tilt_latency, 0);
static IIO_DEVICE_ATTR(wrist_tilt_axes, S_IWUSR | S_IRUGO,
st_lsm6ds3h_sysfs_get_wrist_tilt_axes, st_lsm6ds3h_sysfs_set_wrist_tilt_axes, 0);
static IIO_DEVICE_ATTR(wrist_tilt_threshold, S_IWUSR | S_IRUGO,
st_lsm6ds3h_sysfs_get_wrist_tilt_threshold, st_lsm6ds3h_sysfs_set_wrist_tilt_threshold, 0);
static IIO_DEVICE_ATTR(wrist_tilt_filter_timer, S_IWUSR | S_IRUGO,
st_lsm6ds3h_sysfs_get_wrist_tilt_filter_timer, st_lsm6ds3h_sysfs_set_wrist_tilt_filter_timer, 0);
#endif
#ifdef CONFIG_ST_LSM6DS3H_XL_DATA_INJECTION
static IIO_DEVICE_ATTR(injection_mode, S_IWUSR | S_IRUGO,
st_lsm6ds3h_sysfs_get_injection_mode,
st_lsm6ds3h_sysfs_set_injection_mode, 0);
static IIO_DEVICE_ATTR(in_accel_injection_raw, S_IWUSR, NULL,
st_lsm6ds3h_sysfs_upload_xl_data, 0);
static IIO_DEVICE_ATTR(injection_sensors, S_IRUGO,
st_lsm6ds3h_sysfs_get_injection_sensors,
NULL, 0);
#endif /* CONFIG_ST_LSM6DS3H_XL_DATA_INJECTION */
static struct attribute *st_lsm6ds3h_accel_attributes[] = {
&iio_dev_attr_sampling_frequency_available.dev_attr.attr,
&iio_dev_attr_in_accel_scale_available.dev_attr.attr,
&iio_dev_attr_sampling_frequency.dev_attr.attr,
&iio_dev_attr_selftest_available.dev_attr.attr,
&iio_dev_attr_selftest.dev_attr.attr,
&iio_dev_attr_hwfifo_enabled.dev_attr.attr,
&iio_dev_attr_hwfifo_watermark.dev_attr.attr,
&iio_dev_attr_hwfifo_watermark_min.dev_attr.attr,
&iio_dev_attr_hwfifo_watermark_max.dev_attr.attr,
&iio_dev_attr_hwfifo_flush.dev_attr.attr,
#ifdef CONFIG_ST_LSM6DS3H_XL_DATA_INJECTION
&iio_dev_attr_injection_mode.dev_attr.attr,
&iio_dev_attr_in_accel_injection_raw.dev_attr.attr,
#endif /* CONFIG_ST_LSM6DS3H_XL_DATA_INJECTION */
&iio_dev_attr_do_calibrate.dev_attr.attr,
NULL,
};
static struct attribute *st_lsm6ds3h_accel_wk_attributes[] = {
&iio_dev_attr_sampling_frequency_available.dev_attr.attr,
&iio_dev_attr_in_accel_scale_available.dev_attr.attr,
&iio_dev_attr_sampling_frequency.dev_attr.attr,
&iio_dev_attr_hwfifo_enabled.dev_attr.attr,
&iio_dev_attr_hwfifo_watermark.dev_attr.attr,
&iio_dev_attr_hwfifo_watermark_min.dev_attr.attr,
&iio_dev_attr_hwfifo_watermark_max.dev_attr.attr,
&iio_dev_attr_hwfifo_flush.dev_attr.attr,
NULL,
};
static const struct attribute_group st_lsm6ds3h_accel_attribute_group = {
.attrs = st_lsm6ds3h_accel_attributes,
};
static const struct attribute_group st_lsm6ds3h_accel_wk_attribute_group = {
.attrs = st_lsm6ds3h_accel_wk_attributes,
};
static const struct iio_info st_lsm6ds3h_accel_info = {
.driver_module = THIS_MODULE,
.attrs = &st_lsm6ds3h_accel_attribute_group,
.read_raw = &st_lsm6ds3h_read_raw,
.write_raw = &st_lsm6ds3h_write_raw,
};
static const struct iio_info st_lsm6ds3h_accel_wk_info = {
.driver_module = THIS_MODULE,
.attrs = &st_lsm6ds3h_accel_wk_attribute_group,
.read_raw = &st_lsm6ds3h_read_raw,
.write_raw = &st_lsm6ds3h_write_raw,
};
static struct attribute *st_lsm6ds3h_gyro_attributes[] = {
&iio_dev_attr_sampling_frequency_available.dev_attr.attr,
&iio_dev_attr_in_anglvel_scale_available.dev_attr.attr,
&iio_dev_attr_sampling_frequency.dev_attr.attr,
&iio_dev_attr_selftest_available.dev_attr.attr,
&iio_dev_attr_selftest.dev_attr.attr,
&iio_dev_attr_hwfifo_enabled.dev_attr.attr,
&iio_dev_attr_hwfifo_watermark.dev_attr.attr,
&iio_dev_attr_hwfifo_watermark_min.dev_attr.attr,
&iio_dev_attr_hwfifo_watermark_max.dev_attr.attr,
&iio_dev_attr_hwfifo_flush.dev_attr.attr,
&iio_dev_attr_do_calibrate.dev_attr.attr,
NULL,
};
static struct attribute *st_lsm6ds3h_gyro_wk_attributes[] = {
&iio_dev_attr_sampling_frequency_available.dev_attr.attr,
&iio_dev_attr_in_anglvel_scale_available.dev_attr.attr,
&iio_dev_attr_sampling_frequency.dev_attr.attr,
&iio_dev_attr_hwfifo_enabled.dev_attr.attr,
&iio_dev_attr_hwfifo_watermark.dev_attr.attr,
&iio_dev_attr_hwfifo_watermark_min.dev_attr.attr,
&iio_dev_attr_hwfifo_watermark_max.dev_attr.attr,
&iio_dev_attr_hwfifo_flush.dev_attr.attr,
NULL,
};
static const struct attribute_group st_lsm6ds3h_gyro_attribute_group = {
.attrs = st_lsm6ds3h_gyro_attributes,
};
static const struct attribute_group st_lsm6ds3h_gyro_wk_attribute_group = {
.attrs = st_lsm6ds3h_gyro_wk_attributes,
};
static const struct iio_info st_lsm6ds3h_gyro_info = {
.driver_module = THIS_MODULE,
.attrs = &st_lsm6ds3h_gyro_attribute_group,
.read_raw = &st_lsm6ds3h_read_raw,
.write_raw = &st_lsm6ds3h_write_raw,
};
static const struct iio_info st_lsm6ds3h_gyro_wk_info = {
.driver_module = THIS_MODULE,
.attrs = &st_lsm6ds3h_gyro_wk_attribute_group,
.read_raw = &st_lsm6ds3h_read_raw,
.write_raw = &st_lsm6ds3h_write_raw,
};
static struct attribute *st_lsm6ds3h_sign_motion_attributes[] = {
#ifdef CONFIG_ST_LSM6DS3H_XL_DATA_INJECTION
&iio_dev_attr_injection_sensors.dev_attr.attr,
#endif /* CONFIG_ST_LSM6DS3H_XL_DATA_INJECTION */
NULL,
};
static const struct attribute_group st_lsm6ds3h_sign_motion_attribute_group = {
.attrs = st_lsm6ds3h_sign_motion_attributes,
};
static const struct iio_info st_lsm6ds3h_sign_motion_info = {
.driver_module = THIS_MODULE,
.attrs = &st_lsm6ds3h_sign_motion_attribute_group,
};
static struct attribute *st_lsm6ds3h_step_c_attributes[] = {
&iio_dev_attr_reset_counter.dev_attr.attr,
&iio_dev_attr_max_delivery_rate.dev_attr.attr,
#ifdef CONFIG_ST_LSM6DS3H_XL_DATA_INJECTION
&iio_dev_attr_injection_sensors.dev_attr.attr,
#endif /* CONFIG_ST_LSM6DS3H_XL_DATA_INJECTION */
&iio_dev_attr_pedo_threshold.dev_attr.attr,
NULL,
};
static const struct attribute_group st_lsm6ds3h_step_c_attribute_group = {
.attrs = st_lsm6ds3h_step_c_attributes,
};
static const struct iio_info st_lsm6ds3h_step_c_info = {
.driver_module = THIS_MODULE,
.attrs = &st_lsm6ds3h_step_c_attribute_group,
.read_raw = &st_lsm6ds3h_read_raw,
};
static struct attribute *st_lsm6ds3h_step_d_attributes[] = {
#ifdef CONFIG_ST_LSM6DS3H_XL_DATA_INJECTION
&iio_dev_attr_injection_sensors.dev_attr.attr,
#endif /* CONFIG_ST_LSM6DS3H_XL_DATA_INJECTION */
NULL,
};
static const struct attribute_group st_lsm6ds3h_step_d_attribute_group = {
.attrs = st_lsm6ds3h_step_d_attributes,
};
static const struct iio_info st_lsm6ds3h_step_d_info = {
.driver_module = THIS_MODULE,
.attrs = &st_lsm6ds3h_step_d_attribute_group,
};
static struct attribute *st_lsm6ds3h_tilt_attributes[] = {
#ifdef CONFIG_ST_LSM6DS3H_XL_DATA_INJECTION
&iio_dev_attr_injection_sensors.dev_attr.attr,
#endif /* CONFIG_ST_LSM6DS3H_XL_DATA_INJECTION */
NULL,
};
static const struct attribute_group st_lsm6ds3h_tilt_attribute_group = {
.attrs = st_lsm6ds3h_tilt_attributes,
};
static const struct iio_info st_lsm6ds3h_tilt_info = {
.driver_module = THIS_MODULE,
.attrs = &st_lsm6ds3h_tilt_attribute_group,
};
#ifdef CONFIG_ST_LSM6DS3H_IIO_ALGO_UPLOAD_WRIST_TILT
static struct attribute *st_lsm6ds3h_wrist_tilt_attributes[] = {
&iio_dev_attr_wrist_tilt_latency.dev_attr.attr,
&iio_dev_attr_wrist_tilt_axes.dev_attr.attr,
&iio_dev_attr_wrist_tilt_threshold.dev_attr.attr,
&iio_dev_attr_wrist_tilt_filter_timer.dev_attr.attr,
#ifdef CONFIG_ST_LSM6DS3H_XL_DATA_INJECTION
&iio_dev_attr_injection_sensors.dev_attr.attr,
#endif /* CONFIG_ST_LSM6DS3H_XL_DATA_INJECTION */
NULL,
};
static const struct attribute_group st_lsm6ds3h_wrist_tilt_attribute_group = {
.attrs = st_lsm6ds3h_wrist_tilt_attributes,
};
static const struct iio_info st_lsm6ds3h_wrist_tilt_info = {
.driver_module = THIS_MODULE,
.attrs = &st_lsm6ds3h_wrist_tilt_attribute_group,
};
#endif /* CONFIG_ST_LSM6DS3H_IIO_ALGO_UPLOAD_WRIST_TILT */
#ifdef CONFIG_ST_LSM6DS3H_IIO_TAP_TAP_ENABLED
static struct attribute *st_lsm6ds3h_tap_tap_attributes[] = {
NULL,
};
static const struct attribute_group st_lsm6ds3h_tap_tap_attribute_group = {
.attrs = st_lsm6ds3h_tap_tap_attributes,
};
static const struct iio_info st_lsm6ds3h_tap_tap_info = {
.driver_module = THIS_MODULE,
.attrs = &st_lsm6ds3h_tap_tap_attribute_group,
};
#endif /* CONFIG_ST_LSM6DS3H_IIO_TAP_TAP_ENABLED */
#ifdef CONFIG_IIO_TRIGGER
static const struct iio_trigger_ops st_lsm6ds3h_trigger_ops = {
.owner = THIS_MODULE,
.set_trigger_state = ST_LSM6DS3H_TRIGGER_SET_STATE,
};
#define ST_LSM6DS3H_TRIGGER_OPS (&st_lsm6ds3h_trigger_ops)
#else
#define ST_LSM6DS3H_TRIGGER_OPS NULL
#endif
int st_lsm6ds3h_common_probe(struct lsm6ds3h_data *cdata, int irq)
{
u8 wai = 0x00;
int i, n, err;
struct lsm6ds3h_sensor_data *sdata;
u8 reset_value = ST_LSM6DS3H_RESET_MASK;
mutex_init(&cdata->bank_registers_lock);
mutex_init(&cdata->fifo_lock);
mutex_init(&cdata->tb.buf_lock);
mutex_init(&cdata->odr_lock);
cdata->fifo_watermark = 0;
cdata->fifo_status = BYPASS;
cdata->enable_digfunc_mask = 0;
cdata->enable_pedometer_mask = 0;
#ifndef CONFIG_ST_LSM6DS3H_IIO_ALGO_DISABLED
cdata->fifo2_algo_available = false;
#endif /* CONFIG_ST_LSM6DS3H_IIO_ALGO_DISABLED */
#ifdef CONFIG_ST_LSM6DS3H_IIO_ALGO_UPLOAD_WRIST_TILT
cdata->wrist_tilt_available = false;
#endif /* CONFIG_ST_LSM6DS3H_IIO_ALGO_UPLOAD_WRIST_TILT */
#ifdef CONFIG_ST_LSM6DS3H_IIO_MASTER_SUPPORT
cdata->enable_sensorhub_mask = 0;
#endif /* CONFIG_ST_LSM6DS3H_IIO_MASTER_SUPPORT */
cdata->irq_enable_fifo_mask = 0;
cdata->irq_enable_accel_ext_mask = 0;
for (i = 0; i < ST_INDIO_DEV_NUM + 1; i++) {
cdata->hw_odr[i] = 0;
cdata->v_odr[i] = 0;
cdata->hwfifo_enabled[i] = false;
cdata->hwfifo_decimator[i] = 0;
cdata->hwfifo_watermark[i] = 1;
cdata->nofifo_decimation[i].decimator = 1;
cdata->nofifo_decimation[i].num_samples = 0;
cdata->fifo_output[i].sip = 0;
cdata->fifo_output[i].decimator = 1;
}
cdata->sensors_use_fifo = 0;
cdata->sensors_enabled = 0;
cdata->system_state = SF_NORMAL;
cdata->gyro_selftest_status = 0;
cdata->accel_selftest_status = 0;
cdata->accel_on = false;
cdata->accel_wk_on = false;
cdata->gyro_on = false;
cdata->gyro_wk_on = false;
cdata->magn_on = false;
cdata->reset_steps = false;
cdata->accel_odr_dependency[0] = 0;
cdata->accel_odr_dependency[1] = 0;
cdata->accel_odr_dependency[2] = 0;
cdata->trigger_odr = 0;
cdata->fifo_odr = 0;
cdata->fifo_data = kmalloc(ST_LSM6DS3H_MAX_FIFO_SIZE *
sizeof(u8), GFP_KERNEL);
if (!cdata->fifo_data)
return -ENOMEM;
#ifdef CONFIG_ST_LSM6DS3H_XL_DATA_INJECTION
cdata->injection_mode = false;
cdata->last_injection_timestamp = 0;
INIT_WORK(&cdata->injection_work, &st_lsm6ds3h_injection_work);
hrtimer_init(&cdata->injection_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
cdata->injection_timer.function = &st_lsm6ds3h_injection_timer_func;
spin_lock_init(&cdata->injection_spinlock);
#endif /* CONFIG_ST_LSM6DS3H_XL_DATA_INJECTION */
err = cdata->tf->read(cdata, ST_LSM6DS3H_WAI_ADDRESS, 1, &wai, true);
if (err < 0) {
dev_err(cdata->dev, "failed to read Who-Am-I register.\n");
err = -EPROBE_DEFER;
goto free_fifo_data;
}
if (wai != ST_LSM6DS3H_WAI_EXP) {
dev_err(cdata->dev,
"Who-Am-I value not valid. Expected %x, Found %x\n",
ST_LSM6DS3H_WAI_EXP, wai);
err = -ENODEV;
goto free_fifo_data;
}
if (irq > 0) {
cdata->irq = irq;
dev_info(cdata->dev, "driver use DRDY int pin 1 at irq:%d\n", irq);
} else {
err = -EINVAL;
dev_info(cdata->dev,
"DRDY not available, curernt implementation needs irq!\n");
goto free_fifo_data;
}
for (i = 0; i < ST_INDIO_DEV_NUM; i++) {
cdata->indio_dev[i] = iio_device_alloc(sizeof(struct lsm6ds3h_sensor_data));
if (!cdata->indio_dev[i]) {
err = -ENOMEM;
goto iio_device_free;
}
sdata = iio_priv(cdata->indio_dev[i]);
sdata->cdata = cdata;
sdata->sindex = i;
switch (i) {
case ST_MASK_ID_ACCEL:
case ST_MASK_ID_ACCEL_WK:
sdata->data_out_reg = st_lsm6ds3h_accel_ch[0].address;
cdata->v_odr[i] = st_lsm6ds3h_odr_table.odr_avl[0].hz;
sdata->c_gain[0] = st_lsm6ds3h_fs_table[i].fs_avl[0].gain;
sdata->num_data_channels = 3;
break;
case ST_MASK_ID_GYRO:
case ST_MASK_ID_GYRO_WK:
sdata->data_out_reg = st_lsm6ds3h_gyro_ch[0].address;
cdata->v_odr[i] = st_lsm6ds3h_odr_table.odr_avl[0].hz;
sdata->c_gain[0] = st_lsm6ds3h_fs_table[i].fs_avl[0].gain;
sdata->num_data_channels = 3;
break;
case ST_MASK_ID_STEP_COUNTER:
sdata->data_out_reg = st_lsm6ds3h_step_c_ch[0].address;
sdata->num_data_channels = 1;
break;
default:
sdata->num_data_channels = 0;
break;
}
cdata->indio_dev[i]->modes = INDIO_DIRECT_MODE;
}
cdata->indio_dev[ST_MASK_ID_ACCEL]->name =
kasprintf(GFP_KERNEL, "%s_%s", cdata->name,
ST_LSM6DS3H_ACCEL_SUFFIX_NAME);
cdata->indio_dev[ST_MASK_ID_ACCEL]->info = &st_lsm6ds3h_accel_info;
cdata->indio_dev[ST_MASK_ID_ACCEL]->channels = st_lsm6ds3h_accel_ch;
cdata->indio_dev[ST_MASK_ID_ACCEL]->num_channels =
ARRAY_SIZE(st_lsm6ds3h_accel_ch);
cdata->indio_dev[ST_MASK_ID_ACCEL_WK]->name =
kasprintf(GFP_KERNEL, "%s_%s_%s", cdata->name,
ST_LSM6DS3H_ACCEL_SUFFIX_NAME, ST_LSM6DS3H_WAKEUP_SUFFIX_NAME);
cdata->indio_dev[ST_MASK_ID_ACCEL_WK]->info = &st_lsm6ds3h_accel_wk_info;
cdata->indio_dev[ST_MASK_ID_ACCEL_WK]->channels = st_lsm6ds3h_accel_ch;
cdata->indio_dev[ST_MASK_ID_ACCEL_WK]->num_channels =
ARRAY_SIZE(st_lsm6ds3h_accel_ch);
cdata->indio_dev[ST_MASK_ID_GYRO]->name =
kasprintf(GFP_KERNEL, "%s_%s", cdata->name,
ST_LSM6DS3H_GYRO_SUFFIX_NAME);
cdata->indio_dev[ST_MASK_ID_GYRO]->info = &st_lsm6ds3h_gyro_info;
cdata->indio_dev[ST_MASK_ID_GYRO]->channels = st_lsm6ds3h_gyro_ch;
cdata->indio_dev[ST_MASK_ID_GYRO]->num_channels =
ARRAY_SIZE(st_lsm6ds3h_gyro_ch);
cdata->indio_dev[ST_MASK_ID_GYRO_WK]->name =
kasprintf(GFP_KERNEL, "%s_%s_%s", cdata->name,
ST_LSM6DS3H_GYRO_SUFFIX_NAME, ST_LSM6DS3H_WAKEUP_SUFFIX_NAME);
cdata->indio_dev[ST_MASK_ID_GYRO_WK]->info = &st_lsm6ds3h_gyro_wk_info;
cdata->indio_dev[ST_MASK_ID_GYRO_WK]->channels = st_lsm6ds3h_gyro_ch;
cdata->indio_dev[ST_MASK_ID_GYRO_WK]->num_channels =
ARRAY_SIZE(st_lsm6ds3h_gyro_ch);
cdata->indio_dev[ST_MASK_ID_SIGN_MOTION]->name =
kasprintf(GFP_KERNEL, "%s_%s", cdata->name,
ST_LSM6DS3H_SIGN_MOTION_SUFFIX_NAME);
cdata->indio_dev[ST_MASK_ID_SIGN_MOTION]->info =
&st_lsm6ds3h_sign_motion_info;
cdata->indio_dev[ST_MASK_ID_SIGN_MOTION]->channels =
st_lsm6ds3h_sign_motion_ch;
cdata->indio_dev[ST_MASK_ID_SIGN_MOTION]->num_channels =
ARRAY_SIZE(st_lsm6ds3h_sign_motion_ch);
cdata->indio_dev[ST_MASK_ID_STEP_COUNTER]->name =
kasprintf(GFP_KERNEL, "%s_%s", cdata->name,
ST_LSM6DS3H_STEP_COUNTER_SUFFIX_NAME);
cdata->indio_dev[ST_MASK_ID_STEP_COUNTER]->info =
&st_lsm6ds3h_step_c_info;
cdata->indio_dev[ST_MASK_ID_STEP_COUNTER]->channels =
st_lsm6ds3h_step_c_ch;
cdata->indio_dev[ST_MASK_ID_STEP_COUNTER]->num_channels =
ARRAY_SIZE(st_lsm6ds3h_step_c_ch);
cdata->indio_dev[ST_MASK_ID_STEP_DETECTOR]->name =
kasprintf(GFP_KERNEL, "%s_%s", cdata->name,
ST_LSM6DS3H_STEP_DETECTOR_SUFFIX_NAME);
cdata->indio_dev[ST_MASK_ID_STEP_DETECTOR]->info =
&st_lsm6ds3h_step_d_info;
cdata->indio_dev[ST_MASK_ID_STEP_DETECTOR]->channels =
st_lsm6ds3h_step_d_ch;
cdata->indio_dev[ST_MASK_ID_STEP_DETECTOR]->num_channels =
ARRAY_SIZE(st_lsm6ds3h_step_d_ch);
cdata->indio_dev[ST_MASK_ID_TILT]->name =
kasprintf(GFP_KERNEL, "%s_%s", cdata->name,
ST_LSM6DS3H_TILT_SUFFIX_NAME);
cdata->indio_dev[ST_MASK_ID_TILT]->info = &st_lsm6ds3h_tilt_info;
cdata->indio_dev[ST_MASK_ID_TILT]->channels = st_lsm6ds3h_tilt_ch;
cdata->indio_dev[ST_MASK_ID_TILT]->num_channels =
ARRAY_SIZE(st_lsm6ds3h_tilt_ch);
err = cdata->tf->write(cdata, ST_LSM6DS3H_RESET_ADDR, 1,
&reset_value, true);
if (err < 0)
goto iio_device_free;
msleep(200);
err = st_lsm6ds3h_upload_algo(cdata);
if (err < 0)
dev_err(cdata->dev, "failed to upload fw\n");
err = st_lsm6ds3h_init_sensor(cdata);
if (err < 0)
goto iio_device_free;
#ifdef CONFIG_ST_LSM6DS3H_IIO_ALGO_UPLOAD_WRIST_TILT
if (cdata->wrist_tilt_available) {
cdata->indio_dev[ST_MASK_ID_WRIST_TILT] = iio_device_alloc(sizeof(struct lsm6ds3h_sensor_data));
if (!cdata->indio_dev[ST_MASK_ID_WRIST_TILT]) {
cdata->wrist_tilt_available = false;
err = -ENOMEM;
goto iio_device_free;
}
sdata = iio_priv(cdata->indio_dev[ST_MASK_ID_WRIST_TILT]);
sdata->cdata = cdata;
sdata->sindex = ST_MASK_ID_WRIST_TILT;
sdata->num_data_channels = 0;
cdata->indio_dev[ST_MASK_ID_WRIST_TILT]->modes = INDIO_DIRECT_MODE;
cdata->indio_dev[ST_MASK_ID_WRIST_TILT]->name =
kasprintf(GFP_KERNEL, "%s_%s", cdata->name,
ST_LSM6DS3H_WRIST_TILT_SUFFIX_NAME);
cdata->indio_dev[ST_MASK_ID_WRIST_TILT]->info =
&st_lsm6ds3h_wrist_tilt_info;
cdata->indio_dev[ST_MASK_ID_WRIST_TILT]->channels =
st_lsm6ds3h_wrist_tilt_ch;
cdata->indio_dev[ST_MASK_ID_WRIST_TILT]->num_channels =
ARRAY_SIZE(st_lsm6ds3h_wrist_tilt_ch);
}
#endif /* CONFIG_ST_LSM6DS3H_IIO_ALGO_UPLOAD_WRIST_TILT */
st_lsm6ds3h_i2c_master_probe(cdata);
#ifdef CONFIG_ST_LSM6DS3H_IIO_TAP_TAP_ENABLED
cdata->indio_dev[ST_MASK_ID_TAP_TAP] = iio_device_alloc(sizeof(struct lsm6ds3h_sensor_data));
if (!cdata->indio_dev[ST_MASK_ID_TAP_TAP]) {
dev_err(cdata->dev, "failed to allocate TapTap device\n");
err = -ENOMEM;
goto iio_device_free;
}
sdata = iio_priv(cdata->indio_dev[ST_MASK_ID_TAP_TAP]);
sdata->cdata = cdata;
sdata->sindex = ST_MASK_ID_TAP_TAP;
sdata->num_data_channels = 0;
cdata->indio_dev[ST_MASK_ID_TAP_TAP]->modes = INDIO_DIRECT_MODE;
cdata->indio_dev[ST_MASK_ID_TAP_TAP]->name =
kasprintf(GFP_KERNEL, "%s_%s", cdata->name,
ST_LSM6DS3H_TAP_TAP_SUFFIX_NAME);
cdata->indio_dev[ST_MASK_ID_TAP_TAP]->info =
&st_lsm6ds3h_tap_tap_info;
cdata->indio_dev[ST_MASK_ID_TAP_TAP]->channels =
st_lsm6ds3h_tap_tap_ch;
cdata->indio_dev[ST_MASK_ID_TAP_TAP]->num_channels =
ARRAY_SIZE(st_lsm6ds3h_tap_tap_ch);
#endif /* CONFIG_ST_LSM6DS3H_IIO_TAP_TAP_ENABLED */
err = st_lsm6ds3h_allocate_rings(cdata);
if (err < 0)
goto iio_device_free;
if (irq > 0) {
err = st_lsm6ds3h_allocate_triggers(cdata,
ST_LSM6DS3H_TRIGGER_OPS);
if (err < 0)
goto deallocate_ring;
}
for (n = 0; n < ST_INDIO_DEV_NUM; n++) {
err = iio_device_register(cdata->indio_dev[n]);
if (err)
goto iio_device_unregister_and_trigger_deallocate;
}
#ifdef CONFIG_ST_LSM6DS3H_IIO_ALGO_UPLOAD_WRIST_TILT
if (cdata->wrist_tilt_available) {
err = iio_device_register(cdata->indio_dev[ST_MASK_ID_WRIST_TILT]);
if (err)
goto iio_device_unregister_and_trigger_deallocate;
}
#endif /* CONFIG_ST_LSM6DS3H_IIO_ALGO_UPLOAD_WRIST_TILT */
#ifdef CONFIG_ST_LSM6DS3H_IIO_TAP_TAP_ENABLED
err = iio_device_register(cdata->indio_dev[ST_MASK_ID_TAP_TAP]);
if (err)
goto iio_device_unregister_and_trigger_deallocate;
#endif /* CONFIG_ST_LSM6DS3H_IIO_TAP_TAP_ENABLED */
device_init_wakeup(cdata->dev, true);
pm_runtime_enable(cdata->dev);
return 0;
iio_device_unregister_and_trigger_deallocate:
for (n--; n >= 0; n--)
iio_device_unregister(cdata->indio_dev[n]);
if (irq > 0)
st_lsm6ds3h_deallocate_triggers(cdata);
deallocate_ring:
st_lsm6ds3h_deallocate_rings(cdata);
iio_device_free:
for (i--; i >= 0; i--)
iio_device_free(cdata->indio_dev[i]);
free_fifo_data:
kfree(cdata->fifo_data);
return err;
}
EXPORT_SYMBOL(st_lsm6ds3h_common_probe);
void st_lsm6ds3h_common_remove(struct lsm6ds3h_data *cdata, int irq)
{
int i;
pm_runtime_disable(cdata->dev);
#ifdef CONFIG_ST_LSM6DS3H_IIO_TAP_TAP_ENABLED
iio_device_unregister(cdata->indio_dev[ST_MASK_ID_TAP_TAP]);
#endif /* CONFIG_ST_LSM6DS3H_IIO_TAP_TAP_ENABLED */
#ifdef CONFIG_ST_LSM6DS3H_IIO_ALGO_UPLOAD_WRIST_TILT
if (cdata->wrist_tilt_available)
iio_device_unregister(cdata->indio_dev[ST_MASK_ID_WRIST_TILT]);
#endif /* CONFIG_ST_LSM6DS3H_IIO_ALGO_UPLOAD_WRIST_TILT */
for (i = 0; i < ST_INDIO_DEV_NUM; i++)
iio_device_unregister(cdata->indio_dev[i]);
if (irq > 0)
st_lsm6ds3h_deallocate_triggers(cdata);
st_lsm6ds3h_deallocate_rings(cdata);
#ifdef CONFIG_ST_LSM6DS3H_IIO_TAP_TAP_ENABLED
iio_device_free(cdata->indio_dev[ST_MASK_ID_TAP_TAP]);
#endif /* CONFIG_ST_LSM6DS3H_IIO_TAP_TAP_ENABLED */
#ifdef CONFIG_ST_LSM6DS3H_IIO_ALGO_UPLOAD_WRIST_TILT
if (cdata->wrist_tilt_available)
iio_device_free(cdata->indio_dev[ST_MASK_ID_WRIST_TILT]);
#endif /* CONFIG_ST_LSM6DS3H_IIO_ALGO_UPLOAD_WRIST_TILT */
for (i = 0; i < ST_INDIO_DEV_NUM; i++)
iio_device_free(cdata->indio_dev[i]);
kfree(cdata->fifo_data);
st_lsm6ds3h_i2c_master_exit(cdata);
}
EXPORT_SYMBOL(st_lsm6ds3h_common_remove);
#ifdef CONFIG_PM
int st_lsm6ds3h_common_suspend(struct lsm6ds3h_data *cdata)
{
disable_irq(cdata->irq);
int err, i;
u8 tmp_sensors_enabled;
struct lsm6ds3h_sensor_data *sdata;
tmp_sensors_enabled = cdata->sensors_enabled;
dev_dbg(cdata->dev, "st_lsm6ds3h_common_suspend enabled=%d\n",
cdata->sensors_enabled);
for (i = 0; i < ST_INDIO_FULL_DEV_NUM; i++) {
if (((1 << i) & ST_INDIO_DEV_AG_MASK) && stay_wake)
continue;
if ((1 << i) & ST_LSM6DS3H_WAKE_UP_SENSORS)
continue;
sdata = iio_priv(cdata->indio_dev[i]);
err = st_lsm6ds3h_set_drdy_irq(sdata, false);
if (err < 0)
return err;
}
cdata->sensors_enabled = tmp_sensors_enabled;
if (cdata->sensors_enabled & ST_LSM6DS3H_WAKE_UP_SENSORS) {
if (device_may_wakeup(cdata->dev))
enable_irq_wake(cdata->irq);
}
cdata->system_state = SF_SUSPEND;
return 0;
}
EXPORT_SYMBOL(st_lsm6ds3h_common_suspend);
int st_lsm6ds3h_common_resume(struct lsm6ds3h_data *cdata)
{
int err = 0, i;
struct lsm6ds3h_sensor_data *sdata;
dev_dbg(cdata->dev, "st_lsm6ds3h_common_resume enabled=%d\n",
cdata->sensors_enabled);
for (i = 0; i < ST_INDIO_FULL_DEV_NUM; i++) {
if (((1 << i) & ST_INDIO_DEV_AG_MASK) && stay_wake)
continue;
if ((1 << i) & ST_LSM6DS3H_WAKE_UP_SENSORS)
continue;
sdata = iio_priv(cdata->indio_dev[i]);
if (BIT(sdata->sindex) & cdata->sensors_enabled) {
err = st_lsm6ds3h_set_drdy_irq(sdata, true);
if (err < 0)
goto out;
}
}
cdata->system_state = SF_RESUME;
if (cdata->sensors_enabled & ST_LSM6DS3H_WAKE_UP_SENSORS) {
if (device_may_wakeup(cdata->dev))
disable_irq_wake(cdata->irq);
}
out:
enable_irq(cdata->irq);
return err;
}
EXPORT_SYMBOL(st_lsm6ds3h_common_resume);
#endif /* CONFIG_PM */
MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
MODULE_DESCRIPTION("STMicroelectronics lsm6ds3h core driver");
MODULE_LICENSE("GPL v2");