drivers:iio:stm:imu:st_asm330lhhx: Starting support asm330lhhx STMEMS IMU
Renamed st_asm330lhh driver to st_asm330lhhx, in order to include all asm330lhh IMU device sensor family. Signed-off-by: Mario Tesi <mario.tesi@st.com> Change-Id: I98355a2050269eac4576d3e0a2d6243284c37387 Reviewed-on: https://gerrit.st.com/c/linuxandroidopen/stm-ldd-iio/+/260376 Tested-by: CITOOLS <MDG-smet-aci-reviews@list.st.com> Reviewed-by: Denis CIOCCA <denis.ciocca@st.com>
This commit is contained in:
parent
3f13ee31e8
commit
c8055e7d00
@ -1,8 +1,9 @@
|
||||
* st_asm330lhh driver for imu MEMS sensors
|
||||
* st_asm330lhhx driver for imu MEMS sensors
|
||||
|
||||
Required properties for all bus drivers:
|
||||
- compatible: must be one of:
|
||||
"st,asm330lhh"
|
||||
"st,asm330lhhx"
|
||||
|
||||
Required properties for the i2c bindings:
|
||||
- reg: i2c slave address
|
||||
@ -32,7 +33,7 @@ Optional properties for all bus drivers:
|
||||
Refer to interrupt-controller/interrupts.txt for generic interrupt
|
||||
client node bindings.
|
||||
|
||||
Example for an spi device node:
|
||||
Example for an spi asm330lhh device node:
|
||||
|
||||
asm330lhh-imu@0 {
|
||||
compatible = "st,asm330lhh";
|
||||
@ -47,3 +48,18 @@ asm330lhh-imu@0 {
|
||||
"0", "1", "0",
|
||||
"0", "0", "1";
|
||||
};
|
||||
|
||||
Example for an i2c asm330lhhx device node (SA0 pulled down):
|
||||
|
||||
asm330lhhx-imu@0x6a {
|
||||
compatible = "st,asm330lhhx";
|
||||
reg = <0x6a>;
|
||||
interrupt-parent = <&gpio0>;
|
||||
interrupts = <0 IRQ_TYPE_LEVEL_HIGH>;
|
||||
vddio-supply = <&sensors_vddio>;
|
||||
vdd-supply = <&sensors_vdd>;
|
||||
st,int-pin = <1>;
|
||||
mount-matrix = "1", "0", "0",
|
||||
"0", "1", "0",
|
||||
"0", "0", "1";
|
||||
};
|
@ -9,7 +9,7 @@ menu "Inertial measurement units"
|
||||
source "drivers/iio/stm/imu/st_lsm6dsox/Kconfig"
|
||||
source "drivers/iio/stm/imu/st_lsm6dsrx/Kconfig"
|
||||
source "drivers/iio/stm/imu/st_ism330dhcx/Kconfig"
|
||||
source "drivers/iio/stm/imu/st_asm330lhh/Kconfig"
|
||||
source "drivers/iio/stm/imu/st_asm330lhhx/Kconfig"
|
||||
source "drivers/iio/stm/imu/st_imu68/Kconfig"
|
||||
source "drivers/iio/stm/imu/st_ism330dlc/Kconfig"
|
||||
source "drivers/iio/stm/imu/st_lsm6ds3/Kconfig"
|
||||
|
@ -7,7 +7,7 @@
|
||||
obj-y += st_lsm6dsox/
|
||||
obj-y += st_lsm6dsrx/
|
||||
obj-y += st_ism330dhcx/
|
||||
obj-y += st_asm330lhh/
|
||||
obj-y += st_asm330lhhx/
|
||||
obj-y += st_imu68/
|
||||
obj-y += st_ism330dlc/
|
||||
obj-y += st_lsm6ds3/
|
||||
|
@ -1,8 +0,0 @@
|
||||
st_asm330lhh-y := st_asm330lhh_core.o st_asm330lhh_buffer.o \
|
||||
st_asm330lhh_events.o
|
||||
st_asm330lhh-$(CONFIG_IIO_ST_ASM330LHH_ASYNC_HW_TIMESTAMP) += st_asm330lhh_hwtimestamp.o
|
||||
|
||||
obj-$(CONFIG_IIO_ST_ASM330LHH) += st_asm330lhh.o
|
||||
obj-$(CONFIG_IIO_ST_ASM330LHH_I2C) += st_asm330lhh_i2c.o
|
||||
obj-$(CONFIG_IIO_ST_ASM330LHH_SPI) += st_asm330lhh_spi.o
|
||||
obj-$(CONFIG_IIO_ST_ASM330LHH_ASYNC_HW_TIMESTAMP) += st_asm330lhh_hwtimestamp.o
|
@ -1,568 +0,0 @@
|
||||
/*
|
||||
* STMicroelectronics st_asm330lhh sensor driver
|
||||
*
|
||||
* Copyright 2019 STMicroelectronics Inc.
|
||||
*
|
||||
* Lorenzo Bianconi <lorenzo.bianconi@st.com>
|
||||
*
|
||||
* Licensed under the GPL-2.
|
||||
*/
|
||||
|
||||
#ifndef ST_ASM330LHH_H
|
||||
#define ST_ASM330LHH_H
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/hrtimer.h>
|
||||
#include <linux/spinlock.h>
|
||||
|
||||
#define ST_ASM330LHH_DRV_VERSION "1.1"
|
||||
#define ST_ASM330LHH_DEBUG_DISCHARGE
|
||||
|
||||
#define ST_ASM330LHH_MAX_ODR 833
|
||||
#define ST_ASM330LHH_ODR_LIST_SIZE 8
|
||||
#define ST_ASM330LHH_ODR_EXPAND(odr, uodr) ((odr * 1000000) + uodr)
|
||||
|
||||
#define ST_ASM330LHH_DEV_NAME "asm330lhh"
|
||||
|
||||
#define ST_ASM330LHH_DEFAULT_XL_FS_INDEX 2
|
||||
#define ST_ASM330LHH_DEFAULT_XL_ODR_INDEX 1
|
||||
#define ST_ASM330LHH_DEFAULT_G_FS_INDEX 2
|
||||
#define ST_ASM330LHH_DEFAULT_G_ODR_INDEX 1
|
||||
#define ST_ASM330LHH_DEFAULT_T_FS_INDEX 0
|
||||
#define ST_ASM330LHH_DEFAULT_T_ODR_INDEX 1
|
||||
|
||||
#define ST_ASM330LHH_REG_FIFO_CTRL1_ADDR 0x07
|
||||
#define ST_ASM330LHH_REG_FIFO_CTRL2_ADDR 0x08
|
||||
#define ST_ASM330LHH_REG_FIFO_WTM_MASK GENMASK(8, 0)
|
||||
#define ST_ASM330LHH_REG_FIFO_WTM8_MASK BIT(0)
|
||||
#define ST_ASM330LHH_REG_FIFO_STATUS_DIFF GENMASK(9, 0)
|
||||
|
||||
#define ST_ASM330LHH_REG_FIFO_CTRL3_ADDR 0x09
|
||||
#define ST_ASM330LHH_REG_BDR_XL_MASK GENMASK(3, 0)
|
||||
#define ST_ASM330LHH_REG_BDR_GY_MASK GENMASK(7, 4)
|
||||
|
||||
#define ST_ASM330LHH_REG_FIFO_CTRL4_ADDR 0x0a
|
||||
#define ST_ASM330LHH_REG_FIFO_MODE_MASK GENMASK(2, 0)
|
||||
#define ST_ASM330LHH_REG_DEC_TS_MASK GENMASK(7, 6)
|
||||
#define ST_ASM330LHH_REG_ODR_T_BATCH_MASK GENMASK(5, 4)
|
||||
|
||||
#define ST_ASM330LHH_REG_INT1_CTRL_ADDR 0x0d
|
||||
#define ST_ASM330LHH_REG_INT2_CTRL_ADDR 0x0e
|
||||
#define ST_ASM330LHH_REG_INT_FIFO_TH_MASK BIT(3)
|
||||
|
||||
#define ST_ASM330LHH_REG_WHOAMI_ADDR 0x0f
|
||||
#define ST_ASM330LHH_WHOAMI_VAL 0x6b
|
||||
|
||||
#define ST_ASM330LHH_CTRL1_XL_ADDR 0x10
|
||||
#define ST_ASM330LHH_CTRL2_G_ADDR 0x11
|
||||
|
||||
#define ST_ASM330LHH_REG_CTRL3_C_ADDR 0x12
|
||||
#define ST_ASM330LHH_REG_SW_RESET_MASK BIT(0)
|
||||
#define ST_ASM330LHH_REG_PP_OD_MASK BIT(4)
|
||||
#define ST_ASM330LHH_REG_H_LACTIVE_MASK BIT(5)
|
||||
#define ST_ASM330LHH_REG_BDU_MASK BIT(6)
|
||||
#define ST_ASM330LHH_REG_BOOT_MASK BIT(7)
|
||||
|
||||
#define ST_ASM330LHH_REG_CTRL4_C_ADDR 0x13
|
||||
#define ST_ASM330LHH_REG_DRDY_MASK BIT(3)
|
||||
|
||||
#define ST_ASM330LHH_REG_CTRL5_C_ADDR 0x14
|
||||
#define ST_ASM330LHH_REG_ROUNDING_MASK GENMASK(6, 5)
|
||||
#define ST_ASM330LHH_REG_ST_G_MASK GENMASK(3, 2)
|
||||
#define ST_ASM330LHH_REG_ST_XL_MASK GENMASK(1, 0)
|
||||
#define ST_ASM330LHH_SELFTEST_ACCEL_MIN 737
|
||||
#define ST_ASM330LHH_SELFTEST_ACCEL_MAX 13934
|
||||
#define ST_ASM330LHH_SELFTEST_GYRO_MIN 2142
|
||||
#define ST_ASM330LHH_SELFTEST_GYRO_MAX 10000
|
||||
|
||||
#define ST_ASM330LHH_SELF_TEST_DISABLED_VAL 0
|
||||
#define ST_ASM330LHH_SELF_TEST_POS_SIGN_VAL 1
|
||||
#define ST_ASM330LHH_SELF_TEST_NEG_ACCEL_SIGN_VAL 2
|
||||
#define ST_ASM330LHH_SELF_TEST_NEG_GYRO_SIGN_VAL 3
|
||||
|
||||
#define ST_ASM330LHH_REG_CTRL9_XL_ADDR 0x18
|
||||
#define ST_ASM330LHH_REG_DEVICE_CONF_MASK BIT(1)
|
||||
|
||||
#define ST_ASM330LHH_REG_CTRL10_C_ADDR 0x19
|
||||
#define ST_ASM330LHH_REG_TIMESTAMP_EN_MASK BIT(5)
|
||||
|
||||
#define ST_ASM330LHH_REG_STATUS_ADDR 0x1e
|
||||
#define ST_ASM330LHH_REG_STATUS_XLDA BIT(0)
|
||||
#define ST_ASM330LHH_REG_STATUS_GDA BIT(1)
|
||||
#define ST_ASM330LHH_REG_STATUS_TDA BIT(2)
|
||||
|
||||
#define ST_ASM330LHH_REG_OUT_TEMP_L_ADDR 0x20
|
||||
|
||||
#define ST_ASM330LHH_REG_OUTX_L_A_ADDR 0x28
|
||||
#define ST_ASM330LHH_REG_OUTY_L_A_ADDR 0x2a
|
||||
#define ST_ASM330LHH_REG_OUTZ_L_A_ADDR 0x2c
|
||||
|
||||
#define ST_ASM330LHH_REG_OUTX_L_G_ADDR 0x22
|
||||
#define ST_ASM330LHH_REG_OUTY_L_G_ADDR 0x24
|
||||
#define ST_ASM330LHH_REG_OUTZ_L_G_ADDR 0x26
|
||||
|
||||
#define ST_ASM330LHH_REG_TIMESTAMP0_ADDR 0x40
|
||||
|
||||
#define ST_ASM330LHH_REG_TAP_CFG0_ADDR 0x56
|
||||
#define ST_ASM330LHH_REG_LIR_MASK BIT(0)
|
||||
|
||||
#define ST_ASM330LHH_REG_THS_6D_ADDR 0x59
|
||||
#define ST_ASM330LHH_SIXD_THS_MASK GENMASK(6, 5)
|
||||
|
||||
#define ST_ASM330LHH_REG_WAKE_UP_THS_ADDR 0x5b
|
||||
#define ST_ASM330LHH_WAKE_UP_THS_MASK GENMASK(5, 0)
|
||||
|
||||
#define ST_ASM330LHH_REG_WAKE_UP_DUR_ADDR 0x5c
|
||||
#define ST_ASM330LHH_WAKE_UP_DUR_MASK GENMASK(6, 5)
|
||||
|
||||
#define ST_ASM330LHH_REG_FREE_FALL_ADDR 0x5d
|
||||
#define ST_ASM330LHH_FF_THS_MASK GENMASK(2, 0)
|
||||
|
||||
#define ST_ASM330LHH_INTERNAL_FREQ_FINE 0x63
|
||||
|
||||
/* Timestamp Tick 25us/LSB */
|
||||
#define ST_ASM330LHH_TS_DELTA_NS 25000ULL
|
||||
|
||||
/* Temperature in uC */
|
||||
#define ST_ASM330LHH_TEMP_GAIN 256
|
||||
#define ST_ASM330LHH_TEMP_FS_GAIN 1000000 / ST_ASM330LHH_TEMP_GAIN
|
||||
#define ST_ASM330LHH_TEMP_OFFSET 6400
|
||||
|
||||
/* FIFO simple size and depth */
|
||||
#define ST_ASM330LHH_SAMPLE_SIZE 6
|
||||
#define ST_ASM330LHH_TS_SAMPLE_SIZE 4
|
||||
#define ST_ASM330LHH_TAG_SIZE 1
|
||||
#define ST_ASM330LHH_FIFO_SAMPLE_SIZE (ST_ASM330LHH_SAMPLE_SIZE + \
|
||||
ST_ASM330LHH_TAG_SIZE)
|
||||
#define ST_ASM330LHH_MAX_FIFO_DEPTH 416
|
||||
|
||||
#define ST_ASM330LHH_DEFAULT_KTIME (200000000)
|
||||
#define ST_ASM330LHH_FAST_KTIME (5000000)
|
||||
|
||||
#define ST_ASM330LHH_DATA_CHANNEL(chan_type, addr, mod, ch2, scan_idx, \
|
||||
rb, sb, sg) \
|
||||
{ \
|
||||
.type = chan_type, \
|
||||
.address = addr, \
|
||||
.modified = mod, \
|
||||
.channel2 = ch2, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
|
||||
BIT(IIO_CHAN_INFO_SCALE), \
|
||||
.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), \
|
||||
.scan_index = scan_idx, \
|
||||
.scan_type = { \
|
||||
.sign = sg, \
|
||||
.realbits = rb, \
|
||||
.storagebits = sb, \
|
||||
.endianness = IIO_LE, \
|
||||
}, \
|
||||
.ext_info = st_asm330lhh_ext_info, \
|
||||
}
|
||||
|
||||
static const struct iio_event_spec st_asm330lhh_flush_event = {
|
||||
.type = IIO_EV_TYPE_FIFO_FLUSH,
|
||||
.dir = IIO_EV_DIR_EITHER,
|
||||
};
|
||||
|
||||
static const struct iio_event_spec st_asm330lhh_thr_event = {
|
||||
.type = IIO_EV_TYPE_THRESH,
|
||||
.dir = IIO_EV_DIR_RISING,
|
||||
.mask_separate = BIT(IIO_EV_INFO_ENABLE),
|
||||
};
|
||||
|
||||
#define ST_ASM330LHH_EVENT_CHANNEL(ctype, etype) \
|
||||
{ \
|
||||
.type = ctype, \
|
||||
.modified = 0, \
|
||||
.scan_index = -1, \
|
||||
.indexed = -1, \
|
||||
.event_spec = &st_asm330lhh_##etype##_event, \
|
||||
.num_event_specs = 1, \
|
||||
}
|
||||
|
||||
#define ST_ASM330LHH_RX_MAX_LENGTH 64
|
||||
#define ST_ASM330LHH_TX_MAX_LENGTH 16
|
||||
|
||||
struct st_asm330lhh_transfer_buffer {
|
||||
u8 rx_buf[ST_ASM330LHH_RX_MAX_LENGTH];
|
||||
u8 tx_buf[ST_ASM330LHH_TX_MAX_LENGTH] ____cacheline_aligned;
|
||||
};
|
||||
|
||||
struct st_asm330lhh_transfer_function {
|
||||
int (*read)(struct device *dev, u8 addr, int len, u8 *data);
|
||||
int (*write)(struct device *dev, u8 addr, int len, const u8 *data);
|
||||
};
|
||||
|
||||
/**
|
||||
* struct st_asm330lhh_reg - Generic sensor register description (addr + mask)
|
||||
* @addr: Address of register.
|
||||
* @mask: Bitmask register for proper usage.
|
||||
*/
|
||||
struct st_asm330lhh_reg {
|
||||
u8 addr;
|
||||
u8 mask;
|
||||
};
|
||||
|
||||
enum st_asm330lhh_suspend_resume_register {
|
||||
ST_ASM330LHH_CTRL1_XL_REG = 0,
|
||||
ST_ASM330LHH_CTRL2_G_REG,
|
||||
ST_ASM330LHH_REG_CTRL3_C_REG,
|
||||
ST_ASM330LHH_REG_CTRL4_C_REG,
|
||||
ST_ASM330LHH_REG_CTRL5_C_REG,
|
||||
ST_ASM330LHH_REG_CTRL10_C_REG,
|
||||
ST_ASM330LHH_REG_TAP_CFG0_REG,
|
||||
ST_ASM330LHH_REG_INT1_CTRL_REG,
|
||||
ST_ASM330LHH_REG_INT2_CTRL_REG,
|
||||
ST_ASM330LHH_REG_FIFO_CTRL1_REG,
|
||||
ST_ASM330LHH_REG_FIFO_CTRL2_REG,
|
||||
ST_ASM330LHH_REG_FIFO_CTRL3_REG,
|
||||
ST_ASM330LHH_REG_FIFO_CTRL4_REG,
|
||||
ST_ASM330LHH_SUSPEND_RESUME_REGS,
|
||||
};
|
||||
|
||||
struct st_asm330lhh_suspend_resume_entry {
|
||||
u8 addr;
|
||||
u8 val;
|
||||
u8 mask;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct st_asm330lhh_odr - Single ODR entry
|
||||
* @hz: Most significant part of the sensor ODR (Hz).
|
||||
* @uhz: Less significant part of the sensor ODR (micro Hz).
|
||||
* @val: ODR register value.
|
||||
* @batch_val: Batching ODR register value.
|
||||
*/
|
||||
struct st_asm330lhh_odr {
|
||||
u16 hz;
|
||||
u32 uhz;
|
||||
u8 val;
|
||||
u8 batch_val;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct st_asm330lhh_odr_table_entry - Sensor ODR table
|
||||
* @size: Size of ODR table.
|
||||
* @reg: ODR register.
|
||||
* @batching_reg: ODR register for batching on fifo.
|
||||
* @odr_avl: Array of supported ODR value.
|
||||
*/
|
||||
struct st_asm330lhh_odr_table_entry {
|
||||
u8 size;
|
||||
struct st_asm330lhh_reg reg;
|
||||
struct st_asm330lhh_reg batching_reg;
|
||||
struct st_asm330lhh_odr odr_avl[ST_ASM330LHH_ODR_LIST_SIZE];
|
||||
};
|
||||
|
||||
/**
|
||||
* struct st_asm330lhh_fs - Full Scale sensor table entry
|
||||
* @reg: Register description for FS settings.
|
||||
* @gain: Sensor sensitivity (mdps/LSB, mg/LSB and uC/LSB).
|
||||
* @val: FS register value.
|
||||
*/
|
||||
struct st_asm330lhh_fs {
|
||||
struct st_asm330lhh_reg reg;
|
||||
u32 gain;
|
||||
u8 val;
|
||||
};
|
||||
|
||||
#define ST_ASM330LHH_FS_LIST_SIZE 5
|
||||
#define ST_ASM330LHH_FS_ACC_LIST_SIZE 4
|
||||
#define ST_ASM330LHH_FS_GYRO_LIST_SIZE 5
|
||||
#ifdef CONFIG_IIO_ST_ASM330LHH_EN_TEMPERATURE
|
||||
#define ST_ASM330LHH_FS_TEMP_LIST_SIZE 1
|
||||
#endif /* CONFIG_IIO_ST_ASM330LHH_EN_TEMPERATURE */
|
||||
|
||||
/**
|
||||
* struct st_asm330lhh_fs_table_entry - Full Scale sensor table
|
||||
* @size: Full Scale sensor table size.
|
||||
* @fs_avl: Full Scale list entries.
|
||||
*/
|
||||
struct st_asm330lhh_fs_table_entry {
|
||||
u8 size;
|
||||
struct st_asm330lhh_fs fs_avl[ST_ASM330LHH_FS_LIST_SIZE];
|
||||
};
|
||||
|
||||
#define ST_ASM330LHH_ACC_FS_2G_GAIN IIO_G_TO_M_S_2(61000)
|
||||
#define ST_ASM330LHH_ACC_FS_4G_GAIN IIO_G_TO_M_S_2(122000)
|
||||
#define ST_ASM330LHH_ACC_FS_8G_GAIN IIO_G_TO_M_S_2(244000)
|
||||
#define ST_ASM330LHH_ACC_FS_16G_GAIN IIO_G_TO_M_S_2(488000)
|
||||
|
||||
#define ST_ASM330LHH_GYRO_FS_250_GAIN IIO_DEGREE_TO_RAD(8750000)
|
||||
#define ST_ASM330LHH_GYRO_FS_500_GAIN IIO_DEGREE_TO_RAD(17500000)
|
||||
#define ST_ASM330LHH_GYRO_FS_1000_GAIN IIO_DEGREE_TO_RAD(35000000)
|
||||
#define ST_ASM330LHH_GYRO_FS_2000_GAIN IIO_DEGREE_TO_RAD(70000000)
|
||||
#define ST_ASM330LHH_GYRO_FS_4000_GAIN IIO_DEGREE_TO_RAD(140000000)
|
||||
|
||||
enum st_asm330lhh_sensor_id {
|
||||
ST_ASM330LHH_ID_GYRO = 0,
|
||||
ST_ASM330LHH_ID_ACC,
|
||||
#ifdef CONFIG_IIO_ST_ASM330LHH_EN_TEMPERATURE
|
||||
ST_ASM330LHH_ID_TEMP,
|
||||
#endif /* CONFIG_IIO_ST_ASM330LHH_EN_TEMPERATURE */
|
||||
ST_ASM330LHH_ID_EVENT,
|
||||
ST_ASM330LHH_ID_FF = ST_ASM330LHH_ID_EVENT,
|
||||
ST_ASM330LHH_ID_SC,
|
||||
ST_ASM330LHH_ID_TRIGGER,
|
||||
ST_ASM330LHH_ID_WK = ST_ASM330LHH_ID_TRIGGER,
|
||||
ST_ASM330LHH_ID_6D,
|
||||
ST_ASM330LHH_ID_MAX,
|
||||
};
|
||||
|
||||
enum st_asm330lhh_fifo_mode {
|
||||
ST_ASM330LHH_FIFO_BYPASS = 0x0,
|
||||
ST_ASM330LHH_FIFO_CONT = 0x6,
|
||||
};
|
||||
|
||||
enum {
|
||||
ST_ASM330LHH_HW_FLUSH,
|
||||
ST_ASM330LHH_HW_OPERATIONAL,
|
||||
};
|
||||
|
||||
/**
|
||||
* struct st_asm330lhh_sensor - ST IMU sensor instance
|
||||
* @id: Sensor identifier.
|
||||
* @hw: Pointer to instance of struct st_asm330lhh_hw.
|
||||
* @gain: Configured sensor sensitivity.
|
||||
* @offset: Sensor data offset.
|
||||
* @conf: Used in case of sensor event to manage configuration.
|
||||
* @odr: Output data rate of the sensor [Hz].
|
||||
* @uodr: Output data rate of the sensor [uHz].
|
||||
* @max_watermark: Max supported watermark level.
|
||||
* @watermark: Sensor watermark level.
|
||||
* @last_fifo_timestamp: Store last sample timestamp in FIFO, used by flush
|
||||
* @selftest_status: Last status of self test output
|
||||
* @min_st, @max_st: Min/Max acc/gyro data values during self test procedure
|
||||
*/
|
||||
struct st_asm330lhh_sensor {
|
||||
enum st_asm330lhh_sensor_id id;
|
||||
struct st_asm330lhh_hw *hw;
|
||||
struct iio_trigger *trig;
|
||||
|
||||
union {
|
||||
/* sensor with odrs, gain and offset */
|
||||
struct {
|
||||
u32 gain;
|
||||
u32 offset;
|
||||
int odr;
|
||||
int uodr;
|
||||
|
||||
#ifdef ST_ASM330LHH_DEBUG_DISCHARGE
|
||||
u32 discharged_samples;
|
||||
#endif /* ST_ASM330LHH_DEBUG_DISCHARGE */
|
||||
|
||||
u16 max_watermark;
|
||||
u16 watermark;
|
||||
s64 last_fifo_timestamp;
|
||||
|
||||
/* self test */
|
||||
int8_t selftest_status;
|
||||
int min_st;
|
||||
int max_st;
|
||||
};
|
||||
|
||||
/* sensor specific data configuration */
|
||||
struct {
|
||||
u32 conf[6];
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* struct st_asm330lhh_hw - ST IMU MEMS hw instance
|
||||
* @dev: Pointer to instance of struct device (I2C or SPI).
|
||||
* @irq: Device interrupt line (I2C or SPI).
|
||||
* @int_pin: Save interrupt pin used by sensor.
|
||||
* @lock: Mutex to protect read and write operations.
|
||||
* @fifo_lock: Mutex to prevent concurrent access to the hw FIFO.
|
||||
* @page_lock: Mutex to prevent concurrent memory page configuration.
|
||||
* @fifo_mode: FIFO operating mode supported by the device.
|
||||
* @state: hw operational state.
|
||||
* @enable_mask: Enabled sensor bitmask.
|
||||
* @hw_timestamp_global: hw timestamp value always monotonic where the most
|
||||
* significant 8byte are incremented at every disable/enable.
|
||||
* @timesync_workqueue: runs the async task in private workqueue.
|
||||
* @timesync_work: actual work to be done in the async task workqueue.
|
||||
* @timesync_timer: hrtimer used to schedule period read for the async task.
|
||||
* @hwtimestamp_lock: spinlock for the 64bit timestamp value.
|
||||
* @timesync_ktime: interval value used by the hrtimer.
|
||||
* @timestamp_c: counter used for counting number of timesync updates.
|
||||
* @ts_offset: Hw timestamp offset.
|
||||
* @ts_delta_ns: Calibrate delta time tick.
|
||||
* @hw_ts: Latest hw timestamp from the sensor.
|
||||
* @val_ts_old: Hold hw timestamp for timer rollover.
|
||||
* @hw_ts_high: Save MSB hw timestamp.
|
||||
* @tsample: Timestamp for each sensor sample.
|
||||
* @delta_ts: Delta time between two consecutive interrupts.
|
||||
* @ts: Latest timestamp from irq handler.
|
||||
* @odr_table_entry: Sensors ODR table.
|
||||
* @iio_devs: Pointers to acc/gyro iio_dev instances.
|
||||
* @tf: Transfer function structure used by I/O operations.
|
||||
* @tb: Transfer buffers used by SPI I/O operations.
|
||||
* @orientation: sensor chip orientation relative to main hardware.
|
||||
*/
|
||||
struct st_asm330lhh_hw {
|
||||
struct device *dev;
|
||||
int irq;
|
||||
int int_pin;
|
||||
|
||||
struct mutex lock;
|
||||
struct mutex fifo_lock;
|
||||
struct mutex page_lock;
|
||||
|
||||
enum st_asm330lhh_fifo_mode fifo_mode;
|
||||
unsigned long state;
|
||||
u32 enable_mask;
|
||||
|
||||
s64 hw_timestamp_global;
|
||||
|
||||
#if defined (CONFIG_IIO_ST_ASM330LHH_ASYNC_HW_TIMESTAMP)
|
||||
struct workqueue_struct *timesync_workqueue;
|
||||
struct work_struct timesync_work;
|
||||
struct hrtimer timesync_timer;
|
||||
spinlock_t hwtimestamp_lock;
|
||||
ktime_t timesync_ktime;
|
||||
int timesync_c;
|
||||
#endif /* CONFIG_IIO_ST_ASM330LHH_ASYNC_HW_TIMESTAMP */
|
||||
|
||||
s64 ts_offset;
|
||||
u64 ts_delta_ns;
|
||||
s64 hw_ts;
|
||||
u32 val_ts_old;
|
||||
u32 hw_ts_high;
|
||||
s64 tsample;
|
||||
s64 delta_ts;
|
||||
s64 ts;
|
||||
|
||||
const struct st_asm330lhh_odr_table_entry *odr_table_entry;
|
||||
struct iio_dev *iio_devs[ST_ASM330LHH_ID_MAX];
|
||||
|
||||
struct regulator *vdd_supply;
|
||||
struct regulator *vddio_supply;
|
||||
|
||||
const struct st_asm330lhh_transfer_function *tf;
|
||||
struct st_asm330lhh_transfer_buffer tb;
|
||||
|
||||
struct iio_mount_matrix orientation;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct st_asm330lhh_ff_th - Free Fall threshold table
|
||||
* @mg: Threshold in mg.
|
||||
* @val: Register value.
|
||||
*/
|
||||
struct st_asm330lhh_ff_th {
|
||||
u32 mg;
|
||||
u8 val;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct st_asm330lhh_6D_th - 6D threshold table
|
||||
* @deg: Threshold in degrees.
|
||||
* @val: Register value.
|
||||
*/
|
||||
struct st_asm330lhh_6D_th {
|
||||
u8 deg;
|
||||
u8 val;
|
||||
};
|
||||
|
||||
extern const struct dev_pm_ops st_asm330lhh_pm_ops;
|
||||
|
||||
static inline int st_asm330lhh_read_atomic(struct st_asm330lhh_hw *hw, u8 addr,
|
||||
int len, u8 *data)
|
||||
{
|
||||
int err;
|
||||
|
||||
mutex_lock(&hw->page_lock);
|
||||
err = hw->tf->read(hw->dev, addr, len, data);
|
||||
mutex_unlock(&hw->page_lock);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static inline int st_asm330lhh_write_atomic(struct st_asm330lhh_hw *hw, u8 addr,
|
||||
int len, u8 *data)
|
||||
{
|
||||
int err;
|
||||
|
||||
mutex_lock(&hw->page_lock);
|
||||
err = hw->tf->write(hw->dev, addr, len, data);
|
||||
mutex_unlock(&hw->page_lock);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
int __st_asm330lhh_write_with_mask(struct st_asm330lhh_hw *hw, u8 addr, u8 mask,
|
||||
u8 val);
|
||||
static inline int st_asm330lhh_write_with_mask(struct st_asm330lhh_hw *hw, u8 addr,
|
||||
u8 mask, u8 val)
|
||||
{
|
||||
int err;
|
||||
|
||||
mutex_lock(&hw->page_lock);
|
||||
err = __st_asm330lhh_write_with_mask(hw, addr, mask, val);
|
||||
mutex_unlock(&hw->page_lock);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static inline bool st_asm330lhh_is_fifo_enabled(struct st_asm330lhh_hw *hw)
|
||||
{
|
||||
return hw->enable_mask & (BIT(ST_ASM330LHH_ID_GYRO) |
|
||||
BIT(ST_ASM330LHH_ID_ACC));
|
||||
}
|
||||
|
||||
static inline s64 st_asm330lhh_get_time_ns(struct iio_dev *iio_dev)
|
||||
{
|
||||
return iio_get_time_ns(iio_dev);
|
||||
}
|
||||
|
||||
int st_asm330lhh_probe(struct device *dev, int irq,
|
||||
const struct st_asm330lhh_transfer_function *tf_ops);
|
||||
int st_asm330lhh_sensor_set_enable(struct st_asm330lhh_sensor *sensor,
|
||||
bool enable);
|
||||
int st_asm330lhh_buffers_setup(struct st_asm330lhh_hw *hw);
|
||||
int st_asm330lhh_get_batch_val(struct st_asm330lhh_sensor *sensor, int odr,
|
||||
int uodr, u8 *val);
|
||||
int st_asm330lhh_update_watermark(struct st_asm330lhh_sensor *sensor,
|
||||
u16 watermark);
|
||||
ssize_t st_asm330lhh_flush_fifo(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t size);
|
||||
ssize_t st_asm330lhh_get_max_watermark(struct device *dev,
|
||||
struct device_attribute *attr, char *buf);
|
||||
ssize_t st_asm330lhh_get_watermark(struct device *dev,
|
||||
struct device_attribute *attr, char *buf);
|
||||
ssize_t st_asm330lhh_set_watermark(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t size);
|
||||
int st_asm330lhh_suspend_fifo(struct st_asm330lhh_hw *hw);
|
||||
int st_asm330lhh_set_fifo_mode(struct st_asm330lhh_hw *hw,
|
||||
enum st_asm330lhh_fifo_mode fifo_mode);
|
||||
int __st_asm330lhh_set_sensor_batching_odr(struct st_asm330lhh_sensor *sensor,
|
||||
bool enable);
|
||||
int st_asm330lhh_update_batching(struct iio_dev *iio_dev, bool enable);
|
||||
int st_asm330lhh_reset_hwts(struct st_asm330lhh_hw *hw);
|
||||
#ifdef CONFIG_IIO_ST_ASM330LHH_EN_BASIC_FEATURES
|
||||
int st_asm330lhh_event_handler(struct st_asm330lhh_hw *hw);
|
||||
int st_asm330lhh_probe_event(struct st_asm330lhh_hw *hw);
|
||||
int st_asm330lhh_set_wake_up_thershold(struct st_asm330lhh_hw *hw, int th_ug);
|
||||
int st_asm330lhh_set_wake_up_duration(struct st_asm330lhh_hw *hw, int dur_ms);
|
||||
int st_asm330lhh_set_freefall_threshold(struct st_asm330lhh_hw *hw, int th_mg);
|
||||
int st_asm330lhh_set_6D_threshold(struct st_asm330lhh_hw *hw, int deg);
|
||||
int st_asm330lhh_read_with_mask(struct st_asm330lhh_hw *hw, u8 addr, u8 mask,
|
||||
u8 *val);
|
||||
#endif /* CONFIG_IIO_ST_ASM330LHH_EN_BASIC_FEATURES */
|
||||
|
||||
#if defined (CONFIG_IIO_ST_ASM330LHH_ASYNC_HW_TIMESTAMP)
|
||||
int st_asm330lhh_hwtimesync_init(struct st_asm330lhh_hw *hw);
|
||||
#else /* CONFIG_IIO_ST_ASM330LHH_ASYNC_HW_TIMESTAMP */
|
||||
static inline int st_asm330lhh_hwtimesync_init(struct st_asm330lhh_hw *hw)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif /* CONFIG_IIO_ST_ASM330LHH_ASYNC_HW_TIMESTAMP */
|
||||
#endif /* ST_ASM330LHH_H */
|
@ -1,673 +0,0 @@
|
||||
/*
|
||||
* STMicroelectronics st_asm330lhh FIFO buffer library driver
|
||||
*
|
||||
* Copyright 2019 STMicroelectronics Inc.
|
||||
*
|
||||
* Lorenzo Bianconi <lorenzo.bianconi@st.com>
|
||||
* Mario Tesi <mario.tesi@st.com>
|
||||
*
|
||||
* Licensed under the GPL-2.
|
||||
*/
|
||||
#include <linux/module.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/kfifo_buf.h>
|
||||
#include <linux/iio/events.h>
|
||||
#include <linux/iio/buffer.h>
|
||||
#include <asm/unaligned.h>
|
||||
#include <linux/iio/buffer.h>
|
||||
#include "st_asm330lhh.h"
|
||||
|
||||
#define ST_ASM330LHH_REG_FIFO_STATUS1_ADDR 0x3a
|
||||
#define ST_ASM330LHH_REG_TIMESTAMP2_ADDR 0x42
|
||||
#define ST_ASM330LHH_REG_FIFO_DATA_OUT_TAG_ADDR 0x78
|
||||
|
||||
#define ST_ASM330LHH_SAMPLE_DISCHARD 0x7ffd
|
||||
|
||||
/* Timestamp convergence filter parameter */
|
||||
#define ST_ASM330LHH_EWMA_LEVEL 120
|
||||
#define ST_ASM330LHH_EWMA_DIV 128
|
||||
|
||||
#define ST_ASM330LHH_TIMESTAMP_RESET_VALUE 0xaa
|
||||
|
||||
enum {
|
||||
ST_ASM330LHH_GYRO_TAG = 0x01,
|
||||
ST_ASM330LHH_ACC_TAG = 0x02,
|
||||
#ifdef CONFIG_IIO_ST_ASM330LHH_EN_TEMPERATURE
|
||||
ST_ASM330LHH_TEMP_TAG = 0x03,
|
||||
#endif /* CONFIG_IIO_ST_ASM330LHH_EN_TEMPERATURE */
|
||||
ST_ASM330LHH_TS_TAG = 0x04,
|
||||
};
|
||||
|
||||
/* Default timeout before to re-enable gyro */
|
||||
int delay_gyro = 10;
|
||||
module_param(delay_gyro, int, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
|
||||
MODULE_PARM_DESC(delay_gyro, "Delay for Gyro arming");
|
||||
static bool delayed_enable_gyro;
|
||||
|
||||
static inline s64 st_asm330lhh_ewma(s64 old, s64 new, int weight)
|
||||
{
|
||||
s64 diff, incr;
|
||||
|
||||
diff = new - old;
|
||||
incr = div_s64((ST_ASM330LHH_EWMA_DIV - weight) * diff,
|
||||
ST_ASM330LHH_EWMA_DIV);
|
||||
|
||||
return old + incr;
|
||||
}
|
||||
|
||||
inline int st_asm330lhh_reset_hwts(struct st_asm330lhh_hw *hw)
|
||||
{
|
||||
#if defined(CONFIG_IIO_ST_ASM330LHH_ASYNC_HW_TIMESTAMP)
|
||||
struct st_asm330lhh_sensor *sensor;
|
||||
int i;
|
||||
#endif /* CONFIG_IIO_ST_ASM330LHH_ASYNC_HW_TIMESTAMP */
|
||||
u8 data = ST_ASM330LHH_TIMESTAMP_RESET_VALUE;
|
||||
int ret;
|
||||
|
||||
ret = st_asm330lhh_write_atomic(hw, ST_ASM330LHH_REG_TIMESTAMP2_ADDR,
|
||||
sizeof(data), &data);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
#if defined(CONFIG_IIO_ST_ASM330LHH_ASYNC_HW_TIMESTAMP)
|
||||
spin_lock_irq(&hw->hwtimestamp_lock);
|
||||
hw->hw_timestamp_global = (hw->hw_timestamp_global + (1LL << 32)) &
|
||||
GENMASK_ULL(63, 32);
|
||||
spin_unlock_irq(&hw->hwtimestamp_lock);
|
||||
for (i = 0; i < ST_ASM330LHH_ID_MAX; ++i) {
|
||||
if (!hw->iio_devs[i])
|
||||
continue;
|
||||
|
||||
sensor = iio_priv(hw->iio_devs[i]);
|
||||
}
|
||||
hw->timesync_c = 0;
|
||||
hw->timesync_ktime = ktime_set(0, ST_ASM330LHH_FAST_KTIME);
|
||||
#else /* CONFIG_IIO_ST_ASM330LHH_ASYNC_HW_TIMESTAMP */
|
||||
hw->hw_timestamp_global = (hw->hw_timestamp_global + (1LL << 32)) &
|
||||
GENMASK_ULL(63, 32);
|
||||
#endif /* CONFIG_IIO_ST_ASM330LHH_ASYNC_HW_TIMESTAMP */
|
||||
|
||||
hw->ts = st_asm330lhh_get_time_ns(hw->iio_devs[0]);
|
||||
hw->ts_offset = hw->ts;
|
||||
hw->val_ts_old = 0;
|
||||
hw->hw_ts_high = 0;
|
||||
hw->tsample = 0ull;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int st_asm330lhh_set_fifo_mode(struct st_asm330lhh_hw *hw,
|
||||
enum st_asm330lhh_fifo_mode fifo_mode)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = st_asm330lhh_write_with_mask(hw, ST_ASM330LHH_REG_FIFO_CTRL4_ADDR,
|
||||
ST_ASM330LHH_REG_FIFO_MODE_MASK,
|
||||
fifo_mode);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
hw->fifo_mode = fifo_mode;
|
||||
|
||||
if (fifo_mode == ST_ASM330LHH_FIFO_BYPASS)
|
||||
clear_bit(ST_ASM330LHH_HW_OPERATIONAL, &hw->state);
|
||||
else
|
||||
set_bit(ST_ASM330LHH_HW_OPERATIONAL, &hw->state);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int __st_asm330lhh_set_sensor_batching_odr(struct st_asm330lhh_sensor *s,
|
||||
bool enable)
|
||||
{
|
||||
enum st_asm330lhh_sensor_id id = s->id;
|
||||
struct st_asm330lhh_hw *hw = s->hw;
|
||||
u8 data = 0;
|
||||
int err;
|
||||
|
||||
if (enable) {
|
||||
err = st_asm330lhh_get_batch_val(s, s->odr, s->uodr, &data);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
|
||||
return __st_asm330lhh_write_with_mask(hw,
|
||||
hw->odr_table_entry[id].batching_reg.addr,
|
||||
hw->odr_table_entry[id].batching_reg.mask,
|
||||
data);
|
||||
}
|
||||
|
||||
static inline int
|
||||
st_asm330lhh_set_sensor_batching_odr(struct st_asm330lhh_sensor *sensor,
|
||||
bool enable)
|
||||
{
|
||||
struct st_asm330lhh_hw *hw = sensor->hw;
|
||||
int err;
|
||||
|
||||
mutex_lock(&hw->page_lock);
|
||||
err = __st_asm330lhh_set_sensor_batching_odr(sensor, enable);
|
||||
mutex_unlock(&hw->page_lock);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
int st_asm330lhh_update_watermark(struct st_asm330lhh_sensor *sensor,
|
||||
u16 watermark)
|
||||
{
|
||||
u16 fifo_watermark = ST_ASM330LHH_MAX_FIFO_DEPTH, cur_watermark = 0;
|
||||
struct st_asm330lhh_hw *hw = sensor->hw;
|
||||
struct st_asm330lhh_sensor *cur_sensor;
|
||||
__le16 wdata;
|
||||
int i, err;
|
||||
u8 data;
|
||||
|
||||
for (i = ST_ASM330LHH_ID_GYRO; i < ST_ASM330LHH_ID_MAX; i++) {
|
||||
if (!hw->iio_devs[i])
|
||||
continue;
|
||||
|
||||
cur_sensor = iio_priv(hw->iio_devs[i]);
|
||||
|
||||
if (!(hw->enable_mask & BIT(cur_sensor->id)))
|
||||
continue;
|
||||
|
||||
cur_watermark = (cur_sensor == sensor) ? watermark
|
||||
: cur_sensor->watermark;
|
||||
|
||||
fifo_watermark = min_t(u16, fifo_watermark, cur_watermark);
|
||||
}
|
||||
|
||||
fifo_watermark = max_t(u16, fifo_watermark, 2);
|
||||
|
||||
mutex_lock(&hw->lock);
|
||||
|
||||
err = st_asm330lhh_read_atomic(hw, ST_ASM330LHH_REG_FIFO_CTRL1_ADDR + 1,
|
||||
sizeof(data), &data);
|
||||
if (err < 0)
|
||||
goto out;
|
||||
|
||||
fifo_watermark = ((data << 8) & ~ST_ASM330LHH_REG_FIFO_WTM_MASK) |
|
||||
(fifo_watermark & ST_ASM330LHH_REG_FIFO_WTM_MASK);
|
||||
wdata = cpu_to_le16(fifo_watermark);
|
||||
err = st_asm330lhh_write_atomic(hw, ST_ASM330LHH_REG_FIFO_CTRL1_ADDR,
|
||||
sizeof(wdata), (u8 *)&wdata);
|
||||
|
||||
out:
|
||||
mutex_unlock(&hw->lock);
|
||||
|
||||
return err < 0 ? err : 0;
|
||||
}
|
||||
|
||||
static struct iio_dev *st_asm330lhh_get_iiodev_from_tag(struct st_asm330lhh_hw *hw,
|
||||
u8 tag)
|
||||
{
|
||||
struct iio_dev *iio_dev;
|
||||
|
||||
switch (tag) {
|
||||
case ST_ASM330LHH_GYRO_TAG:
|
||||
iio_dev = hw->iio_devs[ST_ASM330LHH_ID_GYRO];
|
||||
break;
|
||||
case ST_ASM330LHH_ACC_TAG:
|
||||
iio_dev = hw->iio_devs[ST_ASM330LHH_ID_ACC];
|
||||
break;
|
||||
#ifdef CONFIG_IIO_ST_ASM330LHH_EN_TEMPERATURE
|
||||
case ST_ASM330LHH_TEMP_TAG:
|
||||
iio_dev = hw->iio_devs[ST_ASM330LHH_ID_TEMP];
|
||||
break;
|
||||
#endif /* CONFIG_IIO_ST_ASM330LHH_EN_TEMPERATURE */
|
||||
default:
|
||||
iio_dev = NULL;
|
||||
break;
|
||||
}
|
||||
|
||||
return iio_dev;
|
||||
}
|
||||
|
||||
static inline void st_asm330lhh_sync_hw_ts(struct st_asm330lhh_hw *hw, s64 ts)
|
||||
{
|
||||
s64 delta = ts - hw->hw_ts;
|
||||
|
||||
hw->ts_offset = st_asm330lhh_ewma(hw->ts_offset, delta,
|
||||
ST_ASM330LHH_EWMA_LEVEL);
|
||||
}
|
||||
|
||||
static int st_asm330lhh_read_fifo(struct st_asm330lhh_hw *hw)
|
||||
{
|
||||
u8 iio_buf[ALIGN(ST_ASM330LHH_SAMPLE_SIZE, sizeof(s64)) +
|
||||
sizeof(s64) + sizeof(s64)];
|
||||
u8 buf[6 * ST_ASM330LHH_FIFO_SAMPLE_SIZE], tag, *ptr;
|
||||
int i, err, word_len, fifo_len, read_len;
|
||||
__le64 hw_timestamp_push;
|
||||
struct iio_dev *iio_dev;
|
||||
s64 ts_irq, hw_ts_old;
|
||||
__le16 fifo_status;
|
||||
u16 fifo_depth;
|
||||
s16 drdymask;
|
||||
u32 val;
|
||||
|
||||
/* return if FIFO is already disabled */
|
||||
if (!test_bit(ST_ASM330LHH_HW_OPERATIONAL, &hw->state)) {
|
||||
dev_warn(hw->dev, "%s: FIFO in bypass mode\n", __func__);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
ts_irq = hw->ts - hw->delta_ts;
|
||||
|
||||
err = st_asm330lhh_read_atomic(hw, ST_ASM330LHH_REG_FIFO_STATUS1_ADDR,
|
||||
sizeof(fifo_status), (u8 *)&fifo_status);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
fifo_depth = le16_to_cpu(fifo_status) & ST_ASM330LHH_REG_FIFO_STATUS_DIFF;
|
||||
if (!fifo_depth)
|
||||
return 0;
|
||||
|
||||
fifo_len = fifo_depth * ST_ASM330LHH_FIFO_SAMPLE_SIZE;
|
||||
read_len = 0;
|
||||
while (read_len < fifo_len) {
|
||||
word_len = min_t(int, fifo_len - read_len, sizeof(buf));
|
||||
err = st_asm330lhh_read_atomic(hw,
|
||||
ST_ASM330LHH_REG_FIFO_DATA_OUT_TAG_ADDR,
|
||||
word_len, buf);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
for (i = 0; i < word_len; i += ST_ASM330LHH_FIFO_SAMPLE_SIZE) {
|
||||
ptr = &buf[i + ST_ASM330LHH_TAG_SIZE];
|
||||
tag = buf[i] >> 3;
|
||||
|
||||
if (tag == ST_ASM330LHH_TS_TAG) {
|
||||
val = get_unaligned_le32(ptr);
|
||||
|
||||
#if defined(CONFIG_IIO_ST_ASM330LHH_ASYNC_HW_TIMESTAMP)
|
||||
spin_lock_irq(&hw->hwtimestamp_lock);
|
||||
#endif /* CONFIG_IIO_ST_ASM330LHH_ASYNC_HW_TIMESTAMP */
|
||||
hw->hw_timestamp_global =
|
||||
(hw->hw_timestamp_global &
|
||||
GENMASK_ULL(63, 32)) |
|
||||
(u32)le32_to_cpu(get_unaligned_le32(ptr));
|
||||
#if defined(CONFIG_IIO_ST_ASM330LHH_ASYNC_HW_TIMESTAMP)
|
||||
spin_unlock_irq(&hw->hwtimestamp_lock);
|
||||
#endif /* CONFIG_IIO_ST_ASM330LHH_ASYNC_HW_TIMESTAMP */
|
||||
|
||||
if (hw->val_ts_old > val)
|
||||
hw->hw_ts_high++;
|
||||
|
||||
hw_ts_old = hw->hw_ts;
|
||||
|
||||
/* check hw rollover */
|
||||
hw->val_ts_old = val;
|
||||
hw->hw_ts = (val + ((s64)hw->hw_ts_high << 32)) *
|
||||
hw->ts_delta_ns;
|
||||
hw->ts_offset = st_asm330lhh_ewma(hw->ts_offset,
|
||||
ts_irq - hw->hw_ts,
|
||||
ST_ASM330LHH_EWMA_LEVEL);
|
||||
|
||||
if (!test_bit(ST_ASM330LHH_HW_FLUSH, &hw->state))
|
||||
/* sync ap timestamp and sensor one */
|
||||
st_asm330lhh_sync_hw_ts(hw, ts_irq);
|
||||
|
||||
ts_irq += hw->hw_ts;
|
||||
|
||||
if (!hw->tsample)
|
||||
hw->tsample = hw->ts_offset + hw->hw_ts;
|
||||
else
|
||||
hw->tsample = hw->tsample + hw->hw_ts - hw_ts_old;
|
||||
} else {
|
||||
struct st_asm330lhh_sensor *sensor;
|
||||
|
||||
iio_dev = st_asm330lhh_get_iiodev_from_tag(hw, tag);
|
||||
if (!iio_dev)
|
||||
continue;
|
||||
|
||||
sensor = iio_priv(iio_dev);
|
||||
|
||||
/* skip samples if not ready */
|
||||
drdymask = (s16)le16_to_cpu(get_unaligned_le16(ptr));
|
||||
if (unlikely(drdymask >= ST_ASM330LHH_SAMPLE_DISCHARD)) {
|
||||
#ifdef ST_ASM330LHH_DEBUG_DISCHARGE
|
||||
sensor->discharged_samples++;
|
||||
#endif /* ST_ASM330LHH_DEBUG_DISCHARGE */
|
||||
continue;
|
||||
}
|
||||
|
||||
memcpy(iio_buf, ptr, ST_ASM330LHH_SAMPLE_SIZE);
|
||||
#if defined(CONFIG_IIO_ST_ASM330LHH_ASYNC_HW_TIMESTAMP)
|
||||
spin_lock_irq(&hw->hwtimestamp_lock);
|
||||
#endif /* CONFIG_IIO_ST_ASM330LHH_ASYNC_HW_TIMESTAMP */
|
||||
hw_timestamp_push = cpu_to_le64(hw->hw_timestamp_global);
|
||||
#if defined(CONFIG_IIO_ST_ASM330LHH_ASYNC_HW_TIMESTAMP)
|
||||
spin_unlock_irq(&hw->hwtimestamp_lock);
|
||||
#endif /* CONFIG_IIO_ST_ASM330LHH_ASYNC_HW_TIMESTAMP */
|
||||
memcpy(&iio_buf[ALIGN(ST_ASM330LHH_SAMPLE_SIZE, sizeof(s64))],
|
||||
&hw_timestamp_push, sizeof(hw_timestamp_push));
|
||||
|
||||
hw->tsample = min_t(s64,
|
||||
st_asm330lhh_get_time_ns(hw->iio_devs[0]),
|
||||
hw->tsample);
|
||||
|
||||
iio_push_to_buffers_with_timestamp(iio_dev,
|
||||
iio_buf,
|
||||
hw->tsample);
|
||||
sensor->last_fifo_timestamp = hw_timestamp_push;
|
||||
}
|
||||
}
|
||||
read_len += word_len;
|
||||
}
|
||||
|
||||
return read_len;
|
||||
}
|
||||
|
||||
ssize_t st_asm330lhh_get_max_watermark(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct st_asm330lhh_sensor *sensor = iio_priv(dev_get_drvdata(dev));
|
||||
|
||||
return sprintf(buf, "%d\n", sensor->max_watermark);
|
||||
}
|
||||
|
||||
ssize_t st_asm330lhh_get_watermark(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct st_asm330lhh_sensor *sensor = iio_priv(dev_get_drvdata(dev));
|
||||
|
||||
return sprintf(buf, "%d\n", sensor->watermark);
|
||||
}
|
||||
|
||||
ssize_t st_asm330lhh_set_watermark(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t size)
|
||||
{
|
||||
struct iio_dev *iio_dev = dev_get_drvdata(dev);
|
||||
struct st_asm330lhh_sensor *sensor = iio_priv(iio_dev);
|
||||
int err, val;
|
||||
|
||||
err = iio_device_claim_direct_mode(iio_dev);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = kstrtoint(buf, 10, &val);
|
||||
if (err < 0)
|
||||
goto out;
|
||||
|
||||
err = st_asm330lhh_update_watermark(sensor, val);
|
||||
if (err < 0)
|
||||
goto out;
|
||||
|
||||
sensor->watermark = val;
|
||||
|
||||
out:
|
||||
iio_device_release_direct_mode(iio_dev);
|
||||
|
||||
return err < 0 ? err : size;
|
||||
}
|
||||
|
||||
ssize_t st_asm330lhh_flush_fifo(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t size)
|
||||
{
|
||||
struct iio_dev *iio_dev = dev_get_drvdata(dev);
|
||||
struct st_asm330lhh_sensor *sensor = iio_priv(iio_dev);
|
||||
struct st_asm330lhh_hw *hw = sensor->hw;
|
||||
s64 event;
|
||||
int count;
|
||||
s64 type;
|
||||
s64 fts;
|
||||
s64 ts;
|
||||
|
||||
mutex_lock(&hw->fifo_lock);
|
||||
ts = st_asm330lhh_get_time_ns(iio_dev);
|
||||
hw->delta_ts = ts - hw->ts;
|
||||
hw->ts = ts;
|
||||
set_bit(ST_ASM330LHH_HW_FLUSH, &hw->state);
|
||||
count = st_asm330lhh_read_fifo(hw);
|
||||
fts = sensor->last_fifo_timestamp;
|
||||
mutex_unlock(&hw->fifo_lock);
|
||||
|
||||
type = count > 0 ? IIO_EV_DIR_FIFO_DATA : IIO_EV_DIR_FIFO_EMPTY;
|
||||
event = IIO_UNMOD_EVENT_CODE(iio_dev->channels[0].type, -1,
|
||||
IIO_EV_TYPE_FIFO_FLUSH, type);
|
||||
iio_push_event(iio_dev, event, fts);
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
int st_asm330lhh_suspend_fifo(struct st_asm330lhh_hw *hw)
|
||||
{
|
||||
int err;
|
||||
|
||||
mutex_lock(&hw->fifo_lock);
|
||||
st_asm330lhh_read_fifo(hw);
|
||||
err = st_asm330lhh_set_fifo_mode(hw, ST_ASM330LHH_FIFO_BYPASS);
|
||||
mutex_unlock(&hw->fifo_lock);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
int st_asm330lhh_update_batching(struct iio_dev *iio_dev, bool enable)
|
||||
{
|
||||
struct st_asm330lhh_sensor *sensor = iio_priv(iio_dev);
|
||||
struct st_asm330lhh_hw *hw = sensor->hw;
|
||||
int err;
|
||||
|
||||
disable_irq(hw->irq);
|
||||
|
||||
err = st_asm330lhh_set_sensor_batching_odr(sensor, enable);
|
||||
enable_irq(hw->irq);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int st_asm330lhh_update_fifo(struct iio_dev *iio_dev, bool enable)
|
||||
{
|
||||
struct st_asm330lhh_sensor *sensor = iio_priv(iio_dev);
|
||||
struct st_asm330lhh_hw *hw = sensor->hw;
|
||||
int err;
|
||||
|
||||
if (sensor->id == ST_ASM330LHH_ID_GYRO && !enable)
|
||||
delayed_enable_gyro = true;
|
||||
|
||||
if (sensor->id == ST_ASM330LHH_ID_GYRO &&
|
||||
enable && delayed_enable_gyro) {
|
||||
delayed_enable_gyro = false;
|
||||
msleep(delay_gyro);
|
||||
}
|
||||
|
||||
disable_irq(hw->irq);
|
||||
|
||||
#if defined(CONFIG_IIO_ST_ASM330LHH_ASYNC_HW_TIMESTAMP)
|
||||
hrtimer_cancel(&hw->timesync_timer);
|
||||
cancel_work_sync(&hw->timesync_work);
|
||||
#endif /* CONFIG_IIO_ST_ASM330LHH_ASYNC_HW_TIMESTAMP */
|
||||
|
||||
err = st_asm330lhh_sensor_set_enable(sensor, enable);
|
||||
if (err < 0)
|
||||
goto out;
|
||||
|
||||
err = st_asm330lhh_set_sensor_batching_odr(sensor, enable);
|
||||
if (err < 0)
|
||||
goto out;
|
||||
|
||||
#ifdef CONFIG_IIO_ST_ASM330LHH_EN_TEMPERATURE
|
||||
/*
|
||||
* This is an auxiliary sensor, it need to get batched
|
||||
* toghether at least with a primary sensor (Acc/Gyro).
|
||||
*/
|
||||
if (sensor->id == ST_ASM330LHH_ID_TEMP) {
|
||||
if (!(hw->enable_mask & (BIT(ST_ASM330LHH_ID_ACC) |
|
||||
BIT(ST_ASM330LHH_ID_GYRO)))) {
|
||||
struct st_asm330lhh_sensor *acc_sensor;
|
||||
u8 data = 0;
|
||||
|
||||
acc_sensor = iio_priv(hw->iio_devs[ST_ASM330LHH_ID_ACC]);
|
||||
if (enable) {
|
||||
err = st_asm330lhh_get_batch_val(acc_sensor,
|
||||
sensor->odr, sensor->uodr,
|
||||
&data);
|
||||
if (err < 0)
|
||||
goto out;
|
||||
}
|
||||
|
||||
err = st_asm330lhh_write_with_mask(hw,
|
||||
hw->odr_table_entry[ST_ASM330LHH_ID_ACC].batching_reg.addr,
|
||||
hw->odr_table_entry[ST_ASM330LHH_ID_ACC].batching_reg.mask,
|
||||
data);
|
||||
if (err < 0)
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
#endif /* CONFIG_IIO_ST_ASM330LHH_EN_TEMPERATURE */
|
||||
|
||||
err = st_asm330lhh_update_watermark(sensor, sensor->watermark);
|
||||
if (err < 0)
|
||||
goto out;
|
||||
|
||||
if (enable && hw->fifo_mode == ST_ASM330LHH_FIFO_BYPASS) {
|
||||
st_asm330lhh_reset_hwts(hw);
|
||||
err = st_asm330lhh_set_fifo_mode(hw, ST_ASM330LHH_FIFO_CONT);
|
||||
} else if (!hw->enable_mask) {
|
||||
err = st_asm330lhh_set_fifo_mode(hw, ST_ASM330LHH_FIFO_BYPASS);
|
||||
}
|
||||
|
||||
#if defined(CONFIG_IIO_ST_ASM330LHH_ASYNC_HW_TIMESTAMP)
|
||||
if (hw->fifo_mode != ST_ASM330LHH_FIFO_BYPASS) {
|
||||
hrtimer_start(&hw->timesync_timer,
|
||||
ktime_set(0, 0),
|
||||
HRTIMER_MODE_REL);
|
||||
}
|
||||
#endif /* CONFIG_IIO_ST_ASM330LHH_ASYNC_HW_TIMESTAMP */
|
||||
|
||||
out:
|
||||
enable_irq(hw->irq);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static irqreturn_t st_asm330lhh_handler_irq(int irq, void *private)
|
||||
{
|
||||
struct st_asm330lhh_hw *hw = (struct st_asm330lhh_hw *)private;
|
||||
s64 ts = st_asm330lhh_get_time_ns(hw->iio_devs[0]);
|
||||
|
||||
hw->delta_ts = ts - hw->ts;
|
||||
hw->ts = ts;
|
||||
|
||||
return IRQ_WAKE_THREAD;
|
||||
}
|
||||
|
||||
static irqreturn_t st_asm330lhh_handler_thread(int irq, void *private)
|
||||
{
|
||||
struct st_asm330lhh_hw *hw = (struct st_asm330lhh_hw *)private;
|
||||
|
||||
mutex_lock(&hw->fifo_lock);
|
||||
st_asm330lhh_read_fifo(hw);
|
||||
clear_bit(ST_ASM330LHH_HW_FLUSH, &hw->state);
|
||||
mutex_unlock(&hw->fifo_lock);
|
||||
|
||||
#ifdef CONFIG_IIO_ST_ASM330LHH_EN_BASIC_FEATURES
|
||||
return st_asm330lhh_event_handler(hw);
|
||||
#else /* CONFIG_IIO_ST_ASM330LHH_EN_BASIC_FEATURES */
|
||||
return IRQ_HANDLED;
|
||||
#endif /* CONFIG_IIO_ST_ASM330LHH_EN_BASIC_FEATURES */
|
||||
}
|
||||
|
||||
static int st_asm330lhh_fifo_preenable(struct iio_dev *iio_dev)
|
||||
{
|
||||
return st_asm330lhh_update_fifo(iio_dev, true);
|
||||
}
|
||||
|
||||
static int st_asm330lhh_fifo_postdisable(struct iio_dev *iio_dev)
|
||||
{
|
||||
return st_asm330lhh_update_fifo(iio_dev, false);
|
||||
}
|
||||
|
||||
static const struct iio_buffer_setup_ops st_asm330lhh_fifo_ops = {
|
||||
.preenable = st_asm330lhh_fifo_preenable,
|
||||
.postdisable = st_asm330lhh_fifo_postdisable,
|
||||
};
|
||||
|
||||
static int st_asm330lhh_enable_timestamp(struct st_asm330lhh_hw *hw,
|
||||
bool enable)
|
||||
{
|
||||
return st_asm330lhh_write_with_mask(hw,
|
||||
ST_ASM330LHH_REG_FIFO_CTRL4_ADDR,
|
||||
ST_ASM330LHH_REG_DEC_TS_MASK,
|
||||
enable == true ? 1 : 0);
|
||||
}
|
||||
|
||||
static int st_asm330lhh_fifo_init(struct st_asm330lhh_hw *hw)
|
||||
{
|
||||
return st_asm330lhh_enable_timestamp(hw, true);
|
||||
}
|
||||
|
||||
int st_asm330lhh_buffers_setup(struct st_asm330lhh_hw *hw)
|
||||
{
|
||||
struct iio_buffer *buffer;
|
||||
unsigned long irq_type;
|
||||
bool irq_active_low;
|
||||
int i, err;
|
||||
|
||||
irq_type = irqd_get_trigger_type(irq_get_irq_data(hw->irq));
|
||||
if (irq_type == IRQF_TRIGGER_NONE)
|
||||
irq_type = IRQF_TRIGGER_HIGH;
|
||||
|
||||
switch (irq_type) {
|
||||
case IRQF_TRIGGER_HIGH:
|
||||
irq_active_low = false;
|
||||
break;
|
||||
case IRQF_TRIGGER_LOW:
|
||||
irq_active_low = true;
|
||||
break;
|
||||
default:
|
||||
dev_info(hw->dev, "mode %lx unsupported\n", irq_type);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
err = st_asm330lhh_write_with_mask(hw, ST_ASM330LHH_REG_CTRL3_C_ADDR,
|
||||
ST_ASM330LHH_REG_H_LACTIVE_MASK,
|
||||
irq_active_low);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
if (device_property_read_bool(hw->dev, "drive-open-drain")) {
|
||||
err = st_asm330lhh_write_with_mask(hw,
|
||||
ST_ASM330LHH_REG_CTRL3_C_ADDR,
|
||||
ST_ASM330LHH_REG_PP_OD_MASK,
|
||||
1);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
irq_type |= IRQF_SHARED;
|
||||
}
|
||||
|
||||
err = devm_request_threaded_irq(hw->dev, hw->irq,
|
||||
st_asm330lhh_handler_irq,
|
||||
st_asm330lhh_handler_thread,
|
||||
irq_type | IRQF_ONESHOT,
|
||||
"asm330lhh", hw);
|
||||
if (err) {
|
||||
dev_err(hw->dev, "failed to request trigger irq %d\n",
|
||||
hw->irq);
|
||||
return err;
|
||||
}
|
||||
|
||||
for (i = 0; i < ST_ASM330LHH_ID_EVENT; i++) {
|
||||
if (!hw->iio_devs[i])
|
||||
continue;
|
||||
|
||||
buffer = devm_iio_kfifo_allocate(hw->dev);
|
||||
if (!buffer)
|
||||
return -ENOMEM;
|
||||
|
||||
iio_device_attach_buffer(hw->iio_devs[i], buffer);
|
||||
hw->iio_devs[i]->modes |= INDIO_BUFFER_SOFTWARE;
|
||||
hw->iio_devs[i]->setup_ops = &st_asm330lhh_fifo_ops;
|
||||
}
|
||||
|
||||
err = st_asm330lhh_hwtimesync_init(hw);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
return st_asm330lhh_fifo_init(hw);
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -1,616 +0,0 @@
|
||||
/**
|
||||
* STMicroelectronics st_asm330lhh events function sensor driver
|
||||
*
|
||||
* Copyright 2020 STMicroelectronics Inc.
|
||||
*
|
||||
* Mario Tesi <mario.tesi@st.com>
|
||||
*
|
||||
* Licensed under the GPL-2.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/iio/events.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
#include <linux/iio/trigger_consumer.h>
|
||||
#include <linux/iio/triggered_buffer.h>
|
||||
#include <linux/iio/trigger.h>
|
||||
#include <linux/iio/buffer.h>
|
||||
|
||||
#include "st_asm330lhh.h"
|
||||
|
||||
#ifdef CONFIG_IIO_ST_ASM330LHH_EN_BASIC_FEATURES
|
||||
|
||||
#define ST_ASM330LHH_REG_ALL_INT_SRC_ADDR 0x1a
|
||||
#define ST_ASM330LHH_FF_IA_MASK BIT(0)
|
||||
#define ST_ASM330LHH_WU_IA_MASK BIT(1)
|
||||
#define ST_ASM330LHH_D6D_IA_MASK BIT(4)
|
||||
#define ST_ASM330LHH_SLEEP_CHANGE_MASK BIT(5)
|
||||
|
||||
#define ST_ASM330LHH_REG_WAKE_UP_SRC_ADDR 0x1b
|
||||
#define ST_ASM330LHH_WAKE_UP_EVENT_MASK GENMASK(3, 0)
|
||||
|
||||
#define ST_ASM330LHH_REG_D6D_SRC_ADDR 0x1d
|
||||
#define ST_ASM330LHH_D6D_EVENT_MASK GENMASK(5, 0)
|
||||
|
||||
#define ST_ASM330LHH_REG_INT_CFG1_ADDR 0x58
|
||||
#define ST_ASM330LHH_INTERRUPTS_ENABLE_MASK BIT(7)
|
||||
|
||||
#define ST_ASM330LHH_REG_MD1_CFG_ADDR 0x5e
|
||||
#define ST_ASM330LHH_REG_MD2_CFG_ADDR 0x5f
|
||||
#define ST_ASM330LHH_INT_6D_MASK BIT(2)
|
||||
#define ST_ASM330LHH_INT_FF_MASK BIT(4)
|
||||
#define ST_ASM330LHH_INT_WU_MASK BIT(5)
|
||||
#define ST_ASM330LHH_INT_SLEEP_CHANGE_MASK BIT(7)
|
||||
|
||||
static const unsigned long st_asm330lhh_event_available_scan_masks[] = {
|
||||
0x1, 0x0
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec st_asm330lhh_wk_channels[] = {
|
||||
{
|
||||
.type = IIO_GESTURE,
|
||||
.scan_index = 0,
|
||||
.scan_type = {
|
||||
.sign = 'u',
|
||||
.realbits = 8,
|
||||
.storagebits = 8,
|
||||
},
|
||||
},
|
||||
IIO_CHAN_SOFT_TIMESTAMP(1),
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec st_asm330lhh_ff_channels[] = {
|
||||
ST_ASM330LHH_EVENT_CHANNEL(IIO_GESTURE, thr),
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec st_asm330lhh_sc_channels[] = {
|
||||
ST_ASM330LHH_EVENT_CHANNEL(IIO_GESTURE, thr),
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec st_asm330lhh_6D_channels[] = {
|
||||
{
|
||||
.type = IIO_GESTURE,
|
||||
.scan_index = 0,
|
||||
.scan_type = {
|
||||
.sign = 'u',
|
||||
.realbits = 8,
|
||||
.storagebits = 8,
|
||||
},
|
||||
},
|
||||
IIO_CHAN_SOFT_TIMESTAMP(1),
|
||||
};
|
||||
|
||||
static
|
||||
int st_asm330lhh_event_sensor_set_enable(struct st_asm330lhh_sensor *sensor,
|
||||
bool enable)
|
||||
{
|
||||
int err, eint = !!enable;
|
||||
struct st_asm330lhh_hw *hw = sensor->hw;
|
||||
u8 int_reg = hw->int_pin == 1 ? ST_ASM330LHH_REG_MD1_CFG_ADDR :
|
||||
ST_ASM330LHH_REG_MD2_CFG_ADDR;
|
||||
|
||||
switch (sensor->id) {
|
||||
case ST_ASM330LHH_ID_WK:
|
||||
err = st_asm330lhh_write_with_mask(hw,
|
||||
int_reg,
|
||||
ST_ASM330LHH_INT_WU_MASK,
|
||||
eint);
|
||||
if (err < 0)
|
||||
return err;
|
||||
break;
|
||||
case ST_ASM330LHH_ID_FF:
|
||||
err = st_asm330lhh_write_with_mask(hw,
|
||||
int_reg,
|
||||
ST_ASM330LHH_INT_FF_MASK,
|
||||
eint);
|
||||
if (err < 0)
|
||||
return err;
|
||||
break;
|
||||
case ST_ASM330LHH_ID_SC:
|
||||
err = st_asm330lhh_write_with_mask(hw,
|
||||
int_reg,
|
||||
ST_ASM330LHH_INT_SLEEP_CHANGE_MASK,
|
||||
eint);
|
||||
if (err < 0)
|
||||
return err;
|
||||
break;
|
||||
case ST_ASM330LHH_ID_6D:
|
||||
err = st_asm330lhh_write_with_mask(hw,
|
||||
int_reg,
|
||||
ST_ASM330LHH_INT_6D_MASK,
|
||||
eint);
|
||||
if (err < 0)
|
||||
return err;
|
||||
break;
|
||||
default:
|
||||
err = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
if (err >= 0) {
|
||||
err = st_asm330lhh_write_with_mask(hw,
|
||||
ST_ASM330LHH_REG_INT_CFG1_ADDR,
|
||||
ST_ASM330LHH_INTERRUPTS_ENABLE_MASK,
|
||||
eint);
|
||||
if (eint == 0)
|
||||
hw->enable_mask &= ~BIT(sensor->id);
|
||||
else
|
||||
hw->enable_mask |= BIT(sensor->id);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int st_asm330lhh_read_event_config(struct iio_dev *iio_dev,
|
||||
const struct iio_chan_spec *chan,
|
||||
enum iio_event_type type,
|
||||
enum iio_event_direction dir)
|
||||
{
|
||||
struct st_asm330lhh_sensor *sensor = iio_priv(iio_dev);
|
||||
struct st_asm330lhh_hw *hw = sensor->hw;
|
||||
|
||||
return !!(hw->enable_mask & BIT(sensor->id));
|
||||
}
|
||||
|
||||
static int st_asm330lhh_write_event_config(struct iio_dev *iio_dev,
|
||||
const struct iio_chan_spec *chan,
|
||||
enum iio_event_type type,
|
||||
enum iio_event_direction dir,
|
||||
int state)
|
||||
{
|
||||
struct st_asm330lhh_sensor *sensor = iio_priv(iio_dev);
|
||||
int err;
|
||||
|
||||
mutex_lock(&iio_dev->mlock);
|
||||
err = st_asm330lhh_event_sensor_set_enable(sensor, state);
|
||||
mutex_unlock(&iio_dev->mlock);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
ssize_t st_asm330lhh_wakeup_threshold_get(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct st_asm330lhh_sensor *sensor = iio_priv(dev_get_drvdata(dev));
|
||||
|
||||
return sprintf(buf, "%d\n", sensor->conf[0]);
|
||||
}
|
||||
|
||||
ssize_t st_asm330lhh_wakeup_threshold_set(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t size)
|
||||
{
|
||||
struct iio_dev *iio_dev = dev_get_drvdata(dev);
|
||||
struct st_asm330lhh_sensor *sensor = iio_priv(iio_dev);
|
||||
int err, val;
|
||||
|
||||
err = iio_device_claim_direct_mode(iio_dev);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = kstrtoint(buf, 10, &val);
|
||||
if (err < 0)
|
||||
goto out;
|
||||
|
||||
err = st_asm330lhh_set_wake_up_thershold(sensor->hw, val);
|
||||
if (err < 0)
|
||||
goto out;
|
||||
|
||||
sensor->conf[0] = val;
|
||||
|
||||
out:
|
||||
iio_device_release_direct_mode(iio_dev);
|
||||
|
||||
return err < 0 ? err : size;
|
||||
}
|
||||
|
||||
ssize_t st_asm330lhh_wakeup_duration_get(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct st_asm330lhh_sensor *sensor = iio_priv(dev_get_drvdata(dev));
|
||||
|
||||
return sprintf(buf, "%d\n", sensor->conf[1]);
|
||||
}
|
||||
|
||||
ssize_t st_asm330lhh_wakeup_duration_set(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t size)
|
||||
{
|
||||
struct iio_dev *iio_dev = dev_get_drvdata(dev);
|
||||
struct st_asm330lhh_sensor *sensor = iio_priv(iio_dev);
|
||||
int err, val;
|
||||
|
||||
err = iio_device_claim_direct_mode(iio_dev);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = kstrtoint(buf, 10, &val);
|
||||
if (err < 0)
|
||||
goto out;
|
||||
|
||||
err = st_asm330lhh_set_wake_up_duration(sensor->hw, val);
|
||||
if (err < 0)
|
||||
goto out;
|
||||
|
||||
sensor->conf[1] = val;
|
||||
|
||||
out:
|
||||
iio_device_release_direct_mode(iio_dev);
|
||||
|
||||
return err < 0 ? err : size;
|
||||
}
|
||||
|
||||
ssize_t st_asm330lhh_freefall_threshold_get(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct st_asm330lhh_sensor *sensor = iio_priv(dev_get_drvdata(dev));
|
||||
|
||||
return sprintf(buf, "%d\n", sensor->conf[2]);
|
||||
}
|
||||
|
||||
ssize_t st_asm330lhh_freefall_threshold_set(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t size)
|
||||
{
|
||||
struct iio_dev *iio_dev = dev_get_drvdata(dev);
|
||||
struct st_asm330lhh_sensor *sensor = iio_priv(iio_dev);
|
||||
int err, val;
|
||||
|
||||
err = iio_device_claim_direct_mode(iio_dev);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = kstrtoint(buf, 10, &val);
|
||||
if (err < 0)
|
||||
goto out;
|
||||
|
||||
err = st_asm330lhh_set_freefall_threshold(sensor->hw, val);
|
||||
if (err < 0)
|
||||
goto out;
|
||||
|
||||
sensor->conf[2] = val;
|
||||
|
||||
out:
|
||||
iio_device_release_direct_mode(iio_dev);
|
||||
|
||||
return err < 0 ? err : size;
|
||||
}
|
||||
|
||||
ssize_t st_asm330lhh_6D_threshold_get(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct st_asm330lhh_sensor *sensor = iio_priv(dev_get_drvdata(dev));
|
||||
|
||||
return sprintf(buf, "%d\n", sensor->conf[3]);
|
||||
}
|
||||
|
||||
ssize_t st_asm330lhh_6D_threshold_set(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t size)
|
||||
{
|
||||
struct iio_dev *iio_dev = dev_get_drvdata(dev);
|
||||
struct st_asm330lhh_sensor *sensor = iio_priv(iio_dev);
|
||||
int err, val;
|
||||
|
||||
err = iio_device_claim_direct_mode(iio_dev);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = kstrtoint(buf, 10, &val);
|
||||
if (err < 0)
|
||||
goto out;
|
||||
|
||||
err = st_asm330lhh_set_6D_threshold(sensor->hw, val);
|
||||
if (err < 0)
|
||||
goto out;
|
||||
|
||||
sensor->conf[3] = val;
|
||||
|
||||
out:
|
||||
iio_device_release_direct_mode(iio_dev);
|
||||
|
||||
return err < 0 ? err : size;
|
||||
}
|
||||
|
||||
static IIO_DEVICE_ATTR(wakeup_threshold, 0644,
|
||||
st_asm330lhh_wakeup_threshold_get,
|
||||
st_asm330lhh_wakeup_threshold_set, 0);
|
||||
|
||||
static IIO_DEVICE_ATTR(wakeup_duration, 0644,
|
||||
st_asm330lhh_wakeup_duration_get,
|
||||
st_asm330lhh_wakeup_duration_set, 0);
|
||||
|
||||
static IIO_DEVICE_ATTR(freefall_threshold, 0644,
|
||||
st_asm330lhh_freefall_threshold_get,
|
||||
st_asm330lhh_freefall_threshold_set, 0);
|
||||
|
||||
static IIO_DEVICE_ATTR(sixd_threshold, 0644,
|
||||
st_asm330lhh_6D_threshold_get,
|
||||
st_asm330lhh_6D_threshold_set, 0);
|
||||
|
||||
static struct attribute *st_asm330lhh_wk_attributes[] = {
|
||||
&iio_dev_attr_wakeup_threshold.dev_attr.attr,
|
||||
&iio_dev_attr_wakeup_duration.dev_attr.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static const struct attribute_group st_asm330lhh_wk_attribute_group = {
|
||||
.attrs = st_asm330lhh_wk_attributes,
|
||||
};
|
||||
|
||||
static const struct iio_info st_asm330lhh_wk_info = {
|
||||
.attrs = &st_asm330lhh_wk_attribute_group,
|
||||
};
|
||||
|
||||
static struct attribute *st_asm330lhh_ff_attributes[] = {
|
||||
&iio_dev_attr_freefall_threshold.dev_attr.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static const struct attribute_group st_asm330lhh_ff_attribute_group = {
|
||||
.attrs = st_asm330lhh_ff_attributes,
|
||||
};
|
||||
|
||||
static const struct iio_info st_asm330lhh_ff_info = {
|
||||
.attrs = &st_asm330lhh_ff_attribute_group,
|
||||
.read_event_config = st_asm330lhh_read_event_config,
|
||||
.write_event_config = st_asm330lhh_write_event_config,
|
||||
};
|
||||
|
||||
static struct attribute *st_asm330lhh_sc_attributes[] = {
|
||||
NULL,
|
||||
};
|
||||
|
||||
static const struct attribute_group st_asm330lhh_sc_attribute_group = {
|
||||
.attrs = st_asm330lhh_sc_attributes,
|
||||
};
|
||||
|
||||
static const struct iio_info st_asm330lhh_sc_info = {
|
||||
.attrs = &st_asm330lhh_sc_attribute_group,
|
||||
.read_event_config = st_asm330lhh_read_event_config,
|
||||
.write_event_config = st_asm330lhh_write_event_config,
|
||||
};
|
||||
|
||||
static struct attribute *st_asm330lhh_6D_attributes[] = {
|
||||
&iio_dev_attr_sixd_threshold.dev_attr.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static const struct attribute_group st_asm330lhh_6D_attribute_group = {
|
||||
.attrs = st_asm330lhh_6D_attributes,
|
||||
};
|
||||
|
||||
static const struct iio_info st_asm330lhh_6D_info = {
|
||||
.attrs = &st_asm330lhh_6D_attribute_group,
|
||||
};
|
||||
|
||||
static
|
||||
struct iio_dev *st_asm330lhh_alloc_event_iiodev(struct st_asm330lhh_hw *hw,
|
||||
enum st_asm330lhh_sensor_id id)
|
||||
{
|
||||
struct st_asm330lhh_sensor *sensor;
|
||||
struct iio_dev *iio_dev;
|
||||
|
||||
iio_dev = devm_iio_device_alloc(hw->dev, sizeof(*sensor));
|
||||
if (!iio_dev)
|
||||
return NULL;
|
||||
|
||||
iio_dev->modes = INDIO_DIRECT_MODE;
|
||||
iio_dev->dev.parent = hw->dev;
|
||||
|
||||
sensor = iio_priv(iio_dev);
|
||||
sensor->id = id;
|
||||
sensor->hw = hw;
|
||||
sensor->watermark = 1;
|
||||
|
||||
iio_dev->available_scan_masks = st_asm330lhh_event_available_scan_masks;
|
||||
|
||||
switch (id) {
|
||||
case ST_ASM330LHH_ID_WK:
|
||||
iio_dev->channels = st_asm330lhh_wk_channels;
|
||||
iio_dev->num_channels = ARRAY_SIZE(st_asm330lhh_wk_channels);
|
||||
iio_dev->name = "asm330lhh_wk";
|
||||
iio_dev->info = &st_asm330lhh_wk_info;
|
||||
break;
|
||||
case ST_ASM330LHH_ID_FF:
|
||||
iio_dev->channels = st_asm330lhh_ff_channels;
|
||||
iio_dev->num_channels = ARRAY_SIZE(st_asm330lhh_ff_channels);
|
||||
iio_dev->name = "asm330lhh_ff";
|
||||
iio_dev->info = &st_asm330lhh_ff_info;
|
||||
break;
|
||||
case ST_ASM330LHH_ID_SC:
|
||||
iio_dev->channels = st_asm330lhh_sc_channels;
|
||||
iio_dev->num_channels = ARRAY_SIZE(st_asm330lhh_sc_channels);
|
||||
iio_dev->name = "asm330lhh_sc";
|
||||
iio_dev->info = &st_asm330lhh_sc_info;
|
||||
break;
|
||||
case ST_ASM330LHH_ID_6D:
|
||||
iio_dev->channels = st_asm330lhh_6D_channels;
|
||||
iio_dev->num_channels = ARRAY_SIZE(st_asm330lhh_6D_channels);
|
||||
iio_dev->name = "asm330lhh_6d";
|
||||
iio_dev->info = &st_asm330lhh_6D_info;
|
||||
break;
|
||||
default:
|
||||
iio_device_free(iio_dev);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return iio_dev;
|
||||
}
|
||||
|
||||
int st_asm330lhh_event_handler(struct st_asm330lhh_hw *hw)
|
||||
{
|
||||
struct iio_dev *iio_dev;
|
||||
u8 status;
|
||||
s64 event;
|
||||
int err;
|
||||
|
||||
if (hw->enable_mask &
|
||||
(BIT(ST_ASM330LHH_ID_WK) | BIT(ST_ASM330LHH_ID_FF) |
|
||||
BIT(ST_ASM330LHH_ID_SC) | BIT(ST_ASM330LHH_ID_6D))) {
|
||||
err = hw->tf->read(hw->dev,
|
||||
ST_ASM330LHH_REG_ALL_INT_SRC_ADDR,
|
||||
sizeof(status), &status);
|
||||
if (err < 0)
|
||||
return IRQ_HANDLED;
|
||||
|
||||
/* base function sensors */
|
||||
if (status & ST_ASM330LHH_FF_IA_MASK) {
|
||||
iio_dev = hw->iio_devs[ST_ASM330LHH_ID_FF];
|
||||
event = IIO_UNMOD_EVENT_CODE(IIO_GESTURE, -1,
|
||||
IIO_EV_TYPE_THRESH,
|
||||
IIO_EV_DIR_RISING);
|
||||
iio_push_event(iio_dev, event,
|
||||
st_asm330lhh_get_time_ns(iio_dev));
|
||||
}
|
||||
if (status & ST_ASM330LHH_WU_IA_MASK) {
|
||||
struct st_asm330lhh_sensor *sensor;
|
||||
|
||||
iio_dev = hw->iio_devs[ST_ASM330LHH_ID_WK];
|
||||
sensor = iio_priv(iio_dev);
|
||||
iio_trigger_poll_chained(sensor->trig);
|
||||
}
|
||||
if (status & ST_ASM330LHH_SLEEP_CHANGE_MASK) {
|
||||
iio_dev = hw->iio_devs[ST_ASM330LHH_ID_SC];
|
||||
event = IIO_UNMOD_EVENT_CODE(IIO_GESTURE, -1,
|
||||
IIO_EV_TYPE_THRESH,
|
||||
IIO_EV_DIR_RISING);
|
||||
iio_push_event(iio_dev, event,
|
||||
st_asm330lhh_get_time_ns(iio_dev));
|
||||
}
|
||||
if (status & ST_ASM330LHH_D6D_IA_MASK) {
|
||||
struct st_asm330lhh_sensor *sensor;
|
||||
|
||||
iio_dev = hw->iio_devs[ST_ASM330LHH_ID_6D];
|
||||
sensor = iio_priv(iio_dev);
|
||||
iio_trigger_poll_chained(sensor->trig);
|
||||
}
|
||||
}
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static inline int st_asm330lhh_get_6D(struct st_asm330lhh_hw *hw, u8 *out)
|
||||
{
|
||||
return st_asm330lhh_read_with_mask(hw, ST_ASM330LHH_REG_D6D_SRC_ADDR,
|
||||
ST_ASM330LHH_D6D_EVENT_MASK, out);
|
||||
}
|
||||
|
||||
static inline int st_asm330lhh_get_wk(struct st_asm330lhh_hw *hw, u8 *out)
|
||||
{
|
||||
return st_asm330lhh_read_with_mask(hw,
|
||||
ST_ASM330LHH_REG_WAKE_UP_SRC_ADDR,
|
||||
ST_ASM330LHH_WAKE_UP_EVENT_MASK, out);
|
||||
}
|
||||
|
||||
static irqreturn_t st_asm330lhh_6D_handler_thread(int irq, void *p)
|
||||
{
|
||||
struct iio_poll_func *pf = p;
|
||||
struct iio_dev *iio_dev = pf->indio_dev;
|
||||
struct st_asm330lhh_sensor *sensor = iio_priv(iio_dev);
|
||||
u8 buffer[sizeof(u8) + sizeof(s64)];
|
||||
|
||||
st_asm330lhh_get_6D(sensor->hw, buffer);
|
||||
iio_push_to_buffers_with_timestamp(iio_dev, buffer,
|
||||
st_asm330lhh_get_time_ns(iio_dev));
|
||||
iio_trigger_notify_done(sensor->trig);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static irqreturn_t st_asm330lhh_wk_handler_thread(int irq, void *p)
|
||||
{
|
||||
struct iio_poll_func *pf = p;
|
||||
struct iio_dev *iio_dev = pf->indio_dev;
|
||||
struct st_asm330lhh_sensor *sensor = iio_priv(iio_dev);
|
||||
u8 buffer[sizeof(u8) + sizeof(s64)];
|
||||
|
||||
st_asm330lhh_get_wk(sensor->hw, buffer);
|
||||
iio_push_to_buffers_with_timestamp(iio_dev, buffer,
|
||||
st_asm330lhh_get_time_ns(iio_dev));
|
||||
iio_trigger_notify_done(sensor->trig);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static const struct iio_trigger_ops st_asm330lhh_trigger_ops = {
|
||||
NULL,
|
||||
};
|
||||
|
||||
static int st_asm330lhh_buffer_preenable(struct iio_dev *iio_dev)
|
||||
{
|
||||
return st_asm330lhh_event_sensor_set_enable(iio_priv(iio_dev), true);
|
||||
}
|
||||
|
||||
static int st_asm330lhh_buffer_postdisable(struct iio_dev *iio_dev)
|
||||
{
|
||||
return st_asm330lhh_event_sensor_set_enable(iio_priv(iio_dev), false);
|
||||
}
|
||||
|
||||
static const struct iio_buffer_setup_ops st_asm330lhh_buffer_ops = {
|
||||
.preenable = st_asm330lhh_buffer_preenable,
|
||||
.postdisable = st_asm330lhh_buffer_postdisable,
|
||||
};
|
||||
|
||||
int st_asm330lhh_probe_event(struct st_asm330lhh_hw *hw)
|
||||
{
|
||||
struct st_asm330lhh_sensor *sensor;
|
||||
struct iio_dev *iio_dev;
|
||||
irqreturn_t (*pthread[ST_ASM330LHH_ID_MAX - ST_ASM330LHH_ID_TRIGGER])(int irq, void *p) = {
|
||||
[0] = st_asm330lhh_wk_handler_thread,
|
||||
[1] = st_asm330lhh_6D_handler_thread,
|
||||
/* add here all other trigger handler funcions */
|
||||
};
|
||||
int i, err;
|
||||
|
||||
for (i = ST_ASM330LHH_ID_EVENT; i < ST_ASM330LHH_ID_MAX; i++) {
|
||||
hw->iio_devs[i] = st_asm330lhh_alloc_event_iiodev(hw, i);
|
||||
if (!hw->iio_devs[i])
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/* configure trigger sensors */
|
||||
for (i = ST_ASM330LHH_ID_TRIGGER; i < ST_ASM330LHH_ID_MAX; i++) {
|
||||
iio_dev = hw->iio_devs[i];
|
||||
sensor = iio_priv(iio_dev);
|
||||
|
||||
err = devm_iio_triggered_buffer_setup(hw->dev, iio_dev,
|
||||
NULL, pthread[i - ST_ASM330LHH_ID_TRIGGER],
|
||||
&st_asm330lhh_buffer_ops);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
sensor->trig = devm_iio_trigger_alloc(hw->dev, "%s-trigger",
|
||||
iio_dev->name);
|
||||
if (!sensor->trig)
|
||||
return -ENOMEM;
|
||||
|
||||
iio_trigger_set_drvdata(sensor->trig, iio_dev);
|
||||
sensor->trig->ops = &st_asm330lhh_trigger_ops;
|
||||
sensor->trig->dev.parent = hw->dev;
|
||||
iio_dev->trig = iio_trigger_get(sensor->trig);
|
||||
|
||||
err = devm_iio_trigger_register(hw->dev, sensor->trig);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
for (i = ST_ASM330LHH_ID_EVENT; i < ST_ASM330LHH_ID_MAX; i++) {
|
||||
if (!hw->iio_devs[i])
|
||||
continue;
|
||||
|
||||
err = devm_iio_device_register(hw->dev, hw->iio_devs[i]);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif /* CONFIG_IIO_ST_ASM330LHH_EN_BASIC_FEATURES */
|
@ -1,111 +0,0 @@
|
||||
/*
|
||||
* STMicroelectronics st_asm330lhh spi driver
|
||||
*
|
||||
* Copyright 2019 STMicroelectronics Inc.
|
||||
*
|
||||
* Lorenzo Bianconi <lorenzo.bianconi@st.com>
|
||||
* Mario Tesi <mario.tesi@st.com>
|
||||
*
|
||||
* Licensed under the GPL-2.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/of.h>
|
||||
|
||||
#include "st_asm330lhh.h"
|
||||
|
||||
#define SENSORS_SPI_READ BIT(7)
|
||||
|
||||
static int st_asm330lhh_spi_read(struct device *dev, u8 addr, int len,
|
||||
u8 *data)
|
||||
{
|
||||
struct spi_device *spi = to_spi_device(dev);
|
||||
struct st_asm330lhh_hw *hw = spi_get_drvdata(spi);
|
||||
int err;
|
||||
|
||||
struct spi_transfer xfers[] = {
|
||||
{
|
||||
.tx_buf = hw->tb.tx_buf,
|
||||
.bits_per_word = 8,
|
||||
.len = 1,
|
||||
},
|
||||
{
|
||||
.rx_buf = hw->tb.rx_buf,
|
||||
.bits_per_word = 8,
|
||||
.len = len,
|
||||
}
|
||||
};
|
||||
|
||||
hw->tb.tx_buf[0] = addr | SENSORS_SPI_READ;
|
||||
|
||||
err = spi_sync_transfer(spi, xfers, ARRAY_SIZE(xfers));
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
memcpy(data, hw->tb.rx_buf, len * sizeof(u8));
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static int st_asm330lhh_spi_write(struct device *dev, u8 addr, int len,
|
||||
const u8 *data)
|
||||
{
|
||||
struct st_asm330lhh_hw *hw;
|
||||
struct spi_device *spi;
|
||||
|
||||
if (len >= ST_ASM330LHH_TX_MAX_LENGTH)
|
||||
return -ENOMEM;
|
||||
|
||||
spi = to_spi_device(dev);
|
||||
hw = spi_get_drvdata(spi);
|
||||
|
||||
hw->tb.tx_buf[0] = addr;
|
||||
memcpy(&hw->tb.tx_buf[1], data, len);
|
||||
|
||||
return spi_write(spi, hw->tb.tx_buf, len + 1);
|
||||
}
|
||||
|
||||
static const struct st_asm330lhh_transfer_function st_asm330lhh_transfer_fn = {
|
||||
.read = st_asm330lhh_spi_read,
|
||||
.write = st_asm330lhh_spi_write,
|
||||
};
|
||||
|
||||
static int st_asm330lhh_spi_probe(struct spi_device *spi)
|
||||
{
|
||||
return st_asm330lhh_probe(&spi->dev, spi->irq,
|
||||
&st_asm330lhh_transfer_fn);
|
||||
}
|
||||
|
||||
static const struct of_device_id st_asm330lhh_spi_of_match[] = {
|
||||
{
|
||||
.compatible = "st,asm330lhh",
|
||||
},
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, st_asm330lhh_spi_of_match);
|
||||
|
||||
static const struct spi_device_id st_asm330lhh_spi_id_table[] = {
|
||||
{ ST_ASM330LHH_DEV_NAME },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, st_asm330lhh_spi_id_table);
|
||||
|
||||
static struct spi_driver st_asm330lhh_driver = {
|
||||
.driver = {
|
||||
.name = "st_asm330lhh_spi",
|
||||
.pm = &st_asm330lhh_pm_ops,
|
||||
.of_match_table = st_asm330lhh_spi_of_match,
|
||||
},
|
||||
.probe = st_asm330lhh_spi_probe,
|
||||
.id_table = st_asm330lhh_spi_id_table,
|
||||
};
|
||||
module_spi_driver(st_asm330lhh_driver);
|
||||
|
||||
MODULE_AUTHOR("Lorenzo Bianconi <lorenzo.bianconi@st.com>");
|
||||
MODULE_AUTHOR("Mario Tesi <mario.tesi@st.com>");
|
||||
MODULE_DESCRIPTION("STMicroelectronics st_asm330lhh spi driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_VERSION(ST_ASM330LHH_DRV_VERSION);
|
@ -1,47 +1,47 @@
|
||||
config IIO_ST_ASM330LHH
|
||||
tristate "STMicroelectronics ASM330LHH sensor"
|
||||
config IIO_ST_ASM330LHHX
|
||||
tristate "STMicroelectronics ASM330LHHX sensor"
|
||||
depends on (I2C || SPI)
|
||||
select IIO_BUFFER
|
||||
select IIO_KFIFO_BUF
|
||||
select IIO_TRIGGERED_BUFFER
|
||||
select IIO_ST_ASM330LHH_I2C if (I2C)
|
||||
select IIO_ST_ASM330LHH_SPI if (SPI_MASTER)
|
||||
select IIO_ST_ASM330LHHX_I2C if (I2C)
|
||||
select IIO_ST_ASM330LHHX_SPI if (SPI_MASTER)
|
||||
help
|
||||
Say yes here to build support for STMicroelectronics ASM330LHH imu
|
||||
sensor.
|
||||
Say yes here to build support for STMicroelectronics ASM330LHH/ASM330LHHX imu
|
||||
sensors.
|
||||
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called st_asm330lhh.
|
||||
will be called st_asm330lhhx.
|
||||
|
||||
config IIO_ST_ASM330LHH_I2C
|
||||
config IIO_ST_ASM330LHHX_I2C
|
||||
tristate
|
||||
depends on IIO_ST_ASM330LHH
|
||||
depends on IIO_ST_ASM330LHHX
|
||||
|
||||
config IIO_ST_ASM330LHH_SPI
|
||||
config IIO_ST_ASM330LHHX_SPI
|
||||
tristate
|
||||
depends on IIO_ST_ASM330LHH
|
||||
depends on IIO_ST_ASM330LHHX
|
||||
|
||||
config IIO_ST_ASM330LHH_MAY_WAKEUP
|
||||
config IIO_ST_ASM330LHHX_MAY_WAKEUP
|
||||
bool "Enable wake-up irq"
|
||||
depends on IIO_ST_ASM330LHH
|
||||
depends on IIO_ST_ASM330LHHX
|
||||
help
|
||||
Enable wake-up irq on interrupt line
|
||||
|
||||
config IIO_ST_ASM330LHH_EN_TEMPERATURE
|
||||
config IIO_ST_ASM330LHHX_EN_TEMPERATURE
|
||||
bool "Enable internal temperature sensor"
|
||||
depends on IIO_ST_ASM330LHH
|
||||
depends on IIO_ST_ASM330LHHX
|
||||
help
|
||||
Enable internal temperature sensor channel
|
||||
|
||||
config IIO_ST_ASM330LHH_EN_BASIC_FEATURES
|
||||
config IIO_ST_ASM330LHHX_EN_BASIC_FEATURES
|
||||
bool "Enable internal basic features event detection"
|
||||
depends on IIO_ST_ASM330LHH
|
||||
depends on IIO_ST_ASM330LHHX
|
||||
help
|
||||
Enable internal basic features event detection sensor
|
||||
|
||||
config IIO_ST_ASM330LHH_ASYNC_HW_TIMESTAMP
|
||||
config IIO_ST_ASM330LHHX_ASYNC_HW_TIMESTAMP
|
||||
bool "Enable async hw timestamp read"
|
||||
depends on IIO_ST_ASM330LHH
|
||||
depends on IIO_ST_ASM330LHHX
|
||||
help
|
||||
Enable async task that sends over hw timestamp events.
|
||||
|
8
drivers/iio/stm/imu/st_asm330lhhx/Makefile
Normal file
8
drivers/iio/stm/imu/st_asm330lhhx/Makefile
Normal file
@ -0,0 +1,8 @@
|
||||
st_asm330lhhx-y := st_asm330lhhx_core.o st_asm330lhhx_buffer.o \
|
||||
st_asm330lhhx_events.o
|
||||
st_asm330lhhx-$(CONFIG_IIO_ST_ASM330LHHX_ASYNC_HW_TIMESTAMP) += st_asm330lhhx_hwtimestamp.o
|
||||
|
||||
obj-$(CONFIG_IIO_ST_ASM330LHHX) += st_asm330lhhx.o
|
||||
obj-$(CONFIG_IIO_ST_ASM330LHHX_I2C) += st_asm330lhhx_i2c.o
|
||||
obj-$(CONFIG_IIO_ST_ASM330LHHX_SPI) += st_asm330lhhx_spi.o
|
||||
obj-$(CONFIG_IIO_ST_ASM330LHHX_ASYNC_HW_TIMESTAMP) += st_asm330lhhx_hwtimestamp.o
|
579
drivers/iio/stm/imu/st_asm330lhhx/st_asm330lhhx.h
Normal file
579
drivers/iio/stm/imu/st_asm330lhhx/st_asm330lhhx.h
Normal file
@ -0,0 +1,579 @@
|
||||
/*
|
||||
* STMicroelectronics st_asm330lhhx sensor driver
|
||||
*
|
||||
* Copyright 2019 STMicroelectronics Inc.
|
||||
*
|
||||
* Lorenzo Bianconi <lorenzo.bianconi@st.com>
|
||||
*
|
||||
* Licensed under the GPL-2.
|
||||
*/
|
||||
|
||||
#ifndef ST_ASM330LHHX_H
|
||||
#define ST_ASM330LHHX_H
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/hrtimer.h>
|
||||
#include <linux/spinlock.h>
|
||||
|
||||
#define ST_ASM330LHHX_DRV_VERSION "1.1"
|
||||
#define ST_ASM330LHHX_DEBUG_DISCHARGE
|
||||
|
||||
#define ST_ASM330LHHX_MAX_ODR 833
|
||||
#define ST_ASM330LHHX_ODR_LIST_SIZE 8
|
||||
#define ST_ASM330LHHX_ODR_EXPAND(odr, uodr) ((odr * 1000000) + uodr)
|
||||
|
||||
#define ST_ASM330LHH_DEV_NAME "asm330lhh"
|
||||
#define ST_ASM330LHHX_DEV_NAME "asm330lhhx"
|
||||
|
||||
#define ST_ASM330LHHX_DEFAULT_XL_FS_INDEX 2
|
||||
#define ST_ASM330LHHX_DEFAULT_XL_ODR_INDEX 1
|
||||
#define ST_ASM330LHHX_DEFAULT_G_FS_INDEX 2
|
||||
#define ST_ASM330LHHX_DEFAULT_G_ODR_INDEX 1
|
||||
#define ST_ASM330LHHX_DEFAULT_T_FS_INDEX 0
|
||||
#define ST_ASM330LHHX_DEFAULT_T_ODR_INDEX 1
|
||||
|
||||
#define ST_ASM330LHHX_REG_FIFO_CTRL1_ADDR 0x07
|
||||
#define ST_ASM330LHHX_REG_FIFO_CTRL2_ADDR 0x08
|
||||
#define ST_ASM330LHHX_REG_FIFO_WTM_MASK GENMASK(8, 0)
|
||||
#define ST_ASM330LHHX_REG_FIFO_WTM8_MASK BIT(0)
|
||||
#define ST_ASM330LHHX_REG_FIFO_STATUS_DIFF GENMASK(9, 0)
|
||||
|
||||
#define ST_ASM330LHHX_REG_FIFO_CTRL3_ADDR 0x09
|
||||
#define ST_ASM330LHHX_REG_BDR_XL_MASK GENMASK(3, 0)
|
||||
#define ST_ASM330LHHX_REG_BDR_GY_MASK GENMASK(7, 4)
|
||||
|
||||
#define ST_ASM330LHHX_REG_FIFO_CTRL4_ADDR 0x0a
|
||||
#define ST_ASM330LHHX_REG_FIFO_MODE_MASK GENMASK(2, 0)
|
||||
#define ST_ASM330LHHX_REG_DEC_TS_MASK GENMASK(7, 6)
|
||||
#define ST_ASM330LHHX_REG_ODR_T_BATCH_MASK GENMASK(5, 4)
|
||||
|
||||
#define ST_ASM330LHHX_REG_INT1_CTRL_ADDR 0x0d
|
||||
#define ST_ASM330LHHX_REG_INT2_CTRL_ADDR 0x0e
|
||||
#define ST_ASM330LHHX_REG_INT_FIFO_TH_MASK BIT(3)
|
||||
|
||||
#define ST_ASM330LHHX_REG_WHOAMI_ADDR 0x0f
|
||||
#define ST_ASM330LHHX_WHOAMI_VAL 0x6b
|
||||
|
||||
#define ST_ASM330LHHX_CTRL1_XL_ADDR 0x10
|
||||
#define ST_ASM330LHHX_CTRL2_G_ADDR 0x11
|
||||
|
||||
#define ST_ASM330LHHX_REG_CTRL3_C_ADDR 0x12
|
||||
#define ST_ASM330LHHX_REG_SW_RESET_MASK BIT(0)
|
||||
#define ST_ASM330LHHX_REG_PP_OD_MASK BIT(4)
|
||||
#define ST_ASM330LHHX_REG_H_LACTIVE_MASK BIT(5)
|
||||
#define ST_ASM330LHHX_REG_BDU_MASK BIT(6)
|
||||
#define ST_ASM330LHHX_REG_BOOT_MASK BIT(7)
|
||||
|
||||
#define ST_ASM330LHHX_REG_CTRL4_C_ADDR 0x13
|
||||
#define ST_ASM330LHHX_REG_DRDY_MASK BIT(3)
|
||||
|
||||
#define ST_ASM330LHHX_REG_CTRL5_C_ADDR 0x14
|
||||
#define ST_ASM330LHHX_REG_ROUNDING_MASK GENMASK(6, 5)
|
||||
#define ST_ASM330LHHX_REG_ST_G_MASK GENMASK(3, 2)
|
||||
#define ST_ASM330LHHX_REG_ST_XL_MASK GENMASK(1, 0)
|
||||
#define ST_ASM330LHHX_SELFTEST_ACCEL_MIN 737
|
||||
#define ST_ASM330LHHX_SELFTEST_ACCEL_MAX 13934
|
||||
#define ST_ASM330LHHX_SELFTEST_GYRO_MIN 2142
|
||||
#define ST_ASM330LHHX_SELFTEST_GYRO_MAX 10000
|
||||
|
||||
#define ST_ASM330LHHX_SELF_TEST_DISABLED_VAL 0
|
||||
#define ST_ASM330LHHX_SELF_TEST_POS_SIGN_VAL 1
|
||||
#define ST_ASM330LHHX_SELF_TEST_NEG_ACCEL_SIGN_VAL 2
|
||||
#define ST_ASM330LHHX_SELF_TEST_NEG_GYRO_SIGN_VAL 3
|
||||
|
||||
#define ST_ASM330LHHX_REG_CTRL9_XL_ADDR 0x18
|
||||
#define ST_ASM330LHHX_REG_DEVICE_CONF_MASK BIT(1)
|
||||
|
||||
#define ST_ASM330LHHX_REG_CTRL10_C_ADDR 0x19
|
||||
#define ST_ASM330LHHX_REG_TIMESTAMP_EN_MASK BIT(5)
|
||||
|
||||
#define ST_ASM330LHHX_REG_STATUS_ADDR 0x1e
|
||||
#define ST_ASM330LHHX_REG_STATUS_XLDA BIT(0)
|
||||
#define ST_ASM330LHHX_REG_STATUS_GDA BIT(1)
|
||||
#define ST_ASM330LHHX_REG_STATUS_TDA BIT(2)
|
||||
|
||||
#define ST_ASM330LHHX_REG_OUT_TEMP_L_ADDR 0x20
|
||||
|
||||
#define ST_ASM330LHHX_REG_OUTX_L_A_ADDR 0x28
|
||||
#define ST_ASM330LHHX_REG_OUTY_L_A_ADDR 0x2a
|
||||
#define ST_ASM330LHHX_REG_OUTZ_L_A_ADDR 0x2c
|
||||
|
||||
#define ST_ASM330LHHX_REG_OUTX_L_G_ADDR 0x22
|
||||
#define ST_ASM330LHHX_REG_OUTY_L_G_ADDR 0x24
|
||||
#define ST_ASM330LHHX_REG_OUTZ_L_G_ADDR 0x26
|
||||
|
||||
#define ST_ASM330LHHX_REG_TIMESTAMP0_ADDR 0x40
|
||||
|
||||
#define ST_ASM330LHHX_REG_TAP_CFG0_ADDR 0x56
|
||||
#define ST_ASM330LHHX_REG_LIR_MASK BIT(0)
|
||||
|
||||
#define ST_ASM330LHHX_REG_THS_6D_ADDR 0x59
|
||||
#define ST_ASM330LHHX_SIXD_THS_MASK GENMASK(6, 5)
|
||||
|
||||
#define ST_ASM330LHHX_REG_WAKE_UP_THS_ADDR 0x5b
|
||||
#define ST_ASM330LHHX_WAKE_UP_THS_MASK GENMASK(5, 0)
|
||||
|
||||
#define ST_ASM330LHHX_REG_WAKE_UP_DUR_ADDR 0x5c
|
||||
#define ST_ASM330LHHX_WAKE_UP_DUR_MASK GENMASK(6, 5)
|
||||
|
||||
#define ST_ASM330LHHX_REG_FREE_FALL_ADDR 0x5d
|
||||
#define ST_ASM330LHHX_FF_THS_MASK GENMASK(2, 0)
|
||||
|
||||
#define ST_ASM330LHHX_INTERNAL_FREQ_FINE 0x63
|
||||
|
||||
/* Timestamp Tick 25us/LSB */
|
||||
#define ST_ASM330LHHX_TS_DELTA_NS 25000ULL
|
||||
|
||||
/* Temperature in uC */
|
||||
#define ST_ASM330LHHX_TEMP_GAIN 256
|
||||
#define ST_ASM330LHHX_TEMP_FS_GAIN 1000000 / ST_ASM330LHHX_TEMP_GAIN
|
||||
#define ST_ASM330LHHX_TEMP_OFFSET 6400
|
||||
|
||||
/* FIFO simple size and depth */
|
||||
#define ST_ASM330LHHX_SAMPLE_SIZE 6
|
||||
#define ST_ASM330LHHX_TS_SAMPLE_SIZE 4
|
||||
#define ST_ASM330LHHX_TAG_SIZE 1
|
||||
#define ST_ASM330LHHX_FIFO_SAMPLE_SIZE (ST_ASM330LHHX_SAMPLE_SIZE + \
|
||||
ST_ASM330LHHX_TAG_SIZE)
|
||||
#define ST_ASM330LHHX_MAX_FIFO_DEPTH 416
|
||||
|
||||
#define ST_ASM330LHHX_DEFAULT_KTIME (200000000)
|
||||
#define ST_ASM330LHHX_FAST_KTIME (5000000)
|
||||
|
||||
#define ST_ASM330LHHX_DATA_CHANNEL(chan_type, addr, mod, ch2, scan_idx, \
|
||||
rb, sb, sg) \
|
||||
{ \
|
||||
.type = chan_type, \
|
||||
.address = addr, \
|
||||
.modified = mod, \
|
||||
.channel2 = ch2, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
|
||||
BIT(IIO_CHAN_INFO_SCALE), \
|
||||
.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), \
|
||||
.scan_index = scan_idx, \
|
||||
.scan_type = { \
|
||||
.sign = sg, \
|
||||
.realbits = rb, \
|
||||
.storagebits = sb, \
|
||||
.endianness = IIO_LE, \
|
||||
}, \
|
||||
.ext_info = st_asm330lhhx_ext_info, \
|
||||
}
|
||||
|
||||
static const struct iio_event_spec st_asm330lhhx_flush_event = {
|
||||
.type = IIO_EV_TYPE_FIFO_FLUSH,
|
||||
.dir = IIO_EV_DIR_EITHER,
|
||||
};
|
||||
|
||||
static const struct iio_event_spec st_asm330lhhx_thr_event = {
|
||||
.type = IIO_EV_TYPE_THRESH,
|
||||
.dir = IIO_EV_DIR_RISING,
|
||||
.mask_separate = BIT(IIO_EV_INFO_ENABLE),
|
||||
};
|
||||
|
||||
#define ST_ASM330LHHX_EVENT_CHANNEL(ctype, etype) \
|
||||
{ \
|
||||
.type = ctype, \
|
||||
.modified = 0, \
|
||||
.scan_index = -1, \
|
||||
.indexed = -1, \
|
||||
.event_spec = &st_asm330lhhx_##etype##_event, \
|
||||
.num_event_specs = 1, \
|
||||
}
|
||||
|
||||
#define ST_ASM330LHHX_RX_MAX_LENGTH 64
|
||||
#define ST_ASM330LHHX_TX_MAX_LENGTH 16
|
||||
|
||||
struct st_asm330lhhx_transfer_buffer {
|
||||
u8 rx_buf[ST_ASM330LHHX_RX_MAX_LENGTH];
|
||||
u8 tx_buf[ST_ASM330LHHX_TX_MAX_LENGTH] ____cacheline_aligned;
|
||||
};
|
||||
|
||||
struct st_asm330lhhx_transfer_function {
|
||||
int (*read)(struct device *dev, u8 addr, int len, u8 *data);
|
||||
int (*write)(struct device *dev, u8 addr, int len, const u8 *data);
|
||||
};
|
||||
|
||||
/**
|
||||
* struct st_asm330lhhx_reg - Generic sensor register description (addr + mask)
|
||||
* @addr: Address of register.
|
||||
* @mask: Bitmask register for proper usage.
|
||||
*/
|
||||
struct st_asm330lhhx_reg {
|
||||
u8 addr;
|
||||
u8 mask;
|
||||
};
|
||||
|
||||
enum st_asm330lhhx_suspend_resume_register {
|
||||
ST_ASM330LHHX_CTRL1_XL_REG = 0,
|
||||
ST_ASM330LHHX_CTRL2_G_REG,
|
||||
ST_ASM330LHHX_REG_CTRL3_C_REG,
|
||||
ST_ASM330LHHX_REG_CTRL4_C_REG,
|
||||
ST_ASM330LHHX_REG_CTRL5_C_REG,
|
||||
ST_ASM330LHHX_REG_CTRL10_C_REG,
|
||||
ST_ASM330LHHX_REG_TAP_CFG0_REG,
|
||||
ST_ASM330LHHX_REG_INT1_CTRL_REG,
|
||||
ST_ASM330LHHX_REG_INT2_CTRL_REG,
|
||||
ST_ASM330LHHX_REG_FIFO_CTRL1_REG,
|
||||
ST_ASM330LHHX_REG_FIFO_CTRL2_REG,
|
||||
ST_ASM330LHHX_REG_FIFO_CTRL3_REG,
|
||||
ST_ASM330LHHX_REG_FIFO_CTRL4_REG,
|
||||
ST_ASM330LHHX_SUSPEND_RESUME_REGS,
|
||||
};
|
||||
|
||||
struct st_asm330lhhx_suspend_resume_entry {
|
||||
u8 addr;
|
||||
u8 val;
|
||||
u8 mask;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct st_asm330lhhx_odr - Single ODR entry
|
||||
* @hz: Most significant part of the sensor ODR (Hz).
|
||||
* @uhz: Less significant part of the sensor ODR (micro Hz).
|
||||
* @val: ODR register value.
|
||||
* @batch_val: Batching ODR register value.
|
||||
*/
|
||||
struct st_asm330lhhx_odr {
|
||||
u16 hz;
|
||||
u32 uhz;
|
||||
u8 val;
|
||||
u8 batch_val;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct st_asm330lhhx_odr_table_entry - Sensor ODR table
|
||||
* @size: Size of ODR table.
|
||||
* @reg: ODR register.
|
||||
* @batching_reg: ODR register for batching on fifo.
|
||||
* @odr_avl: Array of supported ODR value.
|
||||
*/
|
||||
struct st_asm330lhhx_odr_table_entry {
|
||||
u8 size;
|
||||
struct st_asm330lhhx_reg reg;
|
||||
struct st_asm330lhhx_reg batching_reg;
|
||||
struct st_asm330lhhx_odr odr_avl[ST_ASM330LHHX_ODR_LIST_SIZE];
|
||||
};
|
||||
|
||||
/**
|
||||
* struct st_asm330lhhx_fs - Full Scale sensor table entry
|
||||
* @reg: Register description for FS settings.
|
||||
* @gain: Sensor sensitivity (mdps/LSB, mg/LSB and uC/LSB).
|
||||
* @val: FS register value.
|
||||
*/
|
||||
struct st_asm330lhhx_fs {
|
||||
struct st_asm330lhhx_reg reg;
|
||||
u32 gain;
|
||||
u8 val;
|
||||
};
|
||||
|
||||
#define ST_ASM330LHHX_FS_LIST_SIZE 5
|
||||
#define ST_ASM330LHHX_FS_ACC_LIST_SIZE 4
|
||||
#define ST_ASM330LHHX_FS_GYRO_LIST_SIZE 5
|
||||
#ifdef CONFIG_IIO_ST_ASM330LHHX_EN_TEMPERATURE
|
||||
#define ST_ASM330LHHX_FS_TEMP_LIST_SIZE 1
|
||||
#endif /* CONFIG_IIO_ST_ASM330LHHX_EN_TEMPERATURE */
|
||||
|
||||
/**
|
||||
* struct st_asm330lhhx_fs_table_entry - Full Scale sensor table
|
||||
* @size: Full Scale sensor table size.
|
||||
* @fs_avl: Full Scale list entries.
|
||||
*/
|
||||
struct st_asm330lhhx_fs_table_entry {
|
||||
u8 size;
|
||||
struct st_asm330lhhx_fs fs_avl[ST_ASM330LHHX_FS_LIST_SIZE];
|
||||
};
|
||||
|
||||
#define ST_ASM330LHHX_ACC_FS_2G_GAIN IIO_G_TO_M_S_2(61000)
|
||||
#define ST_ASM330LHHX_ACC_FS_4G_GAIN IIO_G_TO_M_S_2(122000)
|
||||
#define ST_ASM330LHHX_ACC_FS_8G_GAIN IIO_G_TO_M_S_2(244000)
|
||||
#define ST_ASM330LHHX_ACC_FS_16G_GAIN IIO_G_TO_M_S_2(488000)
|
||||
|
||||
#define ST_ASM330LHHX_GYRO_FS_250_GAIN IIO_DEGREE_TO_RAD(8750000)
|
||||
#define ST_ASM330LHHX_GYRO_FS_500_GAIN IIO_DEGREE_TO_RAD(17500000)
|
||||
#define ST_ASM330LHHX_GYRO_FS_1000_GAIN IIO_DEGREE_TO_RAD(35000000)
|
||||
#define ST_ASM330LHHX_GYRO_FS_2000_GAIN IIO_DEGREE_TO_RAD(70000000)
|
||||
#define ST_ASM330LHHX_GYRO_FS_4000_GAIN IIO_DEGREE_TO_RAD(140000000)
|
||||
|
||||
enum st_asm330lhhx_sensor_id {
|
||||
ST_ASM330LHHX_ID_GYRO = 0,
|
||||
ST_ASM330LHHX_ID_ACC,
|
||||
#ifdef CONFIG_IIO_ST_ASM330LHHX_EN_TEMPERATURE
|
||||
ST_ASM330LHHX_ID_TEMP,
|
||||
#endif /* CONFIG_IIO_ST_ASM330LHHX_EN_TEMPERATURE */
|
||||
ST_ASM330LHHX_ID_EVENT,
|
||||
ST_ASM330LHHX_ID_FF = ST_ASM330LHHX_ID_EVENT,
|
||||
ST_ASM330LHHX_ID_SC,
|
||||
ST_ASM330LHHX_ID_TRIGGER,
|
||||
ST_ASM330LHHX_ID_WK = ST_ASM330LHHX_ID_TRIGGER,
|
||||
ST_ASM330LHHX_ID_6D,
|
||||
ST_ASM330LHHX_ID_MAX,
|
||||
};
|
||||
|
||||
enum st_asm330lhhx_fifo_mode {
|
||||
ST_ASM330LHHX_FIFO_BYPASS = 0x0,
|
||||
ST_ASM330LHHX_FIFO_CONT = 0x6,
|
||||
};
|
||||
|
||||
enum {
|
||||
ST_ASM330LHHX_HW_FLUSH,
|
||||
ST_ASM330LHHX_HW_OPERATIONAL,
|
||||
};
|
||||
|
||||
/**
|
||||
* struct st_asm330lhhx_sensor - ST IMU sensor instance
|
||||
* @id: Sensor identifier.
|
||||
* @hw: Pointer to instance of struct st_asm330lhhx_hw.
|
||||
* @gain: Configured sensor sensitivity.
|
||||
* @offset: Sensor data offset.
|
||||
* @conf: Used in case of sensor event to manage configuration.
|
||||
* @odr: Output data rate of the sensor [Hz].
|
||||
* @uodr: Output data rate of the sensor [uHz].
|
||||
* @max_watermark: Max supported watermark level.
|
||||
* @watermark: Sensor watermark level.
|
||||
* @last_fifo_timestamp: Store last sample timestamp in FIFO, used by flush
|
||||
* @selftest_status: Last status of self test output
|
||||
* @min_st, @max_st: Min/Max acc/gyro data values during self test procedure
|
||||
*/
|
||||
struct st_asm330lhhx_sensor {
|
||||
enum st_asm330lhhx_sensor_id id;
|
||||
struct st_asm330lhhx_hw *hw;
|
||||
struct iio_trigger *trig;
|
||||
|
||||
union {
|
||||
/* sensor with odrs, gain and offset */
|
||||
struct {
|
||||
u32 gain;
|
||||
u32 offset;
|
||||
int odr;
|
||||
int uodr;
|
||||
|
||||
#ifdef ST_ASM330LHHX_DEBUG_DISCHARGE
|
||||
u32 discharged_samples;
|
||||
#endif /* ST_ASM330LHHX_DEBUG_DISCHARGE */
|
||||
|
||||
u16 max_watermark;
|
||||
u16 watermark;
|
||||
s64 last_fifo_timestamp;
|
||||
|
||||
/* self test */
|
||||
int8_t selftest_status;
|
||||
int min_st;
|
||||
int max_st;
|
||||
};
|
||||
|
||||
/* sensor specific data configuration */
|
||||
struct {
|
||||
u32 conf[6];
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* struct st_asm330lhhx_hw - ST IMU MEMS hw instance
|
||||
* @dev: Pointer to instance of struct device (I2C or SPI).
|
||||
* @irq: Device interrupt line (I2C or SPI).
|
||||
* @int_pin: Save interrupt pin used by sensor.
|
||||
* @lock: Mutex to protect read and write operations.
|
||||
* @fifo_lock: Mutex to prevent concurrent access to the hw FIFO.
|
||||
* @page_lock: Mutex to prevent concurrent memory page configuration.
|
||||
* @fifo_mode: FIFO operating mode supported by the device.
|
||||
* @state: hw operational state.
|
||||
* @enable_mask: Enabled sensor bitmask.
|
||||
* @hw_timestamp_global: hw timestamp value always monotonic where the most
|
||||
* significant 8byte are incremented at every disable/enable.
|
||||
* @timesync_workqueue: runs the async task in private workqueue.
|
||||
* @timesync_work: actual work to be done in the async task workqueue.
|
||||
* @timesync_timer: hrtimer used to schedule period read for the async task.
|
||||
* @hwtimestamp_lock: spinlock for the 64bit timestamp value.
|
||||
* @timesync_ktime: interval value used by the hrtimer.
|
||||
* @timestamp_c: counter used for counting number of timesync updates.
|
||||
* @ts_offset: Hw timestamp offset.
|
||||
* @ts_delta_ns: Calibrate delta time tick.
|
||||
* @hw_ts: Latest hw timestamp from the sensor.
|
||||
* @val_ts_old: Hold hw timestamp for timer rollover.
|
||||
* @hw_ts_high: Save MSB hw timestamp.
|
||||
* @tsample: Timestamp for each sensor sample.
|
||||
* @delta_ts: Delta time between two consecutive interrupts.
|
||||
* @ts: Latest timestamp from irq handler.
|
||||
* @odr_table_entry: Sensors ODR table.
|
||||
* @iio_devs: Pointers to acc/gyro iio_dev instances.
|
||||
* @tf: Transfer function structure used by I/O operations.
|
||||
* @tb: Transfer buffers used by SPI I/O operations.
|
||||
* @orientation: sensor chip orientation relative to main hardware.
|
||||
*/
|
||||
struct st_asm330lhhx_hw {
|
||||
struct device *dev;
|
||||
int irq;
|
||||
int int_pin;
|
||||
|
||||
struct mutex lock;
|
||||
struct mutex fifo_lock;
|
||||
struct mutex page_lock;
|
||||
|
||||
enum st_asm330lhhx_fifo_mode fifo_mode;
|
||||
unsigned long state;
|
||||
u32 enable_mask;
|
||||
|
||||
s64 hw_timestamp_global;
|
||||
|
||||
#if defined (CONFIG_IIO_ST_ASM330LHHX_ASYNC_HW_TIMESTAMP)
|
||||
struct workqueue_struct *timesync_workqueue;
|
||||
struct work_struct timesync_work;
|
||||
struct hrtimer timesync_timer;
|
||||
spinlock_t hwtimestamp_lock;
|
||||
ktime_t timesync_ktime;
|
||||
int timesync_c;
|
||||
#endif /* CONFIG_IIO_ST_ASM330LHHX_ASYNC_HW_TIMESTAMP */
|
||||
|
||||
s64 ts_offset;
|
||||
u64 ts_delta_ns;
|
||||
s64 hw_ts;
|
||||
u32 val_ts_old;
|
||||
u32 hw_ts_high;
|
||||
s64 tsample;
|
||||
s64 delta_ts;
|
||||
s64 ts;
|
||||
|
||||
const struct st_asm330lhhx_odr_table_entry *odr_table_entry;
|
||||
struct iio_dev *iio_devs[ST_ASM330LHHX_ID_MAX];
|
||||
|
||||
struct regulator *vdd_supply;
|
||||
struct regulator *vddio_supply;
|
||||
|
||||
const struct st_asm330lhhx_transfer_function *tf;
|
||||
struct st_asm330lhhx_transfer_buffer tb;
|
||||
|
||||
struct iio_mount_matrix orientation;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct st_asm330lhhx_ff_th - Free Fall threshold table
|
||||
* @mg: Threshold in mg.
|
||||
* @val: Register value.
|
||||
*/
|
||||
struct st_asm330lhhx_ff_th {
|
||||
u32 mg;
|
||||
u8 val;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct st_asm330lhhx_6D_th - 6D threshold table
|
||||
* @deg: Threshold in degrees.
|
||||
* @val: Register value.
|
||||
*/
|
||||
struct st_asm330lhhx_6D_th {
|
||||
u8 deg;
|
||||
u8 val;
|
||||
};
|
||||
|
||||
extern const struct dev_pm_ops st_asm330lhhx_pm_ops;
|
||||
|
||||
static inline int st_asm330lhhx_read_atomic(struct st_asm330lhhx_hw *hw,
|
||||
u8 addr, int len, u8 *data)
|
||||
{
|
||||
int err;
|
||||
|
||||
mutex_lock(&hw->page_lock);
|
||||
err = hw->tf->read(hw->dev, addr, len, data);
|
||||
mutex_unlock(&hw->page_lock);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static inline int
|
||||
st_asm330lhhx_write_atomic(struct st_asm330lhhx_hw *hw,
|
||||
u8 addr, int len, u8 *data)
|
||||
{
|
||||
int err;
|
||||
|
||||
mutex_lock(&hw->page_lock);
|
||||
err = hw->tf->write(hw->dev, addr, len, data);
|
||||
mutex_unlock(&hw->page_lock);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
int __st_asm330lhhx_write_with_mask(struct st_asm330lhhx_hw *hw, u8 addr,
|
||||
u8 mask, u8 val);
|
||||
static inline int
|
||||
st_asm330lhhx_write_with_mask(struct st_asm330lhhx_hw *hw, u8 addr,
|
||||
u8 mask, u8 val)
|
||||
{
|
||||
int err;
|
||||
|
||||
mutex_lock(&hw->page_lock);
|
||||
err = __st_asm330lhhx_write_with_mask(hw, addr, mask, val);
|
||||
mutex_unlock(&hw->page_lock);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
st_asm330lhhx_is_fifo_enabled(struct st_asm330lhhx_hw *hw)
|
||||
{
|
||||
return hw->enable_mask & (BIT(ST_ASM330LHHX_ID_GYRO) |
|
||||
BIT(ST_ASM330LHHX_ID_ACC));
|
||||
}
|
||||
|
||||
static inline s64 st_asm330lhhx_get_time_ns(struct iio_dev *iio_dev)
|
||||
{
|
||||
return iio_get_time_ns(iio_dev);
|
||||
}
|
||||
|
||||
int st_asm330lhhx_probe(struct device *dev, int irq,
|
||||
const struct st_asm330lhhx_transfer_function *tf_ops);
|
||||
int st_asm330lhhx_sensor_set_enable(struct st_asm330lhhx_sensor *sensor,
|
||||
bool enable);
|
||||
int st_asm330lhhx_buffers_setup(struct st_asm330lhhx_hw *hw);
|
||||
int st_asm330lhhx_get_batch_val(struct st_asm330lhhx_sensor *sensor,
|
||||
int odr, int uodr, u8 *val);
|
||||
int st_asm330lhhx_update_watermark(struct st_asm330lhhx_sensor *sensor,
|
||||
u16 watermark);
|
||||
ssize_t st_asm330lhhx_flush_fifo(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t size);
|
||||
ssize_t st_asm330lhhx_get_max_watermark(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf);
|
||||
ssize_t st_asm330lhhx_get_watermark(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf);
|
||||
ssize_t st_asm330lhhx_set_watermark(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t size);
|
||||
int st_asm330lhhx_suspend_fifo(struct st_asm330lhhx_hw *hw);
|
||||
int st_asm330lhhx_set_fifo_mode(struct st_asm330lhhx_hw *hw,
|
||||
enum st_asm330lhhx_fifo_mode fifo_mode);
|
||||
int __st_asm330lhhx_set_sensor_batching_odr(struct st_asm330lhhx_sensor *sensor,
|
||||
bool enable);
|
||||
int st_asm330lhhx_update_batching(struct iio_dev *iio_dev, bool enable);
|
||||
int st_asm330lhhx_reset_hwts(struct st_asm330lhhx_hw *hw);
|
||||
#ifdef CONFIG_IIO_ST_ASM330LHHX_EN_BASIC_FEATURES
|
||||
int st_asm330lhhx_event_handler(struct st_asm330lhhx_hw *hw);
|
||||
int st_asm330lhhx_probe_event(struct st_asm330lhhx_hw *hw);
|
||||
int st_asm330lhhx_set_wake_up_thershold(struct st_asm330lhhx_hw *hw,
|
||||
int th_ug);
|
||||
int st_asm330lhhx_set_wake_up_duration(struct st_asm330lhhx_hw *hw,
|
||||
int dur_ms);
|
||||
int st_asm330lhhx_set_freefall_threshold(struct st_asm330lhhx_hw *hw,
|
||||
int th_mg);
|
||||
int st_asm330lhhx_set_6D_threshold(struct st_asm330lhhx_hw *hw,
|
||||
int deg);
|
||||
int st_asm330lhhx_read_with_mask(struct st_asm330lhhx_hw *hw, u8 addr,
|
||||
u8 mask, u8 *val);
|
||||
#endif /* CONFIG_IIO_ST_ASM330LHHX_EN_BASIC_FEATURES */
|
||||
|
||||
#if defined (CONFIG_IIO_ST_ASM330LHHX_ASYNC_HW_TIMESTAMP)
|
||||
int st_asm330lhhx_hwtimesync_init(struct st_asm330lhhx_hw *hw);
|
||||
#else /* CONFIG_IIO_ST_ASM330LHHX_ASYNC_HW_TIMESTAMP */
|
||||
static inline int
|
||||
st_asm330lhhx_hwtimesync_init(struct st_asm330lhhx_hw *hw)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif /* CONFIG_IIO_ST_ASM330LHHX_ASYNC_HW_TIMESTAMP */
|
||||
#endif /* ST_ASM330LHHX_H */
|
676
drivers/iio/stm/imu/st_asm330lhhx/st_asm330lhhx_buffer.c
Normal file
676
drivers/iio/stm/imu/st_asm330lhhx/st_asm330lhhx_buffer.c
Normal file
@ -0,0 +1,676 @@
|
||||
/*
|
||||
* STMicroelectronics st_asm330lhhx FIFO buffer library driver
|
||||
*
|
||||
* Copyright 2019 STMicroelectronics Inc.
|
||||
*
|
||||
* Lorenzo Bianconi <lorenzo.bianconi@st.com>
|
||||
* Mario Tesi <mario.tesi@st.com>
|
||||
*
|
||||
* Licensed under the GPL-2.
|
||||
*/
|
||||
#include <linux/module.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/kfifo_buf.h>
|
||||
#include <linux/iio/events.h>
|
||||
#include <linux/iio/buffer.h>
|
||||
#include <asm/unaligned.h>
|
||||
#include <linux/iio/buffer.h>
|
||||
#include "st_asm330lhhx.h"
|
||||
|
||||
#define ST_ASM330LHHX_REG_FIFO_STATUS1_ADDR 0x3a
|
||||
#define ST_ASM330LHHX_REG_TIMESTAMP2_ADDR 0x42
|
||||
#define ST_ASM330LHHX_REG_FIFO_DATA_OUT_TAG_ADDR 0x78
|
||||
|
||||
#define ST_ASM330LHHX_SAMPLE_DISCHARD 0x7ffd
|
||||
|
||||
/* Timestamp convergence filter parameter */
|
||||
#define ST_ASM330LHHX_EWMA_LEVEL 120
|
||||
#define ST_ASM330LHHX_EWMA_DIV 128
|
||||
|
||||
#define ST_ASM330LHHX_TIMESTAMP_RESET_VALUE 0xaa
|
||||
|
||||
enum {
|
||||
ST_ASM330LHHX_GYRO_TAG = 0x01,
|
||||
ST_ASM330LHHX_ACC_TAG = 0x02,
|
||||
#ifdef CONFIG_IIO_ST_ASM330LHHX_EN_TEMPERATURE
|
||||
ST_ASM330LHHX_TEMP_TAG = 0x03,
|
||||
#endif /* CONFIG_IIO_ST_ASM330LHHX_EN_TEMPERATURE */
|
||||
ST_ASM330LHHX_TS_TAG = 0x04,
|
||||
};
|
||||
|
||||
/* Default timeout before to re-enable gyro */
|
||||
int delay_gyro = 10;
|
||||
module_param(delay_gyro, int, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
|
||||
MODULE_PARM_DESC(delay_gyro, "Delay for Gyro arming");
|
||||
static bool delayed_enable_gyro;
|
||||
|
||||
static inline s64 st_asm330lhhx_ewma(s64 old, s64 new, int weight)
|
||||
{
|
||||
s64 diff, incr;
|
||||
|
||||
diff = new - old;
|
||||
incr = div_s64((ST_ASM330LHHX_EWMA_DIV - weight) * diff,
|
||||
ST_ASM330LHHX_EWMA_DIV);
|
||||
|
||||
return old + incr;
|
||||
}
|
||||
|
||||
inline int st_asm330lhhx_reset_hwts(struct st_asm330lhhx_hw *hw)
|
||||
{
|
||||
#if defined(CONFIG_IIO_ST_ASM330LHHX_ASYNC_HW_TIMESTAMP)
|
||||
struct st_asm330lhhx_sensor *sensor;
|
||||
int i;
|
||||
#endif /* CONFIG_IIO_ST_ASM330LHHX_ASYNC_HW_TIMESTAMP */
|
||||
u8 data = ST_ASM330LHHX_TIMESTAMP_RESET_VALUE;
|
||||
int ret;
|
||||
|
||||
ret = st_asm330lhhx_write_atomic(hw, ST_ASM330LHHX_REG_TIMESTAMP2_ADDR,
|
||||
sizeof(data), &data);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
#if defined(CONFIG_IIO_ST_ASM330LHHX_ASYNC_HW_TIMESTAMP)
|
||||
spin_lock_irq(&hw->hwtimestamp_lock);
|
||||
hw->hw_timestamp_global = (hw->hw_timestamp_global + (1LL << 32)) &
|
||||
GENMASK_ULL(63, 32);
|
||||
spin_unlock_irq(&hw->hwtimestamp_lock);
|
||||
for (i = 0; i < ST_ASM330LHHX_ID_MAX; ++i) {
|
||||
if (!hw->iio_devs[i])
|
||||
continue;
|
||||
|
||||
sensor = iio_priv(hw->iio_devs[i]);
|
||||
}
|
||||
hw->timesync_c = 0;
|
||||
hw->timesync_ktime = ktime_set(0, ST_ASM330LHHX_FAST_KTIME);
|
||||
#else /* CONFIG_IIO_ST_ASM330LHHX_ASYNC_HW_TIMESTAMP */
|
||||
hw->hw_timestamp_global = (hw->hw_timestamp_global + (1LL << 32)) &
|
||||
GENMASK_ULL(63, 32);
|
||||
#endif /* CONFIG_IIO_ST_ASM330LHHX_ASYNC_HW_TIMESTAMP */
|
||||
|
||||
hw->ts = st_asm330lhhx_get_time_ns(hw->iio_devs[0]);
|
||||
hw->ts_offset = hw->ts;
|
||||
hw->val_ts_old = 0;
|
||||
hw->hw_ts_high = 0;
|
||||
hw->tsample = 0ull;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int st_asm330lhhx_set_fifo_mode(struct st_asm330lhhx_hw *hw,
|
||||
enum st_asm330lhhx_fifo_mode fifo_mode)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = st_asm330lhhx_write_with_mask(hw, ST_ASM330LHHX_REG_FIFO_CTRL4_ADDR,
|
||||
ST_ASM330LHHX_REG_FIFO_MODE_MASK,
|
||||
fifo_mode);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
hw->fifo_mode = fifo_mode;
|
||||
|
||||
if (fifo_mode == ST_ASM330LHHX_FIFO_BYPASS)
|
||||
clear_bit(ST_ASM330LHHX_HW_OPERATIONAL, &hw->state);
|
||||
else
|
||||
set_bit(ST_ASM330LHHX_HW_OPERATIONAL, &hw->state);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int __st_asm330lhhx_set_sensor_batching_odr(struct st_asm330lhhx_sensor *s,
|
||||
bool enable)
|
||||
{
|
||||
enum st_asm330lhhx_sensor_id id = s->id;
|
||||
struct st_asm330lhhx_hw *hw = s->hw;
|
||||
u8 data = 0;
|
||||
int err;
|
||||
|
||||
if (enable) {
|
||||
err = st_asm330lhhx_get_batch_val(s, s->odr, s->uodr, &data);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
|
||||
return __st_asm330lhhx_write_with_mask(hw,
|
||||
hw->odr_table_entry[id].batching_reg.addr,
|
||||
hw->odr_table_entry[id].batching_reg.mask,
|
||||
data);
|
||||
}
|
||||
|
||||
static inline int
|
||||
st_asm330lhhx_set_sensor_batching_odr(struct st_asm330lhhx_sensor *sensor,
|
||||
bool enable)
|
||||
{
|
||||
struct st_asm330lhhx_hw *hw = sensor->hw;
|
||||
int err;
|
||||
|
||||
mutex_lock(&hw->page_lock);
|
||||
err = __st_asm330lhhx_set_sensor_batching_odr(sensor, enable);
|
||||
mutex_unlock(&hw->page_lock);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
int st_asm330lhhx_update_watermark(struct st_asm330lhhx_sensor *sensor,
|
||||
u16 watermark)
|
||||
{
|
||||
u16 fifo_watermark = ST_ASM330LHHX_MAX_FIFO_DEPTH, cur_watermark = 0;
|
||||
struct st_asm330lhhx_hw *hw = sensor->hw;
|
||||
struct st_asm330lhhx_sensor *cur_sensor;
|
||||
__le16 wdata;
|
||||
int i, err;
|
||||
u8 data;
|
||||
|
||||
for (i = ST_ASM330LHHX_ID_GYRO; i < ST_ASM330LHHX_ID_MAX; i++) {
|
||||
if (!hw->iio_devs[i])
|
||||
continue;
|
||||
|
||||
cur_sensor = iio_priv(hw->iio_devs[i]);
|
||||
|
||||
if (!(hw->enable_mask & BIT(cur_sensor->id)))
|
||||
continue;
|
||||
|
||||
cur_watermark = (cur_sensor == sensor) ? watermark
|
||||
: cur_sensor->watermark;
|
||||
|
||||
fifo_watermark = min_t(u16, fifo_watermark, cur_watermark);
|
||||
}
|
||||
|
||||
fifo_watermark = max_t(u16, fifo_watermark, 2);
|
||||
|
||||
mutex_lock(&hw->lock);
|
||||
|
||||
err = st_asm330lhhx_read_atomic(hw, ST_ASM330LHHX_REG_FIFO_CTRL1_ADDR + 1,
|
||||
sizeof(data), &data);
|
||||
if (err < 0)
|
||||
goto out;
|
||||
|
||||
fifo_watermark = ((data << 8) & ~ST_ASM330LHHX_REG_FIFO_WTM_MASK) |
|
||||
(fifo_watermark & ST_ASM330LHHX_REG_FIFO_WTM_MASK);
|
||||
wdata = cpu_to_le16(fifo_watermark);
|
||||
err = st_asm330lhhx_write_atomic(hw, ST_ASM330LHHX_REG_FIFO_CTRL1_ADDR,
|
||||
sizeof(wdata), (u8 *)&wdata);
|
||||
|
||||
out:
|
||||
mutex_unlock(&hw->lock);
|
||||
|
||||
return err < 0 ? err : 0;
|
||||
}
|
||||
|
||||
static struct iio_dev *st_asm330lhhx_get_iiodev_from_tag(struct st_asm330lhhx_hw *hw,
|
||||
u8 tag)
|
||||
{
|
||||
struct iio_dev *iio_dev;
|
||||
|
||||
switch (tag) {
|
||||
case ST_ASM330LHHX_GYRO_TAG:
|
||||
iio_dev = hw->iio_devs[ST_ASM330LHHX_ID_GYRO];
|
||||
break;
|
||||
case ST_ASM330LHHX_ACC_TAG:
|
||||
iio_dev = hw->iio_devs[ST_ASM330LHHX_ID_ACC];
|
||||
break;
|
||||
#ifdef CONFIG_IIO_ST_ASM330LHHX_EN_TEMPERATURE
|
||||
case ST_ASM330LHHX_TEMP_TAG:
|
||||
iio_dev = hw->iio_devs[ST_ASM330LHHX_ID_TEMP];
|
||||
break;
|
||||
#endif /* CONFIG_IIO_ST_ASM330LHHX_EN_TEMPERATURE */
|
||||
default:
|
||||
iio_dev = NULL;
|
||||
break;
|
||||
}
|
||||
|
||||
return iio_dev;
|
||||
}
|
||||
|
||||
static inline void st_asm330lhhx_sync_hw_ts(struct st_asm330lhhx_hw *hw, s64 ts)
|
||||
{
|
||||
s64 delta = ts - hw->hw_ts;
|
||||
|
||||
hw->ts_offset = st_asm330lhhx_ewma(hw->ts_offset, delta,
|
||||
ST_ASM330LHHX_EWMA_LEVEL);
|
||||
}
|
||||
|
||||
static int st_asm330lhhx_read_fifo(struct st_asm330lhhx_hw *hw)
|
||||
{
|
||||
u8 iio_buf[ALIGN(ST_ASM330LHHX_SAMPLE_SIZE, sizeof(s64)) +
|
||||
sizeof(s64) + sizeof(s64)];
|
||||
u8 buf[6 * ST_ASM330LHHX_FIFO_SAMPLE_SIZE], tag, *ptr;
|
||||
int i, err, word_len, fifo_len, read_len;
|
||||
__le64 hw_timestamp_push;
|
||||
struct iio_dev *iio_dev;
|
||||
s64 ts_irq, hw_ts_old;
|
||||
__le16 fifo_status;
|
||||
u16 fifo_depth;
|
||||
s16 drdymask;
|
||||
u32 val;
|
||||
|
||||
/* return if FIFO is already disabled */
|
||||
if (!test_bit(ST_ASM330LHHX_HW_OPERATIONAL, &hw->state)) {
|
||||
dev_warn(hw->dev, "%s: FIFO in bypass mode\n", __func__);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
ts_irq = hw->ts - hw->delta_ts;
|
||||
|
||||
err = st_asm330lhhx_read_atomic(hw, ST_ASM330LHHX_REG_FIFO_STATUS1_ADDR,
|
||||
sizeof(fifo_status), (u8 *)&fifo_status);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
fifo_depth = le16_to_cpu(fifo_status) & ST_ASM330LHHX_REG_FIFO_STATUS_DIFF;
|
||||
if (!fifo_depth)
|
||||
return 0;
|
||||
|
||||
fifo_len = fifo_depth * ST_ASM330LHHX_FIFO_SAMPLE_SIZE;
|
||||
read_len = 0;
|
||||
while (read_len < fifo_len) {
|
||||
word_len = min_t(int, fifo_len - read_len, sizeof(buf));
|
||||
err = st_asm330lhhx_read_atomic(hw,
|
||||
ST_ASM330LHHX_REG_FIFO_DATA_OUT_TAG_ADDR,
|
||||
word_len, buf);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
for (i = 0; i < word_len; i += ST_ASM330LHHX_FIFO_SAMPLE_SIZE) {
|
||||
ptr = &buf[i + ST_ASM330LHHX_TAG_SIZE];
|
||||
tag = buf[i] >> 3;
|
||||
|
||||
if (tag == ST_ASM330LHHX_TS_TAG) {
|
||||
val = get_unaligned_le32(ptr);
|
||||
|
||||
#if defined(CONFIG_IIO_ST_ASM330LHHX_ASYNC_HW_TIMESTAMP)
|
||||
spin_lock_irq(&hw->hwtimestamp_lock);
|
||||
#endif /* CONFIG_IIO_ST_ASM330LHHX_ASYNC_HW_TIMESTAMP */
|
||||
hw->hw_timestamp_global =
|
||||
(hw->hw_timestamp_global &
|
||||
GENMASK_ULL(63, 32)) |
|
||||
(u32)le32_to_cpu(get_unaligned_le32(ptr));
|
||||
#if defined(CONFIG_IIO_ST_ASM330LHHX_ASYNC_HW_TIMESTAMP)
|
||||
spin_unlock_irq(&hw->hwtimestamp_lock);
|
||||
#endif /* CONFIG_IIO_ST_ASM330LHHX_ASYNC_HW_TIMESTAMP */
|
||||
|
||||
if (hw->val_ts_old > val)
|
||||
hw->hw_ts_high++;
|
||||
|
||||
hw_ts_old = hw->hw_ts;
|
||||
|
||||
/* check hw rollover */
|
||||
hw->val_ts_old = val;
|
||||
hw->hw_ts = (val + ((s64)hw->hw_ts_high << 32)) *
|
||||
hw->ts_delta_ns;
|
||||
hw->ts_offset = st_asm330lhhx_ewma(hw->ts_offset,
|
||||
ts_irq - hw->hw_ts,
|
||||
ST_ASM330LHHX_EWMA_LEVEL);
|
||||
|
||||
if (!test_bit(ST_ASM330LHHX_HW_FLUSH, &hw->state))
|
||||
/* sync ap timestamp and sensor one */
|
||||
st_asm330lhhx_sync_hw_ts(hw, ts_irq);
|
||||
|
||||
ts_irq += hw->hw_ts;
|
||||
|
||||
if (!hw->tsample)
|
||||
hw->tsample = hw->ts_offset + hw->hw_ts;
|
||||
else
|
||||
hw->tsample = hw->tsample + hw->hw_ts - hw_ts_old;
|
||||
} else {
|
||||
struct st_asm330lhhx_sensor *sensor;
|
||||
|
||||
iio_dev = st_asm330lhhx_get_iiodev_from_tag(hw, tag);
|
||||
if (!iio_dev)
|
||||
continue;
|
||||
|
||||
sensor = iio_priv(iio_dev);
|
||||
|
||||
/* skip samples if not ready */
|
||||
drdymask = (s16)le16_to_cpu(get_unaligned_le16(ptr));
|
||||
if (unlikely(drdymask >= ST_ASM330LHHX_SAMPLE_DISCHARD)) {
|
||||
#ifdef ST_ASM330LHHX_DEBUG_DISCHARGE
|
||||
sensor->discharged_samples++;
|
||||
#endif /* ST_ASM330LHHX_DEBUG_DISCHARGE */
|
||||
continue;
|
||||
}
|
||||
|
||||
memcpy(iio_buf, ptr, ST_ASM330LHHX_SAMPLE_SIZE);
|
||||
#if defined(CONFIG_IIO_ST_ASM330LHHX_ASYNC_HW_TIMESTAMP)
|
||||
spin_lock_irq(&hw->hwtimestamp_lock);
|
||||
#endif /* CONFIG_IIO_ST_ASM330LHHX_ASYNC_HW_TIMESTAMP */
|
||||
hw_timestamp_push = cpu_to_le64(hw->hw_timestamp_global);
|
||||
#if defined(CONFIG_IIO_ST_ASM330LHHX_ASYNC_HW_TIMESTAMP)
|
||||
spin_unlock_irq(&hw->hwtimestamp_lock);
|
||||
#endif /* CONFIG_IIO_ST_ASM330LHHX_ASYNC_HW_TIMESTAMP */
|
||||
memcpy(&iio_buf[ALIGN(ST_ASM330LHHX_SAMPLE_SIZE, sizeof(s64))],
|
||||
&hw_timestamp_push, sizeof(hw_timestamp_push));
|
||||
|
||||
hw->tsample = min_t(s64,
|
||||
st_asm330lhhx_get_time_ns(hw->iio_devs[0]),
|
||||
hw->tsample);
|
||||
|
||||
iio_push_to_buffers_with_timestamp(iio_dev,
|
||||
iio_buf,
|
||||
hw->tsample);
|
||||
sensor->last_fifo_timestamp = hw_timestamp_push;
|
||||
}
|
||||
}
|
||||
read_len += word_len;
|
||||
}
|
||||
|
||||
return read_len;
|
||||
}
|
||||
|
||||
ssize_t st_asm330lhhx_get_max_watermark(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct st_asm330lhhx_sensor *sensor = iio_priv(dev_get_drvdata(dev));
|
||||
|
||||
return sprintf(buf, "%d\n", sensor->max_watermark);
|
||||
}
|
||||
|
||||
ssize_t st_asm330lhhx_get_watermark(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct st_asm330lhhx_sensor *sensor = iio_priv(dev_get_drvdata(dev));
|
||||
|
||||
return sprintf(buf, "%d\n", sensor->watermark);
|
||||
}
|
||||
|
||||
ssize_t st_asm330lhhx_set_watermark(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t size)
|
||||
{
|
||||
struct iio_dev *iio_dev = dev_get_drvdata(dev);
|
||||
struct st_asm330lhhx_sensor *sensor = iio_priv(iio_dev);
|
||||
int err, val;
|
||||
|
||||
err = iio_device_claim_direct_mode(iio_dev);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = kstrtoint(buf, 10, &val);
|
||||
if (err < 0)
|
||||
goto out;
|
||||
|
||||
err = st_asm330lhhx_update_watermark(sensor, val);
|
||||
if (err < 0)
|
||||
goto out;
|
||||
|
||||
sensor->watermark = val;
|
||||
|
||||
out:
|
||||
iio_device_release_direct_mode(iio_dev);
|
||||
|
||||
return err < 0 ? err : size;
|
||||
}
|
||||
|
||||
ssize_t st_asm330lhhx_flush_fifo(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t size)
|
||||
{
|
||||
struct iio_dev *iio_dev = dev_get_drvdata(dev);
|
||||
struct st_asm330lhhx_sensor *sensor = iio_priv(iio_dev);
|
||||
struct st_asm330lhhx_hw *hw = sensor->hw;
|
||||
s64 event;
|
||||
int count;
|
||||
s64 type;
|
||||
s64 fts;
|
||||
s64 ts;
|
||||
|
||||
mutex_lock(&hw->fifo_lock);
|
||||
ts = st_asm330lhhx_get_time_ns(iio_dev);
|
||||
hw->delta_ts = ts - hw->ts;
|
||||
hw->ts = ts;
|
||||
set_bit(ST_ASM330LHHX_HW_FLUSH, &hw->state);
|
||||
count = st_asm330lhhx_read_fifo(hw);
|
||||
fts = sensor->last_fifo_timestamp;
|
||||
mutex_unlock(&hw->fifo_lock);
|
||||
|
||||
type = count > 0 ? IIO_EV_DIR_FIFO_DATA : IIO_EV_DIR_FIFO_EMPTY;
|
||||
event = IIO_UNMOD_EVENT_CODE(iio_dev->channels[0].type, -1,
|
||||
IIO_EV_TYPE_FIFO_FLUSH, type);
|
||||
iio_push_event(iio_dev, event, fts);
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
int st_asm330lhhx_suspend_fifo(struct st_asm330lhhx_hw *hw)
|
||||
{
|
||||
int err;
|
||||
|
||||
mutex_lock(&hw->fifo_lock);
|
||||
st_asm330lhhx_read_fifo(hw);
|
||||
err = st_asm330lhhx_set_fifo_mode(hw, ST_ASM330LHHX_FIFO_BYPASS);
|
||||
mutex_unlock(&hw->fifo_lock);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
int st_asm330lhhx_update_batching(struct iio_dev *iio_dev, bool enable)
|
||||
{
|
||||
struct st_asm330lhhx_sensor *sensor = iio_priv(iio_dev);
|
||||
struct st_asm330lhhx_hw *hw = sensor->hw;
|
||||
int err;
|
||||
|
||||
disable_irq(hw->irq);
|
||||
|
||||
err = st_asm330lhhx_set_sensor_batching_odr(sensor, enable);
|
||||
enable_irq(hw->irq);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int st_asm330lhhx_update_fifo(struct iio_dev *iio_dev,
|
||||
bool enable)
|
||||
{
|
||||
struct st_asm330lhhx_sensor *sensor = iio_priv(iio_dev);
|
||||
struct st_asm330lhhx_hw *hw = sensor->hw;
|
||||
int err;
|
||||
|
||||
if (sensor->id == ST_ASM330LHHX_ID_GYRO && !enable)
|
||||
delayed_enable_gyro = true;
|
||||
|
||||
if (sensor->id == ST_ASM330LHHX_ID_GYRO &&
|
||||
enable && delayed_enable_gyro) {
|
||||
delayed_enable_gyro = false;
|
||||
msleep(delay_gyro);
|
||||
}
|
||||
|
||||
disable_irq(hw->irq);
|
||||
|
||||
#if defined(CONFIG_IIO_ST_ASM330LHHX_ASYNC_HW_TIMESTAMP)
|
||||
hrtimer_cancel(&hw->timesync_timer);
|
||||
cancel_work_sync(&hw->timesync_work);
|
||||
#endif /* CONFIG_IIO_ST_ASM330LHHX_ASYNC_HW_TIMESTAMP */
|
||||
|
||||
err = st_asm330lhhx_sensor_set_enable(sensor, enable);
|
||||
if (err < 0)
|
||||
goto out;
|
||||
|
||||
err = st_asm330lhhx_set_sensor_batching_odr(sensor, enable);
|
||||
if (err < 0)
|
||||
goto out;
|
||||
|
||||
#ifdef CONFIG_IIO_ST_ASM330LHHX_EN_TEMPERATURE
|
||||
/*
|
||||
* This is an auxiliary sensor, it need to get batched
|
||||
* toghether at least with a primary sensor (Acc/Gyro).
|
||||
*/
|
||||
if (sensor->id == ST_ASM330LHHX_ID_TEMP) {
|
||||
if (!(hw->enable_mask & (BIT(ST_ASM330LHHX_ID_ACC) |
|
||||
BIT(ST_ASM330LHHX_ID_GYRO)))) {
|
||||
struct st_asm330lhhx_sensor *acc_sensor;
|
||||
u8 data = 0;
|
||||
|
||||
acc_sensor = iio_priv(hw->iio_devs[ST_ASM330LHHX_ID_ACC]);
|
||||
if (enable) {
|
||||
err = st_asm330lhhx_get_batch_val(acc_sensor,
|
||||
sensor->odr, sensor->uodr,
|
||||
&data);
|
||||
if (err < 0)
|
||||
goto out;
|
||||
}
|
||||
|
||||
err = st_asm330lhhx_write_with_mask(hw,
|
||||
hw->odr_table_entry[ST_ASM330LHHX_ID_ACC].batching_reg.addr,
|
||||
hw->odr_table_entry[ST_ASM330LHHX_ID_ACC].batching_reg.mask,
|
||||
data);
|
||||
if (err < 0)
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
#endif /* CONFIG_IIO_ST_ASM330LHHX_EN_TEMPERATURE */
|
||||
|
||||
err = st_asm330lhhx_update_watermark(sensor, sensor->watermark);
|
||||
if (err < 0)
|
||||
goto out;
|
||||
|
||||
if (enable && hw->fifo_mode == ST_ASM330LHHX_FIFO_BYPASS) {
|
||||
st_asm330lhhx_reset_hwts(hw);
|
||||
err = st_asm330lhhx_set_fifo_mode(hw, ST_ASM330LHHX_FIFO_CONT);
|
||||
} else if (!hw->enable_mask) {
|
||||
err = st_asm330lhhx_set_fifo_mode(hw, ST_ASM330LHHX_FIFO_BYPASS);
|
||||
}
|
||||
|
||||
#if defined(CONFIG_IIO_ST_ASM330LHHX_ASYNC_HW_TIMESTAMP)
|
||||
if (hw->fifo_mode != ST_ASM330LHHX_FIFO_BYPASS) {
|
||||
hrtimer_start(&hw->timesync_timer,
|
||||
ktime_set(0, 0),
|
||||
HRTIMER_MODE_REL);
|
||||
}
|
||||
#endif /* CONFIG_IIO_ST_ASM330LHHX_ASYNC_HW_TIMESTAMP */
|
||||
|
||||
out:
|
||||
enable_irq(hw->irq);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static irqreturn_t st_asm330lhhx_handler_irq(int irq, void *private)
|
||||
{
|
||||
struct st_asm330lhhx_hw *hw = (struct st_asm330lhhx_hw *)private;
|
||||
s64 ts = st_asm330lhhx_get_time_ns(hw->iio_devs[0]);
|
||||
|
||||
hw->delta_ts = ts - hw->ts;
|
||||
hw->ts = ts;
|
||||
|
||||
return IRQ_WAKE_THREAD;
|
||||
}
|
||||
|
||||
static irqreturn_t st_asm330lhhx_handler_thread(int irq, void *private)
|
||||
{
|
||||
struct st_asm330lhhx_hw *hw = (struct st_asm330lhhx_hw *)private;
|
||||
|
||||
mutex_lock(&hw->fifo_lock);
|
||||
st_asm330lhhx_read_fifo(hw);
|
||||
clear_bit(ST_ASM330LHHX_HW_FLUSH, &hw->state);
|
||||
mutex_unlock(&hw->fifo_lock);
|
||||
|
||||
#ifdef CONFIG_IIO_ST_ASM330LHHX_EN_BASIC_FEATURES
|
||||
return st_asm330lhhx_event_handler(hw);
|
||||
#else /* CONFIG_IIO_ST_ASM330LHHX_EN_BASIC_FEATURES */
|
||||
return IRQ_HANDLED;
|
||||
#endif /* CONFIG_IIO_ST_ASM330LHHX_EN_BASIC_FEATURES */
|
||||
}
|
||||
|
||||
static int st_asm330lhhx_fifo_preenable(struct iio_dev *iio_dev)
|
||||
{
|
||||
return st_asm330lhhx_update_fifo(iio_dev, true);
|
||||
}
|
||||
|
||||
static int st_asm330lhhx_fifo_postdisable(struct iio_dev *iio_dev)
|
||||
{
|
||||
return st_asm330lhhx_update_fifo(iio_dev, false);
|
||||
}
|
||||
|
||||
static const struct iio_buffer_setup_ops st_asm330lhhx_fifo_ops = {
|
||||
.preenable = st_asm330lhhx_fifo_preenable,
|
||||
.postdisable = st_asm330lhhx_fifo_postdisable,
|
||||
};
|
||||
|
||||
static int st_asm330lhhx_enable_timestamp(struct st_asm330lhhx_hw *hw,
|
||||
bool enable)
|
||||
{
|
||||
return st_asm330lhhx_write_with_mask(hw,
|
||||
ST_ASM330LHHX_REG_FIFO_CTRL4_ADDR,
|
||||
ST_ASM330LHHX_REG_DEC_TS_MASK,
|
||||
enable == true ? 1 : 0);
|
||||
}
|
||||
|
||||
static int st_asm330lhhx_fifo_init(struct st_asm330lhhx_hw *hw)
|
||||
{
|
||||
return st_asm330lhhx_enable_timestamp(hw, true);
|
||||
}
|
||||
|
||||
int st_asm330lhhx_buffers_setup(struct st_asm330lhhx_hw *hw)
|
||||
{
|
||||
struct iio_buffer *buffer;
|
||||
unsigned long irq_type;
|
||||
bool irq_active_low;
|
||||
int i, err;
|
||||
|
||||
irq_type = irqd_get_trigger_type(irq_get_irq_data(hw->irq));
|
||||
if (irq_type == IRQF_TRIGGER_NONE)
|
||||
irq_type = IRQF_TRIGGER_HIGH;
|
||||
|
||||
switch (irq_type) {
|
||||
case IRQF_TRIGGER_HIGH:
|
||||
irq_active_low = false;
|
||||
break;
|
||||
case IRQF_TRIGGER_LOW:
|
||||
irq_active_low = true;
|
||||
break;
|
||||
default:
|
||||
dev_info(hw->dev, "mode %lx unsupported\n", irq_type);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
err = st_asm330lhhx_write_with_mask(hw, ST_ASM330LHHX_REG_CTRL3_C_ADDR,
|
||||
ST_ASM330LHHX_REG_H_LACTIVE_MASK,
|
||||
irq_active_low);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
if (device_property_read_bool(hw->dev, "drive-open-drain")) {
|
||||
err = st_asm330lhhx_write_with_mask(hw,
|
||||
ST_ASM330LHHX_REG_CTRL3_C_ADDR,
|
||||
ST_ASM330LHHX_REG_PP_OD_MASK,
|
||||
1);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
irq_type |= IRQF_SHARED;
|
||||
}
|
||||
|
||||
err = devm_request_threaded_irq(hw->dev, hw->irq,
|
||||
st_asm330lhhx_handler_irq,
|
||||
st_asm330lhhx_handler_thread,
|
||||
irq_type | IRQF_ONESHOT,
|
||||
"asm330lhhx", hw);
|
||||
if (err) {
|
||||
dev_err(hw->dev, "failed to request trigger irq %d\n",
|
||||
hw->irq);
|
||||
return err;
|
||||
}
|
||||
|
||||
for (i = 0; i < ST_ASM330LHHX_ID_EVENT; i++) {
|
||||
if (!hw->iio_devs[i])
|
||||
continue;
|
||||
|
||||
buffer = devm_iio_kfifo_allocate(hw->dev);
|
||||
if (!buffer)
|
||||
return -ENOMEM;
|
||||
|
||||
iio_device_attach_buffer(hw->iio_devs[i], buffer);
|
||||
hw->iio_devs[i]->modes |= INDIO_BUFFER_SOFTWARE;
|
||||
hw->iio_devs[i]->setup_ops = &st_asm330lhhx_fifo_ops;
|
||||
}
|
||||
|
||||
err = st_asm330lhhx_hwtimesync_init(hw);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
return st_asm330lhhx_fifo_init(hw);
|
||||
}
|
1946
drivers/iio/stm/imu/st_asm330lhhx/st_asm330lhhx_core.c
Normal file
1946
drivers/iio/stm/imu/st_asm330lhhx/st_asm330lhhx_core.c
Normal file
File diff suppressed because it is too large
Load Diff
616
drivers/iio/stm/imu/st_asm330lhhx/st_asm330lhhx_events.c
Normal file
616
drivers/iio/stm/imu/st_asm330lhhx/st_asm330lhhx_events.c
Normal file
@ -0,0 +1,616 @@
|
||||
/**
|
||||
* STMicroelectronics st_asm330lhhx events function sensor driver
|
||||
*
|
||||
* Copyright 2020 STMicroelectronics Inc.
|
||||
*
|
||||
* Mario Tesi <mario.tesi@st.com>
|
||||
*
|
||||
* Licensed under the GPL-2.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/iio/events.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
#include <linux/iio/trigger_consumer.h>
|
||||
#include <linux/iio/triggered_buffer.h>
|
||||
#include <linux/iio/trigger.h>
|
||||
#include <linux/iio/buffer.h>
|
||||
|
||||
#include "st_asm330lhhx.h"
|
||||
|
||||
#ifdef CONFIG_IIO_ST_ASM330LHHX_EN_BASIC_FEATURES
|
||||
|
||||
#define ST_ASM330LHHX_REG_ALL_INT_SRC_ADDR 0x1a
|
||||
#define ST_ASM330LHHX_FF_IA_MASK BIT(0)
|
||||
#define ST_ASM330LHHX_WU_IA_MASK BIT(1)
|
||||
#define ST_ASM330LHHX_D6D_IA_MASK BIT(4)
|
||||
#define ST_ASM330LHHX_SLEEP_CHANGE_MASK BIT(5)
|
||||
|
||||
#define ST_ASM330LHHX_REG_WAKE_UP_SRC_ADDR 0x1b
|
||||
#define ST_ASM330LHHX_WAKE_UP_EVENT_MASK GENMASK(3, 0)
|
||||
|
||||
#define ST_ASM330LHHX_REG_D6D_SRC_ADDR 0x1d
|
||||
#define ST_ASM330LHHX_D6D_EVENT_MASK GENMASK(5, 0)
|
||||
|
||||
#define ST_ASM330LHHX_REG_INT_CFG1_ADDR 0x58
|
||||
#define ST_ASM330LHHX_INTERRUPTS_ENABLE_MASK BIT(7)
|
||||
|
||||
#define ST_ASM330LHHX_REG_MD1_CFG_ADDR 0x5e
|
||||
#define ST_ASM330LHHX_REG_MD2_CFG_ADDR 0x5f
|
||||
#define ST_ASM330LHHX_INT_6D_MASK BIT(2)
|
||||
#define ST_ASM330LHHX_INT_FF_MASK BIT(4)
|
||||
#define ST_ASM330LHHX_INT_WU_MASK BIT(5)
|
||||
#define ST_ASM330LHHX_INT_SLEEP_CHANGE_MASK BIT(7)
|
||||
|
||||
static const unsigned long st_asm330lhhx_event_available_scan_masks[] = {
|
||||
0x1, 0x0
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec st_asm330lhhx_wk_channels[] = {
|
||||
{
|
||||
.type = IIO_GESTURE,
|
||||
.scan_index = 0,
|
||||
.scan_type = {
|
||||
.sign = 'u',
|
||||
.realbits = 8,
|
||||
.storagebits = 8,
|
||||
},
|
||||
},
|
||||
IIO_CHAN_SOFT_TIMESTAMP(1),
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec st_asm330lhhx_ff_channels[] = {
|
||||
ST_ASM330LHHX_EVENT_CHANNEL(IIO_GESTURE, thr),
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec st_asm330lhhx_sc_channels[] = {
|
||||
ST_ASM330LHHX_EVENT_CHANNEL(IIO_GESTURE, thr),
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec st_asm330lhhx_6D_channels[] = {
|
||||
{
|
||||
.type = IIO_GESTURE,
|
||||
.scan_index = 0,
|
||||
.scan_type = {
|
||||
.sign = 'u',
|
||||
.realbits = 8,
|
||||
.storagebits = 8,
|
||||
},
|
||||
},
|
||||
IIO_CHAN_SOFT_TIMESTAMP(1),
|
||||
};
|
||||
|
||||
static
|
||||
int st_asm330lhhx_event_sensor_set_enable(struct st_asm330lhhx_sensor *sensor,
|
||||
bool enable)
|
||||
{
|
||||
int err, eint = !!enable;
|
||||
struct st_asm330lhhx_hw *hw = sensor->hw;
|
||||
u8 int_reg = hw->int_pin == 1 ? ST_ASM330LHHX_REG_MD1_CFG_ADDR :
|
||||
ST_ASM330LHHX_REG_MD2_CFG_ADDR;
|
||||
|
||||
switch (sensor->id) {
|
||||
case ST_ASM330LHHX_ID_WK:
|
||||
err = st_asm330lhhx_write_with_mask(hw,
|
||||
int_reg,
|
||||
ST_ASM330LHHX_INT_WU_MASK,
|
||||
eint);
|
||||
if (err < 0)
|
||||
return err;
|
||||
break;
|
||||
case ST_ASM330LHHX_ID_FF:
|
||||
err = st_asm330lhhx_write_with_mask(hw,
|
||||
int_reg,
|
||||
ST_ASM330LHHX_INT_FF_MASK,
|
||||
eint);
|
||||
if (err < 0)
|
||||
return err;
|
||||
break;
|
||||
case ST_ASM330LHHX_ID_SC:
|
||||
err = st_asm330lhhx_write_with_mask(hw,
|
||||
int_reg,
|
||||
ST_ASM330LHHX_INT_SLEEP_CHANGE_MASK,
|
||||
eint);
|
||||
if (err < 0)
|
||||
return err;
|
||||
break;
|
||||
case ST_ASM330LHHX_ID_6D:
|
||||
err = st_asm330lhhx_write_with_mask(hw,
|
||||
int_reg,
|
||||
ST_ASM330LHHX_INT_6D_MASK,
|
||||
eint);
|
||||
if (err < 0)
|
||||
return err;
|
||||
break;
|
||||
default:
|
||||
err = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
if (err >= 0) {
|
||||
err = st_asm330lhhx_write_with_mask(hw,
|
||||
ST_ASM330LHHX_REG_INT_CFG1_ADDR,
|
||||
ST_ASM330LHHX_INTERRUPTS_ENABLE_MASK,
|
||||
eint);
|
||||
if (eint == 0)
|
||||
hw->enable_mask &= ~BIT(sensor->id);
|
||||
else
|
||||
hw->enable_mask |= BIT(sensor->id);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int st_asm330lhhx_read_event_config(struct iio_dev *iio_dev,
|
||||
const struct iio_chan_spec *chan,
|
||||
enum iio_event_type type,
|
||||
enum iio_event_direction dir)
|
||||
{
|
||||
struct st_asm330lhhx_sensor *sensor = iio_priv(iio_dev);
|
||||
struct st_asm330lhhx_hw *hw = sensor->hw;
|
||||
|
||||
return !!(hw->enable_mask & BIT(sensor->id));
|
||||
}
|
||||
|
||||
static int st_asm330lhhx_write_event_config(struct iio_dev *iio_dev,
|
||||
const struct iio_chan_spec *chan,
|
||||
enum iio_event_type type,
|
||||
enum iio_event_direction dir,
|
||||
int state)
|
||||
{
|
||||
struct st_asm330lhhx_sensor *sensor = iio_priv(iio_dev);
|
||||
int err;
|
||||
|
||||
mutex_lock(&iio_dev->mlock);
|
||||
err = st_asm330lhhx_event_sensor_set_enable(sensor, state);
|
||||
mutex_unlock(&iio_dev->mlock);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
ssize_t st_asm330lhhx_wakeup_threshold_get(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct st_asm330lhhx_sensor *sensor = iio_priv(dev_get_drvdata(dev));
|
||||
|
||||
return sprintf(buf, "%d\n", sensor->conf[0]);
|
||||
}
|
||||
|
||||
ssize_t st_asm330lhhx_wakeup_threshold_set(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t size)
|
||||
{
|
||||
struct iio_dev *iio_dev = dev_get_drvdata(dev);
|
||||
struct st_asm330lhhx_sensor *sensor = iio_priv(iio_dev);
|
||||
int err, val;
|
||||
|
||||
err = iio_device_claim_direct_mode(iio_dev);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = kstrtoint(buf, 10, &val);
|
||||
if (err < 0)
|
||||
goto out;
|
||||
|
||||
err = st_asm330lhhx_set_wake_up_thershold(sensor->hw, val);
|
||||
if (err < 0)
|
||||
goto out;
|
||||
|
||||
sensor->conf[0] = val;
|
||||
|
||||
out:
|
||||
iio_device_release_direct_mode(iio_dev);
|
||||
|
||||
return err < 0 ? err : size;
|
||||
}
|
||||
|
||||
ssize_t st_asm330lhhx_wakeup_duration_get(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct st_asm330lhhx_sensor *sensor = iio_priv(dev_get_drvdata(dev));
|
||||
|
||||
return sprintf(buf, "%d\n", sensor->conf[1]);
|
||||
}
|
||||
|
||||
ssize_t st_asm330lhhx_wakeup_duration_set(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t size)
|
||||
{
|
||||
struct iio_dev *iio_dev = dev_get_drvdata(dev);
|
||||
struct st_asm330lhhx_sensor *sensor = iio_priv(iio_dev);
|
||||
int err, val;
|
||||
|
||||
err = iio_device_claim_direct_mode(iio_dev);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = kstrtoint(buf, 10, &val);
|
||||
if (err < 0)
|
||||
goto out;
|
||||
|
||||
err = st_asm330lhhx_set_wake_up_duration(sensor->hw, val);
|
||||
if (err < 0)
|
||||
goto out;
|
||||
|
||||
sensor->conf[1] = val;
|
||||
|
||||
out:
|
||||
iio_device_release_direct_mode(iio_dev);
|
||||
|
||||
return err < 0 ? err : size;
|
||||
}
|
||||
|
||||
ssize_t st_asm330lhhx_freefall_threshold_get(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct st_asm330lhhx_sensor *sensor = iio_priv(dev_get_drvdata(dev));
|
||||
|
||||
return sprintf(buf, "%d\n", sensor->conf[2]);
|
||||
}
|
||||
|
||||
ssize_t st_asm330lhhx_freefall_threshold_set(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t size)
|
||||
{
|
||||
struct iio_dev *iio_dev = dev_get_drvdata(dev);
|
||||
struct st_asm330lhhx_sensor *sensor = iio_priv(iio_dev);
|
||||
int err, val;
|
||||
|
||||
err = iio_device_claim_direct_mode(iio_dev);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = kstrtoint(buf, 10, &val);
|
||||
if (err < 0)
|
||||
goto out;
|
||||
|
||||
err = st_asm330lhhx_set_freefall_threshold(sensor->hw, val);
|
||||
if (err < 0)
|
||||
goto out;
|
||||
|
||||
sensor->conf[2] = val;
|
||||
|
||||
out:
|
||||
iio_device_release_direct_mode(iio_dev);
|
||||
|
||||
return err < 0 ? err : size;
|
||||
}
|
||||
|
||||
ssize_t st_asm330lhhx_6D_threshold_get(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct st_asm330lhhx_sensor *sensor = iio_priv(dev_get_drvdata(dev));
|
||||
|
||||
return sprintf(buf, "%d\n", sensor->conf[3]);
|
||||
}
|
||||
|
||||
ssize_t st_asm330lhhx_6D_threshold_set(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t size)
|
||||
{
|
||||
struct iio_dev *iio_dev = dev_get_drvdata(dev);
|
||||
struct st_asm330lhhx_sensor *sensor = iio_priv(iio_dev);
|
||||
int err, val;
|
||||
|
||||
err = iio_device_claim_direct_mode(iio_dev);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = kstrtoint(buf, 10, &val);
|
||||
if (err < 0)
|
||||
goto out;
|
||||
|
||||
err = st_asm330lhhx_set_6D_threshold(sensor->hw, val);
|
||||
if (err < 0)
|
||||
goto out;
|
||||
|
||||
sensor->conf[3] = val;
|
||||
|
||||
out:
|
||||
iio_device_release_direct_mode(iio_dev);
|
||||
|
||||
return err < 0 ? err : size;
|
||||
}
|
||||
|
||||
static IIO_DEVICE_ATTR(wakeup_threshold, 0644,
|
||||
st_asm330lhhx_wakeup_threshold_get,
|
||||
st_asm330lhhx_wakeup_threshold_set, 0);
|
||||
|
||||
static IIO_DEVICE_ATTR(wakeup_duration, 0644,
|
||||
st_asm330lhhx_wakeup_duration_get,
|
||||
st_asm330lhhx_wakeup_duration_set, 0);
|
||||
|
||||
static IIO_DEVICE_ATTR(freefall_threshold, 0644,
|
||||
st_asm330lhhx_freefall_threshold_get,
|
||||
st_asm330lhhx_freefall_threshold_set, 0);
|
||||
|
||||
static IIO_DEVICE_ATTR(sixd_threshold, 0644,
|
||||
st_asm330lhhx_6D_threshold_get,
|
||||
st_asm330lhhx_6D_threshold_set, 0);
|
||||
|
||||
static struct attribute *st_asm330lhhx_wk_attributes[] = {
|
||||
&iio_dev_attr_wakeup_threshold.dev_attr.attr,
|
||||
&iio_dev_attr_wakeup_duration.dev_attr.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static const struct attribute_group st_asm330lhhx_wk_attribute_group = {
|
||||
.attrs = st_asm330lhhx_wk_attributes,
|
||||
};
|
||||
|
||||
static const struct iio_info st_asm330lhhx_wk_info = {
|
||||
.attrs = &st_asm330lhhx_wk_attribute_group,
|
||||
};
|
||||
|
||||
static struct attribute *st_asm330lhhx_ff_attributes[] = {
|
||||
&iio_dev_attr_freefall_threshold.dev_attr.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static const struct attribute_group st_asm330lhhx_ff_attribute_group = {
|
||||
.attrs = st_asm330lhhx_ff_attributes,
|
||||
};
|
||||
|
||||
static const struct iio_info st_asm330lhhx_ff_info = {
|
||||
.attrs = &st_asm330lhhx_ff_attribute_group,
|
||||
.read_event_config = st_asm330lhhx_read_event_config,
|
||||
.write_event_config = st_asm330lhhx_write_event_config,
|
||||
};
|
||||
|
||||
static struct attribute *st_asm330lhhx_sc_attributes[] = {
|
||||
NULL,
|
||||
};
|
||||
|
||||
static const struct attribute_group st_asm330lhhx_sc_attribute_group = {
|
||||
.attrs = st_asm330lhhx_sc_attributes,
|
||||
};
|
||||
|
||||
static const struct iio_info st_asm330lhhx_sc_info = {
|
||||
.attrs = &st_asm330lhhx_sc_attribute_group,
|
||||
.read_event_config = st_asm330lhhx_read_event_config,
|
||||
.write_event_config = st_asm330lhhx_write_event_config,
|
||||
};
|
||||
|
||||
static struct attribute *st_asm330lhhx_6D_attributes[] = {
|
||||
&iio_dev_attr_sixd_threshold.dev_attr.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static const struct attribute_group st_asm330lhhx_6D_attribute_group = {
|
||||
.attrs = st_asm330lhhx_6D_attributes,
|
||||
};
|
||||
|
||||
static const struct iio_info st_asm330lhhx_6D_info = {
|
||||
.attrs = &st_asm330lhhx_6D_attribute_group,
|
||||
};
|
||||
|
||||
static
|
||||
struct iio_dev *st_asm330lhhx_alloc_event_iiodev(struct st_asm330lhhx_hw *hw,
|
||||
enum st_asm330lhhx_sensor_id id)
|
||||
{
|
||||
struct st_asm330lhhx_sensor *sensor;
|
||||
struct iio_dev *iio_dev;
|
||||
|
||||
iio_dev = devm_iio_device_alloc(hw->dev, sizeof(*sensor));
|
||||
if (!iio_dev)
|
||||
return NULL;
|
||||
|
||||
iio_dev->modes = INDIO_DIRECT_MODE;
|
||||
iio_dev->dev.parent = hw->dev;
|
||||
|
||||
sensor = iio_priv(iio_dev);
|
||||
sensor->id = id;
|
||||
sensor->hw = hw;
|
||||
sensor->watermark = 1;
|
||||
|
||||
iio_dev->available_scan_masks = st_asm330lhhx_event_available_scan_masks;
|
||||
|
||||
switch (id) {
|
||||
case ST_ASM330LHHX_ID_WK:
|
||||
iio_dev->channels = st_asm330lhhx_wk_channels;
|
||||
iio_dev->num_channels = ARRAY_SIZE(st_asm330lhhx_wk_channels);
|
||||
iio_dev->name = "asm330lhhx_wk";
|
||||
iio_dev->info = &st_asm330lhhx_wk_info;
|
||||
break;
|
||||
case ST_ASM330LHHX_ID_FF:
|
||||
iio_dev->channels = st_asm330lhhx_ff_channels;
|
||||
iio_dev->num_channels = ARRAY_SIZE(st_asm330lhhx_ff_channels);
|
||||
iio_dev->name = "asm330lhhx_ff";
|
||||
iio_dev->info = &st_asm330lhhx_ff_info;
|
||||
break;
|
||||
case ST_ASM330LHHX_ID_SC:
|
||||
iio_dev->channels = st_asm330lhhx_sc_channels;
|
||||
iio_dev->num_channels = ARRAY_SIZE(st_asm330lhhx_sc_channels);
|
||||
iio_dev->name = "asm330lhhx_sc";
|
||||
iio_dev->info = &st_asm330lhhx_sc_info;
|
||||
break;
|
||||
case ST_ASM330LHHX_ID_6D:
|
||||
iio_dev->channels = st_asm330lhhx_6D_channels;
|
||||
iio_dev->num_channels = ARRAY_SIZE(st_asm330lhhx_6D_channels);
|
||||
iio_dev->name = "asm330lhhx_6d";
|
||||
iio_dev->info = &st_asm330lhhx_6D_info;
|
||||
break;
|
||||
default:
|
||||
iio_device_free(iio_dev);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return iio_dev;
|
||||
}
|
||||
|
||||
int st_asm330lhhx_event_handler(struct st_asm330lhhx_hw *hw)
|
||||
{
|
||||
struct iio_dev *iio_dev;
|
||||
u8 status;
|
||||
s64 event;
|
||||
int err;
|
||||
|
||||
if (hw->enable_mask &
|
||||
(BIT(ST_ASM330LHHX_ID_WK) | BIT(ST_ASM330LHHX_ID_FF) |
|
||||
BIT(ST_ASM330LHHX_ID_SC) | BIT(ST_ASM330LHHX_ID_6D))) {
|
||||
err = hw->tf->read(hw->dev,
|
||||
ST_ASM330LHHX_REG_ALL_INT_SRC_ADDR,
|
||||
sizeof(status), &status);
|
||||
if (err < 0)
|
||||
return IRQ_HANDLED;
|
||||
|
||||
/* base function sensors */
|
||||
if (status & ST_ASM330LHHX_FF_IA_MASK) {
|
||||
iio_dev = hw->iio_devs[ST_ASM330LHHX_ID_FF];
|
||||
event = IIO_UNMOD_EVENT_CODE(IIO_GESTURE, -1,
|
||||
IIO_EV_TYPE_THRESH,
|
||||
IIO_EV_DIR_RISING);
|
||||
iio_push_event(iio_dev, event,
|
||||
st_asm330lhhx_get_time_ns(iio_dev));
|
||||
}
|
||||
if (status & ST_ASM330LHHX_WU_IA_MASK) {
|
||||
struct st_asm330lhhx_sensor *sensor;
|
||||
|
||||
iio_dev = hw->iio_devs[ST_ASM330LHHX_ID_WK];
|
||||
sensor = iio_priv(iio_dev);
|
||||
iio_trigger_poll_chained(sensor->trig);
|
||||
}
|
||||
if (status & ST_ASM330LHHX_SLEEP_CHANGE_MASK) {
|
||||
iio_dev = hw->iio_devs[ST_ASM330LHHX_ID_SC];
|
||||
event = IIO_UNMOD_EVENT_CODE(IIO_GESTURE, -1,
|
||||
IIO_EV_TYPE_THRESH,
|
||||
IIO_EV_DIR_RISING);
|
||||
iio_push_event(iio_dev, event,
|
||||
st_asm330lhhx_get_time_ns(iio_dev));
|
||||
}
|
||||
if (status & ST_ASM330LHHX_D6D_IA_MASK) {
|
||||
struct st_asm330lhhx_sensor *sensor;
|
||||
|
||||
iio_dev = hw->iio_devs[ST_ASM330LHHX_ID_6D];
|
||||
sensor = iio_priv(iio_dev);
|
||||
iio_trigger_poll_chained(sensor->trig);
|
||||
}
|
||||
}
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static inline int st_asm330lhhx_get_6D(struct st_asm330lhhx_hw *hw, u8 *out)
|
||||
{
|
||||
return st_asm330lhhx_read_with_mask(hw, ST_ASM330LHHX_REG_D6D_SRC_ADDR,
|
||||
ST_ASM330LHHX_D6D_EVENT_MASK, out);
|
||||
}
|
||||
|
||||
static inline int st_asm330lhhx_get_wk(struct st_asm330lhhx_hw *hw, u8 *out)
|
||||
{
|
||||
return st_asm330lhhx_read_with_mask(hw,
|
||||
ST_ASM330LHHX_REG_WAKE_UP_SRC_ADDR,
|
||||
ST_ASM330LHHX_WAKE_UP_EVENT_MASK, out);
|
||||
}
|
||||
|
||||
static irqreturn_t st_asm330lhhx_6D_handler_thread(int irq, void *p)
|
||||
{
|
||||
struct iio_poll_func *pf = p;
|
||||
struct iio_dev *iio_dev = pf->indio_dev;
|
||||
struct st_asm330lhhx_sensor *sensor = iio_priv(iio_dev);
|
||||
u8 buffer[sizeof(u8) + sizeof(s64)];
|
||||
|
||||
st_asm330lhhx_get_6D(sensor->hw, buffer);
|
||||
iio_push_to_buffers_with_timestamp(iio_dev, buffer,
|
||||
st_asm330lhhx_get_time_ns(iio_dev));
|
||||
iio_trigger_notify_done(sensor->trig);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static irqreturn_t st_asm330lhhx_wk_handler_thread(int irq, void *p)
|
||||
{
|
||||
struct iio_poll_func *pf = p;
|
||||
struct iio_dev *iio_dev = pf->indio_dev;
|
||||
struct st_asm330lhhx_sensor *sensor = iio_priv(iio_dev);
|
||||
u8 buffer[sizeof(u8) + sizeof(s64)];
|
||||
|
||||
st_asm330lhhx_get_wk(sensor->hw, buffer);
|
||||
iio_push_to_buffers_with_timestamp(iio_dev, buffer,
|
||||
st_asm330lhhx_get_time_ns(iio_dev));
|
||||
iio_trigger_notify_done(sensor->trig);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static const struct iio_trigger_ops st_asm330lhhx_trigger_ops = {
|
||||
NULL,
|
||||
};
|
||||
|
||||
static int st_asm330lhhx_buffer_preenable(struct iio_dev *iio_dev)
|
||||
{
|
||||
return st_asm330lhhx_event_sensor_set_enable(iio_priv(iio_dev), true);
|
||||
}
|
||||
|
||||
static int st_asm330lhhx_buffer_postdisable(struct iio_dev *iio_dev)
|
||||
{
|
||||
return st_asm330lhhx_event_sensor_set_enable(iio_priv(iio_dev), false);
|
||||
}
|
||||
|
||||
static const struct iio_buffer_setup_ops st_asm330lhhx_buffer_ops = {
|
||||
.preenable = st_asm330lhhx_buffer_preenable,
|
||||
.postdisable = st_asm330lhhx_buffer_postdisable,
|
||||
};
|
||||
|
||||
int st_asm330lhhx_probe_event(struct st_asm330lhhx_hw *hw)
|
||||
{
|
||||
struct st_asm330lhhx_sensor *sensor;
|
||||
struct iio_dev *iio_dev;
|
||||
irqreturn_t (*pthread[ST_ASM330LHHX_ID_MAX - ST_ASM330LHHX_ID_TRIGGER])(int irq, void *p) = {
|
||||
[0] = st_asm330lhhx_wk_handler_thread,
|
||||
[1] = st_asm330lhhx_6D_handler_thread,
|
||||
/* add here all other trigger handler funcions */
|
||||
};
|
||||
int i, err;
|
||||
|
||||
for (i = ST_ASM330LHHX_ID_EVENT; i < ST_ASM330LHHX_ID_MAX; i++) {
|
||||
hw->iio_devs[i] = st_asm330lhhx_alloc_event_iiodev(hw, i);
|
||||
if (!hw->iio_devs[i])
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/* configure trigger sensors */
|
||||
for (i = ST_ASM330LHHX_ID_TRIGGER; i < ST_ASM330LHHX_ID_MAX; i++) {
|
||||
iio_dev = hw->iio_devs[i];
|
||||
sensor = iio_priv(iio_dev);
|
||||
|
||||
err = devm_iio_triggered_buffer_setup(hw->dev, iio_dev,
|
||||
NULL, pthread[i - ST_ASM330LHHX_ID_TRIGGER],
|
||||
&st_asm330lhhx_buffer_ops);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
sensor->trig = devm_iio_trigger_alloc(hw->dev, "%s-trigger",
|
||||
iio_dev->name);
|
||||
if (!sensor->trig)
|
||||
return -ENOMEM;
|
||||
|
||||
iio_trigger_set_drvdata(sensor->trig, iio_dev);
|
||||
sensor->trig->ops = &st_asm330lhhx_trigger_ops;
|
||||
sensor->trig->dev.parent = hw->dev;
|
||||
iio_dev->trig = iio_trigger_get(sensor->trig);
|
||||
|
||||
err = devm_iio_trigger_register(hw->dev, sensor->trig);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
for (i = ST_ASM330LHHX_ID_EVENT; i < ST_ASM330LHHX_ID_MAX; i++) {
|
||||
if (!hw->iio_devs[i])
|
||||
continue;
|
||||
|
||||
err = devm_iio_device_register(hw->dev, hw->iio_devs[i]);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif /* CONFIG_IIO_ST_ASM330LHHX_EN_BASIC_FEATURES */
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* STMicroelectronics st_asm330lhh hwtimestamp library driver
|
||||
* STMicroelectronics st_asm330lhhx hwtimestamp library driver
|
||||
*
|
||||
* Copyright 2021 STMicroelectronics Inc.
|
||||
*
|
||||
@ -14,11 +14,11 @@
|
||||
#include <linux/iio/buffer.h>
|
||||
#include <asm/unaligned.h>
|
||||
#include <linux/iio/buffer.h>
|
||||
#include "st_asm330lhh.h"
|
||||
#include "st_asm330lhhx.h"
|
||||
|
||||
#define ST_ASM330LHH_TSYNC_OFFSET_NS (300 * 1000LL)
|
||||
#define ST_ASM330LHHX_TSYNC_OFFSET_NS (300 * 1000LL)
|
||||
|
||||
static void st_asm330lhh_read_hw_timestamp(struct st_asm330lhh_hw *hw)
|
||||
static void st_asm330lhhx_read_hw_timestamp(struct st_asm330lhhx_hw *hw)
|
||||
{
|
||||
s64 timestamp_hw_global;
|
||||
s64 eventLSB, eventMSB;
|
||||
@ -27,13 +27,13 @@ static void st_asm330lhh_read_hw_timestamp(struct st_asm330lhh_hw *hw)
|
||||
__le32 tmp;
|
||||
int err;
|
||||
|
||||
err = st_asm330lhh_read_atomic(hw, ST_ASM330LHH_REG_TIMESTAMP0_ADDR,
|
||||
sizeof(timestamp_hw), (u8 *)×tamp_hw);
|
||||
err = st_asm330lhhx_read_atomic(hw, ST_ASM330LHHX_REG_TIMESTAMP0_ADDR,
|
||||
sizeof(timestamp_hw), (u8 *)×tamp_hw);
|
||||
if (err < 0)
|
||||
return;
|
||||
|
||||
timestamp_cpu = st_asm330lhh_get_time_ns(hw->iio_devs[0]) -
|
||||
ST_ASM330LHH_TSYNC_OFFSET_NS;
|
||||
timestamp_cpu = st_asm330lhhx_get_time_ns(hw->iio_devs[0]) -
|
||||
ST_ASM330LHHX_TSYNC_OFFSET_NS;
|
||||
|
||||
eventLSB = IIO_EVENT_CODE(IIO_COUNT, 0, 0, 0,
|
||||
IIO_EV_TYPE_TIME_SYNC, 0, 0, 0);
|
||||
@ -51,58 +51,58 @@ static void st_asm330lhh_read_hw_timestamp(struct st_asm330lhh_hw *hw)
|
||||
tmp = cpu_to_le32((u32)(timestamp_hw_global >> 32));
|
||||
memcpy(&((int8_t *)&eventMSB)[0], &tmp, sizeof(tmp));
|
||||
|
||||
if (hw->enable_mask & BIT(ST_ASM330LHH_ID_GYRO)) {
|
||||
iio_push_event(hw->iio_devs[ST_ASM330LHH_ID_GYRO], eventLSB,
|
||||
if (hw->enable_mask & BIT(ST_ASM330LHHX_ID_GYRO)) {
|
||||
iio_push_event(hw->iio_devs[ST_ASM330LHHX_ID_GYRO], eventLSB,
|
||||
timestamp_cpu);
|
||||
iio_push_event(hw->iio_devs[ST_ASM330LHH_ID_GYRO], eventMSB,
|
||||
iio_push_event(hw->iio_devs[ST_ASM330LHHX_ID_GYRO], eventMSB,
|
||||
timestamp_cpu);
|
||||
}
|
||||
if (hw->enable_mask & BIT(ST_ASM330LHH_ID_ACC)) {
|
||||
iio_push_event(hw->iio_devs[ST_ASM330LHH_ID_ACC], eventLSB,
|
||||
if (hw->enable_mask & BIT(ST_ASM330LHHX_ID_ACC)) {
|
||||
iio_push_event(hw->iio_devs[ST_ASM330LHHX_ID_ACC], eventLSB,
|
||||
timestamp_cpu);
|
||||
iio_push_event(hw->iio_devs[ST_ASM330LHH_ID_ACC], eventMSB,
|
||||
iio_push_event(hw->iio_devs[ST_ASM330LHHX_ID_ACC], eventMSB,
|
||||
timestamp_cpu);
|
||||
}
|
||||
#ifdef CONFIG_IIO_ST_ASM330LHH_EN_TEMPERATURE
|
||||
if (hw->enable_mask & BIT(ST_ASM330LHH_ID_TEMP)) {
|
||||
iio_push_event(hw->iio_devs[ST_ASM330LHH_ID_TEMP], eventLSB,
|
||||
#ifdef CONFIG_IIO_ST_ASM330LHHX_EN_TEMPERATURE
|
||||
if (hw->enable_mask & BIT(ST_ASM330LHHX_ID_TEMP)) {
|
||||
iio_push_event(hw->iio_devs[ST_ASM330LHHX_ID_TEMP], eventLSB,
|
||||
timestamp_cpu);
|
||||
iio_push_event(hw->iio_devs[ST_ASM330LHH_ID_TEMP], eventMSB,
|
||||
iio_push_event(hw->iio_devs[ST_ASM330LHHX_ID_TEMP], eventMSB,
|
||||
timestamp_cpu);
|
||||
}
|
||||
#endif /* CONFIG_IIO_ST_ASM330LHH_EN_TEMPERATURE */
|
||||
#endif /* CONFIG_IIO_ST_ASM330LHHX_EN_TEMPERATURE */
|
||||
|
||||
if (hw->timesync_c < 6)
|
||||
hw->timesync_c++;
|
||||
else
|
||||
hw->timesync_ktime = ktime_set(0, ST_ASM330LHH_DEFAULT_KTIME);
|
||||
hw->timesync_ktime = ktime_set(0, ST_ASM330LHHX_DEFAULT_KTIME);
|
||||
}
|
||||
|
||||
static void st_asm330lhh_timesync_fn(struct work_struct *work)
|
||||
static void st_asm330lhhx_timesync_fn(struct work_struct *work)
|
||||
{
|
||||
struct st_asm330lhh_hw *hw = container_of(work, struct st_asm330lhh_hw,
|
||||
struct st_asm330lhhx_hw *hw = container_of(work, struct st_asm330lhhx_hw,
|
||||
timesync_work);
|
||||
|
||||
st_asm330lhh_read_hw_timestamp(hw);
|
||||
st_asm330lhhx_read_hw_timestamp(hw);
|
||||
}
|
||||
|
||||
static enum hrtimer_restart st_asm330lhh_timer_fn(struct hrtimer *timer)
|
||||
static enum hrtimer_restart st_asm330lhhx_timer_fn(struct hrtimer *timer)
|
||||
{
|
||||
struct st_asm330lhh_hw *hw;
|
||||
struct st_asm330lhhx_hw *hw;
|
||||
|
||||
hw = container_of(timer, struct st_asm330lhh_hw, timesync_timer);
|
||||
hw = container_of(timer, struct st_asm330lhhx_hw, timesync_timer);
|
||||
hrtimer_forward(timer, hrtimer_cb_get_time(timer), hw->timesync_ktime);
|
||||
queue_work(hw->timesync_workqueue, &hw->timesync_work);
|
||||
|
||||
return HRTIMER_RESTART;
|
||||
}
|
||||
|
||||
int st_asm330lhh_hwtimesync_init(struct st_asm330lhh_hw *hw)
|
||||
int st_asm330lhhx_hwtimesync_init(struct st_asm330lhhx_hw *hw)
|
||||
{
|
||||
hw->timesync_c = 0;
|
||||
hw->timesync_ktime = ktime_set(0, ST_ASM330LHH_DEFAULT_KTIME);
|
||||
hw->timesync_ktime = ktime_set(0, ST_ASM330LHHX_DEFAULT_KTIME);
|
||||
hrtimer_init(&hw->timesync_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
|
||||
hw->timesync_timer.function = st_asm330lhh_timer_fn;
|
||||
hw->timesync_timer.function = st_asm330lhhx_timer_fn;
|
||||
|
||||
spin_lock_init(&hw->hwtimestamp_lock);
|
||||
hw->hw_timestamp_global = 0;
|
||||
@ -112,8 +112,8 @@ int st_asm330lhh_hwtimesync_init(struct st_asm330lhh_hw *hw)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
INIT_WORK(&hw->timesync_work, st_asm330lhh_timesync_fn);
|
||||
INIT_WORK(&hw->timesync_work, st_asm330lhhx_timesync_fn);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(st_asm330lhh_hwtimesync_init);
|
||||
EXPORT_SYMBOL(st_asm330lhhx_hwtimesync_init);
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* STMicroelectronics st_asm330lhh i2c driver
|
||||
* STMicroelectronics st_asm330lhhx i2c driver
|
||||
*
|
||||
* Copyright 2019 STMicroelectronics Inc.
|
||||
*
|
||||
@ -15,9 +15,9 @@
|
||||
#include <linux/slab.h>
|
||||
#include <linux/of.h>
|
||||
|
||||
#include "st_asm330lhh.h"
|
||||
#include "st_asm330lhhx.h"
|
||||
|
||||
static int st_asm330lhh_i2c_read(struct device *dev, u8 addr, int len, u8 *data)
|
||||
static int st_asm330lhhx_i2c_read(struct device *dev, u8 addr, int len, u8 *data)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct i2c_msg msg[2];
|
||||
@ -35,7 +35,7 @@ static int st_asm330lhh_i2c_read(struct device *dev, u8 addr, int len, u8 *data)
|
||||
return i2c_transfer(client->adapter, msg, 2);
|
||||
}
|
||||
|
||||
static int st_asm330lhh_i2c_write(struct device *dev, u8 addr, int len,
|
||||
static int st_asm330lhhx_i2c_write(struct device *dev, u8 addr, int len,
|
||||
const u8 *data)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
@ -53,45 +53,49 @@ static int st_asm330lhh_i2c_write(struct device *dev, u8 addr, int len,
|
||||
return i2c_transfer(client->adapter, &msg, 1);
|
||||
}
|
||||
|
||||
static const struct st_asm330lhh_transfer_function st_asm330lhh_transfer_fn = {
|
||||
.read = st_asm330lhh_i2c_read,
|
||||
.write = st_asm330lhh_i2c_write,
|
||||
static const struct st_asm330lhhx_transfer_function st_asm330lhhx_transfer_fn = {
|
||||
.read = st_asm330lhhx_i2c_read,
|
||||
.write = st_asm330lhhx_i2c_write,
|
||||
};
|
||||
|
||||
static int st_asm330lhh_i2c_probe(struct i2c_client *client,
|
||||
static int st_asm330lhhx_i2c_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
return st_asm330lhh_probe(&client->dev, client->irq,
|
||||
&st_asm330lhh_transfer_fn);
|
||||
return st_asm330lhhx_probe(&client->dev, client->irq,
|
||||
&st_asm330lhhx_transfer_fn);
|
||||
}
|
||||
|
||||
static const struct of_device_id st_asm330lhh_i2c_of_match[] = {
|
||||
static const struct of_device_id st_asm330lhhx_i2c_of_match[] = {
|
||||
{
|
||||
.compatible = "st,asm330lhhx",
|
||||
},
|
||||
{
|
||||
.compatible = "st,asm330lhh",
|
||||
},
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, st_asm330lhh_i2c_of_match);
|
||||
MODULE_DEVICE_TABLE(of, st_asm330lhhx_i2c_of_match);
|
||||
|
||||
static const struct i2c_device_id st_asm330lhh_i2c_id_table[] = {
|
||||
static const struct i2c_device_id st_asm330lhhx_i2c_id_table[] = {
|
||||
{ ST_ASM330LHH_DEV_NAME },
|
||||
{ ST_ASM330LHHX_DEV_NAME },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, st_asm330lhh_i2c_id_table);
|
||||
MODULE_DEVICE_TABLE(i2c, st_asm330lhhx_i2c_id_table);
|
||||
|
||||
static struct i2c_driver st_asm330lhh_driver = {
|
||||
static struct i2c_driver st_asm330lhhx_driver = {
|
||||
.driver = {
|
||||
.name = "st_asm330lhh_i2c",
|
||||
.pm = &st_asm330lhh_pm_ops,
|
||||
.of_match_table = st_asm330lhh_i2c_of_match,
|
||||
.name = "st_asm330lhhx_i2c",
|
||||
.pm = &st_asm330lhhx_pm_ops,
|
||||
.of_match_table = st_asm330lhhx_i2c_of_match,
|
||||
},
|
||||
.probe = st_asm330lhh_i2c_probe,
|
||||
.id_table = st_asm330lhh_i2c_id_table,
|
||||
.probe = st_asm330lhhx_i2c_probe,
|
||||
.id_table = st_asm330lhhx_i2c_id_table,
|
||||
};
|
||||
module_i2c_driver(st_asm330lhh_driver);
|
||||
module_i2c_driver(st_asm330lhhx_driver);
|
||||
|
||||
MODULE_AUTHOR("Lorenzo Bianconi <lorenzo.bianconi@st.com>");
|
||||
MODULE_AUTHOR("Mario Tesi <mario.tesi@st.com>");
|
||||
MODULE_DESCRIPTION("STMicroelectronics st_asm330lhh i2c driver");
|
||||
MODULE_DESCRIPTION("STMicroelectronics st_asm330lhhx i2c driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_VERSION(ST_ASM330LHH_DRV_VERSION);
|
||||
MODULE_VERSION(ST_ASM330LHHX_DRV_VERSION);
|
115
drivers/iio/stm/imu/st_asm330lhhx/st_asm330lhhx_spi.c
Normal file
115
drivers/iio/stm/imu/st_asm330lhhx/st_asm330lhhx_spi.c
Normal file
@ -0,0 +1,115 @@
|
||||
/*
|
||||
* STMicroelectronics st_asm330lhhx spi driver
|
||||
*
|
||||
* Copyright 2019 STMicroelectronics Inc.
|
||||
*
|
||||
* Lorenzo Bianconi <lorenzo.bianconi@st.com>
|
||||
* Mario Tesi <mario.tesi@st.com>
|
||||
*
|
||||
* Licensed under the GPL-2.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/of.h>
|
||||
|
||||
#include "st_asm330lhhx.h"
|
||||
|
||||
#define SENSORS_SPI_READ BIT(7)
|
||||
|
||||
static int st_asm330lhhx_spi_read(struct device *dev, u8 addr, int len,
|
||||
u8 *data)
|
||||
{
|
||||
struct spi_device *spi = to_spi_device(dev);
|
||||
struct st_asm330lhhx_hw *hw = spi_get_drvdata(spi);
|
||||
int err;
|
||||
|
||||
struct spi_transfer xfers[] = {
|
||||
{
|
||||
.tx_buf = hw->tb.tx_buf,
|
||||
.bits_per_word = 8,
|
||||
.len = 1,
|
||||
},
|
||||
{
|
||||
.rx_buf = hw->tb.rx_buf,
|
||||
.bits_per_word = 8,
|
||||
.len = len,
|
||||
}
|
||||
};
|
||||
|
||||
hw->tb.tx_buf[0] = addr | SENSORS_SPI_READ;
|
||||
|
||||
err = spi_sync_transfer(spi, xfers, ARRAY_SIZE(xfers));
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
memcpy(data, hw->tb.rx_buf, len * sizeof(u8));
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static int st_asm330lhhx_spi_write(struct device *dev, u8 addr, int len,
|
||||
const u8 *data)
|
||||
{
|
||||
struct st_asm330lhhx_hw *hw;
|
||||
struct spi_device *spi;
|
||||
|
||||
if (len >= ST_ASM330LHHX_TX_MAX_LENGTH)
|
||||
return -ENOMEM;
|
||||
|
||||
spi = to_spi_device(dev);
|
||||
hw = spi_get_drvdata(spi);
|
||||
|
||||
hw->tb.tx_buf[0] = addr;
|
||||
memcpy(&hw->tb.tx_buf[1], data, len);
|
||||
|
||||
return spi_write(spi, hw->tb.tx_buf, len + 1);
|
||||
}
|
||||
|
||||
static const struct st_asm330lhhx_transfer_function st_asm330lhhx_transfer_fn = {
|
||||
.read = st_asm330lhhx_spi_read,
|
||||
.write = st_asm330lhhx_spi_write,
|
||||
};
|
||||
|
||||
static int st_asm330lhhx_spi_probe(struct spi_device *spi)
|
||||
{
|
||||
return st_asm330lhhx_probe(&spi->dev, spi->irq,
|
||||
&st_asm330lhhx_transfer_fn);
|
||||
}
|
||||
|
||||
static const struct of_device_id st_asm330lhhx_spi_of_match[] = {
|
||||
{
|
||||
.compatible = "st,asm330lhhx",
|
||||
},
|
||||
{
|
||||
.compatible = "st,asm330lhh",
|
||||
},
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, st_asm330lhhx_spi_of_match);
|
||||
|
||||
static const struct spi_device_id st_asm330lhhx_spi_id_table[] = {
|
||||
{ ST_ASM330LHH_DEV_NAME },
|
||||
{ ST_ASM330LHHX_DEV_NAME },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, st_asm330lhhx_spi_id_table);
|
||||
|
||||
static struct spi_driver st_asm330lhhx_driver = {
|
||||
.driver = {
|
||||
.name = "st_asm330lhhx_spi",
|
||||
.pm = &st_asm330lhhx_pm_ops,
|
||||
.of_match_table = st_asm330lhhx_spi_of_match,
|
||||
},
|
||||
.probe = st_asm330lhhx_spi_probe,
|
||||
.id_table = st_asm330lhhx_spi_id_table,
|
||||
};
|
||||
module_spi_driver(st_asm330lhhx_driver);
|
||||
|
||||
MODULE_AUTHOR("Lorenzo Bianconi <lorenzo.bianconi@st.com>");
|
||||
MODULE_AUTHOR("Mario Tesi <mario.tesi@st.com>");
|
||||
MODULE_DESCRIPTION("STMicroelectronics st_asm330lhhx spi driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_VERSION(ST_ASM330LHHX_DRV_VERSION);
|
@ -1,6 +0,0 @@
|
||||
CONFIG_IIO_ST_ASM330LHH=m
|
||||
CONFIG_IIO_ST_ASM330LHH_I2C=m
|
||||
CONFIG_IIO_ST_ASM330LHH_SPI=m
|
||||
CONFIG_IIO_ST_ASM330LHH_EN_TEMPERATURE=y
|
||||
CONFIG_IIO_ST_ASM330LHH_EN_BASIC_FEATURES=y
|
||||
CONFIG_IIO_ST_ASM330LHH_MAY_WAKEUP=y
|
6
stm_iio_configs/asm330lhhx_defconfig
Normal file
6
stm_iio_configs/asm330lhhx_defconfig
Normal file
@ -0,0 +1,6 @@
|
||||
CONFIG_IIO_ST_ASM330LHHX=m
|
||||
CONFIG_IIO_ST_ASM330LHHX_I2C=m
|
||||
CONFIG_IIO_ST_ASM330LHHX_SPI=m
|
||||
CONFIG_IIO_ST_ASM330LHHX_EN_TEMPERATURE=y
|
||||
CONFIG_IIO_ST_ASM330LHHX_EN_BASIC_FEATURES=y
|
||||
CONFIG_IIO_ST_ASM330LHHX_MAY_WAKEUP=y
|
Loading…
Reference in New Issue
Block a user