drivers:iio:stm:imu: add support to lsm6dsv16bx STMEMS IMU sensor
Added support for the imu lsm6dsv16bx sensor. The current driver version supports the following features: - Acc and Gyro in continuous mode supported by the internal HW FIFO - QVAR sensor for presence and touch detection, data of QVAR are managed internally by HW FIFO. - SFLP Embedded Sensor Fusion Function Game Rotation vector sensor. - TDM slave interface configuration for accelerometer data. - MLC / FSM programmable functions. - Free fall, step counter, step detection, wake-up, significant motion, tilt, tap, double tap, 6D and sleep change embedded functions event detection. Signed-off-by: Mario Tesi <mario.tesi@st.com> Change-Id: I1f2ad688261cba90b5c3633ccd9fc6fb0301d771
This commit is contained in:
parent
115ea4079c
commit
876ab6c015
@ -0,0 +1,95 @@
|
||||
* st_lsm6dsvx driver for imu MEMS sensors
|
||||
|
||||
Required properties for all bus drivers:
|
||||
- compatible: must be "st,lsm6dsv16bx"
|
||||
|
||||
Required properties for the i2c bindings:
|
||||
- reg: i2c slave address
|
||||
|
||||
Required properties for the spi bindings:
|
||||
- reg: the chipselect index
|
||||
|
||||
- spi-max-frequency: maximal bus speed, should be set to 1000000 unless
|
||||
constrained by external circuitry
|
||||
|
||||
Optional properties for all bus drivers:
|
||||
- vdd-supply: an optional regulator that needs to be on to provide VDD
|
||||
power to the sensor.
|
||||
|
||||
- vddio-supply: an optional regulator that needs to be on to provide the
|
||||
VDD IO power to the sensor.
|
||||
|
||||
- st,int-pin: the pin on the package that will be used to signal
|
||||
"data ready" (valid values: 1 or 2, default: 1).
|
||||
|
||||
- mount-matrix: mount rotation matrix.
|
||||
|
||||
Refer to iio/mount-matrix.txt for details.
|
||||
|
||||
- drive-open-drain: the interrupt/data ready line will be configured as open drain,
|
||||
which is useful if several sensors share the same interrupt line.
|
||||
|
||||
- interrupts: interrupt mapping for IRQ. It should be configured with
|
||||
flags IRQ_TYPE_LEVEL_HIGH, IRQ_TYPE_EDGE_RISING,
|
||||
IRQ_TYPE_LEVEL_LOW or IRQ_TYPE_EDGE_FALLING.
|
||||
|
||||
Refer to interrupt-controller/interrupts.txt for generic interrupt
|
||||
client node bindings.
|
||||
|
||||
- enable-qvar: enable QVAR sensor feature. Default is disabled.
|
||||
|
||||
- st,module_id: module identifier.
|
||||
This is used by user-space to identify which devices
|
||||
are part of the same module (particularly important for
|
||||
supporting multiple sensors of the same type).
|
||||
|
||||
- tdm_wclk: TDM word clock frequency selection:
|
||||
- 0 8 kHz 2048 kHz (default)
|
||||
- 1 16 kHz 2048 kHz
|
||||
|
||||
- tdm_slot_sel: Selection of TDM slot for transmission. Default value: 0
|
||||
- 0: TDM slot {0, 1, 2} (default)
|
||||
- 1: TDM slot {4, 5, 6}
|
||||
|
||||
- tdm_ord_sel: Selects the order of transmission of the TDM axes
|
||||
- 0 -> Z, Y, X
|
||||
- 1 -> X, Z, Y
|
||||
- 2 -> X, Y, Z (default)
|
||||
|
||||
- tdm_fs_xl: TDM channel accelerometer full-scale selection:
|
||||
- 0: ±2 g
|
||||
- 1: ±4 g (default)
|
||||
- 2: ±8 g
|
||||
|
||||
Example for an spi device node:
|
||||
|
||||
lsm6dsvx-imu@0 {
|
||||
compatible = "st,lsm6dsv16x";
|
||||
reg = <0x0>;
|
||||
spi-max-frequency = <1000000>;
|
||||
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";
|
||||
st,module_id = <2>;
|
||||
};
|
||||
|
||||
Example for an i2c device node (SA0 connected to ground):
|
||||
|
||||
lsm6dsvx-imu@6a {
|
||||
compatible = "st,lsm6dsv16x";
|
||||
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";
|
||||
st,module_id = <1>;
|
||||
};
|
@ -17,5 +17,6 @@ source "drivers/iio/stm/imu/st_lsm6ds3h/Kconfig"
|
||||
source "drivers/iio/stm/imu/st_lsm6dsm/Kconfig"
|
||||
source "drivers/iio/stm/imu/st_lsm6dsvx/Kconfig"
|
||||
source "drivers/iio/stm/imu/st_lsm6dso16is/Kconfig"
|
||||
source "drivers/iio/stm/imu/st_lsm6dsv16bx/Kconfig"
|
||||
|
||||
endmenu
|
||||
|
@ -15,3 +15,4 @@ obj-y += st_lsm6ds3h/
|
||||
obj-y += st_lsm6dsm/
|
||||
obj-y += st_lsm6dsvx/
|
||||
obj-y += st_lsm6dso16is/
|
||||
obj-y += st_lsm6dsv16bx/
|
||||
|
45
drivers/iio/stm/imu/st_lsm6dsv16bx/Kconfig
Normal file
45
drivers/iio/stm/imu/st_lsm6dsv16bx/Kconfig
Normal file
@ -0,0 +1,45 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
config IIO_ST_LSM6DSV16BX
|
||||
tristate "STMicroelectronics LSM6DSV16BX sensor"
|
||||
depends on (I2C || SPI)
|
||||
select IIO_BUFFER
|
||||
select IIO_TRIGGERED_BUFFER
|
||||
select IIO_ST_LSM6DSV16BX_I2C if (I2C)
|
||||
select IIO_ST_LSM6DSV16BX_SPI if (SPI_MASTER)
|
||||
select IIO_ST_LSM6DSV16BX_I3C if (I3C)
|
||||
help
|
||||
Say yes here to build support for STMicroelectronics LSM6DSV16BX imu
|
||||
sensor.
|
||||
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called st_lsm6dsv16bx.
|
||||
|
||||
config IIO_ST_LSM6DSV16BX_I2C
|
||||
tristate
|
||||
depends on IIO_ST_LSM6DSV16BX
|
||||
|
||||
config IIO_ST_LSM6DSV16BX_SPI
|
||||
tristate
|
||||
depends on IIO_ST_LSM6DSV16BX
|
||||
|
||||
config IIO_ST_LSM6DSV16BX_I3C
|
||||
tristate
|
||||
depends on IIO_ST_LSM6DSV16BX
|
||||
select REGMAP_I3C
|
||||
|
||||
config IIO_ST_LSM6DSV16BX_MLC_PRELOAD
|
||||
bool "Preload some examples on MLC/FSM core"
|
||||
depends on IIO_ST_LSM6DSV16BX
|
||||
help
|
||||
Select yes if you want to preload some examples on machine learning core
|
||||
and finite state machine.
|
||||
|
||||
The examples code is a motion intensity recognition and is hardcoded in the
|
||||
driver in the mlcdata structure.
|
||||
|
||||
config IIO_ST_LSM6DSV16BX_ASYNC_HW_TIMESTAMP
|
||||
bool "Enable async hw timestamp read"
|
||||
depends on IIO_ST_LSM6DSV16BX
|
||||
help
|
||||
Enable async task that sends over hw timestamp events.
|
11
drivers/iio/stm/imu/st_lsm6dsv16bx/Makefile
Normal file
11
drivers/iio/stm/imu/st_lsm6dsv16bx/Makefile
Normal file
@ -0,0 +1,11 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
st_lsm6dsv16bx-y := st_lsm6dsv16bx_core.o st_lsm6dsv16bx_buffer.o \
|
||||
st_lsm6dsv16bx_qvar.o st_lsm6dsv16bx_mlc.o \
|
||||
st_lsm6dsv16bx_events.o st_lsm6dsv16bx_embfunc.o
|
||||
|
||||
st_lsm6dsv16bx-$(CONFIG_IIO_ST_LSM6DSV16BX_ASYNC_HW_TIMESTAMP) += st_lsm6dsv16bx_hwtimestamp.o
|
||||
|
||||
obj-$(CONFIG_IIO_ST_LSM6DSV16BX) += st_lsm6dsv16bx.o
|
||||
obj-$(CONFIG_IIO_ST_LSM6DSV16BX_I2C) += st_lsm6dsv16bx_i2c.o
|
||||
obj-$(CONFIG_IIO_ST_LSM6DSV16BX_SPI) += st_lsm6dsv16bx_spi.o
|
||||
obj-$(CONFIG_IIO_ST_LSM6DSV16BX_I3C) += st_lsm6dsv16bx_i3c.o
|
991
drivers/iio/stm/imu/st_lsm6dsv16bx/st_lsm6dsv16bx.h
Normal file
991
drivers/iio/stm/imu/st_lsm6dsv16bx/st_lsm6dsv16bx.h
Normal file
@ -0,0 +1,991 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* STMicroelectronics st_lsm6dsv16bx sensor driver
|
||||
*
|
||||
* MEMS Software Solutions Team
|
||||
*
|
||||
* Copyright 2023 STMicroelectronics Inc.
|
||||
*/
|
||||
|
||||
#ifndef ST_LSM6DSV16BX_H
|
||||
#define ST_LSM6DSV16BX_H
|
||||
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/hrtimer.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/workqueue.h>
|
||||
|
||||
#include "../../common/stm_iio_types.h"
|
||||
|
||||
#define ST_LSM6DSV16BX_ODR_LIST_SIZE 9
|
||||
#define ST_LSM6DSV16BX_ODR_EXPAND(odr, uodr) ((odr * 1000000) + uodr)
|
||||
|
||||
#define ST_LSM6DSV16BX_DEV_NAME "lsm6dsv16bx"
|
||||
|
||||
#define ST_LSM6DSV16BX_SAMPLE_SIZE 6
|
||||
#define ST_LSM6DSV16BX_TS_SAMPLE_SIZE 4
|
||||
#define ST_LSM6DSV16BX_TAG_SIZE 1
|
||||
#define ST_LSM6DSV16BX_FIFO_SAMPLE_SIZE (ST_LSM6DSV16BX_SAMPLE_SIZE + \
|
||||
ST_LSM6DSV16BX_TAG_SIZE)
|
||||
#define ST_LSM6DSV16BX_MAX_FIFO_DEPTH 208
|
||||
|
||||
/* register map */
|
||||
#define ST_LSM6DSV16BX_REG_FUNC_CFG_ACCESS_ADDR 0x01
|
||||
#define ST_LSM6DSV16BX_EMB_FUNC_REG_ACCESS_MASK BIT(7)
|
||||
|
||||
#define ST_LSM6DSV16BX_REG_IF_CFG_ADDR 0x03
|
||||
#define ST_LSM6DSV16BX_PP_OD_MASK BIT(3)
|
||||
#define ST_LSM6DSV16BX_H_LACTIVE_MASK BIT(4)
|
||||
#define ST_LSM6DSV16BX_TDM_OUT_PU_EN_MASK BIT(6)
|
||||
|
||||
#define ST_LSM6DSV16BX_REG_FIFO_CTRL1_ADDR 0x07
|
||||
#define ST_LSM6DSV16BX_WTM_MASK GENMASK(7, 0)
|
||||
|
||||
#define ST_LSM6DSV16BX_REG_FIFO_CTRL3_ADDR 0x09
|
||||
#define ST_LSM6DSV16BX_BDR_XL_MASK GENMASK(3, 0)
|
||||
#define ST_LSM6DSV16BX_BDR_GY_MASK GENMASK(7, 4)
|
||||
|
||||
#define ST_LSM6DSV16BX_REG_FIFO_CTRL4_ADDR 0x0a
|
||||
#define ST_LSM6DSV16BX_FIFO_MODE_MASK GENMASK(2, 0)
|
||||
#define ST_LSM6DSV16BX_ODR_T_BATCH_MASK GENMASK(5, 4)
|
||||
#define ST_LSM6DSV16BX_DEC_TS_BATCH_MASK GENMASK(7, 6)
|
||||
|
||||
#define ST_LSM6DSV16BX_COUNTER_BDR_REG1_ADDR 0x0b
|
||||
#define ST_LSM6DSV16BX_AH_QVAR_BATCH_EN_MASK BIT(2)
|
||||
|
||||
#define ST_LSM6DSV16BX_REG_INT1_CTRL_ADDR 0x0d
|
||||
#define ST_LSM6DSV16BX_REG_INT2_CTRL_ADDR 0x0e
|
||||
#define ST_LSM6DSV16BX_INT_FIFO_TH_MASK BIT(3)
|
||||
|
||||
#define ST_LSM6DSV16BX_REG_WHOAMI_ADDR 0x0f
|
||||
#define ST_LSM6DSV16BX_WHOAMI_VAL 0x71
|
||||
|
||||
#define ST_LSM6DSV16BX_REG_CTRL1_ADDR 0x10
|
||||
#define ST_LSM6DSV16BX_REG_CTRL2_ADDR 0x11
|
||||
#define ST_LSM6DSV16BX_ODR_MASK GENMASK(3, 0)
|
||||
#define ST_LSM6DSV16BX_OP_MODE_MASK GENMASK(6, 4)
|
||||
|
||||
#define ST_LSM6DSV16BX_REG_CTRL3_ADDR 0x12
|
||||
#define ST_LSM6DSV16BX_SW_RESET_MASK BIT(0)
|
||||
#define ST_LSM6DSV16BX_BDU_MASK BIT(6)
|
||||
#define ST_LSM6DSV16BX_BOOT_MASK BIT(7)
|
||||
|
||||
#define ST_LSM6DSV16BX_REG_CTRL4_ADDR 0x13
|
||||
#define ST_LSM6DSV16BX_DRDY_MASK BIT(3)
|
||||
|
||||
#define ST_LSM6DSV16BX_REG_CTRL6_ADDR 0x15
|
||||
|
||||
#define ST_LSM6DSV16BX_REG_CTRL7_ADDR 0x16
|
||||
#define ST_LSM6DSV16BX_AH_QVARx_EN_MASK GENMASK(3, 2)
|
||||
#define ST_LSM6DSV16BX_AH_QVAR_C_ZIN_MASK GENMASK(5, 4)
|
||||
#define ST_LSM6DSV16BX_AH_QVAR_EN_MASK BIT(7)
|
||||
|
||||
#define ST_LSM6DSV16BX_REG_CTRL8_ADDR 0x17
|
||||
|
||||
#define ST_LSM6DSV16BX_REG_CTRL10_ADDR 0x19
|
||||
#define ST_LSM6DSV16BX_ST_XL_MASK GENMASK(1, 0)
|
||||
#define ST_LSM6DSV16BX_ST_G_MASK GENMASK(3, 2)
|
||||
|
||||
#define ST_LSM6DSV16BX_REG_FIFO_STATUS1_ADDR 0x1b
|
||||
#define ST_LSM6DSV16BX_FIFO_DIFF_MASK GENMASK(8, 0)
|
||||
|
||||
#define ST_LSM6DSV16BX_REG_ALL_INT_SRC_ADDR 0x1d
|
||||
#define ST_LSM6DSV16BX_FF_IA_MASK BIT(0)
|
||||
#define ST_LSM6DSV16BX_WU_IA_MASK BIT(1)
|
||||
#define ST_LSM6DSV16BX_TAP_IA_MASK BIT(2)
|
||||
#define ST_LSM6DSV16BX_D6D_IA_MASK BIT(4)
|
||||
#define ST_LSM6DSV16BX_SLEEP_CHANGE_MASK BIT(5)
|
||||
|
||||
#define ST_LSM6DSV16BX_REG_D6D_SRC_ADDR 0x1d
|
||||
#define ST_LSM6DSV16BX_D6D_EVENT_MASK GENMASK(5, 0)
|
||||
|
||||
#define ST_LSM6DSV16BX_REG_STATUS_REG_ADDR 0x1e
|
||||
#define ST_LSM6DSV16BX_XLDA_MASK BIT(0)
|
||||
#define ST_LSM6DSV16BX_GDA_MASK BIT(1)
|
||||
#define ST_LSM6DSV16BX_TDA_MASK BIT(2)
|
||||
|
||||
#define ST_LSM6DSV16BX_REG_OUT_TEMP_L_ADDR 0x20
|
||||
|
||||
#define ST_LSM6DSV16BX_REG_OUTZ_L_G_ADDR 0x22
|
||||
#define ST_LSM6DSV16BX_REG_OUTY_L_G_ADDR 0x24
|
||||
#define ST_LSM6DSV16BX_REG_OUTX_L_G_ADDR 0x26
|
||||
#define ST_LSM6DSV16BX_REG_OUTZ_L_A_ADDR 0x28
|
||||
#define ST_LSM6DSV16BX_REG_OUTY_L_A_ADDR 0x2a
|
||||
#define ST_LSM6DSV16BX_REG_OUTX_L_A_ADDR 0x2c
|
||||
|
||||
#define ST_LSM6DSV16BX_REG_OUT_QVAR_ADDR 0x3a
|
||||
|
||||
#define ST_LSM6DSV16BX_REG_TIMESTAMP0_ADDR 0x40
|
||||
#define ST_LSM6DSV16BX_REG_TIMESTAMP2_ADDR 0x42
|
||||
|
||||
#define ST_LSM6DSV16BX_REG_WAKE_UP_SRC_ADDR 0x45
|
||||
#define ST_LSM6DSV16BX_WAKE_UP_EVENT_MASK GENMASK(3, 0)
|
||||
|
||||
#define ST_LSM6DSV16BX_REG_EMB_FUNC_STATUS_MAINPAGE_ADDR 0x49
|
||||
#define ST_LSM6DSV16BX_IS_STEP_DET_MASK BIT(3)
|
||||
#define ST_LSM6DSV16BX_IS_TILT_MASK BIT(4)
|
||||
#define ST_LSM6DSV16BX_IS_SIGMOT_MASK BIT(5)
|
||||
|
||||
#define ST_LSM6DSV16BX_REG_FSM_STATUS_MAINPAGE_ADDR 0x4a
|
||||
#define ST_LSM6DSV16BX_REG_MLC_STATUS_MAINPAGE_ADDR 0x4b
|
||||
|
||||
#define ST_LSM6DSV16BX_REG_INTERNAL_FREQ_FINE 0x4f
|
||||
|
||||
#define ST_LSM6DSV16BX_REG_FUNCTIONS_ENABLE_ADDR 0x50
|
||||
#define ST_LSM6DSV16BX_TIMESTAMP_EN_MASK BIT(6)
|
||||
#define ST_LSM6DSV16BX_INTERRUPTS_ENABLE_MASK BIT(7)
|
||||
|
||||
#define ST_LSM6DSV16BX_REG_TAP_CFG0_ADDR 0x56
|
||||
#define ST_LSM6DSV16BX_LIR_MASK BIT(0)
|
||||
#define ST_LSM6DSV16BX_REG_TAP_Z_EN_MASK BIT(1)
|
||||
#define ST_LSM6DSV16BX_REG_TAP_Y_EN_MASK BIT(2)
|
||||
#define ST_LSM6DSV16BX_REG_TAP_X_EN_MASK BIT(3)
|
||||
#define ST_LSM6DSV16BX_REG_TAP_EN_MASK GENMASK(3, 1)
|
||||
|
||||
#define ST_LSM6DSV16BX_REG_TAP_CFG1_ADDR 0x57
|
||||
#define ST_LSM6DSV16BX_TAP_THS_X_MASK GENMASK(4, 0)
|
||||
#define ST_LSM6DSV16BX_TAP_PRIORITY_MASK GENMASK(7, 5)
|
||||
|
||||
#define ST_LSM6DSV16BX_REG_TAP_CFG2_ADDR 0x58
|
||||
#define ST_LSM6DSV16BX_TAP_THS_Y_MASK GENMASK(4, 0)
|
||||
|
||||
#define ST_LSM6DSV16BX_REG_TAP_THS_6D_ADDR 0x59
|
||||
#define ST_LSM6DSV16BX_TAP_THS_Z_MASK GENMASK(4, 0)
|
||||
#define ST_LSM6DSV16BX_SIXD_THS_MASK GENMASK(6, 5)
|
||||
|
||||
#define ST_LSM6DSV16BX_REG_TAP_DUR_ADDR 0x5a
|
||||
#define ST_LSM6DSV16BX_SHOCK_MASK GENMASK(1, 0)
|
||||
#define ST_LSM6DSV16BX_QUIET_MASK GENMASK(3, 2)
|
||||
#define ST_LSM6DSV16BX_DUR_MASK GENMASK(7, 4)
|
||||
|
||||
#define ST_LSM6DSV16BX_REG_WAKE_UP_THS_ADDR 0x5b
|
||||
#define ST_LSM6DSV16BX_WK_THS_MASK GENMASK(5, 0)
|
||||
#define ST_LSM6DSV16BX_SINGLE_DOUBLE_TAP_MASK BIT(7)
|
||||
|
||||
#define ST_LSM6DSV16BX_REG_WAKE_UP_DUR_ADDR 0x5c
|
||||
#define ST_LSM6DSV16BX_WAKE_DUR_MASK GENMASK(6, 5)
|
||||
|
||||
#define ST_LSM6DSV16BX_REG_FREE_FALL_ADDR 0x5d
|
||||
#define ST_LSM6DSV16BX_FF_THS_MASK GENMASK(2, 0)
|
||||
|
||||
#define ST_LSM6DSV16BX_REG_MD1_CFG_ADDR 0x5e
|
||||
#define ST_LSM6DSV16BX_REG_MD2_CFG_ADDR 0x5f
|
||||
#define ST_LSM6DSV16BX_REG_INT2_TIMESTAMP_MASK BIT(0)
|
||||
#define ST_LSM6DSV16BX_REG_INT_EMB_FUNC_MASK BIT(1)
|
||||
#define ST_LSM6DSV16BX_INT_6D_MASK BIT(2)
|
||||
#define ST_LSM6DSV16BX_INT_DOUBLE_TAP_MASK BIT(3)
|
||||
#define ST_LSM6DSV16BX_INT_FF_MASK BIT(4)
|
||||
#define ST_LSM6DSV16BX_INT_WU_MASK BIT(5)
|
||||
#define ST_LSM6DSV16BX_INT_SINGLE_TAP_MASK BIT(6)
|
||||
#define ST_LSM6DSV16BX_INT_SLEEP_CHANGE_MASK BIT(7)
|
||||
|
||||
#define ST_LSM6DSV16BX_REG_TDM_CFG0_ADDR 0x6c
|
||||
#define ST_LSM6DSV16BX_REG_TDM_SLOT_SEL_MASK BIT(4)
|
||||
#define ST_LSM6DSV16BX_REG_TDM_WCLK_MASK GENMASK(2, 1)
|
||||
#define ST_LSM6DSV16BX_REG_TDM_WCLK_BCLK_SEL_MASK BIT(0)
|
||||
|
||||
#define ST_LSM6DSV16BX_REG_TDM_CFG1_ADDR 0x6d
|
||||
#define ST_LSM6DSV16BX_REG_TDM_AXES_ORD_SEL_MASK GENMASK(4, 3)
|
||||
|
||||
#define ST_LSM6DSV16BX_REG_TDM_CFG2_ADDR 0x6e
|
||||
#define ST_LSM6DSV16BX_REG_TDM_FS_XL_MASK GENMASK(1, 0)
|
||||
|
||||
#define ST_LSM6DSV16BX_REG_FIFO_DATA_OUT_TAG_ADDR 0x78
|
||||
|
||||
/* embedded function registers */
|
||||
#define ST_LSM6DSV16BX_REG_PAGE_SEL_ADDR 0x02
|
||||
#define ST_LSM6DSV16BX_PAGE_SEL_MASK GENMASK(6, 4)
|
||||
|
||||
#define ST_LSM6DSV16BX_REG_EMB_FUNC_EN_A_ADDR 0x04
|
||||
#define ST_LSM6DSV16BX_SFLP_GAME_EN_MASK BIT(1)
|
||||
#define ST_LSM6DSV16BX_REG_PEDO_EN_MASK BIT(3)
|
||||
#define ST_LSM6DSV16BX_REG_TILT_EN_MASK BIT(4)
|
||||
#define ST_LSM6DSV16BX_REG_SIGN_MOTION_EN_MASK BIT(5)
|
||||
|
||||
#define ST_LSM6DSV16BX_REG_EMB_FUNC_EN_B_ADDR 0x05
|
||||
#define ST_LSM6DSV16BX_FSM_EN_MASK BIT(0)
|
||||
#define ST_LSM6DSV16BX_MLC_EN_MASK BIT(4)
|
||||
|
||||
#define ST_LSM6DSV16BX_REG_PAGE_ADDRESS_ADDR 0x08
|
||||
#define ST_LSM6DSV16BX_REG_PAGE_VALUE_ADDR 0x09
|
||||
|
||||
#define ST_LSM6DSV16BX_REG_EMB_FUNC_INT1_ADDR 0x0a
|
||||
#define ST_LSM6DSV16BX_INT_STEP_DETECTOR_MASK BIT(3)
|
||||
#define ST_LSM6DSV16BX_INT_TILT_MASK BIT(4)
|
||||
#define ST_LSM6DSV16BX_INT_SIG_MOT_MASK BIT(5)
|
||||
|
||||
#define ST_LSM6DSV16BX_REG_FSM_INT1_ADDR 0x0b
|
||||
#define ST_LSM6DSV16BX_REG_MLC_INT1_ADDR 0x0d
|
||||
#define ST_LSM6DSV16BX_REG_EMB_FUNC_INT2_ADDR 0x0e
|
||||
#define ST_LSM6DSV16BX_REG_FSM_INT2_ADDR 0x0f
|
||||
#define ST_LSM6DSV16BX_REG_MLC_INT2_ADDR 0x11
|
||||
|
||||
#define ST_LSM6DSV16BX_REG_FSM_STATUS_ADDR 0x13
|
||||
#define ST_LSM6DSV16BX_REG_MLC_STATUS_ADDR 0x15
|
||||
|
||||
#define ST_LSM6DSV16BX_REG_PAGE_RW_ADDR 0x17
|
||||
#define ST_LSM6DSV16BX_PAGE_READ_MASK BIT(5)
|
||||
#define ST_LSM6DSV16BX_PAGE_WRITE_MASK BIT(6)
|
||||
#define ST_LSM6DSV16BX_EMB_FUNC_LIR_MASK BIT(7)
|
||||
|
||||
#define ST_LSM6DSV16BX_REG_EMB_FUNC_FIFO_EN_A_ADDR 0x44
|
||||
#define ST_LSM6DSV16BX_SFLP_GAME_FIFO_EN BIT(1)
|
||||
#define ST_LSM6DSV16BX_SFLP_GRAVITY_FIFO_EN BIT(4)
|
||||
#define ST_LSM6DSV16BX_SFLP_GBIAS_FIFO_EN_MASK BIT(5)
|
||||
#define ST_LSM6DSV16BX_STEP_COUNTER_FIFO_EN_MASK BIT(6)
|
||||
|
||||
#define ST_LSM6DSV16BX_REG_FSM_ENABLE_ADDR 0x46
|
||||
|
||||
#define ST_LSM6DSV16BX_REG_FSM_OUTS1_ADDR 0x4c
|
||||
|
||||
#define ST_LSM6DSV16BX_REG_SFLP_ODR_ADDR 0x5e
|
||||
#define ST_LSM6DSV16BX_SFLP_GAME_ODR_MASK GENMASK(5, 3)
|
||||
|
||||
#define ST_LSM6DSV16BX_REG_FSM_ODR_ADDR 0x5f
|
||||
#define ST_LSM6DSV16BX_FSM_ODR_MASK GENMASK(5, 3)
|
||||
|
||||
#define ST_LSM6DSV16BX_REG_MLC_ODR_ADDR 0x60
|
||||
#define ST_LSM6DSV16BX_MLC_ODR_MASK GENMASK(6, 4)
|
||||
|
||||
#define ST_LSM6DSV16BX_REG_STEP_COUNTER_L_ADDR 0x62
|
||||
|
||||
#define ST_LSM6DSV16BX_REG_EMB_FUNC_SRC_ADDR 0x64
|
||||
#define ST_LSM6DSV16BX_STEPCOUNTER_BIT_SET_MASK BIT(2)
|
||||
#define ST_LSM6DSV16BX_STEP_OVERFLOW_MASK BIT(3)
|
||||
#define ST_LSM6DSV16BX_STEP_COUNT_DELTA_IA_MASK BIT(4)
|
||||
#define ST_LSM6DSV16BX_STEP_DETECTED_MASK BIT(5)
|
||||
#define ST_LSM6DSV16BX_PEDO_RST_STEP_MASK BIT(7)
|
||||
|
||||
#define ST_LSM6DSV16BX_REG_EMB_FUNC_INIT_A_ADDR 0x66
|
||||
#define ST_LSM6DSV16BX_SFLP_GAME_INIT_MASK BIT(1)
|
||||
|
||||
#define ST_LSM6DSV16BX_REG_EMB_FUNC_INIT_B_ADDR 0x67
|
||||
#define ST_LSM6DSV16BX_FSM_INIT_MASK BIT(0)
|
||||
#define ST_LSM6DSV16BX_MLC_INIT_MASK BIT(4)
|
||||
|
||||
#define ST_LSM6DSV16BX_REG_MLC1_SRC_ADDR 0x70
|
||||
|
||||
#define ST_LSM6DSV16BX_TS_DELTA_NS 21700ULL
|
||||
|
||||
/* temperature in uC */
|
||||
#define ST_LSM6DSV16BX_TEMP_GAIN 256
|
||||
#define ST_LSM6DSV16BX_TEMP_OFFSET 6400
|
||||
|
||||
/* self test values */
|
||||
#define ST_LSM6DSV16BX_SELFTEST_ACCEL_MIN 410
|
||||
#define ST_LSM6DSV16BX_SELFTEST_ACCEL_MAX 13935
|
||||
#define ST_LSM6DSV16BX_SELFTEST_GYRO_MIN 2143
|
||||
#define ST_LSM6DSV16BX_SELFTEST_GYRO_MAX 10000
|
||||
|
||||
#define ST_LSM6DSV16BX_SELF_TEST_NORMAL_MODE_VAL 0
|
||||
#define ST_LSM6DSV16BX_SELF_TEST_POS_SIGN_VAL 1
|
||||
#define ST_LSM6DSV16BX_SELF_TEST_NEG_SIGN_VAL 2
|
||||
|
||||
#define ST_LSM6DSV16BX_DEFAULT_KTIME (200000000)
|
||||
#define ST_LSM6DSV16BX_FAST_KTIME (5000000)
|
||||
|
||||
#define ST_LSM6DSV16BX_DATA_CHANNEL(chan_type, addr, mod, ch2, scan_idx, \
|
||||
rb, sb, sg, ext_inf) \
|
||||
{ \
|
||||
.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 = ext_inf, \
|
||||
}
|
||||
|
||||
#define ST_LSM6DSV16BX_SFLP_DATA_CHANNEL(chan_type, mod, ch2, scan_idx, \
|
||||
rb, sb, sg) \
|
||||
{ \
|
||||
.type = chan_type, \
|
||||
.modified = mod, \
|
||||
.channel2 = ch2, \
|
||||
.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, \
|
||||
}, \
|
||||
}
|
||||
|
||||
static const struct iio_event_spec st_lsm6dsv16bx_flush_event = {
|
||||
.type = STM_IIO_EV_TYPE_FIFO_FLUSH,
|
||||
.dir = IIO_EV_DIR_EITHER,
|
||||
};
|
||||
|
||||
static const struct iio_event_spec st_lsm6dsv16bx_thr_event = {
|
||||
.type = IIO_EV_TYPE_THRESH,
|
||||
.dir = IIO_EV_DIR_RISING,
|
||||
.mask_separate = BIT(IIO_EV_INFO_ENABLE),
|
||||
};
|
||||
|
||||
#define ST_LSM6DSV16BX_EVENT_CHANNEL(ctype, etype) \
|
||||
{ \
|
||||
.type = ctype, \
|
||||
.modified = 0, \
|
||||
.scan_index = -1, \
|
||||
.indexed = -1, \
|
||||
.event_spec = &st_lsm6dsv16bx_##etype##_event, \
|
||||
.num_event_specs = 1, \
|
||||
}
|
||||
|
||||
#define ST_LSM6DSV16BX_SHIFT_VAL(val, mask) (((val) << __ffs(mask)) & (mask))
|
||||
|
||||
struct st_lsm6dsv16bx_reg {
|
||||
u8 addr;
|
||||
u8 mask;
|
||||
};
|
||||
|
||||
struct st_lsm6dsv16bx_odr {
|
||||
u16 hz;
|
||||
int uhz;
|
||||
u8 val;
|
||||
u8 batch_val;
|
||||
};
|
||||
|
||||
struct st_lsm6dsv16bx_odr_table_entry {
|
||||
u8 size;
|
||||
struct st_lsm6dsv16bx_reg reg;
|
||||
struct st_lsm6dsv16bx_odr odr_avl[ST_LSM6DSV16BX_ODR_LIST_SIZE];
|
||||
};
|
||||
|
||||
struct st_lsm6dsv16bx_fs {
|
||||
u32 gain;
|
||||
u8 val;
|
||||
};
|
||||
|
||||
#define ST_LSM6DSV16BX_FS_LIST_SIZE 6
|
||||
#define ST_LSM6DSV16BX_FS_ACC_LIST_SIZE 4
|
||||
#define ST_LSM6DSV16BX_FS_GYRO_LIST_SIZE 6
|
||||
struct st_lsm6dsv16bx_fs_table_entry {
|
||||
u8 size;
|
||||
struct st_lsm6dsv16bx_reg reg;
|
||||
struct st_lsm6dsv16bx_fs fs_avl[ST_LSM6DSV16BX_FS_LIST_SIZE];
|
||||
};
|
||||
|
||||
#define ST_LSM6DSV16BX_ACC_FS_2G_GAIN IIO_G_TO_M_S_2(61000)
|
||||
#define ST_LSM6DSV16BX_ACC_FS_4G_GAIN IIO_G_TO_M_S_2(122000)
|
||||
#define ST_LSM6DSV16BX_ACC_FS_8G_GAIN IIO_G_TO_M_S_2(244000)
|
||||
#define ST_LSM6DSV16BX_ACC_FS_16G_GAIN IIO_G_TO_M_S_2(488000)
|
||||
|
||||
#define ST_LSM6DSV16BX_GYRO_FS_125_GAIN IIO_DEGREE_TO_RAD(4375000)
|
||||
#define ST_LSM6DSV16BX_GYRO_FS_250_GAIN IIO_DEGREE_TO_RAD(8750000)
|
||||
#define ST_LSM6DSV16BX_GYRO_FS_500_GAIN IIO_DEGREE_TO_RAD(17500000)
|
||||
#define ST_LSM6DSV16BX_GYRO_FS_1000_GAIN IIO_DEGREE_TO_RAD(35000000)
|
||||
#define ST_LSM6DSV16BX_GYRO_FS_2000_GAIN IIO_DEGREE_TO_RAD(70000000)
|
||||
#define ST_LSM6DSV16BX_GYRO_FS_4000_GAIN IIO_DEGREE_TO_RAD(140000000)
|
||||
|
||||
/**
|
||||
* enum st_lsm6dsv16bx_hw_id - list of HW device id supported by the
|
||||
* lsm6dsv16bx driver
|
||||
*/
|
||||
enum st_lsm6dsv16bx_hw_id {
|
||||
ST_LSM6DSV16BX_ID,
|
||||
ST_LSM6DSV16BX_MAX_ID,
|
||||
};
|
||||
|
||||
enum st_lsm6dsv16bx_fsm_mlc_enable_id {
|
||||
ST_LSM6DSV16BX_MLC_FSM_DISABLED = 0,
|
||||
ST_LSM6DSV16BX_MLC_ENABLED = BIT(0),
|
||||
ST_LSM6DSV16BX_FSM_ENABLED = BIT(1),
|
||||
};
|
||||
|
||||
/**
|
||||
* struct mlc_config_t - MLC/FSM configuration report struct
|
||||
* @mlc_int_addr: interrupt register address.
|
||||
* @mlc_int_mask: interrupt register mask.
|
||||
* @fsm_int_addr: interrupt register address.
|
||||
* @fsm_int_mask: interrupt register mask.
|
||||
* @mlc_configured: number of mlc configured.
|
||||
* @fsm_configured: number of fsm configured.
|
||||
* @bin_len: fw binary size.
|
||||
* @requested_odr: Min ODR requested to works properly.
|
||||
* @status: MLC / FSM enabled status.
|
||||
*/
|
||||
struct st_lsm6dsv16bx_mlc_config_t {
|
||||
uint8_t mlc_int_addr;
|
||||
uint8_t mlc_int_mask;
|
||||
uint8_t fsm_int_addr;
|
||||
uint8_t fsm_int_mask;
|
||||
uint8_t mlc_configured;
|
||||
uint8_t fsm_configured;
|
||||
uint16_t bin_len;
|
||||
uint16_t requested_odr;
|
||||
enum st_lsm6dsv16bx_fsm_mlc_enable_id status;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct st_lsm6dsv16bx_settings - ST IMU sensor settings
|
||||
* @hw_id: Hw id supported by the driver configuration.
|
||||
* @name: Device name supported by the driver configuration.
|
||||
* @st_qvar_probe: QVAR probe flag, indicate if QVAR feature is supported.
|
||||
* @st_mlc_probe: MLC probe flag, indicate if MLC feature is supported.
|
||||
* @st_fsm_probe: FSM probe flag, indicate if FSM feature is supported.
|
||||
* @st_sflp_probe: SFLP probe flag, indicate if SFLP feature is supported.
|
||||
* @fs_table: full scale table for main sensors.
|
||||
*
|
||||
* main sensors are ST_LSM6DSV16BX_ID_GYRO, ST_LSM6DSV16BX_ID_ACC and
|
||||
* ST_LSM6DSV16BX_ID_TEMP
|
||||
*/
|
||||
#define ST_LSM6DSV16BX_MAIN_SENSOR_NUM 3
|
||||
|
||||
struct st_lsm6dsv16bx_settings {
|
||||
struct {
|
||||
enum st_lsm6dsv16bx_hw_id hw_id;
|
||||
const char *name;
|
||||
} id;
|
||||
|
||||
bool st_qvar_probe;
|
||||
bool st_mlc_probe;
|
||||
bool st_fsm_probe;
|
||||
bool st_sflp_probe;
|
||||
bool st_tdm_probe;
|
||||
struct st_lsm6dsv16bx_fs_table_entry fs_table[ST_LSM6DSV16BX_MAIN_SENSOR_NUM];
|
||||
};
|
||||
|
||||
enum st_lsm6dsv16bx_sensor_id {
|
||||
ST_LSM6DSV16BX_ID_GYRO,
|
||||
ST_LSM6DSV16BX_ID_ACC,
|
||||
ST_LSM6DSV16BX_ID_TEMP,
|
||||
ST_LSM6DSV16BX_ID_6X_GAME,
|
||||
ST_LSM6DSV16BX_ID_QVAR,
|
||||
ST_LSM6DSV16BX_ID_MLC,
|
||||
ST_LSM6DSV16BX_ID_MLC_0,
|
||||
ST_LSM6DSV16BX_ID_MLC_1,
|
||||
ST_LSM6DSV16BX_ID_MLC_2,
|
||||
ST_LSM6DSV16BX_ID_MLC_3,
|
||||
ST_LSM6DSV16BX_ID_FSM_0,
|
||||
ST_LSM6DSV16BX_ID_FSM_1,
|
||||
ST_LSM6DSV16BX_ID_FSM_2,
|
||||
ST_LSM6DSV16BX_ID_FSM_3,
|
||||
ST_LSM6DSV16BX_ID_FSM_4,
|
||||
ST_LSM6DSV16BX_ID_FSM_5,
|
||||
ST_LSM6DSV16BX_ID_FSM_6,
|
||||
ST_LSM6DSV16BX_ID_FSM_7,
|
||||
ST_LSM6DSV16BX_ID_STEP_COUNTER,
|
||||
ST_LSM6DSV16BX_ID_STEP_DETECTOR,
|
||||
ST_LSM6DSV16BX_ID_SIGN_MOTION,
|
||||
ST_LSM6DSV16BX_ID_TILT,
|
||||
ST_LSM6DSV16BX_ID_TAP,
|
||||
ST_LSM6DSV16BX_ID_DTAP,
|
||||
ST_LSM6DSV16BX_ID_FF,
|
||||
ST_LSM6DSV16BX_ID_SLPCHG,
|
||||
ST_LSM6DSV16BX_ID_WK,
|
||||
ST_LSM6DSV16BX_ID_6D,
|
||||
ST_LSM6DSV16BX_ID_MAX,
|
||||
};
|
||||
|
||||
static const enum st_lsm6dsv16bx_sensor_id st_lsm6dsv16bx_main_sensor_list[] = {
|
||||
[0] = ST_LSM6DSV16BX_ID_GYRO,
|
||||
[1] = ST_LSM6DSV16BX_ID_ACC,
|
||||
[2] = ST_LSM6DSV16BX_ID_6X_GAME,
|
||||
[3] = ST_LSM6DSV16BX_ID_TEMP,
|
||||
};
|
||||
|
||||
static const enum st_lsm6dsv16bx_sensor_id st_lsm6dsv16bx_gyro_dep_sensor_list[] = {
|
||||
[0] = ST_LSM6DSV16BX_ID_GYRO,
|
||||
[1] = ST_LSM6DSV16BX_ID_6X_GAME,
|
||||
[4] = ST_LSM6DSV16BX_ID_MLC,
|
||||
[5] = ST_LSM6DSV16BX_ID_MLC_0,
|
||||
[6] = ST_LSM6DSV16BX_ID_MLC_1,
|
||||
[7] = ST_LSM6DSV16BX_ID_MLC_2,
|
||||
[8] = ST_LSM6DSV16BX_ID_MLC_3,
|
||||
[9] = ST_LSM6DSV16BX_ID_FSM_0,
|
||||
[10] = ST_LSM6DSV16BX_ID_FSM_1,
|
||||
[11] = ST_LSM6DSV16BX_ID_FSM_2,
|
||||
[12] = ST_LSM6DSV16BX_ID_FSM_3,
|
||||
[13] = ST_LSM6DSV16BX_ID_FSM_4,
|
||||
[14] = ST_LSM6DSV16BX_ID_FSM_5,
|
||||
[15] = ST_LSM6DSV16BX_ID_FSM_6,
|
||||
[16] = ST_LSM6DSV16BX_ID_FSM_7,
|
||||
};
|
||||
|
||||
static const enum st_lsm6dsv16bx_sensor_id st_lsm6dsv16bx_acc_dep_sensor_list[] = {
|
||||
[0] = ST_LSM6DSV16BX_ID_ACC,
|
||||
[1] = ST_LSM6DSV16BX_ID_TEMP,
|
||||
[2] = ST_LSM6DSV16BX_ID_6X_GAME,
|
||||
[3] = ST_LSM6DSV16BX_ID_QVAR,
|
||||
[6] = ST_LSM6DSV16BX_ID_MLC,
|
||||
[7] = ST_LSM6DSV16BX_ID_MLC_0,
|
||||
[8] = ST_LSM6DSV16BX_ID_MLC_1,
|
||||
[9] = ST_LSM6DSV16BX_ID_MLC_2,
|
||||
[10] = ST_LSM6DSV16BX_ID_MLC_3,
|
||||
[11] = ST_LSM6DSV16BX_ID_FSM_0,
|
||||
[12] = ST_LSM6DSV16BX_ID_FSM_1,
|
||||
[13] = ST_LSM6DSV16BX_ID_FSM_2,
|
||||
[14] = ST_LSM6DSV16BX_ID_FSM_3,
|
||||
[15] = ST_LSM6DSV16BX_ID_FSM_4,
|
||||
[16] = ST_LSM6DSV16BX_ID_FSM_5,
|
||||
[17] = ST_LSM6DSV16BX_ID_FSM_6,
|
||||
[18] = ST_LSM6DSV16BX_ID_FSM_7,
|
||||
[19] = ST_LSM6DSV16BX_ID_STEP_COUNTER,
|
||||
[20] = ST_LSM6DSV16BX_ID_STEP_DETECTOR,
|
||||
[21] = ST_LSM6DSV16BX_ID_SIGN_MOTION,
|
||||
[22] = ST_LSM6DSV16BX_ID_TILT,
|
||||
[23] = ST_LSM6DSV16BX_ID_TAP,
|
||||
[24] = ST_LSM6DSV16BX_ID_DTAP,
|
||||
[25] = ST_LSM6DSV16BX_ID_FF,
|
||||
[26] = ST_LSM6DSV16BX_ID_SLPCHG,
|
||||
[27] = ST_LSM6DSV16BX_ID_WK,
|
||||
[28] = ST_LSM6DSV16BX_ID_6D,
|
||||
};
|
||||
|
||||
static const enum st_lsm6dsv16bx_sensor_id st_lsm6dsv16bx_buffered_sensor_list[] = {
|
||||
[0] = ST_LSM6DSV16BX_ID_GYRO,
|
||||
[1] = ST_LSM6DSV16BX_ID_ACC,
|
||||
[2] = ST_LSM6DSV16BX_ID_TEMP,
|
||||
[3] = ST_LSM6DSV16BX_ID_6X_GAME,
|
||||
[4] = ST_LSM6DSV16BX_ID_QVAR,
|
||||
[7] = ST_LSM6DSV16BX_ID_STEP_COUNTER,
|
||||
};
|
||||
|
||||
/**
|
||||
* The mlc only sensor list used by mlc loader
|
||||
*/
|
||||
static const enum st_lsm6dsv16bx_sensor_id st_lsm6dsv16bx_mlc_sensor_list[] = {
|
||||
[0] = ST_LSM6DSV16BX_ID_MLC_0,
|
||||
[1] = ST_LSM6DSV16BX_ID_MLC_1,
|
||||
[2] = ST_LSM6DSV16BX_ID_MLC_2,
|
||||
[3] = ST_LSM6DSV16BX_ID_MLC_3,
|
||||
};
|
||||
|
||||
/**
|
||||
* The fsm only sensor list used by mlc loader
|
||||
*/
|
||||
static const enum st_lsm6dsv16bx_sensor_id st_lsm6dsv16bx_fsm_sensor_list[] = {
|
||||
[0] = ST_LSM6DSV16BX_ID_FSM_0,
|
||||
[1] = ST_LSM6DSV16BX_ID_FSM_1,
|
||||
[2] = ST_LSM6DSV16BX_ID_FSM_2,
|
||||
[3] = ST_LSM6DSV16BX_ID_FSM_3,
|
||||
[4] = ST_LSM6DSV16BX_ID_FSM_4,
|
||||
[5] = ST_LSM6DSV16BX_ID_FSM_5,
|
||||
[6] = ST_LSM6DSV16BX_ID_FSM_6,
|
||||
[7] = ST_LSM6DSV16BX_ID_FSM_7,
|
||||
};
|
||||
|
||||
/**
|
||||
* The low power embedded function only sensor list
|
||||
*/
|
||||
static const enum st_lsm6dsv16bx_sensor_id st_lsm6dsv16bx_embfunc_sensor_list[] = {
|
||||
[0] = ST_LSM6DSV16BX_ID_STEP_COUNTER,
|
||||
[1] = ST_LSM6DSV16BX_ID_STEP_DETECTOR,
|
||||
[2] = ST_LSM6DSV16BX_ID_SIGN_MOTION,
|
||||
[3] = ST_LSM6DSV16BX_ID_TILT,
|
||||
};
|
||||
|
||||
/**
|
||||
* The low power event only sensor list
|
||||
*/
|
||||
static const enum st_lsm6dsv16bx_sensor_id st_lsm6dsv16bx_event_sensor_list[] = {
|
||||
[0] = ST_LSM6DSV16BX_ID_TAP,
|
||||
[1] = ST_LSM6DSV16BX_ID_DTAP,
|
||||
[2] = ST_LSM6DSV16BX_ID_FF,
|
||||
[3] = ST_LSM6DSV16BX_ID_SLPCHG,
|
||||
[4] = ST_LSM6DSV16BX_ID_WK,
|
||||
[5] = ST_LSM6DSV16BX_ID_6D,
|
||||
};
|
||||
|
||||
/**
|
||||
* The low power event triggered only sensor list
|
||||
*/
|
||||
static const enum st_lsm6dsv16bx_sensor_id
|
||||
st_lsm6dsv16bx_event_trigger_sensor_list[] = {
|
||||
[0] = ST_LSM6DSV16BX_ID_WK,
|
||||
[1] = ST_LSM6DSV16BX_ID_6D,
|
||||
};
|
||||
|
||||
#define ST_LSM6DSV16BX_ID_ALL_FSM_MLC (BIT(ST_LSM6DSV16BX_ID_MLC_0) | \
|
||||
BIT(ST_LSM6DSV16BX_ID_MLC_1) | \
|
||||
BIT(ST_LSM6DSV16BX_ID_MLC_2) | \
|
||||
BIT(ST_LSM6DSV16BX_ID_MLC_3) | \
|
||||
BIT(ST_LSM6DSV16BX_ID_FSM_0) | \
|
||||
BIT(ST_LSM6DSV16BX_ID_FSM_1) | \
|
||||
BIT(ST_LSM6DSV16BX_ID_FSM_2) | \
|
||||
BIT(ST_LSM6DSV16BX_ID_FSM_3) | \
|
||||
BIT(ST_LSM6DSV16BX_ID_FSM_4) | \
|
||||
BIT(ST_LSM6DSV16BX_ID_FSM_5) | \
|
||||
BIT(ST_LSM6DSV16BX_ID_FSM_6) | \
|
||||
BIT(ST_LSM6DSV16BX_ID_FSM_7))
|
||||
enum st_lsm6dsv16bx_fifo_mode {
|
||||
ST_LSM6DSV16BX_FIFO_BYPASS = 0x0,
|
||||
ST_LSM6DSV16BX_FIFO_CONT = 0x6,
|
||||
};
|
||||
|
||||
enum {
|
||||
ST_LSM6DSV16BX_HW_FLUSH,
|
||||
ST_LSM6DSV16BX_HW_OPERATIONAL,
|
||||
};
|
||||
|
||||
/* sensor devices that can wake-up the target */
|
||||
#define ST_LSM6DSV16BX_WAKE_UP_SENSORS (BIT(ST_LSM6DSV16BX_ID_GYRO) | \
|
||||
BIT(ST_LSM6DSV16BX_ID_ACC) | \
|
||||
ST_LSM6DSV16BX_ID_ALL_FSM_MLC)
|
||||
|
||||
/**
|
||||
* struct st_lsm6dsv16bx_sensor - ST IMU sensor instance
|
||||
* @name: Sensor name.
|
||||
* @id: Sensor identifier.
|
||||
* @hw: Pointer to instance of struct st_lsm6dsv16bx_hw.
|
||||
* @trig: Trigger used by IIO event sensors.
|
||||
* @odr: Output data rate of the sensor [Hz].
|
||||
* @uodr: Output data rate of the sensor [uHz].
|
||||
* @gain: Configured sensor sensitivity.
|
||||
* @offset: Sensor data offset.
|
||||
* @std_samples: Counter of samples to discard during sensor bootstrap.
|
||||
* @std_level: Samples to discard threshold.
|
||||
* @decimator: Samples decimate counter.
|
||||
* @dec_counter: Samples decimate value.
|
||||
* @last_fifo_timestamp: Timestamp related to last sample in FIFO.
|
||||
* @max_watermark: Max supported watermark level.
|
||||
* @watermark: Sensor watermark level.
|
||||
* @selftest_status: Report last self test status.
|
||||
* @min_st: Min self test raw data value.
|
||||
* @max_st: Max self test raw data value.
|
||||
* @batch_reg: Batching register info (addr + mask).
|
||||
* @status_reg: MLC/FSM IIO event sensor status register.
|
||||
* @outreg_addr: MLC/FSM IIO event sensor output register.
|
||||
* @status: MLC/FSM enabled IIO event sensor status.
|
||||
* @conf: Used in case of IIO sensor event to store configuration.
|
||||
* @scan: Scan buffer for triggered sensors event.
|
||||
*/
|
||||
struct st_lsm6dsv16bx_sensor {
|
||||
char name[32];
|
||||
enum st_lsm6dsv16bx_sensor_id id;
|
||||
struct st_lsm6dsv16bx_hw *hw;
|
||||
struct iio_trigger *trig;
|
||||
|
||||
int odr;
|
||||
int uodr;
|
||||
|
||||
union {
|
||||
struct {
|
||||
/* data sensors */
|
||||
u32 gain;
|
||||
u32 offset;
|
||||
|
||||
u8 std_samples;
|
||||
u8 std_level;
|
||||
|
||||
u8 decimator;
|
||||
u8 dec_counter;
|
||||
|
||||
s64 last_fifo_timestamp;
|
||||
u16 max_watermark;
|
||||
u16 watermark;
|
||||
|
||||
/* self test */
|
||||
int8_t selftest_status;
|
||||
int min_st;
|
||||
int max_st;
|
||||
|
||||
struct st_lsm6dsv16bx_reg batch_reg;
|
||||
};
|
||||
|
||||
struct {
|
||||
/* mlc or fsm sensor */
|
||||
uint8_t status_reg;
|
||||
uint8_t outreg_addr;
|
||||
enum st_lsm6dsv16bx_fsm_mlc_enable_id status;
|
||||
};
|
||||
|
||||
struct {
|
||||
/* event sensor, data configuration */
|
||||
u32 conf[6];
|
||||
|
||||
/* ensure natural alignment of timestamp */
|
||||
struct {
|
||||
u8 event;
|
||||
s64 ts __aligned(8);
|
||||
} scan;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* struct st_lsm6dsv16bx_hw - ST IMU MEMS hw instance
|
||||
* @dev: Pointer to instance of device struct (I2C / SPI / I3C).
|
||||
* @irq: Device interrupt line (I2C / SPI / I3C).
|
||||
* @regmap: regmap structure pointer.
|
||||
* @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.
|
||||
* @int_pin: selected interrupt pin from configuration.
|
||||
* @ext_data_len: Number of i2c slave devices connected to I2C master.
|
||||
* @ts_offset: Hw timestamp offset.
|
||||
* @ts_delta_ns: Calibrated delta timestamp.
|
||||
* @hw_ts: Latest hw timestamp from the sensor.
|
||||
* @val_ts_old: Last sample timestamp for rollover check.
|
||||
* @hw_ts_high: Manage timestamp rollover.
|
||||
* @tsample: Sample timestamp.
|
||||
* @delta_ts: Estimated delta time between two consecutive interrupts.
|
||||
* @ts: Latest timestamp from irq handler.
|
||||
* @module_id: identify iio devices of the same sensor module.
|
||||
* @orientation: Sensor orientation matrix.
|
||||
* @vdd_supply: Voltage regulator for VDD.
|
||||
* @vddio_supply: Voltage regulator for VDDIIO.
|
||||
* @mlc_config: MLC/FSM data register structure.
|
||||
* @preload_mlc: MLC/FSM preload flag.
|
||||
* @qvar_workqueue: QVAR workqueue (if enabled in Kconfig).
|
||||
* @iio_devs: Pointers to acc/gyro iio_dev instances.
|
||||
* @settings: ST IMU sensor settings.
|
||||
* @fs_table: ST IMU full scale table.
|
||||
* @odr_table: ST IMU output data rate table.
|
||||
* @en_tdm: TDM enable flag.
|
||||
*/
|
||||
struct st_lsm6dsv16bx_hw {
|
||||
struct device *dev;
|
||||
int irq;
|
||||
struct regmap *regmap;
|
||||
struct mutex lock;
|
||||
struct mutex fifo_lock;
|
||||
struct mutex page_lock;
|
||||
|
||||
enum st_lsm6dsv16bx_fifo_mode fifo_mode;
|
||||
unsigned long state;
|
||||
u32 enable_mask;
|
||||
s64 hw_timestamp_global;
|
||||
|
||||
#if defined(CONFIG_IIO_ST_LSM6DSV16BX_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_LSM6DSV16BX_ASYNC_HW_TIMESTAMP */
|
||||
|
||||
u8 int_pin;
|
||||
|
||||
u8 ext_data_len;
|
||||
|
||||
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;
|
||||
|
||||
u32 module_id;
|
||||
struct iio_mount_matrix orientation;
|
||||
struct regulator *vdd_supply;
|
||||
struct regulator *vddio_supply;
|
||||
struct st_lsm6dsv16bx_mlc_config_t *mlc_config;
|
||||
bool preload_mlc;
|
||||
struct workqueue_struct *qvar_workqueue;
|
||||
struct iio_dev *iio_devs[ST_LSM6DSV16BX_ID_MAX];
|
||||
|
||||
const struct st_lsm6dsv16bx_settings *settings;
|
||||
const struct st_lsm6dsv16bx_fs_table_entry *fs_table;
|
||||
const struct st_lsm6dsv16bx_odr_table_entry *odr_table;
|
||||
|
||||
bool en_tdm;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct st_lsm6dsv16bx_ff_th - Free Fall threshold table
|
||||
* @mg: Threshold in mg.
|
||||
* @val: Register value.
|
||||
*/
|
||||
struct st_lsm6dsv16bx_ff_th {
|
||||
u32 mg;
|
||||
u8 val;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct st_lsm6dsv16bx_6D_th - 6D threshold table
|
||||
* @deg: Threshold in degrees.
|
||||
* @val: Register value.
|
||||
*/
|
||||
struct st_lsm6dsv16bx_6D_th {
|
||||
u8 deg;
|
||||
u8 val;
|
||||
};
|
||||
|
||||
extern const struct dev_pm_ops st_lsm6dsv16bx_pm_ops;
|
||||
|
||||
static inline int
|
||||
__st_lsm6dsv16bx_write_with_mask(struct st_lsm6dsv16bx_hw *hw,
|
||||
unsigned int addr, unsigned int mask,
|
||||
unsigned int data)
|
||||
{
|
||||
int err;
|
||||
unsigned int val = ST_LSM6DSV16BX_SHIFT_VAL(data, mask);
|
||||
|
||||
err = regmap_update_bits(hw->regmap, addr, mask, val);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static inline int st_lsm6dsv16bx_write_with_mask(struct st_lsm6dsv16bx_hw *hw,
|
||||
unsigned int addr,
|
||||
unsigned int mask,
|
||||
unsigned int data)
|
||||
{
|
||||
int err;
|
||||
|
||||
mutex_lock(&hw->page_lock);
|
||||
err = __st_lsm6dsv16bx_write_with_mask(hw, addr, mask, data);
|
||||
mutex_unlock(&hw->page_lock);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static inline int
|
||||
st_lsm6dsv16bx_read_locked(struct st_lsm6dsv16bx_hw *hw, unsigned int addr,
|
||||
void *data, unsigned int len)
|
||||
{
|
||||
int err;
|
||||
|
||||
mutex_lock(&hw->page_lock);
|
||||
err = regmap_bulk_read(hw->regmap, addr, data, len);
|
||||
mutex_unlock(&hw->page_lock);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static inline int
|
||||
st_lsm6dsv16bx_write_locked(struct st_lsm6dsv16bx_hw *hw, unsigned int addr,
|
||||
unsigned int val)
|
||||
{
|
||||
int err;
|
||||
|
||||
mutex_lock(&hw->page_lock);
|
||||
err = regmap_write(hw->regmap, addr, val);
|
||||
mutex_unlock(&hw->page_lock);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static inline int
|
||||
st_lsm6dsv16bx_read_with_mask(struct st_lsm6dsv16bx_hw *hw, u8 addr, u8 mask,
|
||||
u8 *val)
|
||||
{
|
||||
u8 data;
|
||||
int err;
|
||||
|
||||
mutex_lock(&hw->page_lock);
|
||||
err = regmap_bulk_read(hw->regmap, addr, &data, sizeof(data));
|
||||
if (err < 0)
|
||||
goto out;
|
||||
|
||||
*val = (data & mask) >> __ffs(mask);
|
||||
|
||||
out:
|
||||
mutex_unlock(&hw->page_lock);
|
||||
|
||||
return (err < 0) ? err : 0;
|
||||
}
|
||||
static inline int st_lsm6dsv16bx_set_page_access(struct st_lsm6dsv16bx_hw *hw,
|
||||
unsigned int mask,
|
||||
unsigned int data)
|
||||
{
|
||||
return regmap_update_bits(hw->regmap,
|
||||
ST_LSM6DSV16BX_REG_FUNC_CFG_ACCESS_ADDR,
|
||||
mask,
|
||||
ST_LSM6DSV16BX_SHIFT_VAL(data, mask));
|
||||
}
|
||||
|
||||
static inline bool st_lsm6dsv16bx_run_mlc_task(struct st_lsm6dsv16bx_hw *hw)
|
||||
{
|
||||
return hw->settings->st_mlc_probe || hw->settings->st_fsm_probe;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
st_lsm6dsv16bx_is_fifo_enabled(struct st_lsm6dsv16bx_hw *hw)
|
||||
{
|
||||
return hw->enable_mask & (BIT(ST_LSM6DSV16BX_ID_GYRO) |
|
||||
BIT(ST_LSM6DSV16BX_ID_ACC) |
|
||||
BIT(ST_LSM6DSV16BX_ID_QVAR));
|
||||
}
|
||||
|
||||
int st_lsm6dsv16bx_probe(struct device *dev, int irq, int hw_id,
|
||||
struct regmap *regmap);
|
||||
int st_lsm6dsv16bx_sensor_set_enable(struct st_lsm6dsv16bx_sensor *sensor,
|
||||
bool enable);
|
||||
int st_lsm6dsv16bx_sflp_set_enable(struct st_lsm6dsv16bx_sensor *sensor,
|
||||
bool enable);
|
||||
int st_lsm6dsv16bx_buffers_setup(struct st_lsm6dsv16bx_hw *hw);
|
||||
ssize_t st_lsm6dsv16bx_flush_fifo(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t size);
|
||||
ssize_t st_lsm6dsv16bx_get_max_watermark(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf);
|
||||
ssize_t st_lsm6dsv16bx_get_watermark(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf);
|
||||
ssize_t st_lsm6dsv16bx_set_watermark(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t size);
|
||||
ssize_t st_lsm6dsv16bx_get_module_id(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf);
|
||||
int st_lsm6dsv16bx_suspend_fifo(struct st_lsm6dsv16bx_hw *hw);
|
||||
int st_lsm6dsv16bx_set_fifo_mode(struct st_lsm6dsv16bx_hw *hw,
|
||||
enum st_lsm6dsv16bx_fifo_mode fifo_mode);
|
||||
int
|
||||
__st_lsm6dsv16bx_set_sensor_batching_odr(struct st_lsm6dsv16bx_sensor *sensor,
|
||||
bool enable);
|
||||
int st_lsm6dsv16bx_fsm_init(struct st_lsm6dsv16bx_hw *hw);
|
||||
int st_lsm6dsv16bx_fsm_get_orientation(struct st_lsm6dsv16bx_hw *hw, u8 *data);
|
||||
int st_lsm6dsv16bx_update_batching(struct iio_dev *iio_dev, bool enable);
|
||||
int st_lsm6dsv16bx_get_batch_val(struct st_lsm6dsv16bx_sensor *sensor,
|
||||
int odr, int uodr, u8 *val);
|
||||
|
||||
int st_lsm6dsv16bx_qvar_probe(struct st_lsm6dsv16bx_hw *hw);
|
||||
int
|
||||
st_lsm6dsv16bx_qvar_sensor_set_enable(struct st_lsm6dsv16bx_sensor *sensor,
|
||||
bool enable);
|
||||
#if defined(CONFIG_IIO_ST_LSM6DSV16BX_ASYNC_HW_TIMESTAMP)
|
||||
int st_lsm6dsv16bx_hwtimesync_init(struct st_lsm6dsv16bx_hw *hw);
|
||||
#else /* CONFIG_IIO_ST_LSM6DSV16BX_ASYNC_HW_TIMESTAMP */
|
||||
static inline int
|
||||
st_lsm6dsv16bx_hwtimesync_init(struct st_lsm6dsv16bx_hw *hw)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif /* CONFIG_IIO_ST_LSM6DSV16BX_ASYNC_HW_TIMESTAMP */
|
||||
|
||||
int st_lsm6dsv16bx_mlc_probe(struct st_lsm6dsv16bx_hw *hw);
|
||||
int st_lsm6dsv16bx_mlc_remove(struct device *dev);
|
||||
int st_lsm6dsv16bx_mlc_check_status(struct st_lsm6dsv16bx_hw *hw);
|
||||
int st_lsm6dsv16bx_mlc_init_preload(struct st_lsm6dsv16bx_hw *hw);
|
||||
|
||||
int st_lsm6dsv16bx_probe_event(struct st_lsm6dsv16bx_hw *hw);
|
||||
int st_lsm6dsv16bx_event_handler(struct st_lsm6dsv16bx_hw *hw);
|
||||
|
||||
int st_lsm6dsv16bx_probe_embfunc(struct st_lsm6dsv16bx_hw *hw);
|
||||
int st_lsm6dsv16bx_embfunc_handler_thread(struct st_lsm6dsv16bx_hw *hw);
|
||||
int st_lsm6dsv16bx_step_counter_set_enable(struct st_lsm6dsv16bx_sensor *sensor,
|
||||
bool enable);
|
||||
#endif /* ST_LSM6DSV16BX_H */
|
768
drivers/iio/stm/imu/st_lsm6dsv16bx/st_lsm6dsv16bx_buffer.c
Normal file
768
drivers/iio/stm/imu/st_lsm6dsv16bx/st_lsm6dsv16bx_buffer.c
Normal file
@ -0,0 +1,768 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* STMicroelectronics st_lsm6dsv16bx FIFO buffer library driver
|
||||
*
|
||||
* MEMS Software Solutions Team
|
||||
*
|
||||
* Copyright 2023 STMicroelectronics Inc.
|
||||
*/
|
||||
|
||||
#include <asm/unaligned.h>
|
||||
#include <linux/iio/buffer.h>
|
||||
#include <linux/iio/events.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/kfifo_buf.h>
|
||||
#include <linux/iio/trigger.h>
|
||||
#include <linux/iio/trigger_consumer.h>
|
||||
#include <linux/iio/triggered_buffer.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/version.h>
|
||||
|
||||
#include "st_lsm6dsv16bx.h"
|
||||
|
||||
#define ST_LSM6DSV16BX_SAMPLE_DISCHARD 0x7ffd
|
||||
#define ST_LSM6DSV16BX_QVAR_FILTER_X 0x03AA
|
||||
#define ST_LSM6DSV16BX_QVAR_SAMPLE_SIZE 2
|
||||
|
||||
enum {
|
||||
ST_LSM6DSV16BX_GYRO_TAG = 0x01,
|
||||
ST_LSM6DSV16BX_ACC_TAG = 0x02,
|
||||
ST_LSM6DSV16BX_TEMP_TAG = 0x03,
|
||||
ST_LSM6DSV16BX_TS_TAG = 0x04,
|
||||
ST_LSM6DSV16BX_EXT0_TAG = 0x0f,
|
||||
ST_LSM6DSV16BX_EXT1_TAG = 0x10,
|
||||
ST_LSM6DSV16BX_STEPC_TAG = 0x12,
|
||||
ST_LSM6DSV16BX_GAMEROT_TAG = 0x13,
|
||||
ST_LSM6DSV16BX_QVAR_TAG = 0x1f,
|
||||
};
|
||||
|
||||
#define ST_LSM6DSV16BX_EWMA_LEVEL 120
|
||||
#define ST_LSM6DSV16BX_EWMA_DIV 128
|
||||
|
||||
#define ST_LSM6DSV16BX_TIMESTAMP_RESET_VALUE 0xaa
|
||||
|
||||
static inline s64 st_lsm6dsv16bx_ewma(s64 old, s64 new, int weight)
|
||||
{
|
||||
s64 diff, incr;
|
||||
|
||||
diff = new - old;
|
||||
incr = div_s64((ST_LSM6DSV16BX_EWMA_DIV - weight) * diff,
|
||||
ST_LSM6DSV16BX_EWMA_DIV);
|
||||
|
||||
return old + incr;
|
||||
}
|
||||
|
||||
static inline int st_lsm6dsv16bx_reset_hwts(struct st_lsm6dsv16bx_hw *hw)
|
||||
{
|
||||
u8 data = ST_LSM6DSV16BX_TIMESTAMP_RESET_VALUE;
|
||||
int ret;
|
||||
|
||||
ret = st_lsm6dsv16bx_write_locked(hw,
|
||||
ST_LSM6DSV16BX_REG_TIMESTAMP2_ADDR,
|
||||
data);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
#if defined(CONFIG_IIO_ST_LSM6DSV16BX_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);
|
||||
hw->timesync_c = 0;
|
||||
hw->timesync_ktime = ktime_set(0, ST_LSM6DSV16BX_FAST_KTIME);
|
||||
#else /* CONFIG_IIO_ST_LSM6DSV16BX_ASYNC_HW_TIMESTAMP */
|
||||
hw->hw_timestamp_global = (hw->hw_timestamp_global + (1LL << 32)) &
|
||||
GENMASK_ULL(63, 32);
|
||||
#endif /* CONFIG_IIO_ST_LSM6DSV16BX_ASYNC_HW_TIMESTAMP */
|
||||
|
||||
hw->ts = iio_get_time_ns(hw->iio_devs[0]);
|
||||
hw->ts_offset = hw->ts;
|
||||
hw->tsample = 0ull;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int st_lsm6dsv16bx_set_fifo_mode(struct st_lsm6dsv16bx_hw *hw,
|
||||
enum st_lsm6dsv16bx_fifo_mode fifo_mode)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = st_lsm6dsv16bx_write_with_mask(hw, ST_LSM6DSV16BX_REG_FIFO_CTRL4_ADDR,
|
||||
ST_LSM6DSV16BX_FIFO_MODE_MASK,
|
||||
fifo_mode);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
hw->fifo_mode = fifo_mode;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
__st_lsm6dsv16bx_set_sensor_batching_odr(struct st_lsm6dsv16bx_sensor *sensor,
|
||||
bool enable)
|
||||
{
|
||||
struct st_lsm6dsv16bx_hw *hw = sensor->hw;
|
||||
u8 data = 0;
|
||||
int err;
|
||||
|
||||
if (enable) {
|
||||
err = st_lsm6dsv16bx_get_batch_val(sensor, sensor->odr,
|
||||
sensor->uodr, &data);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
|
||||
if (sensor->id == ST_LSM6DSV16BX_ID_6X_GAME) {
|
||||
st_lsm6dsv16bx_set_page_access(hw,
|
||||
ST_LSM6DSV16BX_EMB_FUNC_REG_ACCESS_MASK, 1);
|
||||
err = __st_lsm6dsv16bx_write_with_mask(hw,
|
||||
sensor->batch_reg.addr,
|
||||
sensor->batch_reg.mask,
|
||||
data);
|
||||
st_lsm6dsv16bx_set_page_access(hw,
|
||||
ST_LSM6DSV16BX_EMB_FUNC_REG_ACCESS_MASK, 0);
|
||||
} else {
|
||||
err = __st_lsm6dsv16bx_write_with_mask(hw,
|
||||
sensor->batch_reg.addr,
|
||||
sensor->batch_reg.mask,
|
||||
data);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static inline int
|
||||
st_lsm6dsv16bx_set_sensor_batching_odr(struct st_lsm6dsv16bx_sensor *sensor,
|
||||
bool enable)
|
||||
{
|
||||
struct st_lsm6dsv16bx_hw *hw = sensor->hw;
|
||||
int err;
|
||||
|
||||
mutex_lock(&hw->page_lock);
|
||||
err = __st_lsm6dsv16bx_set_sensor_batching_odr(sensor, enable);
|
||||
mutex_unlock(&hw->page_lock);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int
|
||||
st_lsm6dsv16bx_update_watermark(struct st_lsm6dsv16bx_sensor *sensor,
|
||||
u16 watermark)
|
||||
{
|
||||
u16 fifo_watermark = ST_LSM6DSV16BX_MAX_FIFO_DEPTH;
|
||||
struct st_lsm6dsv16bx_hw *hw = sensor->hw;
|
||||
struct st_lsm6dsv16bx_sensor *cur_sensor;
|
||||
u16 cur_watermark = 0;
|
||||
__le16 wdata;
|
||||
int data = 0;
|
||||
int i, err;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(st_lsm6dsv16bx_buffered_sensor_list); i++) {
|
||||
enum st_lsm6dsv16bx_sensor_id id = st_lsm6dsv16bx_buffered_sensor_list[i];
|
||||
|
||||
if (!hw->iio_devs[id])
|
||||
continue;
|
||||
|
||||
cur_sensor = iio_priv(hw->iio_devs[id]);
|
||||
|
||||
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->page_lock);
|
||||
|
||||
err = regmap_read(hw->regmap,
|
||||
ST_LSM6DSV16BX_REG_FIFO_CTRL1_ADDR + 1, &data);
|
||||
if (err < 0)
|
||||
goto out;
|
||||
|
||||
fifo_watermark = ((data << 8) & ~ST_LSM6DSV16BX_WTM_MASK) |
|
||||
(fifo_watermark & ST_LSM6DSV16BX_WTM_MASK);
|
||||
wdata = cpu_to_le16(fifo_watermark);
|
||||
err = regmap_bulk_write(hw->regmap, ST_LSM6DSV16BX_REG_FIFO_CTRL1_ADDR,
|
||||
&wdata, sizeof(wdata));
|
||||
out:
|
||||
mutex_unlock(&hw->page_lock);
|
||||
|
||||
return err < 0 ? err : 0;
|
||||
}
|
||||
|
||||
static inline void st_lsm6dsv16bx_sync_hw_ts(struct st_lsm6dsv16bx_hw *hw,
|
||||
s64 ts)
|
||||
{
|
||||
s64 delta = ts -hw->hw_ts;
|
||||
|
||||
hw->ts_offset = st_lsm6dsv16bx_ewma(hw->ts_offset, delta,
|
||||
ST_LSM6DSV16BX_EWMA_LEVEL);
|
||||
}
|
||||
|
||||
static struct iio_dev *
|
||||
st_lsm6dsv16bx_get_iiodev_from_tag(struct st_lsm6dsv16bx_hw *hw, u8 tag)
|
||||
{
|
||||
struct iio_dev *iio_dev;
|
||||
|
||||
switch (tag) {
|
||||
case ST_LSM6DSV16BX_GYRO_TAG:
|
||||
iio_dev = hw->iio_devs[ST_LSM6DSV16BX_ID_GYRO];
|
||||
break;
|
||||
case ST_LSM6DSV16BX_ACC_TAG:
|
||||
iio_dev = hw->iio_devs[ST_LSM6DSV16BX_ID_ACC];
|
||||
break;
|
||||
case ST_LSM6DSV16BX_TEMP_TAG:
|
||||
iio_dev = hw->iio_devs[ST_LSM6DSV16BX_ID_TEMP];
|
||||
break;
|
||||
case ST_LSM6DSV16BX_STEPC_TAG:
|
||||
iio_dev = hw->iio_devs[ST_LSM6DSV16BX_ID_STEP_COUNTER];
|
||||
break;
|
||||
case ST_LSM6DSV16BX_QVAR_TAG:
|
||||
iio_dev = hw->iio_devs[ST_LSM6DSV16BX_ID_QVAR];
|
||||
break;
|
||||
case ST_LSM6DSV16BX_GAMEROT_TAG:
|
||||
iio_dev = hw->iio_devs[ST_LSM6DSV16BX_ID_6X_GAME];
|
||||
break;
|
||||
default:
|
||||
iio_dev = NULL;
|
||||
break;
|
||||
}
|
||||
|
||||
return iio_dev;
|
||||
}
|
||||
|
||||
static int st_lsm6dsv16bx_read_fifo(struct st_lsm6dsv16bx_hw *hw)
|
||||
{
|
||||
u8 iio_buf[ALIGN(ST_LSM6DSV16BX_FIFO_SAMPLE_SIZE, sizeof(s64)) +
|
||||
sizeof(s64) + sizeof(s64)];
|
||||
u8 buf[6 * ST_LSM6DSV16BX_FIFO_SAMPLE_SIZE], tag, *ptr;
|
||||
int i, err, word_len, fifo_len, read_len;
|
||||
struct st_lsm6dsv16bx_sensor *sensor;
|
||||
__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;
|
||||
|
||||
ts_irq = hw->ts -hw->delta_ts;
|
||||
|
||||
err = st_lsm6dsv16bx_read_locked(hw,
|
||||
ST_LSM6DSV16BX_REG_FIFO_STATUS1_ADDR,
|
||||
&fifo_status, sizeof(fifo_status));
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
fifo_depth = le16_to_cpu(fifo_status) & ST_LSM6DSV16BX_FIFO_DIFF_MASK;
|
||||
if (!fifo_depth)
|
||||
return 0;
|
||||
|
||||
fifo_len = fifo_depth * ST_LSM6DSV16BX_FIFO_SAMPLE_SIZE;
|
||||
read_len = 0;
|
||||
|
||||
while (read_len < fifo_len) {
|
||||
word_len = min_t(int, fifo_len - read_len, sizeof(buf));
|
||||
err = st_lsm6dsv16bx_read_locked(hw,
|
||||
ST_LSM6DSV16BX_REG_FIFO_DATA_OUT_TAG_ADDR,
|
||||
buf, word_len);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
for (i = 0; i < word_len;
|
||||
i += ST_LSM6DSV16BX_FIFO_SAMPLE_SIZE) {
|
||||
ptr = &buf[i + ST_LSM6DSV16BX_TAG_SIZE];
|
||||
tag = buf[i] >> 3;
|
||||
|
||||
if (tag == ST_LSM6DSV16BX_TS_TAG) {
|
||||
val = get_unaligned_le32(ptr);
|
||||
if (hw->val_ts_old > val)
|
||||
hw->hw_ts_high++;
|
||||
|
||||
#if defined(CONFIG_IIO_ST_LSM6DSV16BX_ASYNC_HW_TIMESTAMP)
|
||||
spin_lock_irq(&hw->hwtimestamp_lock);
|
||||
#endif /* CONFIG_IIO_ST_LSM6DSV16BX_ASYNC_HW_TIMESTAMP */
|
||||
|
||||
hw->hw_timestamp_global =
|
||||
(hw->hw_timestamp_global &
|
||||
GENMASK_ULL(63, 32)) |
|
||||
(u32)le32_to_cpu(val);
|
||||
|
||||
#if defined(CONFIG_IIO_ST_LSM6DSV16BX_ASYNC_HW_TIMESTAMP)
|
||||
spin_unlock_irq(&hw->hwtimestamp_lock);
|
||||
#endif /* CONFIG_IIO_ST_LSM6DSV16BX_ASYNC_HW_TIMESTAMP */
|
||||
|
||||
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_lsm6dsv16bx_ewma(hw->ts_offset,
|
||||
ts_irq - hw->hw_ts,
|
||||
ST_LSM6DSV16BX_EWMA_LEVEL);
|
||||
|
||||
if (!test_bit(ST_LSM6DSV16BX_HW_FLUSH, &hw->state))
|
||||
/* sync ap timestamp and sensor one */
|
||||
st_lsm6dsv16bx_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 {
|
||||
iio_dev = st_lsm6dsv16bx_get_iiodev_from_tag(hw, tag);
|
||||
if (!iio_dev)
|
||||
continue;
|
||||
|
||||
if (tag == ST_LSM6DSV16BX_QVAR_TAG) {
|
||||
memcpy(iio_buf, ptr,
|
||||
ST_LSM6DSV16BX_QVAR_SAMPLE_SIZE);
|
||||
iio_push_to_buffers_with_timestamp(iio_dev,
|
||||
iio_buf,
|
||||
iio_get_time_ns(hw->iio_devs[0]));
|
||||
} else {
|
||||
drdymask = (s16)le16_to_cpu(get_unaligned_le16(ptr));
|
||||
if (unlikely(drdymask >= ST_LSM6DSV16BX_SAMPLE_DISCHARD))
|
||||
continue;
|
||||
|
||||
sensor = iio_priv(iio_dev);
|
||||
|
||||
/*
|
||||
* hw ts in not queued in FIFO if only step
|
||||
* counter enabled
|
||||
*/
|
||||
if (sensor->id == ST_LSM6DSV16BX_ID_STEP_COUNTER) {
|
||||
val = get_unaligned_le32(ptr + 2);
|
||||
hw->tsample = val * hw->ts_delta_ns;
|
||||
}
|
||||
|
||||
if ((tag == ST_LSM6DSV16BX_GYRO_TAG) ||
|
||||
(tag == ST_LSM6DSV16BX_ACC_TAG)) {
|
||||
memcpy(&iio_buf[0], ptr + 4, 2);
|
||||
memcpy(&iio_buf[2], ptr + 2, 2);
|
||||
memcpy(&iio_buf[4], ptr, 2);
|
||||
} else {
|
||||
memcpy(iio_buf, ptr,
|
||||
ST_LSM6DSV16BX_SAMPLE_SIZE);
|
||||
}
|
||||
#if defined(CONFIG_IIO_ST_LSM6DSV16BX_ASYNC_HW_TIMESTAMP)
|
||||
spin_lock_irq(&hw->hwtimestamp_lock);
|
||||
#endif /* CONFIG_IIO_ST_LSM6DSV16BX_ASYNC_HW_TIMESTAMP */
|
||||
|
||||
hw_timestamp_push = cpu_to_le64(hw->hw_timestamp_global);
|
||||
|
||||
#if defined(CONFIG_IIO_ST_LSM6DSV16BX_ASYNC_HW_TIMESTAMP)
|
||||
spin_unlock_irq(&hw->hwtimestamp_lock);
|
||||
#endif /* CONFIG_IIO_ST_LSM6DSV16BX_ASYNC_HW_TIMESTAMP */
|
||||
|
||||
memcpy(&iio_buf[ALIGN(ST_LSM6DSV16BX_SAMPLE_SIZE, sizeof(s64))],
|
||||
&hw_timestamp_push, sizeof(hw_timestamp_push));
|
||||
|
||||
/* avoid samples in the future */
|
||||
hw->tsample = min_t(s64,
|
||||
iio_get_time_ns(hw->iio_devs[0]),
|
||||
hw->tsample);
|
||||
|
||||
/* support decimation for ODR < 15 Hz */
|
||||
if (sensor->dec_counter > 0) {
|
||||
sensor->dec_counter--;
|
||||
} else {
|
||||
iio_push_to_buffers_with_timestamp(iio_dev,
|
||||
iio_buf,
|
||||
hw->tsample);
|
||||
sensor->last_fifo_timestamp = hw_timestamp_push;
|
||||
sensor->dec_counter = sensor->decimator;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
read_len += word_len;
|
||||
}
|
||||
|
||||
return read_len;
|
||||
}
|
||||
|
||||
ssize_t st_lsm6dsv16bx_get_max_watermark(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct iio_dev *iio_dev = dev_get_drvdata(dev);
|
||||
struct st_lsm6dsv16bx_sensor *sensor = iio_priv(iio_dev);
|
||||
|
||||
return sprintf(buf, "%d\n", sensor->max_watermark);
|
||||
}
|
||||
|
||||
ssize_t st_lsm6dsv16bx_get_watermark(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct iio_dev *iio_dev = dev_get_drvdata(dev);
|
||||
struct st_lsm6dsv16bx_sensor *sensor = iio_priv(iio_dev);
|
||||
|
||||
return sprintf(buf, "%d\n", sensor->watermark);
|
||||
}
|
||||
|
||||
ssize_t st_lsm6dsv16bx_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_lsm6dsv16bx_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_lsm6dsv16bx_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_lsm6dsv16bx_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_lsm6dsv16bx_sensor *sensor = iio_priv(iio_dev);
|
||||
struct st_lsm6dsv16bx_hw *hw = sensor->hw;
|
||||
s64 type;
|
||||
s64 event;
|
||||
int count;
|
||||
s64 ts;
|
||||
s64 fifo_ts;
|
||||
|
||||
mutex_lock(&hw->fifo_lock);
|
||||
ts = iio_get_time_ns(iio_dev);
|
||||
hw->delta_ts = ts -hw->ts;
|
||||
hw->ts = ts;
|
||||
set_bit(ST_LSM6DSV16BX_HW_FLUSH, &hw->state);
|
||||
count = st_lsm6dsv16bx_read_fifo(hw);
|
||||
sensor->dec_counter = 0;
|
||||
fifo_ts = sensor->last_fifo_timestamp;
|
||||
mutex_unlock(&hw->fifo_lock);
|
||||
|
||||
type = count > 0 ? STM_IIO_EV_DIR_FIFO_DATA : STM_IIO_EV_DIR_FIFO_EMPTY;
|
||||
event = IIO_UNMOD_EVENT_CODE(iio_dev->channels[0].type, -1,
|
||||
STM_IIO_EV_TYPE_FIFO_FLUSH, type);
|
||||
iio_push_event(iio_dev, event, fifo_ts);
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
int st_lsm6dsv16bx_suspend_fifo(struct st_lsm6dsv16bx_hw *hw)
|
||||
{
|
||||
int err;
|
||||
|
||||
mutex_lock(&hw->fifo_lock);
|
||||
|
||||
st_lsm6dsv16bx_read_fifo(hw);
|
||||
err = st_lsm6dsv16bx_set_fifo_mode(hw, ST_LSM6DSV16BX_FIFO_BYPASS);
|
||||
|
||||
mutex_unlock(&hw->fifo_lock);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
int st_lsm6dsv16bx_update_batching(struct iio_dev *iio_dev, bool enable)
|
||||
{
|
||||
struct st_lsm6dsv16bx_sensor *sensor = iio_priv(iio_dev);
|
||||
struct st_lsm6dsv16bx_hw *hw = sensor->hw;
|
||||
int err;
|
||||
|
||||
disable_irq(hw->irq);
|
||||
err = st_lsm6dsv16bx_set_sensor_batching_odr(sensor, enable);
|
||||
enable_irq(hw->irq);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
int st_lsm6dsv16bx_update_fifo(struct iio_dev *iio_dev, bool enable)
|
||||
{
|
||||
struct st_lsm6dsv16bx_sensor *sensor = iio_priv(iio_dev);
|
||||
struct st_lsm6dsv16bx_hw *hw = sensor->hw;
|
||||
int err;
|
||||
|
||||
disable_irq(hw->irq);
|
||||
|
||||
#if defined(CONFIG_IIO_ST_LSM6DSV16BX_ASYNC_HW_TIMESTAMP)
|
||||
hrtimer_cancel(&hw->timesync_timer);
|
||||
cancel_work_sync(&hw->timesync_work);
|
||||
#endif /* CONFIG_IIO_ST_LSM6DSV16BX_ASYNC_HW_TIMESTAMP */
|
||||
|
||||
switch (sensor->id) {
|
||||
case ST_LSM6DSV16BX_ID_QVAR:
|
||||
err = st_lsm6dsv16bx_qvar_sensor_set_enable(sensor, enable);
|
||||
if (err < 0)
|
||||
goto out;
|
||||
break;
|
||||
case ST_LSM6DSV16BX_ID_6X_GAME:
|
||||
err = st_lsm6dsv16bx_sflp_set_enable(sensor, enable);
|
||||
if (err < 0)
|
||||
goto out;
|
||||
break;
|
||||
case ST_LSM6DSV16BX_ID_STEP_COUNTER:
|
||||
err = st_lsm6dsv16bx_step_counter_set_enable(sensor, enable);
|
||||
if (err < 0)
|
||||
goto out;
|
||||
break;
|
||||
case ST_LSM6DSV16BX_ID_TEMP: {
|
||||
u8 data = 0;
|
||||
/*
|
||||
* this is an auxiliary sensor, it need to get batched
|
||||
* toghether at least with a primary sensor (Acc/Gyro).
|
||||
*/
|
||||
if (!(hw->enable_mask & (BIT(ST_LSM6DSV16BX_ID_ACC) |
|
||||
BIT(ST_LSM6DSV16BX_ID_GYRO)))) {
|
||||
struct st_lsm6dsv16bx_sensor *acc_sensor;
|
||||
|
||||
acc_sensor = iio_priv(hw->iio_devs[ST_LSM6DSV16BX_ID_ACC]);
|
||||
if (enable) {
|
||||
err = st_lsm6dsv16bx_get_batch_val(acc_sensor,
|
||||
sensor->odr,
|
||||
sensor->uodr,
|
||||
&data);
|
||||
if (err < 0)
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* batch main sensor */
|
||||
err = st_lsm6dsv16bx_write_with_mask(hw,
|
||||
acc_sensor->batch_reg.addr,
|
||||
acc_sensor->batch_reg.mask,
|
||||
data);
|
||||
if (err < 0)
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (enable) {
|
||||
err = st_lsm6dsv16bx_get_batch_val(sensor, sensor->odr,
|
||||
sensor->uodr, &data);
|
||||
if (err < 0)
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* batch temperature sensor */
|
||||
err = st_lsm6dsv16bx_write_with_mask(hw, sensor->batch_reg.addr,
|
||||
sensor->batch_reg.mask,
|
||||
data);
|
||||
if (err < 0)
|
||||
goto out;
|
||||
|
||||
err = st_lsm6dsv16bx_sensor_set_enable(sensor, enable);
|
||||
if (err < 0)
|
||||
goto out;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
err = st_lsm6dsv16bx_sensor_set_enable(sensor, enable);
|
||||
if (err < 0)
|
||||
goto out;
|
||||
|
||||
err = st_lsm6dsv16bx_set_sensor_batching_odr(sensor, enable);
|
||||
if (err < 0)
|
||||
goto out;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
err = st_lsm6dsv16bx_update_watermark(sensor, sensor->watermark);
|
||||
if (err < 0)
|
||||
goto out;
|
||||
|
||||
if (enable && hw->fifo_mode == ST_LSM6DSV16BX_FIFO_BYPASS) {
|
||||
st_lsm6dsv16bx_reset_hwts(hw);
|
||||
err = st_lsm6dsv16bx_set_fifo_mode(hw, ST_LSM6DSV16BX_FIFO_CONT);
|
||||
} else if (!hw->enable_mask) {
|
||||
err = st_lsm6dsv16bx_set_fifo_mode(hw, ST_LSM6DSV16BX_FIFO_BYPASS);
|
||||
}
|
||||
|
||||
#if defined(CONFIG_IIO_ST_LSM6DSV16BX_ASYNC_HW_TIMESTAMP)
|
||||
if (hw->fifo_mode != ST_LSM6DSV16BX_FIFO_BYPASS) {
|
||||
hrtimer_start(&hw->timesync_timer,
|
||||
ktime_set(0, 0),
|
||||
HRTIMER_MODE_REL);
|
||||
}
|
||||
#endif /* CONFIG_IIO_ST_LSM6DSV16BX_ASYNC_HW_TIMESTAMP */
|
||||
|
||||
out:
|
||||
enable_irq(hw->irq);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static irqreturn_t st_lsm6dsv16bx_handler_irq(int irq, void *private)
|
||||
{
|
||||
struct st_lsm6dsv16bx_hw *hw = (struct st_lsm6dsv16bx_hw *)private;
|
||||
s64 ts = iio_get_time_ns(hw->iio_devs[0]);
|
||||
|
||||
hw->delta_ts = ts -hw->ts;
|
||||
hw->ts = ts;
|
||||
|
||||
return IRQ_WAKE_THREAD;
|
||||
}
|
||||
|
||||
static irqreturn_t st_lsm6dsv16bx_handler_thread(int irq, void *private)
|
||||
{
|
||||
struct st_lsm6dsv16bx_hw *hw = (struct st_lsm6dsv16bx_hw *)private;
|
||||
|
||||
if (st_lsm6dsv16bx_run_mlc_task(hw))
|
||||
st_lsm6dsv16bx_mlc_check_status(hw);
|
||||
|
||||
mutex_lock(&hw->fifo_lock);
|
||||
st_lsm6dsv16bx_read_fifo(hw);
|
||||
clear_bit(ST_LSM6DSV16BX_HW_FLUSH, &hw->state);
|
||||
mutex_unlock(&hw->fifo_lock);
|
||||
|
||||
st_lsm6dsv16bx_event_handler(hw);
|
||||
st_lsm6dsv16bx_embfunc_handler_thread(hw);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int st_lsm6dsv16bx_fifo_preenable(struct iio_dev *iio_dev)
|
||||
{
|
||||
return st_lsm6dsv16bx_update_fifo(iio_dev, true);
|
||||
}
|
||||
|
||||
static int st_lsm6dsv16bx_fifo_postdisable(struct iio_dev *iio_dev)
|
||||
{
|
||||
return st_lsm6dsv16bx_update_fifo(iio_dev, false);
|
||||
}
|
||||
|
||||
static const struct iio_buffer_setup_ops st_lsm6dsv16bx_fifo_ops = {
|
||||
.preenable = st_lsm6dsv16bx_fifo_preenable,
|
||||
.postdisable = st_lsm6dsv16bx_fifo_postdisable,
|
||||
};
|
||||
|
||||
static int st_lsm6dsv16bx_fifo_init(struct st_lsm6dsv16bx_hw *hw)
|
||||
{
|
||||
return st_lsm6dsv16bx_write_with_mask(hw,
|
||||
ST_LSM6DSV16BX_REG_FIFO_CTRL4_ADDR,
|
||||
ST_LSM6DSV16BX_DEC_TS_BATCH_MASK, 1);
|
||||
}
|
||||
|
||||
static const struct iio_trigger_ops st_lsm6dsv16bx_trigger_ops = {
|
||||
NULL
|
||||
};
|
||||
|
||||
int st_lsm6dsv16bx_buffers_setup(struct st_lsm6dsv16bx_hw *hw)
|
||||
{
|
||||
struct device_node *np = hw->dev->of_node;
|
||||
#if KERNEL_VERSION(5, 13, 0) > LINUX_VERSION_CODE
|
||||
struct iio_buffer *buffer;
|
||||
#endif /* LINUX_VERSION_CODE */
|
||||
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:
|
||||
case IRQF_TRIGGER_RISING:
|
||||
irq_active_low = false;
|
||||
break;
|
||||
case IRQF_TRIGGER_LOW:
|
||||
case IRQF_TRIGGER_FALLING:
|
||||
irq_active_low = true;
|
||||
break;
|
||||
default:
|
||||
dev_info(hw->dev, "mode %lx unsupported\n", irq_type);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
err = st_lsm6dsv16bx_write_with_mask(hw, ST_LSM6DSV16BX_REG_IF_CFG_ADDR,
|
||||
ST_LSM6DSV16BX_H_LACTIVE_MASK,
|
||||
irq_active_low);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
if (np && of_property_read_bool(np, "drive-open-drain")) {
|
||||
err = st_lsm6dsv16bx_write_with_mask(hw,
|
||||
ST_LSM6DSV16BX_REG_IF_CFG_ADDR,
|
||||
ST_LSM6DSV16BX_PP_OD_MASK, 1);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
irq_type |= IRQF_SHARED;
|
||||
}
|
||||
|
||||
err = devm_request_threaded_irq(hw->dev, hw->irq,
|
||||
st_lsm6dsv16bx_handler_irq,
|
||||
st_lsm6dsv16bx_handler_thread,
|
||||
irq_type | IRQF_ONESHOT,
|
||||
hw->settings->id.name, hw);
|
||||
if (err) {
|
||||
dev_err(hw->dev, "failed to request trigger irq %d\n",
|
||||
hw->irq);
|
||||
return err;
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(st_lsm6dsv16bx_buffered_sensor_list); i++) {
|
||||
enum st_lsm6dsv16bx_sensor_id id =
|
||||
st_lsm6dsv16bx_buffered_sensor_list[i];
|
||||
|
||||
if (!hw->iio_devs[id])
|
||||
continue;
|
||||
|
||||
#if KERNEL_VERSION(5, 19, 0) <= LINUX_VERSION_CODE
|
||||
err = devm_iio_kfifo_buffer_setup(hw->dev, hw->iio_devs[id],
|
||||
&st_lsm6dsv16bx_fifo_ops);
|
||||
if (err)
|
||||
return err;
|
||||
#elif KERNEL_VERSION(5, 13, 0) <= LINUX_VERSION_CODE
|
||||
err = devm_iio_kfifo_buffer_setup(hw->dev, hw->iio_devs[id],
|
||||
INDIO_BUFFER_SOFTWARE,
|
||||
&st_lsm6dsv16bx_fifo_ops);
|
||||
if (err)
|
||||
return err;
|
||||
#else /* LINUX_VERSION_CODE */
|
||||
buffer = devm_iio_kfifo_allocate(hw->dev);
|
||||
if (!buffer)
|
||||
return -ENOMEM;
|
||||
|
||||
/* check if already allocated (maybe qvar) */
|
||||
if (!hw->iio_devs[id]->buffer) {
|
||||
iio_device_attach_buffer(hw->iio_devs[id], buffer);
|
||||
hw->iio_devs[id]->modes |= INDIO_BUFFER_SOFTWARE;
|
||||
hw->iio_devs[id]->setup_ops = &st_lsm6dsv16bx_fifo_ops;
|
||||
}
|
||||
#endif /* LINUX_VERSION_CODE */
|
||||
|
||||
}
|
||||
|
||||
err = st_lsm6dsv16bx_hwtimesync_init(hw);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
return st_lsm6dsv16bx_fifo_init(hw);
|
||||
}
|
1917
drivers/iio/stm/imu/st_lsm6dsv16bx/st_lsm6dsv16bx_core.c
Normal file
1917
drivers/iio/stm/imu/st_lsm6dsv16bx/st_lsm6dsv16bx_core.c
Normal file
File diff suppressed because it is too large
Load Diff
579
drivers/iio/stm/imu/st_lsm6dsv16bx/st_lsm6dsv16bx_embfunc.c
Normal file
579
drivers/iio/stm/imu/st_lsm6dsv16bx/st_lsm6dsv16bx_embfunc.c
Normal file
@ -0,0 +1,579 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* STMicroelectronics st_lsm6dsv16bx embedded function sensor driver
|
||||
*
|
||||
* MEMS Software Solutions Team
|
||||
*
|
||||
* Copyright 2023 STMicroelectronics Inc.
|
||||
*/
|
||||
|
||||
#include <linux/iio/buffer.h>
|
||||
#include <linux/iio/events.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
#include <linux/iio/trigger.h>
|
||||
#include <linux/iio/triggered_buffer.h>
|
||||
#include <linux/iio/trigger_consumer.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
#include "st_lsm6dsv16bx.h"
|
||||
|
||||
/**
|
||||
* Step Counter IIO channels description
|
||||
*
|
||||
* Step Counter exports to IIO framework the following data channels:
|
||||
* Step Counters (16 bit unsigned in little endian)
|
||||
* Timestamp (64 bit signed in little endian)
|
||||
* Step Counter exports to IIO framework the following event channels:
|
||||
* Flush event done
|
||||
*/
|
||||
static const struct iio_chan_spec st_lsm6dsv16bx_step_counter_channels[] = {
|
||||
{
|
||||
.type = STM_IIO_STEP_COUNTER,
|
||||
.scan_index = 0,
|
||||
.scan_type = {
|
||||
.sign = 'u',
|
||||
.realbits = 16,
|
||||
.storagebits = 16,
|
||||
.endianness = IIO_LE,
|
||||
},
|
||||
},
|
||||
ST_LSM6DSV16BX_EVENT_CHANNEL(STM_IIO_STEP_COUNTER, flush),
|
||||
IIO_CHAN_SOFT_TIMESTAMP(1),
|
||||
};
|
||||
|
||||
/**
|
||||
* Step Detector IIO channels description
|
||||
*
|
||||
* Step Detector exports to IIO framework the following event channels:
|
||||
* Step detection event detection
|
||||
*/
|
||||
static const struct iio_chan_spec st_lsm6dsv16bx_step_detector_channels[] = {
|
||||
ST_LSM6DSV16BX_EVENT_CHANNEL(IIO_STEPS, thr),
|
||||
};
|
||||
|
||||
/**
|
||||
* Significant Motion IIO channels description
|
||||
*
|
||||
* Significant Motion exports to IIO framework the following event
|
||||
* channels:
|
||||
* Significant Motion event detection
|
||||
*/
|
||||
static const struct iio_chan_spec st_lsm6dsv16bx_sign_motion_channels[] = {
|
||||
ST_LSM6DSV16BX_EVENT_CHANNEL(STM_IIO_SIGN_MOTION, thr),
|
||||
};
|
||||
|
||||
/**
|
||||
* Tilt IIO channels description
|
||||
*
|
||||
* Tilt exports to IIO framework the following event channels:
|
||||
* Tilt event detection
|
||||
*/
|
||||
static const struct iio_chan_spec st_lsm6dsv16bx_tilt_channels[] = {
|
||||
ST_LSM6DSV16BX_EVENT_CHANNEL(STM_IIO_TILT, thr),
|
||||
};
|
||||
|
||||
static const unsigned long st_lsm6dsv16bx_embfunc_available_scan_masks[] = {
|
||||
BIT(0), 0x0
|
||||
};
|
||||
|
||||
static int
|
||||
st_lsm6dsv16bx_embfunc_set_enable(struct st_lsm6dsv16bx_sensor *sensor,
|
||||
u8 mask, u8 irq_mask, bool enable)
|
||||
{
|
||||
struct st_lsm6dsv16bx_hw *hw = sensor->hw;
|
||||
u8 int_reg = hw->int_pin == 1 ? ST_LSM6DSV16BX_REG_EMB_FUNC_INT1_ADDR :
|
||||
ST_LSM6DSV16BX_REG_EMB_FUNC_INT2_ADDR;
|
||||
int err;
|
||||
|
||||
err = st_lsm6dsv16bx_sensor_set_enable(sensor, enable);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
mutex_lock(&hw->page_lock);
|
||||
err = st_lsm6dsv16bx_set_page_access(hw,
|
||||
ST_LSM6DSV16BX_EMB_FUNC_REG_ACCESS_MASK,
|
||||
1);
|
||||
if (err < 0)
|
||||
goto unlock;
|
||||
|
||||
err = __st_lsm6dsv16bx_write_with_mask(hw,
|
||||
ST_LSM6DSV16BX_REG_EMB_FUNC_EN_A_ADDR,
|
||||
mask,
|
||||
enable ? 1 : 0);
|
||||
if (err < 0)
|
||||
goto reset_page;
|
||||
|
||||
err = __st_lsm6dsv16bx_write_with_mask(hw, int_reg, irq_mask,
|
||||
enable ? 1 : 0);
|
||||
|
||||
reset_page:
|
||||
st_lsm6dsv16bx_set_page_access(hw,
|
||||
ST_LSM6DSV16BX_EMB_FUNC_REG_ACCESS_MASK,
|
||||
0);
|
||||
|
||||
unlock:
|
||||
mutex_unlock(&hw->page_lock);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* st_lsm6dsv16bx_embfunc_sensor_set_enable() - Enable Embedded Function
|
||||
* sensor [EMB_FUN]
|
||||
*
|
||||
* @sensor: ST IMU sensor instance
|
||||
* @enable: Enable/Disable sensor
|
||||
*
|
||||
* return < 0 if error, 0 otherwise
|
||||
*/
|
||||
static int
|
||||
st_lsm6dsv16bx_embfunc_sensor_set_enable(struct st_lsm6dsv16bx_sensor *sensor,
|
||||
bool enable)
|
||||
{
|
||||
int err;
|
||||
|
||||
switch (sensor->id) {
|
||||
case ST_LSM6DSV16BX_ID_STEP_DETECTOR:
|
||||
err = st_lsm6dsv16bx_embfunc_set_enable(sensor,
|
||||
ST_LSM6DSV16BX_REG_PEDO_EN_MASK,
|
||||
ST_LSM6DSV16BX_INT_STEP_DETECTOR_MASK,
|
||||
enable);
|
||||
break;
|
||||
case ST_LSM6DSV16BX_ID_SIGN_MOTION:
|
||||
err = st_lsm6dsv16bx_embfunc_set_enable(sensor,
|
||||
ST_LSM6DSV16BX_REG_SIGN_MOTION_EN_MASK,
|
||||
ST_LSM6DSV16BX_INT_SIG_MOT_MASK,
|
||||
enable);
|
||||
break;
|
||||
case ST_LSM6DSV16BX_ID_TILT:
|
||||
err = st_lsm6dsv16bx_embfunc_set_enable(sensor,
|
||||
ST_LSM6DSV16BX_REG_TILT_EN_MASK,
|
||||
ST_LSM6DSV16BX_INT_TILT_MASK,
|
||||
enable);
|
||||
break;
|
||||
default:
|
||||
err = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* st_lsm6dsv16bx_reset_step_counter() - Reset Step Counter value [EMB_FUN]
|
||||
*
|
||||
* @iio_dev: IIO device
|
||||
*
|
||||
* return < 0 if error, 0 otherwise
|
||||
*/
|
||||
static int st_lsm6dsv16bx_reset_step_counter(struct iio_dev *iio_dev)
|
||||
{
|
||||
struct st_lsm6dsv16bx_sensor *sensor = iio_priv(iio_dev);
|
||||
struct st_lsm6dsv16bx_hw *hw = sensor->hw;
|
||||
int err;
|
||||
|
||||
mutex_lock(&hw->page_lock);
|
||||
err = st_lsm6dsv16bx_set_page_access(hw,
|
||||
ST_LSM6DSV16BX_EMB_FUNC_REG_ACCESS_MASK,
|
||||
1);
|
||||
if (err < 0)
|
||||
goto unlock_page;
|
||||
|
||||
err = __st_lsm6dsv16bx_write_with_mask(hw,
|
||||
ST_LSM6DSV16BX_REG_EMB_FUNC_SRC_ADDR,
|
||||
ST_LSM6DSV16BX_PEDO_RST_STEP_MASK, 1);
|
||||
st_lsm6dsv16bx_set_page_access(hw,
|
||||
ST_LSM6DSV16BX_EMB_FUNC_REG_ACCESS_MASK,
|
||||
0);
|
||||
|
||||
unlock_page:
|
||||
mutex_unlock(&hw->page_lock);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* st_lsm6dsv16bx_read_embfunc_config() - Read embedded function sensor
|
||||
* event configuration
|
||||
*
|
||||
* @iio_dev: IIO Device.
|
||||
* @chan: IIO Channel.
|
||||
* @type: Event Type.
|
||||
* @dir: Event Direction.
|
||||
*
|
||||
* return 1 if Enabled, 0 Disabled
|
||||
*/
|
||||
static int
|
||||
st_lsm6dsv16bx_read_embfunc_config(struct iio_dev *iio_dev,
|
||||
const struct iio_chan_spec *chan,
|
||||
enum iio_event_type type,
|
||||
enum iio_event_direction dir)
|
||||
{
|
||||
struct st_lsm6dsv16bx_sensor *sensor = iio_priv(iio_dev);
|
||||
struct st_lsm6dsv16bx_hw *hw = sensor->hw;
|
||||
|
||||
return !!(hw->enable_mask & BIT(sensor->id));
|
||||
}
|
||||
|
||||
/**
|
||||
* st_lsm6dsv16bx_write_embfunc_config() - Write embedded function
|
||||
* sensor event configuration
|
||||
*
|
||||
* @iio_dev: IIO Device.
|
||||
* @chan: IIO Channel.
|
||||
* @type: Event Type.
|
||||
* @dir: Event Direction.
|
||||
* @state: New event state.
|
||||
*
|
||||
* return 0 if OK, negative for ERROR
|
||||
*/
|
||||
static int
|
||||
st_lsm6dsv16bx_write_embfunc_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_lsm6dsv16bx_sensor *sensor = iio_priv(iio_dev);
|
||||
int err;
|
||||
|
||||
err = iio_device_claim_direct_mode(iio_dev);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = st_lsm6dsv16bx_embfunc_sensor_set_enable(sensor, state);
|
||||
iio_device_release_direct_mode(iio_dev);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* st_lsm6dsv16bx_sysfs_reset_step_counter() - Reset step counter value
|
||||
*
|
||||
* @dev: IIO Device.
|
||||
* @attr: IIO Channel attribute.
|
||||
* @buf: User buffer.
|
||||
* @size: User buffer size.
|
||||
*
|
||||
* return buffer len, negative for ERROR
|
||||
*/
|
||||
static ssize_t
|
||||
st_lsm6dsv16bx_sysfs_reset_step_counter(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t size)
|
||||
{
|
||||
struct iio_dev *iio_dev = dev_get_drvdata(dev);
|
||||
int err;
|
||||
|
||||
err = iio_device_claim_direct_mode(iio_dev);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = st_lsm6dsv16bx_reset_step_counter(iio_dev);
|
||||
iio_device_release_direct_mode(iio_dev);
|
||||
|
||||
return err < 0 ? err : size;
|
||||
}
|
||||
|
||||
static IIO_DEVICE_ATTR(reset_stepc, 0200, NULL,
|
||||
st_lsm6dsv16bx_sysfs_reset_step_counter, 0);
|
||||
|
||||
static IIO_DEVICE_ATTR(hwfifo_stepc_watermark_max, 0444,
|
||||
st_lsm6dsv16bx_get_max_watermark, NULL, 0);
|
||||
static IIO_DEVICE_ATTR(hwfifo_stepc_flush, 0200, NULL,
|
||||
st_lsm6dsv16bx_flush_fifo, 0);
|
||||
static IIO_DEVICE_ATTR(hwfifo_stepc_watermark, 0644,
|
||||
st_lsm6dsv16bx_get_watermark,
|
||||
st_lsm6dsv16bx_set_watermark, 0);
|
||||
static IIO_DEVICE_ATTR(module_id, 0444, st_lsm6dsv16bx_get_module_id, NULL, 0);
|
||||
|
||||
static struct attribute *st_lsm6dsv16bx_step_counter_attributes[] = {
|
||||
&iio_dev_attr_hwfifo_stepc_watermark_max.dev_attr.attr,
|
||||
&iio_dev_attr_hwfifo_stepc_watermark.dev_attr.attr,
|
||||
&iio_dev_attr_reset_stepc.dev_attr.attr,
|
||||
&iio_dev_attr_hwfifo_stepc_flush.dev_attr.attr,
|
||||
&iio_dev_attr_module_id.dev_attr.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static const struct
|
||||
attribute_group st_lsm6dsv16bx_step_counter_attribute_group = {
|
||||
.attrs = st_lsm6dsv16bx_step_counter_attributes,
|
||||
};
|
||||
|
||||
static const struct iio_info st_lsm6dsv16bx_step_counter_info = {
|
||||
.attrs = &st_lsm6dsv16bx_step_counter_attribute_group,
|
||||
};
|
||||
|
||||
static struct attribute *st_lsm6dsv16bx_step_detector_attributes[] = {
|
||||
&iio_dev_attr_module_id.dev_attr.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static const struct
|
||||
attribute_group st_lsm6dsv16bx_step_detector_attribute_group = {
|
||||
.attrs = st_lsm6dsv16bx_step_detector_attributes,
|
||||
};
|
||||
|
||||
static const struct iio_info st_lsm6dsv16bx_step_detector_info = {
|
||||
.attrs = &st_lsm6dsv16bx_step_detector_attribute_group,
|
||||
.read_event_config = st_lsm6dsv16bx_read_embfunc_config,
|
||||
.write_event_config = st_lsm6dsv16bx_write_embfunc_config,
|
||||
};
|
||||
|
||||
static struct attribute *st_lsm6dsv16bx_sign_motion_attributes[] = {
|
||||
&iio_dev_attr_module_id.dev_attr.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static const struct
|
||||
attribute_group st_lsm6dsv16bx_sign_motion_attribute_group = {
|
||||
.attrs = st_lsm6dsv16bx_sign_motion_attributes,
|
||||
};
|
||||
|
||||
static const struct iio_info st_lsm6dsv16bx_sign_motion_info = {
|
||||
.attrs = &st_lsm6dsv16bx_sign_motion_attribute_group,
|
||||
.read_event_config = st_lsm6dsv16bx_read_embfunc_config,
|
||||
.write_event_config = st_lsm6dsv16bx_write_embfunc_config,
|
||||
};
|
||||
|
||||
static struct attribute *st_lsm6dsv16bx_tilt_attributes[] = {
|
||||
&iio_dev_attr_module_id.dev_attr.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static const struct attribute_group st_lsm6dsv16bx_tilt_attribute_group = {
|
||||
.attrs = st_lsm6dsv16bx_tilt_attributes,
|
||||
};
|
||||
|
||||
static const struct iio_info st_lsm6dsv16bx_tilt_info = {
|
||||
.attrs = &st_lsm6dsv16bx_tilt_attribute_group,
|
||||
.read_event_config = st_lsm6dsv16bx_read_embfunc_config,
|
||||
.write_event_config = st_lsm6dsv16bx_write_embfunc_config,
|
||||
};
|
||||
|
||||
static int st_lsm6dsv16bx_embfunc_init(struct st_lsm6dsv16bx_hw *hw)
|
||||
{
|
||||
u8 int_reg = hw->int_pin == 1 ? ST_LSM6DSV16BX_REG_MD1_CFG_ADDR :
|
||||
ST_LSM6DSV16BX_REG_MD2_CFG_ADDR;
|
||||
int err;
|
||||
|
||||
mutex_lock(&hw->page_lock);
|
||||
err = st_lsm6dsv16bx_set_page_access(hw,
|
||||
ST_LSM6DSV16BX_EMB_FUNC_REG_ACCESS_MASK,
|
||||
1);
|
||||
if (err < 0)
|
||||
goto unlock_page;
|
||||
|
||||
/* enable embedded function latched interrupt */
|
||||
err = __st_lsm6dsv16bx_write_with_mask(hw,
|
||||
ST_LSM6DSV16BX_REG_PAGE_RW_ADDR,
|
||||
ST_LSM6DSV16BX_EMB_FUNC_LIR_MASK, 1);
|
||||
if (err < 0)
|
||||
goto unlock_page;
|
||||
|
||||
st_lsm6dsv16bx_set_page_access(hw,
|
||||
ST_LSM6DSV16BX_EMB_FUNC_REG_ACCESS_MASK,
|
||||
0);
|
||||
|
||||
/* enable embedded function interrupt by default */
|
||||
err = __st_lsm6dsv16bx_write_with_mask(hw, int_reg,
|
||||
ST_LSM6DSV16BX_REG_INT_EMB_FUNC_MASK,
|
||||
1);
|
||||
unlock_page:
|
||||
mutex_unlock(&hw->page_lock);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static struct iio_dev *
|
||||
st_lsm6dsv16bx_alloc_embfunc_iiodev(struct st_lsm6dsv16bx_hw *hw,
|
||||
enum st_lsm6dsv16bx_sensor_id id)
|
||||
{
|
||||
struct st_lsm6dsv16bx_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_lsm6dsv16bx_embfunc_available_scan_masks;
|
||||
|
||||
/* set main sensor odr to 26 Hz */
|
||||
sensor->odr = hw->odr_table[ST_LSM6DSV16BX_ID_ACC].odr_avl[2].hz;
|
||||
switch (id) {
|
||||
case ST_LSM6DSV16BX_ID_STEP_COUNTER:
|
||||
iio_dev->channels = st_lsm6dsv16bx_step_counter_channels;
|
||||
iio_dev->num_channels = ARRAY_SIZE(st_lsm6dsv16bx_step_counter_channels);
|
||||
scnprintf(sensor->name, sizeof(sensor->name),
|
||||
"%s_stepc", hw->settings->id.name);
|
||||
iio_dev->info = &st_lsm6dsv16bx_step_counter_info;
|
||||
break;
|
||||
case ST_LSM6DSV16BX_ID_STEP_DETECTOR:
|
||||
iio_dev->channels = st_lsm6dsv16bx_step_detector_channels;
|
||||
iio_dev->num_channels = ARRAY_SIZE(st_lsm6dsv16bx_step_detector_channels);
|
||||
scnprintf(sensor->name, sizeof(sensor->name),
|
||||
"%s_stepd", hw->settings->id.name);
|
||||
iio_dev->info = &st_lsm6dsv16bx_step_detector_info;
|
||||
break;
|
||||
case ST_LSM6DSV16BX_ID_SIGN_MOTION:
|
||||
iio_dev->channels = st_lsm6dsv16bx_sign_motion_channels;
|
||||
iio_dev->num_channels = ARRAY_SIZE(st_lsm6dsv16bx_sign_motion_channels);
|
||||
scnprintf(sensor->name, sizeof(sensor->name),
|
||||
"%s_sigmot", hw->settings->id.name);
|
||||
iio_dev->info = &st_lsm6dsv16bx_sign_motion_info;
|
||||
break;
|
||||
case ST_LSM6DSV16BX_ID_TILT:
|
||||
iio_dev->channels = st_lsm6dsv16bx_tilt_channels;
|
||||
iio_dev->num_channels = ARRAY_SIZE(st_lsm6dsv16bx_tilt_channels);
|
||||
scnprintf(sensor->name, sizeof(sensor->name),
|
||||
"%s_tilt", hw->settings->id.name);
|
||||
iio_dev->info = &st_lsm6dsv16bx_tilt_info;
|
||||
break;
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
iio_dev->name = sensor->name;
|
||||
|
||||
return iio_dev;
|
||||
}
|
||||
|
||||
/**
|
||||
* st_lsm6dsv16bx_step_counter_set_enable() - Enable Step Counter
|
||||
* Sensor [EMB_FUN]
|
||||
*
|
||||
* @sensor: ST IMU sensor instance
|
||||
* @enable: Enable/Disable sensor
|
||||
*
|
||||
* return < 0 if error, 0 otherwise
|
||||
*/
|
||||
int st_lsm6dsv16bx_step_counter_set_enable(struct st_lsm6dsv16bx_sensor *sensor,
|
||||
bool enable)
|
||||
{
|
||||
struct st_lsm6dsv16bx_hw *hw = sensor->hw;
|
||||
int err;
|
||||
|
||||
err = st_lsm6dsv16bx_sensor_set_enable(sensor, enable);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
mutex_lock(&hw->page_lock);
|
||||
err = st_lsm6dsv16bx_set_page_access(hw,
|
||||
ST_LSM6DSV16BX_EMB_FUNC_REG_ACCESS_MASK,
|
||||
1);
|
||||
if (err < 0)
|
||||
goto unlock;
|
||||
|
||||
err = __st_lsm6dsv16bx_write_with_mask(hw,
|
||||
ST_LSM6DSV16BX_REG_EMB_FUNC_EN_A_ADDR,
|
||||
ST_LSM6DSV16BX_REG_PEDO_EN_MASK,
|
||||
enable);
|
||||
if (err < 0)
|
||||
goto reset_page;
|
||||
|
||||
/* enable step counter batching in fifo */
|
||||
err = __st_lsm6dsv16bx_write_with_mask(hw,
|
||||
ST_LSM6DSV16BX_REG_EMB_FUNC_FIFO_EN_A_ADDR,
|
||||
ST_LSM6DSV16BX_STEP_COUNTER_FIFO_EN_MASK,
|
||||
enable);
|
||||
|
||||
reset_page:
|
||||
st_lsm6dsv16bx_set_page_access(hw,
|
||||
ST_LSM6DSV16BX_EMB_FUNC_REG_ACCESS_MASK,
|
||||
0);
|
||||
unlock:
|
||||
mutex_unlock(&hw->page_lock);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* st_lsm6dsv16bx_embfunc_handler_thread() - Bottom handler for embedded
|
||||
* function event detection
|
||||
*
|
||||
* @hw: ST IMU MEMS hw instance.
|
||||
*
|
||||
* return IRQ_HANDLED or < 0 for error
|
||||
*/
|
||||
int st_lsm6dsv16bx_embfunc_handler_thread(struct st_lsm6dsv16bx_hw *hw)
|
||||
{
|
||||
if (hw->enable_mask & (BIT(ST_LSM6DSV16BX_ID_STEP_DETECTOR) |
|
||||
BIT(ST_LSM6DSV16BX_ID_SIGN_MOTION) |
|
||||
BIT(ST_LSM6DSV16BX_ID_TILT))) {
|
||||
struct iio_dev *iio_dev;
|
||||
u8 status;
|
||||
s64 event;
|
||||
int err;
|
||||
|
||||
err = st_lsm6dsv16bx_read_locked(hw,
|
||||
ST_LSM6DSV16BX_REG_EMB_FUNC_STATUS_MAINPAGE_ADDR,
|
||||
&status, sizeof(status));
|
||||
if (err < 0)
|
||||
return IRQ_HANDLED;
|
||||
|
||||
/* embedded function sensors */
|
||||
if (status & ST_LSM6DSV16BX_IS_STEP_DET_MASK) {
|
||||
iio_dev = hw->iio_devs[ST_LSM6DSV16BX_ID_STEP_DETECTOR];
|
||||
event = IIO_UNMOD_EVENT_CODE(IIO_STEPS, -1,
|
||||
IIO_EV_TYPE_THRESH,
|
||||
IIO_EV_DIR_RISING);
|
||||
iio_push_event(iio_dev, event,
|
||||
iio_get_time_ns(iio_dev));
|
||||
}
|
||||
|
||||
if (status & ST_LSM6DSV16BX_IS_SIGMOT_MASK) {
|
||||
iio_dev = hw->iio_devs[ST_LSM6DSV16BX_ID_SIGN_MOTION];
|
||||
event = IIO_UNMOD_EVENT_CODE(STM_IIO_SIGN_MOTION, -1,
|
||||
IIO_EV_TYPE_THRESH,
|
||||
IIO_EV_DIR_RISING);
|
||||
iio_push_event(iio_dev, event,
|
||||
iio_get_time_ns(iio_dev));
|
||||
}
|
||||
|
||||
if (status & ST_LSM6DSV16BX_IS_TILT_MASK) {
|
||||
iio_dev = hw->iio_devs[ST_LSM6DSV16BX_ID_TILT];
|
||||
event = IIO_UNMOD_EVENT_CODE(STM_IIO_TILT, -1,
|
||||
IIO_EV_TYPE_THRESH,
|
||||
IIO_EV_DIR_RISING);
|
||||
iio_push_event(iio_dev, event,
|
||||
iio_get_time_ns(iio_dev));
|
||||
}
|
||||
}
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
/**
|
||||
* st_lsm6dsv16bx_probe_embfunc() - Allocate IIO embedded function device
|
||||
*
|
||||
* @hw: ST IMU MEMS hw instance.
|
||||
*
|
||||
* return 0 or < 0 for error
|
||||
*/
|
||||
int st_lsm6dsv16bx_probe_embfunc(struct st_lsm6dsv16bx_hw *hw)
|
||||
{
|
||||
enum st_lsm6dsv16bx_sensor_id id;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(st_lsm6dsv16bx_embfunc_sensor_list);
|
||||
i++) {
|
||||
|
||||
id = st_lsm6dsv16bx_embfunc_sensor_list[i];
|
||||
hw->iio_devs[id] = st_lsm6dsv16bx_alloc_embfunc_iiodev(hw, id);
|
||||
if (!hw->iio_devs[id])
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
return st_lsm6dsv16bx_embfunc_init(hw);
|
||||
}
|
1035
drivers/iio/stm/imu/st_lsm6dsv16bx/st_lsm6dsv16bx_events.c
Normal file
1035
drivers/iio/stm/imu/st_lsm6dsv16bx/st_lsm6dsv16bx_events.c
Normal file
File diff suppressed because it is too large
Load Diff
116
drivers/iio/stm/imu/st_lsm6dsv16bx/st_lsm6dsv16bx_hwtimestamp.c
Normal file
116
drivers/iio/stm/imu/st_lsm6dsv16bx/st_lsm6dsv16bx_hwtimestamp.c
Normal file
@ -0,0 +1,116 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* STMicroelectronics st_lsm6dsv16bx hwtimestamp library driver
|
||||
*
|
||||
* MEMS Software Solutions Team
|
||||
*
|
||||
* Copyright 2023 STMicroelectronics Inc.
|
||||
*/
|
||||
#include <asm/unaligned.h>
|
||||
#include <linux/iio/buffer.h>
|
||||
#include <linux/iio/events.h>
|
||||
#include <linux/iio/kfifo_buf.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
#include "st_lsm6dsv16bx.h"
|
||||
|
||||
#define ST_LSM6DSV16BX_TSYNC_OFFSET_NS (300 * 1000LL)
|
||||
|
||||
static void st_lsm6dsv16bx_read_hw_timestamp(struct st_lsm6dsv16bx_hw *hw)
|
||||
{
|
||||
s64 timestamp_hw_global;
|
||||
s64 eventLSB, eventMSB;
|
||||
__le32 timestamp_hw;
|
||||
s64 timestamp_cpu;
|
||||
__le32 tmp;
|
||||
int err;
|
||||
|
||||
err = st_lsm6dsv16bx_read_locked(hw, ST_LSM6DSV16BX_REG_TIMESTAMP0_ADDR,
|
||||
(u8 *)×tamp_hw,
|
||||
sizeof(timestamp_hw));
|
||||
if (err < 0)
|
||||
return;
|
||||
|
||||
timestamp_cpu = iio_get_time_ns(hw->iio_devs[0]) -
|
||||
ST_LSM6DSV16BX_TSYNC_OFFSET_NS;
|
||||
|
||||
eventLSB = IIO_EVENT_CODE(IIO_COUNT, 0, 0, 0,
|
||||
STM_IIO_EV_TYPE_TIME_SYNC, 0, 0, 0);
|
||||
eventMSB = IIO_EVENT_CODE(IIO_COUNT, 0, 0, 1,
|
||||
STM_IIO_EV_TYPE_TIME_SYNC, 0, 0, 0);
|
||||
|
||||
spin_lock_irq(&hw->hwtimestamp_lock);
|
||||
timestamp_hw_global = (hw->hw_timestamp_global & GENMASK_ULL(63, 32)) |
|
||||
(u32)le32_to_cpu(timestamp_hw);
|
||||
spin_unlock_irq(&hw->hwtimestamp_lock);
|
||||
|
||||
tmp = cpu_to_le32((u32)timestamp_hw_global);
|
||||
memcpy(&((int8_t *)&eventLSB)[0], &tmp, sizeof(tmp));
|
||||
|
||||
tmp = cpu_to_le32((u32)(timestamp_hw_global >> 32));
|
||||
memcpy(&((int8_t *)&eventMSB)[0], &tmp, sizeof(tmp));
|
||||
|
||||
if (hw->enable_mask & BIT_ULL(ST_LSM6DSV16BX_ID_GYRO)) {
|
||||
iio_push_event(hw->iio_devs[ST_LSM6DSV16BX_ID_GYRO], eventLSB,
|
||||
timestamp_cpu);
|
||||
iio_push_event(hw->iio_devs[ST_LSM6DSV16BX_ID_GYRO], eventMSB,
|
||||
timestamp_cpu);
|
||||
}
|
||||
if (hw->enable_mask & BIT_ULL(ST_LSM6DSV16BX_ID_ACC)) {
|
||||
iio_push_event(hw->iio_devs[ST_LSM6DSV16BX_ID_ACC], eventLSB,
|
||||
timestamp_cpu);
|
||||
iio_push_event(hw->iio_devs[ST_LSM6DSV16BX_ID_ACC], eventMSB,
|
||||
timestamp_cpu);
|
||||
}
|
||||
if (hw->enable_mask & BIT_ULL(ST_LSM6DSV16BX_ID_TEMP)) {
|
||||
iio_push_event(hw->iio_devs[ST_LSM6DSV16BX_ID_TEMP], eventLSB,
|
||||
timestamp_cpu);
|
||||
iio_push_event(hw->iio_devs[ST_LSM6DSV16BX_ID_TEMP], eventMSB,
|
||||
timestamp_cpu);
|
||||
}
|
||||
|
||||
if (hw->timesync_c < 6)
|
||||
hw->timesync_c++;
|
||||
else
|
||||
hw->timesync_ktime = ktime_set(0, ST_LSM6DSV16BX_DEFAULT_KTIME);
|
||||
}
|
||||
|
||||
static void st_lsm6dsv16bx_timesync_fn(struct work_struct *work)
|
||||
{
|
||||
struct st_lsm6dsv16bx_hw *hw = container_of(work, struct st_lsm6dsv16bx_hw,
|
||||
timesync_work);
|
||||
|
||||
st_lsm6dsv16bx_read_hw_timestamp(hw);
|
||||
}
|
||||
|
||||
static enum hrtimer_restart st_lsm6dsv16bx_timer_fn(struct hrtimer *timer)
|
||||
{
|
||||
struct st_lsm6dsv16bx_hw *hw;
|
||||
|
||||
hw = container_of(timer, struct st_lsm6dsv16bx_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_lsm6dsv16bx_hwtimesync_init(struct st_lsm6dsv16bx_hw *hw)
|
||||
{
|
||||
hw->timesync_c = 0;
|
||||
hw->timesync_ktime = ktime_set(0, ST_LSM6DSV16BX_DEFAULT_KTIME);
|
||||
hrtimer_init(&hw->timesync_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
|
||||
hw->timesync_timer.function = st_lsm6dsv16bx_timer_fn;
|
||||
|
||||
spin_lock_init(&hw->hwtimestamp_lock);
|
||||
hw->hw_timestamp_global = 0;
|
||||
|
||||
hw->timesync_workqueue = create_singlethread_workqueue("st_lsm6dsv16bx_workqueue");
|
||||
if (!hw->timesync_workqueue)
|
||||
return -ENOMEM;
|
||||
|
||||
INIT_WORK(&hw->timesync_work, st_lsm6dsv16bx_timesync_fn);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(st_lsm6dsv16bx_hwtimesync_init);
|
69
drivers/iio/stm/imu/st_lsm6dsv16bx/st_lsm6dsv16bx_i2c.c
Normal file
69
drivers/iio/stm/imu/st_lsm6dsv16bx/st_lsm6dsv16bx_i2c.c
Normal file
@ -0,0 +1,69 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* STMicroelectronics st_lsm6dsv16bx i2c driver
|
||||
*
|
||||
* MEMS Software Solutions Team
|
||||
*
|
||||
* Copyright 2023 STMicroelectronics Inc.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/of.h>
|
||||
|
||||
#include "st_lsm6dsv16bx.h"
|
||||
|
||||
static const struct regmap_config st_lsm6dsv16bx_i2c_regmap_config = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
};
|
||||
|
||||
static int st_lsm6dsv16bx_i2c_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
int hw_id = id->driver_data;
|
||||
struct regmap *regmap;
|
||||
|
||||
regmap = devm_regmap_init_i2c(client,
|
||||
&st_lsm6dsv16bx_i2c_regmap_config);
|
||||
if (IS_ERR(regmap)) {
|
||||
dev_err(&client->dev,
|
||||
"Failed to register i2c regmap %d\n",
|
||||
(int)PTR_ERR(regmap));
|
||||
|
||||
return PTR_ERR(regmap);
|
||||
}
|
||||
|
||||
return st_lsm6dsv16bx_probe(&client->dev, client->irq, hw_id, regmap);
|
||||
}
|
||||
|
||||
static const struct of_device_id st_lsm6dsv16bx_i2c_of_match[] = {
|
||||
{
|
||||
.compatible = "st," ST_LSM6DSV16BX_DEV_NAME,
|
||||
.data = (void *)ST_LSM6DSV16BX_ID,
|
||||
},
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, st_lsm6dsv16bx_i2c_of_match);
|
||||
|
||||
static const struct i2c_device_id st_lsm6dsv16bx_i2c_id_table[] = {
|
||||
{ ST_LSM6DSV16BX_DEV_NAME, ST_LSM6DSV16BX_ID },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, st_lsm6dsv16bx_i2c_id_table);
|
||||
|
||||
static struct i2c_driver st_lsm6dsv16bx_driver = {
|
||||
.driver = {
|
||||
.name = "st_lsm6dsv16bx_i2c",
|
||||
.pm = &st_lsm6dsv16bx_pm_ops,
|
||||
.of_match_table = of_match_ptr(st_lsm6dsv16bx_i2c_of_match),
|
||||
},
|
||||
.probe = st_lsm6dsv16bx_i2c_probe,
|
||||
.id_table = st_lsm6dsv16bx_i2c_id_table,
|
||||
};
|
||||
module_i2c_driver(st_lsm6dsv16bx_driver);
|
||||
|
||||
MODULE_AUTHOR("MEMS Software Solutions Team");
|
||||
MODULE_DESCRIPTION("STMicroelectronics st_lsm6dsv16bx i2c driver");
|
||||
MODULE_LICENSE("GPL v2");
|
60
drivers/iio/stm/imu/st_lsm6dsv16bx/st_lsm6dsv16bx_i3c.c
Normal file
60
drivers/iio/stm/imu/st_lsm6dsv16bx/st_lsm6dsv16bx_i3c.c
Normal file
@ -0,0 +1,60 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* STMicroelectronics st_lsm6dsv16bx i3c driver
|
||||
*
|
||||
* MEMS Software Solutions Team
|
||||
*
|
||||
* Copyright 2023 STMicroelectronics Inc.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/i3c/device.h>
|
||||
#include <linux/i3c/master.h>
|
||||
#include <linux/of.h>
|
||||
|
||||
#include "st_lsm6dsv16bx.h"
|
||||
|
||||
static const struct i3c_device_id st_lsm6dsv16bx_i3c_ids[] = {
|
||||
I3C_DEVICE(0x0104, ST_LSM6DSV16BX_WHOAMI_VAL, (void *)ST_LSM6DSV16BX_ID),
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i3c, st_lsm6dsv16bx_i3c_ids);
|
||||
|
||||
static int st_lsm6dsv16bx_i3c_probe(struct i3c_device *i3cdev)
|
||||
{
|
||||
struct regmap_config st_lsm6dsv16bx_i3c_regmap_config = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
};
|
||||
const struct i3c_device_id *id =
|
||||
i3c_device_match_id(i3cdev, st_lsm6dsv16bx_i3c_ids);
|
||||
struct regmap *regmap;
|
||||
|
||||
regmap = devm_regmap_init_i3c(i3cdev,
|
||||
&st_lsm6dsv16bx_i3c_regmap_config);
|
||||
if (IS_ERR(regmap)) {
|
||||
dev_err(&i3cdev->dev,
|
||||
"Failed to register i3c regmap %d\n",
|
||||
(int)PTR_ERR(regmap));
|
||||
|
||||
return PTR_ERR(regmap);
|
||||
}
|
||||
|
||||
return st_lsm6dsv16bx_probe(&i3cdev->dev, 0,
|
||||
(uintptr_t)id->data, regmap);
|
||||
}
|
||||
|
||||
static struct i3c_driver st_lsm6dsv16bx_driver = {
|
||||
.driver = {
|
||||
.name = "st_lsm6dsv16bx_i3c",
|
||||
.pm = &st_lsm6dsv16bx_pm_ops,
|
||||
},
|
||||
.probe = st_lsm6dsv16bx_i3c_probe,
|
||||
.id_table = st_lsm6dsv16bx_i3c_ids,
|
||||
};
|
||||
module_i3c_driver(st_lsm6dsv16bx_driver);
|
||||
|
||||
MODULE_AUTHOR("MEMS Software Solutions Team");
|
||||
MODULE_DESCRIPTION("STMicroelectronics st_lsm6dsv16bx i3c driver");
|
||||
MODULE_LICENSE("GPL v2");
|
847
drivers/iio/stm/imu/st_lsm6dsv16bx/st_lsm6dsv16bx_mlc.c
Normal file
847
drivers/iio/stm/imu/st_lsm6dsv16bx/st_lsm6dsv16bx_mlc.c
Normal file
@ -0,0 +1,847 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* STMicroelectronics st_lsm6dsv16bx machine learning core driver
|
||||
*
|
||||
* MEMS Software Solutions Team
|
||||
*
|
||||
* Copyright 2023 STMicroelectronics Inc.
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
#include <linux/iio/trigger.h>
|
||||
#include <linux/iio/buffer.h>
|
||||
#include <linux/iio/events.h>
|
||||
#include <linux/firmware.h>
|
||||
#include <linux/version.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include <linux/platform_data/st_sensors_pdata.h>
|
||||
|
||||
#include "st_lsm6dsv16bx.h"
|
||||
|
||||
#define ST_LSM6DSV16BX_MLC_LOADER_VERSION "0.3"
|
||||
|
||||
/* number of machine learning core available on device hardware */
|
||||
#define ST_LSM6DSV16BX_MLC_MAX_NUMBER 4
|
||||
#define ST_LSM6DSV16BX_FSM_MAX_NUMBER 8
|
||||
|
||||
#ifdef CONFIG_IIO_LSM6DSV16BX_MLC_BUILTIN_FIRMWARE
|
||||
static const u8 st_lsm6dsv16bx_mlc_fw[] = {
|
||||
#include "st_lsm6dsv16bx_mlc.fw"
|
||||
};
|
||||
DECLARE_BUILTIN_FIRMWARE(LSM6DSV16BX_MLC_FIRMWARE_NAME, st_lsm6dsv16bx_mlc_fw);
|
||||
#else /* CONFIG_IIO_LSM6DSV16BX_MLC_BUILTIN_FIRMWARE */
|
||||
#define LSM6DSV16BX_MLC_FIRMWARE_NAME "st_lsm6dsv16bx_mlc.bin"
|
||||
#endif /* CONFIG_IIO_LSM6DSV16BX_MLC_BUILTIN_FIRMWARE */
|
||||
|
||||
#ifdef CONFIG_IIO_ST_LSM6DSV16BX_MLC_PRELOAD
|
||||
#include "st_lsm6dsv16bx_preload_mlc.h"
|
||||
#endif /* CONFIG_IIO_ST_LSM6DSV16BX_MLC_PRELOAD */
|
||||
|
||||
|
||||
/* converts MLC odr to main sensor trigger odr (acc) */
|
||||
static const uint16_t mlc_odr_data[] = {
|
||||
[0x00] = 15,
|
||||
[0x01] = 30,
|
||||
[0x02] = 50,
|
||||
[0x03] = 120,
|
||||
[0x04] = 240,
|
||||
[0x05] = 480,
|
||||
[0x06] = 960,
|
||||
};
|
||||
|
||||
static const uint16_t fsm_odr_data[] = {
|
||||
[0x00] = 15,
|
||||
[0x01] = 30,
|
||||
[0x02] = 50,
|
||||
[0x03] = 120,
|
||||
[0x04] = 240,
|
||||
[0x05] = 480,
|
||||
[0x06] = 960,
|
||||
};
|
||||
|
||||
static struct iio_dev *
|
||||
st_lsm6dsv16bx_mlc_alloc_iio_dev(struct st_lsm6dsv16bx_hw *hw,
|
||||
enum st_lsm6dsv16bx_sensor_id id);
|
||||
|
||||
static const unsigned long st_lsm6dsv16bx_mlc_available_scan_masks[] = {
|
||||
0x1, 0x0
|
||||
};
|
||||
|
||||
static inline int
|
||||
st_lsm6dsv16bx_read_page_locked(struct st_lsm6dsv16bx_hw *hw, unsigned int addr,
|
||||
void *val, unsigned int len)
|
||||
{
|
||||
int err;
|
||||
|
||||
st_lsm6dsv16bx_set_page_access(hw,
|
||||
ST_LSM6DSV16BX_EMB_FUNC_REG_ACCESS_MASK,
|
||||
1);
|
||||
err = regmap_bulk_read(hw->regmap, addr, val, len);
|
||||
st_lsm6dsv16bx_set_page_access(hw,
|
||||
ST_LSM6DSV16BX_EMB_FUNC_REG_ACCESS_MASK,
|
||||
0);
|
||||
mutex_unlock(&hw->page_lock);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static inline int
|
||||
st_lsm6dsv16bx_write_page_locked(struct st_lsm6dsv16bx_hw *hw, unsigned int addr,
|
||||
unsigned int *val, unsigned int len)
|
||||
{
|
||||
int err;
|
||||
|
||||
mutex_lock(&hw->page_lock);
|
||||
st_lsm6dsv16bx_set_page_access(hw,
|
||||
ST_LSM6DSV16BX_EMB_FUNC_REG_ACCESS_MASK,
|
||||
1);
|
||||
err = regmap_bulk_write(hw->regmap, addr, val, len);
|
||||
st_lsm6dsv16bx_set_page_access(hw,
|
||||
ST_LSM6DSV16BX_EMB_FUNC_REG_ACCESS_MASK,
|
||||
0);
|
||||
mutex_unlock(&hw->page_lock);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static inline int
|
||||
st_lsm6dsv16bx_update_page_bits_locked(struct st_lsm6dsv16bx_hw *hw,
|
||||
unsigned int addr, unsigned int mask,
|
||||
unsigned int val)
|
||||
{
|
||||
int err;
|
||||
|
||||
mutex_lock(&hw->page_lock);
|
||||
st_lsm6dsv16bx_set_page_access(hw,
|
||||
ST_LSM6DSV16BX_EMB_FUNC_REG_ACCESS_MASK,
|
||||
1);
|
||||
err = regmap_update_bits(hw->regmap, addr, mask, val);
|
||||
st_lsm6dsv16bx_set_page_access(hw,
|
||||
ST_LSM6DSV16BX_EMB_FUNC_REG_ACCESS_MASK,
|
||||
0);
|
||||
mutex_unlock(&hw->page_lock);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int
|
||||
st_lsm6dsv16bx_mlc_enable_sensor(struct st_lsm6dsv16bx_sensor *sensor,
|
||||
bool enable)
|
||||
{
|
||||
struct st_lsm6dsv16bx_hw *hw = sensor->hw;
|
||||
int i, id, err = 0;
|
||||
|
||||
/* enable acc sensor as trigger */
|
||||
err = st_lsm6dsv16bx_sensor_set_enable(sensor, enable);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
if (sensor->status == ST_LSM6DSV16BX_MLC_ENABLED) {
|
||||
int value;
|
||||
|
||||
value = enable ? hw->mlc_config->mlc_int_mask : 0;
|
||||
err = st_lsm6dsv16bx_write_page_locked(hw,
|
||||
hw->mlc_config->mlc_int_addr,
|
||||
&value, 1);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
/*
|
||||
* enable mlc core
|
||||
* only one mlc so not need to check if other running
|
||||
*/
|
||||
err = st_lsm6dsv16bx_update_page_bits_locked(hw,
|
||||
ST_LSM6DSV16BX_REG_EMB_FUNC_EN_B_ADDR,
|
||||
ST_LSM6DSV16BX_MLC_EN_MASK,
|
||||
ST_LSM6DSV16BX_SHIFT_VAL(enable,
|
||||
ST_LSM6DSV16BX_MLC_EN_MASK));
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
dev_info(sensor->hw->dev,
|
||||
"Enabling MLC sensor %d to %d (INT %x)\n",
|
||||
sensor->id, enable, value);
|
||||
} else if (sensor->status == ST_LSM6DSV16BX_FSM_ENABLED) {
|
||||
int value;
|
||||
|
||||
value = enable ? hw->mlc_config->fsm_int_mask : 0;
|
||||
err = st_lsm6dsv16bx_write_page_locked(hw,
|
||||
hw->mlc_config->fsm_int_addr,
|
||||
&value, 1);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
/* enable fsm core */
|
||||
for (i = 0; i < ST_LSM6DSV16BX_FSM_MAX_NUMBER; i++) {
|
||||
id = st_lsm6dsv16bx_fsm_sensor_list[i];
|
||||
if (hw->enable_mask & BIT(id))
|
||||
break;
|
||||
}
|
||||
|
||||
/* check for any other fsm already enabled */
|
||||
if (enable || i == ST_LSM6DSV16BX_FSM_MAX_NUMBER) {
|
||||
err = st_lsm6dsv16bx_update_page_bits_locked(hw,
|
||||
ST_LSM6DSV16BX_REG_EMB_FUNC_EN_B_ADDR,
|
||||
ST_LSM6DSV16BX_FSM_EN_MASK,
|
||||
ST_LSM6DSV16BX_SHIFT_VAL(enable,
|
||||
ST_LSM6DSV16BX_FSM_EN_MASK));
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
|
||||
dev_info(sensor->hw->dev,
|
||||
"Enabling FSM sensor %d to %d (INT %x)\n",
|
||||
sensor->id, enable, value);
|
||||
} else {
|
||||
dev_err(hw->dev, "invalid sensor configuration\n");
|
||||
err = -ENODEV;
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
if (enable)
|
||||
hw->enable_mask |= BIT(sensor->id);
|
||||
else
|
||||
hw->enable_mask &= ~BIT(sensor->id);
|
||||
|
||||
return err < 0 ? err : 0;
|
||||
}
|
||||
|
||||
static int
|
||||
st_lsm6dsv16bx_mlc_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_lsm6dsv16bx_sensor *sensor = iio_priv(iio_dev);
|
||||
|
||||
return st_lsm6dsv16bx_mlc_enable_sensor(sensor, state);
|
||||
}
|
||||
|
||||
static int
|
||||
st_lsm6dsv16bx_mlc_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_lsm6dsv16bx_sensor *sensor = iio_priv(iio_dev);
|
||||
struct st_lsm6dsv16bx_hw *hw = sensor->hw;
|
||||
|
||||
return !!(hw->enable_mask & BIT(sensor->id));
|
||||
}
|
||||
|
||||
/*
|
||||
* st_lsm6dsv16bx_verify_mlc_fsm_support - Verify device supports MLC/FSM
|
||||
*
|
||||
* Before to load a MLC/FSM configuration check the MLC/FSM HW block
|
||||
* available for this hw device id.
|
||||
*/
|
||||
static int st_lsm6dsv16bx_verify_mlc_fsm_support(const struct firmware *fw,
|
||||
struct st_lsm6dsv16bx_hw *hw)
|
||||
{
|
||||
bool stmc_page = false;
|
||||
u8 reg, val;
|
||||
int i = 0;
|
||||
|
||||
while (i < fw->size) {
|
||||
reg = fw->data[i++];
|
||||
val = fw->data[i++];
|
||||
|
||||
if (reg == ST_LSM6DSV16BX_REG_FUNC_CFG_ACCESS_ADDR &&
|
||||
(val & ST_LSM6DSV16BX_EMB_FUNC_REG_ACCESS_MASK)) {
|
||||
stmc_page = true;
|
||||
} else if (reg == ST_LSM6DSV16BX_REG_FUNC_CFG_ACCESS_ADDR &&
|
||||
(val & ~ST_LSM6DSV16BX_EMB_FUNC_REG_ACCESS_MASK)) {
|
||||
stmc_page = false;
|
||||
} else if (stmc_page) {
|
||||
switch (reg) {
|
||||
case ST_LSM6DSV16BX_REG_MLC_INT1_ADDR:
|
||||
case ST_LSM6DSV16BX_REG_MLC_INT2_ADDR:
|
||||
if (!hw->settings->st_mlc_probe)
|
||||
return -ENODEV;
|
||||
break;
|
||||
case ST_LSM6DSV16BX_REG_FSM_INT1_ADDR:
|
||||
case ST_LSM6DSV16BX_REG_FSM_INT2_ADDR:
|
||||
if (!hw->settings->st_fsm_probe)
|
||||
return -ENODEV;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* parse and program mlc fragments */
|
||||
static int st_lsm6dsv16bx_program_mlc(const struct firmware *fw,
|
||||
struct st_lsm6dsv16bx_hw *hw)
|
||||
{
|
||||
u8 mlc_int = 0, mlc_num = 0, fsm_num = 0, skip = 0;
|
||||
u8 fsm_int = 0, reg, val, req_odr = 0;
|
||||
bool stmc_page = false;
|
||||
int ret, i = 0;
|
||||
|
||||
while (i < fw->size) {
|
||||
reg = fw->data[i++];
|
||||
val = fw->data[i++];
|
||||
|
||||
if (reg == ST_LSM6DSV16BX_REG_FUNC_CFG_ACCESS_ADDR &&
|
||||
(val & ST_LSM6DSV16BX_EMB_FUNC_REG_ACCESS_MASK)) {
|
||||
stmc_page = true;
|
||||
} else if (reg == ST_LSM6DSV16BX_REG_FUNC_CFG_ACCESS_ADDR &&
|
||||
(val & ~ST_LSM6DSV16BX_EMB_FUNC_REG_ACCESS_MASK)) {
|
||||
stmc_page = false;
|
||||
} else if (stmc_page) {
|
||||
switch (reg) {
|
||||
case ST_LSM6DSV16BX_REG_MLC_INT1_ADDR:
|
||||
case ST_LSM6DSV16BX_REG_MLC_INT2_ADDR:
|
||||
mlc_int |= val;
|
||||
mlc_num++;
|
||||
skip = 1;
|
||||
break;
|
||||
case ST_LSM6DSV16BX_REG_FSM_INT1_ADDR:
|
||||
case ST_LSM6DSV16BX_REG_FSM_INT2_ADDR:
|
||||
fsm_int |= val;
|
||||
fsm_num++;
|
||||
skip = 1;
|
||||
break;
|
||||
case ST_LSM6DSV16BX_REG_EMB_FUNC_EN_B_ADDR:
|
||||
skip = 1;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} else if (reg == ST_LSM6DSV16BX_REG_CTRL1_ADDR) {
|
||||
/* save required xl odr and skip write to reg */
|
||||
req_odr = max_t(u8, req_odr, (val & GENMASK(3, 0)));
|
||||
skip = 1;
|
||||
}
|
||||
|
||||
if (!skip) {
|
||||
ret = regmap_write(hw->regmap, reg, val);
|
||||
if (ret) {
|
||||
dev_err(hw->dev,
|
||||
"regmap_write fails\n");
|
||||
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
skip = 0;
|
||||
|
||||
if (mlc_num >= ST_LSM6DSV16BX_MLC_MAX_NUMBER ||
|
||||
fsm_num >= ST_LSM6DSV16BX_FSM_MAX_NUMBER)
|
||||
break;
|
||||
}
|
||||
|
||||
hw->mlc_config->bin_len = fw->size;
|
||||
|
||||
if (mlc_num) {
|
||||
hw->mlc_config->mlc_int_mask = mlc_int;
|
||||
hw->mlc_config->mlc_int_addr = (hw->int_pin == 1 ?
|
||||
ST_LSM6DSV16BX_REG_MLC_INT1_ADDR :
|
||||
ST_LSM6DSV16BX_REG_MLC_INT2_ADDR);
|
||||
|
||||
hw->mlc_config->status |= ST_LSM6DSV16BX_MLC_ENABLED;
|
||||
hw->mlc_config->mlc_configured += mlc_num;
|
||||
hw->mlc_config->requested_odr = mlc_odr_data[req_odr];
|
||||
}
|
||||
|
||||
if (fsm_num) {
|
||||
hw->mlc_config->fsm_int_mask = fsm_int;
|
||||
hw->mlc_config->fsm_int_addr = (hw->int_pin == 1 ?
|
||||
ST_LSM6DSV16BX_REG_FSM_INT1_ADDR :
|
||||
ST_LSM6DSV16BX_REG_FSM_INT2_ADDR);
|
||||
|
||||
hw->mlc_config->status |= ST_LSM6DSV16BX_FSM_ENABLED;
|
||||
hw->mlc_config->fsm_configured += fsm_num;
|
||||
hw->mlc_config->requested_odr = fsm_odr_data[req_odr];
|
||||
}
|
||||
|
||||
return fsm_num + mlc_num;
|
||||
}
|
||||
|
||||
static void st_lsm6dsv16bx_mlc_update(const struct firmware *fw, void *context)
|
||||
{
|
||||
struct st_lsm6dsv16bx_hw *hw = context;
|
||||
enum st_lsm6dsv16bx_sensor_id id;
|
||||
int ret, i;
|
||||
|
||||
if (!fw) {
|
||||
dev_err(hw->dev, "could not get binary firmware\n");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
ret = st_lsm6dsv16bx_verify_mlc_fsm_support(fw, hw);
|
||||
if (ret) {
|
||||
dev_err(hw->dev, "invalid file format for device\n");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
ret = st_lsm6dsv16bx_program_mlc(fw, hw);
|
||||
if (ret > 0) {
|
||||
u8 fsm_mask = hw->mlc_config->fsm_int_mask;
|
||||
u8 mlc_mask = hw->mlc_config->mlc_int_mask;
|
||||
|
||||
dev_info(hw->dev,
|
||||
"MLC loaded (%d) MLC %01x FSM %02x\n",
|
||||
ret, mlc_mask, fsm_mask);
|
||||
|
||||
for (i = 0; i < ST_LSM6DSV16BX_MLC_MAX_NUMBER; i++) {
|
||||
if (mlc_mask & BIT(i)) {
|
||||
id = st_lsm6dsv16bx_mlc_sensor_list[i];
|
||||
hw->iio_devs[id] =
|
||||
st_lsm6dsv16bx_mlc_alloc_iio_dev(hw, id);
|
||||
if (!hw->iio_devs[id])
|
||||
goto release;
|
||||
|
||||
ret = iio_device_register(hw->iio_devs[id]);
|
||||
if (ret)
|
||||
goto release;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < ST_LSM6DSV16BX_FSM_MAX_NUMBER; i++) {
|
||||
if (fsm_mask & BIT(i)) {
|
||||
id = st_lsm6dsv16bx_fsm_sensor_list[i];
|
||||
hw->iio_devs[id] =
|
||||
st_lsm6dsv16bx_mlc_alloc_iio_dev(hw, id);
|
||||
if (!hw->iio_devs[id])
|
||||
goto release;
|
||||
|
||||
ret = iio_device_register(hw->iio_devs[id]);
|
||||
if (ret)
|
||||
goto release;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
release:
|
||||
/*
|
||||
* internal firmware don't release it because stored in
|
||||
* const segment
|
||||
*/
|
||||
if (hw->preload_mlc) {
|
||||
hw->preload_mlc = 0;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
release_firmware(fw);
|
||||
}
|
||||
|
||||
static int st_lsm6dsv16bx_mlc_flush_single(struct st_lsm6dsv16bx_hw *hw,
|
||||
enum st_lsm6dsv16bx_sensor_id id)
|
||||
{
|
||||
struct st_lsm6dsv16bx_sensor *sensor_mlc;
|
||||
struct iio_dev *iio_dev;
|
||||
int ret;
|
||||
|
||||
iio_dev = hw->iio_devs[id];
|
||||
if (!iio_dev)
|
||||
return -ENODEV;
|
||||
|
||||
sensor_mlc = iio_priv(iio_dev);
|
||||
ret = st_lsm6dsv16bx_mlc_enable_sensor(sensor_mlc, false);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
iio_device_unregister(iio_dev);
|
||||
kfree(iio_dev->channels);
|
||||
iio_device_free(iio_dev);
|
||||
hw->iio_devs[id] = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int st_lsm6dsv16bx_mlc_flush_all(struct st_lsm6dsv16bx_hw *hw)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(st_lsm6dsv16bx_mlc_sensor_list); i++)
|
||||
st_lsm6dsv16bx_mlc_flush_single(hw, st_lsm6dsv16bx_mlc_sensor_list[i]);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(st_lsm6dsv16bx_fsm_sensor_list); i++)
|
||||
st_lsm6dsv16bx_mlc_flush_single(hw, st_lsm6dsv16bx_fsm_sensor_list[i]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t st_lsm6dsv16bx_mlc_info(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct st_lsm6dsv16bx_sensor *sensor = iio_priv(dev_get_drvdata(dev));
|
||||
struct st_lsm6dsv16bx_hw *hw = sensor->hw;
|
||||
|
||||
return scnprintf(buf, PAGE_SIZE, "mlc %02x fsm %02x\n",
|
||||
hw->mlc_config->mlc_configured,
|
||||
hw->mlc_config->fsm_configured);
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
st_lsm6dsv16bx_mlc_get_version(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
return scnprintf(buf, PAGE_SIZE, "mlc loader Version %s\n",
|
||||
ST_LSM6DSV16BX_MLC_LOADER_VERSION);
|
||||
}
|
||||
|
||||
static ssize_t st_lsm6dsv16bx_mlc_flush(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t size)
|
||||
{
|
||||
struct st_lsm6dsv16bx_sensor *sensor = iio_priv(dev_get_drvdata(dev));
|
||||
struct st_lsm6dsv16bx_hw *hw = sensor->hw;
|
||||
int ret;
|
||||
|
||||
ret = st_lsm6dsv16bx_mlc_flush_all(hw);
|
||||
memset(hw->mlc_config, 0, sizeof(*hw->mlc_config));
|
||||
|
||||
return ret < 0 ? ret : size;
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
st_lsm6dsv16bx_mlc_upload_firmware(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t size)
|
||||
{
|
||||
struct st_lsm6dsv16bx_sensor *sensor = iio_priv(dev_get_drvdata(dev));
|
||||
int err;
|
||||
|
||||
err = request_firmware_nowait(THIS_MODULE, true,
|
||||
LSM6DSV16BX_MLC_FIRMWARE_NAME,
|
||||
dev, GFP_KERNEL,
|
||||
sensor->hw,
|
||||
st_lsm6dsv16bx_mlc_update);
|
||||
|
||||
return err < 0 ? err : size;
|
||||
}
|
||||
|
||||
static ssize_t st_lsm6dsv16bx_mlc_odr(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct st_lsm6dsv16bx_sensor *sensor = iio_priv(dev_get_drvdata(dev));
|
||||
struct st_lsm6dsv16bx_hw *hw = sensor->hw;
|
||||
|
||||
return scnprintf(buf, PAGE_SIZE, "%d\n",
|
||||
hw->mlc_config->requested_odr);
|
||||
}
|
||||
|
||||
static IIO_DEVICE_ATTR(config_info, 0444, st_lsm6dsv16bx_mlc_info, NULL, 0);
|
||||
static IIO_DEVICE_ATTR(flush_config, 0200, NULL, st_lsm6dsv16bx_mlc_flush, 0);
|
||||
static IIO_DEVICE_ATTR(loader_version, 0444,
|
||||
st_lsm6dsv16bx_mlc_get_version, NULL, 0);
|
||||
static IIO_DEVICE_ATTR(load_mlc, 0200,
|
||||
NULL, st_lsm6dsv16bx_mlc_upload_firmware, 0);
|
||||
static IIO_DEVICE_ATTR(odr, 0444, st_lsm6dsv16bx_mlc_odr, NULL, 0);
|
||||
static IIO_DEVICE_ATTR(module_id, 0444, st_lsm6dsv16bx_get_module_id, NULL, 0);
|
||||
|
||||
static struct attribute *st_lsm6dsv16bx_mlc_event_attributes[] = {
|
||||
&iio_dev_attr_config_info.dev_attr.attr,
|
||||
&iio_dev_attr_loader_version.dev_attr.attr,
|
||||
&iio_dev_attr_load_mlc.dev_attr.attr,
|
||||
&iio_dev_attr_flush_config.dev_attr.attr,
|
||||
&iio_dev_attr_odr.dev_attr.attr,
|
||||
&iio_dev_attr_module_id.dev_attr.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static const struct attribute_group
|
||||
st_lsm6dsv16bx_mlc_event_attribute_group = {
|
||||
.attrs = st_lsm6dsv16bx_mlc_event_attributes,
|
||||
};
|
||||
|
||||
static const struct iio_info st_lsm6dsv16bx_mlc_event_info = {
|
||||
.attrs = &st_lsm6dsv16bx_mlc_event_attribute_group,
|
||||
.read_event_config = st_lsm6dsv16bx_mlc_read_event_config,
|
||||
.write_event_config = st_lsm6dsv16bx_mlc_write_event_config,
|
||||
};
|
||||
|
||||
static ssize_t st_lsm6dsv16bx_mlc_x_odr(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct st_lsm6dsv16bx_sensor *sensor = iio_priv(dev_get_drvdata(dev));
|
||||
|
||||
return scnprintf(buf, PAGE_SIZE, "%d.%02d\n",
|
||||
sensor->odr, sensor->uodr);
|
||||
}
|
||||
|
||||
static IIO_DEVICE_ATTR(odr_x, 0444, st_lsm6dsv16bx_mlc_x_odr, NULL, 0);
|
||||
|
||||
static struct attribute *st_lsm6dsv16bx_mlc_x_event_attributes[] = {
|
||||
&iio_dev_attr_odr_x.dev_attr.attr,
|
||||
&iio_dev_attr_module_id.dev_attr.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static const struct attribute_group
|
||||
st_lsm6dsv16bx_mlc_x_event_attribute_group = {
|
||||
.attrs = st_lsm6dsv16bx_mlc_x_event_attributes,
|
||||
};
|
||||
static const struct iio_info st_lsm6dsv16bx_mlc_x_event_info = {
|
||||
.attrs = &st_lsm6dsv16bx_mlc_x_event_attribute_group,
|
||||
.read_event_config = st_lsm6dsv16bx_mlc_read_event_config,
|
||||
.write_event_config = st_lsm6dsv16bx_mlc_write_event_config,
|
||||
};
|
||||
|
||||
static struct iio_dev *
|
||||
st_lsm6dsv16bx_mlc_alloc_iio_dev(struct st_lsm6dsv16bx_hw *hw,
|
||||
enum st_lsm6dsv16bx_sensor_id id)
|
||||
{
|
||||
struct st_lsm6dsv16bx_sensor *sensor;
|
||||
struct iio_chan_spec *channels;
|
||||
struct iio_dev *iio_dev;
|
||||
|
||||
/* devm management only for ST_LSM6DSV16BX_ID_MLC */
|
||||
if (id == ST_LSM6DSV16BX_ID_MLC) {
|
||||
iio_dev = devm_iio_device_alloc(hw->dev, sizeof(*sensor));
|
||||
} else {
|
||||
#if KERNEL_VERSION(5, 9, 0) <= LINUX_VERSION_CODE
|
||||
iio_dev = iio_device_alloc(NULL, sizeof(*sensor));
|
||||
#else /* LINUX_VERSION_CODE */
|
||||
iio_dev = iio_device_alloc(sizeof(*sensor));
|
||||
#endif /* LINUX_VERSION_CODE */
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
switch (id) {
|
||||
case ST_LSM6DSV16BX_ID_MLC: {
|
||||
const struct iio_chan_spec st_lsm6dsv16bx_mlc_channels[] = {
|
||||
ST_LSM6DSV16BX_EVENT_CHANNEL(IIO_ACTIVITY, thr),
|
||||
};
|
||||
|
||||
channels = devm_kzalloc(hw->dev,
|
||||
sizeof(st_lsm6dsv16bx_mlc_channels),
|
||||
GFP_KERNEL);
|
||||
if (!channels)
|
||||
return NULL;
|
||||
|
||||
memcpy(channels, st_lsm6dsv16bx_mlc_channels,
|
||||
sizeof(st_lsm6dsv16bx_mlc_channels));
|
||||
|
||||
iio_dev->available_scan_masks =
|
||||
st_lsm6dsv16bx_mlc_available_scan_masks;
|
||||
iio_dev->channels = channels;
|
||||
iio_dev->num_channels = ARRAY_SIZE(st_lsm6dsv16bx_mlc_channels);
|
||||
iio_dev->info = &st_lsm6dsv16bx_mlc_event_info;
|
||||
scnprintf(sensor->name, sizeof(sensor->name),
|
||||
"%s_loader", hw->settings->id.name);
|
||||
break;
|
||||
}
|
||||
case ST_LSM6DSV16BX_ID_MLC_0:
|
||||
case ST_LSM6DSV16BX_ID_MLC_1:
|
||||
case ST_LSM6DSV16BX_ID_MLC_2:
|
||||
case ST_LSM6DSV16BX_ID_MLC_3: {
|
||||
const struct iio_chan_spec st_lsm6dsv16bx_mlc_x_ch[] = {
|
||||
ST_LSM6DSV16BX_EVENT_CHANNEL(IIO_ACTIVITY, thr),
|
||||
};
|
||||
|
||||
channels = kzalloc(sizeof(st_lsm6dsv16bx_mlc_x_ch), GFP_KERNEL);
|
||||
if (!channels)
|
||||
return NULL;
|
||||
|
||||
memcpy(channels, st_lsm6dsv16bx_mlc_x_ch,
|
||||
sizeof(st_lsm6dsv16bx_mlc_x_ch));
|
||||
|
||||
iio_dev->available_scan_masks =
|
||||
st_lsm6dsv16bx_mlc_available_scan_masks;
|
||||
iio_dev->channels = channels;
|
||||
iio_dev->num_channels = ARRAY_SIZE(st_lsm6dsv16bx_mlc_x_ch);
|
||||
iio_dev->info = &st_lsm6dsv16bx_mlc_x_event_info;
|
||||
scnprintf(sensor->name, sizeof(sensor->name),
|
||||
"%s_mlc_%d", hw->settings->id.name,
|
||||
id - ST_LSM6DSV16BX_ID_MLC_0);
|
||||
sensor->outreg_addr = ST_LSM6DSV16BX_REG_MLC1_SRC_ADDR +
|
||||
id - ST_LSM6DSV16BX_ID_MLC_0;
|
||||
sensor->status = ST_LSM6DSV16BX_MLC_ENABLED;
|
||||
sensor->odr = hw->mlc_config->requested_odr;
|
||||
sensor->uodr = 0;
|
||||
|
||||
break;
|
||||
}
|
||||
case ST_LSM6DSV16BX_ID_FSM_0:
|
||||
case ST_LSM6DSV16BX_ID_FSM_1:
|
||||
case ST_LSM6DSV16BX_ID_FSM_2:
|
||||
case ST_LSM6DSV16BX_ID_FSM_3:
|
||||
case ST_LSM6DSV16BX_ID_FSM_4:
|
||||
case ST_LSM6DSV16BX_ID_FSM_5:
|
||||
case ST_LSM6DSV16BX_ID_FSM_6:
|
||||
case ST_LSM6DSV16BX_ID_FSM_7: {
|
||||
const struct iio_chan_spec st_lsm6dsv16bx_fsm_x_ch[] = {
|
||||
ST_LSM6DSV16BX_EVENT_CHANNEL(IIO_ACTIVITY, thr),
|
||||
};
|
||||
|
||||
channels = kzalloc(sizeof(st_lsm6dsv16bx_fsm_x_ch), GFP_KERNEL);
|
||||
if (!channels)
|
||||
return NULL;
|
||||
|
||||
memcpy(channels, st_lsm6dsv16bx_fsm_x_ch,
|
||||
sizeof(st_lsm6dsv16bx_fsm_x_ch));
|
||||
|
||||
iio_dev->available_scan_masks =
|
||||
st_lsm6dsv16bx_mlc_available_scan_masks;
|
||||
iio_dev->channels = channels;
|
||||
iio_dev->num_channels = ARRAY_SIZE(st_lsm6dsv16bx_fsm_x_ch);
|
||||
iio_dev->info = &st_lsm6dsv16bx_mlc_x_event_info;
|
||||
scnprintf(sensor->name, sizeof(sensor->name),
|
||||
"%s_fsm_%d", hw->settings->id.name,
|
||||
id - ST_LSM6DSV16BX_ID_FSM_0);
|
||||
sensor->outreg_addr = ST_LSM6DSV16BX_REG_FSM_OUTS1_ADDR +
|
||||
id - ST_LSM6DSV16BX_ID_FSM_0;
|
||||
sensor->status = ST_LSM6DSV16BX_FSM_ENABLED;
|
||||
sensor->odr = hw->mlc_config->requested_odr;
|
||||
sensor->uodr = 0;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
dev_err(hw->dev, "invalid sensor id %d\n", id);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
iio_dev->name = sensor->name;
|
||||
|
||||
return iio_dev;
|
||||
}
|
||||
|
||||
int st_lsm6dsv16bx_mlc_check_status(struct st_lsm6dsv16bx_hw *hw)
|
||||
{
|
||||
struct st_lsm6dsv16bx_sensor *sensor;
|
||||
u8 i, mlc_status, id, event[8];
|
||||
struct iio_dev *iio_dev;
|
||||
u8 fsm_status;
|
||||
int err = 0;
|
||||
|
||||
if (hw->mlc_config->status & ST_LSM6DSV16BX_MLC_ENABLED) {
|
||||
err = st_lsm6dsv16bx_read_locked(hw,
|
||||
ST_LSM6DSV16BX_REG_MLC_STATUS_MAINPAGE_ADDR,
|
||||
(void *)&mlc_status, 1);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (mlc_status) {
|
||||
for (i = 0; i < ST_LSM6DSV16BX_MLC_MAX_NUMBER; i++) {
|
||||
id = st_lsm6dsv16bx_mlc_sensor_list[i];
|
||||
if (!(hw->enable_mask & BIT(id)))
|
||||
continue;
|
||||
|
||||
if (mlc_status & BIT(i)) {
|
||||
iio_dev = hw->iio_devs[id];
|
||||
if (!iio_dev) {
|
||||
err = -ENOENT;
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
sensor = iio_priv(iio_dev);
|
||||
err = st_lsm6dsv16bx_read_page_locked(hw,
|
||||
sensor->outreg_addr,
|
||||
(void *)&event[i], 1);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
iio_push_event(iio_dev, (u64)event[i],
|
||||
iio_get_time_ns(iio_dev));
|
||||
|
||||
dev_info(hw->dev,
|
||||
"MLC %d Status %x MLC EVENT %llx\n",
|
||||
id, mlc_status, (u64)event[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (hw->mlc_config->status & ST_LSM6DSV16BX_FSM_ENABLED) {
|
||||
err = st_lsm6dsv16bx_read_locked(hw,
|
||||
ST_LSM6DSV16BX_REG_FSM_STATUS_MAINPAGE_ADDR,
|
||||
(void *)&fsm_status, 1);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (fsm_status) {
|
||||
for (i = 0; i < ST_LSM6DSV16BX_FSM_MAX_NUMBER; i++) {
|
||||
id = st_lsm6dsv16bx_fsm_sensor_list[i];
|
||||
if (!(hw->enable_mask & BIT(id)))
|
||||
continue;
|
||||
|
||||
if (fsm_status & BIT(i)) {
|
||||
iio_dev = hw->iio_devs[id];
|
||||
if (!iio_dev) {
|
||||
err = -ENOENT;
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
sensor = iio_priv(iio_dev);
|
||||
err = st_lsm6dsv16bx_read_page_locked(hw,
|
||||
sensor->outreg_addr,
|
||||
(void *)&event[i], 1);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
iio_push_event(iio_dev, (u64)event[i],
|
||||
iio_get_time_ns(iio_dev));
|
||||
|
||||
dev_info(hw->dev,
|
||||
"FSM %d Status %x FSM EVENT %llx\n",
|
||||
id, mlc_status, (u64)event[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
int st_lsm6dsv16bx_mlc_init_preload(struct st_lsm6dsv16bx_hw *hw)
|
||||
{
|
||||
#ifdef CONFIG_IIO_ST_LSM6DSV16BX_MLC_PRELOAD
|
||||
hw->preload_mlc = 1;
|
||||
st_lsm6dsv16bx_mlc_update(&st_lsm6dsv16bx_mlc_preload, hw);
|
||||
#endif /* CONFIG_IIO_ST_LSM6DSV16BX_MLC_PRELOAD */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int st_lsm6dsv16bx_mlc_probe(struct st_lsm6dsv16bx_hw *hw)
|
||||
{
|
||||
hw->iio_devs[ST_LSM6DSV16BX_ID_MLC] =
|
||||
st_lsm6dsv16bx_mlc_alloc_iio_dev(hw, ST_LSM6DSV16BX_ID_MLC);
|
||||
if (!hw->iio_devs[ST_LSM6DSV16BX_ID_MLC])
|
||||
return -ENOMEM;
|
||||
|
||||
hw->mlc_config = devm_kzalloc(hw->dev,
|
||||
sizeof(struct st_lsm6dsv16bx_mlc_config_t),
|
||||
GFP_KERNEL);
|
||||
if (!hw->mlc_config)
|
||||
return -ENOMEM;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int st_lsm6dsv16bx_mlc_remove(struct device *dev)
|
||||
{
|
||||
struct st_lsm6dsv16bx_hw *hw = dev_get_drvdata(dev);
|
||||
|
||||
return st_lsm6dsv16bx_mlc_flush_all(hw);
|
||||
}
|
||||
EXPORT_SYMBOL(st_lsm6dsv16bx_mlc_remove);
|
@ -0,0 +1,20 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* MEMS Software Solutions Team
|
||||
*
|
||||
* Copyright 2023 STMicroelectronics Inc.
|
||||
*/
|
||||
|
||||
#ifndef ST_LSM6DSV16BX_PRELOAD_MLC_H
|
||||
#define ST_LSM6DSV16BX_PRELOAD_MLC_H
|
||||
|
||||
static const u8 mlcdata[] = {
|
||||
/* put here MLC/FSM configuration */
|
||||
};
|
||||
|
||||
static struct firmware st_lsm6dsv16bx_mlc_preload = {
|
||||
.size = sizeof(mlcdata),
|
||||
.data = mlcdata
|
||||
};
|
||||
|
||||
#endif /* ST_LSM6DSV16BX_PRELOAD_MLC_H */
|
191
drivers/iio/stm/imu/st_lsm6dsv16bx/st_lsm6dsv16bx_qvar.c
Normal file
191
drivers/iio/stm/imu/st_lsm6dsv16bx/st_lsm6dsv16bx_qvar.c
Normal file
@ -0,0 +1,191 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* STMicroelectronics st_lsm6dsv16bx qvar sensor driver
|
||||
*
|
||||
* MEMS Software Solutions Team
|
||||
*
|
||||
* Copyright 2023 STMicroelectronics Inc.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/iio/buffer.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/kfifo_buf.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/pm.h>
|
||||
#include <linux/workqueue.h>
|
||||
|
||||
#include "st_lsm6dsv16bx.h"
|
||||
|
||||
static const struct st_lsm6dsv16bx_odr_table_entry
|
||||
st_lsm6dsv16bx_qvar_odr_table = {
|
||||
.size = 1,
|
||||
.odr_avl[0] = { 240, 0, 0x00, 0x00 },
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec st_lsm6dsv16bx_qvar_channels[] = {
|
||||
{
|
||||
.type = IIO_ALTVOLTAGE,
|
||||
.address = ST_LSM6DSV16BX_REG_OUT_QVAR_ADDR,
|
||||
.scan_index = 0,
|
||||
.scan_type = {
|
||||
.sign = 's',
|
||||
.realbits = 16,
|
||||
.storagebits = 16,
|
||||
.endianness = IIO_LE,
|
||||
}
|
||||
},
|
||||
IIO_CHAN_SOFT_TIMESTAMP(1),
|
||||
};
|
||||
|
||||
static int st_lsm6dsv16bx_qvar_init(struct st_lsm6dsv16bx_hw *hw)
|
||||
{
|
||||
int err;
|
||||
|
||||
/* enable fifo batching by default */
|
||||
err = st_lsm6dsv16bx_write_with_mask(hw,
|
||||
ST_LSM6DSV16BX_COUNTER_BDR_REG1_ADDR,
|
||||
ST_LSM6DSV16BX_AH_QVAR_BATCH_EN_MASK,
|
||||
1);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
/* impedance selection 235 Mohm */
|
||||
return st_lsm6dsv16bx_write_with_mask(hw,
|
||||
ST_LSM6DSV16BX_REG_CTRL7_ADDR,
|
||||
ST_LSM6DSV16BX_AH_QVAR_C_ZIN_MASK,
|
||||
3);
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
st_lsm6dsv16bx_sysfs_qvar_sampling_freq_avail(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
int len = 0;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < st_lsm6dsv16bx_qvar_odr_table.size; i++) {
|
||||
if (!st_lsm6dsv16bx_qvar_odr_table.odr_avl[i].hz)
|
||||
continue;
|
||||
|
||||
len += scnprintf(buf + len, PAGE_SIZE - len, "%d.%06d ",
|
||||
st_lsm6dsv16bx_qvar_odr_table.odr_avl[i].hz,
|
||||
st_lsm6dsv16bx_qvar_odr_table.odr_avl[i].uhz);
|
||||
}
|
||||
|
||||
buf[len - 1] = '\n';
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static IIO_DEV_ATTR_SAMP_FREQ_AVAIL(st_lsm6dsv16bx_sysfs_qvar_sampling_freq_avail);
|
||||
static IIO_DEVICE_ATTR(module_id, 0444, st_lsm6dsv16bx_get_module_id, NULL, 0);
|
||||
|
||||
static struct attribute *st_lsm6dsv16bx_qvar_attributes[] = {
|
||||
&iio_dev_attr_sampling_frequency_available.dev_attr.attr,
|
||||
&iio_dev_attr_module_id.dev_attr.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static const struct attribute_group
|
||||
st_lsm6dsv16bx_qvar_attribute_group = {
|
||||
.attrs = st_lsm6dsv16bx_qvar_attributes,
|
||||
};
|
||||
|
||||
static const struct iio_info st_lsm6dsv16bx_qvar_info = {
|
||||
.attrs = &st_lsm6dsv16bx_qvar_attribute_group,
|
||||
};
|
||||
|
||||
static const unsigned long st_lsm6dsv16bx_qvar_available_scan_masks[] = {
|
||||
BIT(0), 0x0
|
||||
};
|
||||
|
||||
static int
|
||||
_st_lsm6dsv16bx_qvar_sensor_set_enable(struct st_lsm6dsv16bx_sensor *sensor,
|
||||
bool enable)
|
||||
{
|
||||
u16 odr = enable ? sensor->odr : 0;
|
||||
int err;
|
||||
|
||||
err = st_lsm6dsv16bx_sensor_set_enable(sensor, odr);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
err = st_lsm6dsv16bx_write_with_mask(sensor->hw,
|
||||
ST_LSM6DSV16BX_REG_CTRL7_ADDR,
|
||||
ST_LSM6DSV16BX_AH_QVARx_EN_MASK,
|
||||
enable ? 3 : 0);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
return st_lsm6dsv16bx_write_with_mask(sensor->hw,
|
||||
ST_LSM6DSV16BX_REG_CTRL7_ADDR,
|
||||
ST_LSM6DSV16BX_AH_QVAR_EN_MASK,
|
||||
enable ? 1 : 0);
|
||||
}
|
||||
|
||||
int st_lsm6dsv16bx_qvar_sensor_set_enable(struct st_lsm6dsv16bx_sensor *sensor,
|
||||
bool enable)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = _st_lsm6dsv16bx_qvar_sensor_set_enable(sensor, enable);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
if (enable)
|
||||
sensor->hw->enable_mask |= BIT(sensor->id);
|
||||
else
|
||||
sensor->hw->enable_mask &= ~BIT(sensor->id);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct iio_dev *
|
||||
st_lsm6dsv16bx_alloc_qvar_iiodev(struct st_lsm6dsv16bx_hw *hw)
|
||||
{
|
||||
struct st_lsm6dsv16bx_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 = ST_LSM6DSV16BX_ID_QVAR;
|
||||
sensor->hw = hw;
|
||||
|
||||
iio_dev->channels = st_lsm6dsv16bx_qvar_channels;
|
||||
iio_dev->num_channels = ARRAY_SIZE(st_lsm6dsv16bx_qvar_channels);
|
||||
scnprintf(sensor->name, sizeof(sensor->name),
|
||||
"%s_qvar", hw->settings->id.name);
|
||||
iio_dev->info = &st_lsm6dsv16bx_qvar_info;
|
||||
iio_dev->available_scan_masks = st_lsm6dsv16bx_qvar_available_scan_masks;
|
||||
iio_dev->name = sensor->name;
|
||||
|
||||
sensor->odr = st_lsm6dsv16bx_qvar_odr_table.odr_avl[0].hz;
|
||||
sensor->uodr = st_lsm6dsv16bx_qvar_odr_table.odr_avl[0].uhz;
|
||||
sensor->gain = 1;
|
||||
sensor->watermark = 1;
|
||||
|
||||
return iio_dev;
|
||||
}
|
||||
|
||||
int st_lsm6dsv16bx_qvar_probe(struct st_lsm6dsv16bx_hw *hw)
|
||||
{
|
||||
int err;
|
||||
|
||||
hw->iio_devs[ST_LSM6DSV16BX_ID_QVAR] = st_lsm6dsv16bx_alloc_qvar_iiodev(hw);
|
||||
if (!hw->iio_devs[ST_LSM6DSV16BX_ID_QVAR])
|
||||
return -ENOMEM;
|
||||
|
||||
err = st_lsm6dsv16bx_qvar_init(hw);
|
||||
|
||||
return err < 0 ? err : 0;
|
||||
}
|
67
drivers/iio/stm/imu/st_lsm6dsv16bx/st_lsm6dsv16bx_spi.c
Normal file
67
drivers/iio/stm/imu/st_lsm6dsv16bx/st_lsm6dsv16bx_spi.c
Normal file
@ -0,0 +1,67 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* STMicroelectronics st_lsm6dsv16bx spi driver
|
||||
*
|
||||
* MEMS Software Solutions Team
|
||||
*
|
||||
* Copyright 2023 STMicroelectronics Inc.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/of.h>
|
||||
|
||||
#include "st_lsm6dsv16bx.h"
|
||||
|
||||
static const struct regmap_config st_lsm6dsv16bx_spi_regmap_config = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
};
|
||||
|
||||
static int st_lsm6dsv16bx_spi_probe(struct spi_device *spi)
|
||||
{
|
||||
const struct spi_device_id *id = spi_get_device_id(spi);
|
||||
int hw_id = id->driver_data;
|
||||
struct regmap *regmap;
|
||||
|
||||
regmap = devm_regmap_init_spi(spi, &st_lsm6dsv16bx_spi_regmap_config);
|
||||
if (IS_ERR(regmap)) {
|
||||
dev_err(&spi->dev, "Failed to register spi regmap %d\n",
|
||||
(int)PTR_ERR(regmap));
|
||||
|
||||
return PTR_ERR(regmap);
|
||||
}
|
||||
|
||||
return st_lsm6dsv16bx_probe(&spi->dev, spi->irq, hw_id, regmap);
|
||||
}
|
||||
|
||||
static const struct of_device_id st_lsm6dsv16bx_spi_of_match[] = {
|
||||
{
|
||||
.compatible = "st," ST_LSM6DSV16BX_DEV_NAME,
|
||||
.data = (void *)ST_LSM6DSV16BX_ID,
|
||||
},
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, st_lsm6dsv16bx_spi_of_match);
|
||||
|
||||
static const struct spi_device_id st_lsm6dsv16bx_spi_id_table[] = {
|
||||
{ ST_LSM6DSV16BX_DEV_NAME, ST_LSM6DSV16BX_ID },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, st_lsm6dsv16bx_spi_id_table);
|
||||
|
||||
static struct spi_driver st_lsm6dsv16bx_driver = {
|
||||
.driver = {
|
||||
.name = "st_lsm6dsv16bx_spi",
|
||||
.pm = &st_lsm6dsv16bx_pm_ops,
|
||||
.of_match_table = of_match_ptr(st_lsm6dsv16bx_spi_of_match),
|
||||
},
|
||||
.probe = st_lsm6dsv16bx_spi_probe,
|
||||
.id_table = st_lsm6dsv16bx_spi_id_table,
|
||||
};
|
||||
module_spi_driver(st_lsm6dsv16bx_driver);
|
||||
|
||||
MODULE_AUTHOR("MEMS Software Solutions Team");
|
||||
MODULE_DESCRIPTION("STMicroelectronics st_lsm6dsv16bx spi driver");
|
||||
MODULE_LICENSE("GPL v2");
|
6
stm_iio_configs/lsm6dsv16bx_defconfig
Normal file
6
stm_iio_configs/lsm6dsv16bx_defconfig
Normal file
@ -0,0 +1,6 @@
|
||||
CONFIG_IIO_ST_LSM6DSV16BX=m
|
||||
CONFIG_IIO_ST_LSM6DSV16BX_I2C=m
|
||||
CONFIG_IIO_ST_LSM6DSV16BX_I3C=m
|
||||
CONFIG_IIO_ST_LSM6DSV16BX_SPI=m
|
||||
CONFIG_IIO_ST_LSM6DSV16BX_MLC_PRELOAD=y
|
||||
CONFIG_IIO_ST_LSM6DSV16BX_ASYNC_HW_TIMESTAMP=y
|
Loading…
Reference in New Issue
Block a user