drivers:iio:stm:accel: Add support to LIS2DUXS12 STMEMS accel
Added support to STMEMS acc LIS2DUXS12 sensor. The following features has been included: - Acc, Temp and QVAR sensors in FIFO - Self Test - Embedded features event detection supported: - Wake-up - Free fall - Single Tap - Double Tap - Triple Tap - 6D orientation - Sleep change - Step Counter - Step Detection - Significant Motion - Tilt - MLC and FSM configuration - I2C / SPI / I3C interfaces through regmap - Multi kernel version support up to v5.15 - Preload MLC/FSM presence detection using QVAR Signed-off-by: Mario Tesi <mario.tesi@st.com> Change-Id: I014d990f55c178f15a4a087a897d7eb8ed17ed8f Reviewed-on: https://gerrit.st.com/c/linuxandroidopen/stm-ldd-iio/+/261051 Tested-by: CITOOLS <MDG-smet-aci-reviews@list.st.com> Reviewed-by: Matteo DAMENO <matteo.dameno@st.com>
This commit is contained in:
parent
d93ddd10b6
commit
52224a7247
@ -0,0 +1,48 @@
|
||||
* lis2duxs12 driver for accel MEMS sensors
|
||||
|
||||
Required properties for all bus drivers:
|
||||
- compatible: must be one of:
|
||||
"st,lis2duxs12"
|
||||
|
||||
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:
|
||||
- st,int-pin: the pin on the package that will be used to signal when
|
||||
sensor data are available (valid values: 1 or 2, default: 1).
|
||||
|
||||
- interrupts: interrupt mapping for IRQ. It should be configured with
|
||||
flags IRQ_TYPE_LEVEL_HIGH.
|
||||
|
||||
Refer to interrupt-controller/interrupts.txt for generic
|
||||
interrupt client node bindings.
|
||||
|
||||
- pd_dis_int1: disable pull down on int1 pin.
|
||||
- pd_dis_int2: disable pull down on int2 pin.
|
||||
- drive-open-drain: set interrupt pin in open drain (disable push-pull)
|
||||
|
||||
Example for an spi device node:
|
||||
|
||||
lis2duxs12-accel@0 {
|
||||
compatible = "st,lis2duxs12";
|
||||
reg = <0x0>;
|
||||
spi-max-frequency = <1000000>;
|
||||
interrupt-parent = <&gpio0>;
|
||||
interrupts = <0 IRQ_TYPE_LEVEL_HIGH>;
|
||||
st,int-pin = <1>;
|
||||
};
|
||||
|
||||
Example for an i2c device node (I2C slave address 0x19):
|
||||
|
||||
lis2duxs12-accel@19 {
|
||||
compatible = "st,lis2duxs12";
|
||||
reg = <0x19>;
|
||||
interrupt-parent = <&gpio0>;
|
||||
interrupts = <0 IRQ_TYPE_LEVEL_HIGH>;
|
||||
st,int-pin = <1>;
|
||||
};
|
@ -159,4 +159,40 @@ config IIO_ST_LIS2HH12_SPI
|
||||
tristate
|
||||
depends on IIO_ST_LIS2HH12
|
||||
|
||||
config IIO_ST_LIS2DUXS12
|
||||
tristate "STMicroelectronics LIS2DUXS12 sensor"
|
||||
depends on (I2C || SPI || I3C)
|
||||
select IIO_BUFFER
|
||||
select IIO_KFIFO_BUF
|
||||
select IIO_ST_LIS2DUXS12_I2C if (I2C)
|
||||
select IIO_ST_LIS2DUXS12_SPI if (SPI_MASTER)
|
||||
select IIO_ST_LIS2DUXS12_I3C if (I3C)
|
||||
help
|
||||
Say yes here to build support for STMicroelectronics LIS2DUXS12 accel
|
||||
sensor.
|
||||
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called lis2duxs12.
|
||||
|
||||
config IIO_ST_LIS2DUXS12_QVAR
|
||||
bool "Enable QVAR"
|
||||
depends on IIO_ST_LIS2DUXS12
|
||||
help
|
||||
Enable support to QVAR sensor.
|
||||
|
||||
config IIO_ST_LIS2DUXS12_I2C
|
||||
tristate
|
||||
select REGMAP_I2C
|
||||
depends on IIO_ST_LIS2DUXS12
|
||||
|
||||
config IIO_ST_LIS2DUXS12_SPI
|
||||
tristate
|
||||
select REGMAP_SPI
|
||||
depends on IIO_ST_LIS2DUXS12
|
||||
|
||||
config IIO_ST_LIS2DUXS12_I3C
|
||||
tristate
|
||||
select REGMAP_I3C
|
||||
depends on IIO_ST_LIS2DUXS12
|
||||
|
||||
endmenu
|
||||
|
@ -34,3 +34,13 @@ obj-$(CONFIG_IIO_ST_LIS2HH12) += lis2hh12.o
|
||||
obj-$(CONFIG_IIO_ST_LIS2HH12_I2C) += st_lis2hh12_i2c.o
|
||||
obj-$(CONFIG_IIO_ST_LIS2HH12_SPI) += st_lis2hh12_spi.o
|
||||
|
||||
st_lis2duxs12-y := st_lis2duxs12_core.o st_lis2duxs12_buffer.o \
|
||||
st_lis2duxs12_mlc.o st_lis2duxs12_embfunc.o \
|
||||
st_lis2duxs12_basicfunc.o
|
||||
|
||||
st_lis2duxs12-$(CONFIG_IIO_ST_LIS2DUXS12_QVAR) += st_lis2duxs12_qvar.o
|
||||
|
||||
obj-$(CONFIG_IIO_ST_LIS2DUXS12) += st_lis2duxs12.o
|
||||
obj-$(CONFIG_IIO_ST_LIS2DUXS12_I2C) += st_lis2duxs12_i2c.o
|
||||
obj-$(CONFIG_IIO_ST_LIS2DUXS12_SPI) += st_lis2duxs12_spi.o
|
||||
obj-$(CONFIG_IIO_ST_LIS2DUXS12_I3C) += st_lis2duxs12_i3c.o
|
||||
|
886
drivers/iio/stm/accel/st_lis2duxs12.h
Normal file
886
drivers/iio/stm/accel/st_lis2duxs12.h
Normal file
@ -0,0 +1,886 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* STMicroelectronics st_lis2duxs12 sensor driver
|
||||
*
|
||||
* MEMS Software Solutions Team
|
||||
*
|
||||
* Copyright 2022 STMicroelectronics Inc.
|
||||
*/
|
||||
|
||||
#ifndef ST_LIS2DUXS12_H
|
||||
#define ST_LIS2DUXS12_H
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/bitfield.h>
|
||||
|
||||
#define ST_LIS2DUXS12_ODR_EXPAND(odr, uodr) (((odr) * 1000000) + (uodr))
|
||||
|
||||
#define ST_LIS2DUXS12_DEV_NAME "lis2duxs12"
|
||||
|
||||
/* default configuration values */
|
||||
#define ST_LIS2DUXS12_DEFAULT_WK_TH 154000
|
||||
|
||||
#define ST_LIS2DUXS12_PIN_CTRL_ADDR 0x0c
|
||||
#define ST_LIS2DUXS12_PP_OD_MASK BIT(1)
|
||||
#define ST_LIS2DUXS12_CS_PU_DIS_MASK BIT(2)
|
||||
#define ST_LIS2DUXS12_H_LACTIVE_MASK BIT(3)
|
||||
#define ST_LIS2DUXS12_PD_DIS_INT1_MASK BIT(4)
|
||||
#define ST_LIS2DUXS12_PD_DIS_INT2_MASK BIT(5)
|
||||
#define ST_LIS2DUXS12_SDA_PU_EN_MASK BIT(6)
|
||||
#define ST_LIS2DUXS12_SDO_PU_EN_MASK BIT(7)
|
||||
|
||||
#define ST_LIS2DUXS12_WHOAMI_ADDR 0x0f
|
||||
#define ST_LIS2DUXS12_WHOAMI_VAL 0x47
|
||||
|
||||
#define ST_LIS2DUXS12_CTRL1_ADDR 0x10
|
||||
#define ST_LIS2DUXS12_WU_EN_MASK GENMASK(2, 0)
|
||||
#define ST_LIS2DUXS12_DRDY_PULSED_MASK BIT(3)
|
||||
#define ST_LIS2DUXS12_IF_ADD_INC_MASK BIT(4)
|
||||
#define ST_LIS2DUXS12_SW_RESET_MASK BIT(5)
|
||||
#define ST_LIS2DUXS12_INT1_ON_RES_MASK BIT(6)
|
||||
|
||||
#define ST_LIS2DUXS12_CTRL2_ADDR 0x11
|
||||
#define ST_LIS2DUXS12_CTRL3_ADDR 0x12
|
||||
#define ST_LIS2DUXS12_HP_EN_MASK BIT(2)
|
||||
#define ST_LIS2DUXS12_INT_FIFO_TH_MASK BIT(5)
|
||||
|
||||
#define ST_LIS2DUXS12_CTRL4_ADDR 0x13
|
||||
#define ST_LIS2DUXS12_BOOT_MASK BIT(0)
|
||||
#define ST_LIS2DUXS12_SOC_MASK BIT(1)
|
||||
#define ST_LIS2DUXS12_FIFO_EN_MASK BIT(3)
|
||||
#define ST_LIS2DUXS12_EMB_FUNC_EN_MASK BIT(4)
|
||||
#define ST_LIS2DUXS12_BDU_MASK BIT(5)
|
||||
#define ST_LIS2DUXS12_INACT_ODR_MASK GENMASK(7, 6)
|
||||
|
||||
#define ST_LIS2DUXS12_CTRL5_ADDR 0x14
|
||||
#define ST_LIS2DUXS12_FS_MASK GENMASK(1, 0)
|
||||
#define ST_LIS2DUXS12_ODR_MASK GENMASK(7, 4)
|
||||
|
||||
#define ST_LIS2DUXS12_FIFO_CTRL_ADDR 0x15
|
||||
#define ST_LIS2DUXS12_FIFO_MODE_MASK GENMASK(2, 0)
|
||||
|
||||
#define ST_LIS2DUXS12_FIFO_WTM_ADDR 0x16
|
||||
#define ST_LIS2DUXS12_FIFO_WTM_MASK GENMASK(6, 0)
|
||||
#define ST_LIS2DUXS12_XL_ONLY_FIFO_MASK BIT(7)
|
||||
|
||||
#define ST_LIS2DUXS12_INTERRUPT_CFG_ADDR 0x17
|
||||
#define ST_LIS2DUXS12_INTERRUPTS_ENABLE_MASK BIT(0)
|
||||
#define ST_LIS2DUXS12_LIR_MASK BIT(1)
|
||||
#define ST_LIS2DUXS12_TIMESTAMP_EN_MASK BIT(7)
|
||||
|
||||
#define ST_LIS2DUXS12_SIXD_ADDR 0x18
|
||||
#define ST_LIS2DUXS12_D6D_THS_MASK GENMASK(6, 5)
|
||||
|
||||
#define ST_LIS2DUXS12_WAKE_UP_THS_ADDR 0x1c
|
||||
#define ST_LIS2DUXS12_WK_THS_MASK GENMASK(5, 0)
|
||||
#define ST_LIS2DUXS12_SLEEP_ON_MASK BIT(6)
|
||||
|
||||
#define ST_LIS2DUXS12_WAKE_UP_DUR_ADDR 0x1d
|
||||
#define ST_LIS2DUXS12_SLEEP_DUR_MASK GENMASK(3, 0)
|
||||
#define ST_LIS2DUXS12_WAKE_DUR_MASK GENMASK(6, 5)
|
||||
#define ST_LIS2DUXS12_FF_DUR5_MASK BIT(7)
|
||||
|
||||
#define ST_LIS2DUXS12_FREE_FALL_ADDR 0x1e
|
||||
#define ST_LIS2DUXS12_FF_THS_MASK GENMASK(2, 0)
|
||||
#define ST_LIS2DUXS12_FF_DUR_MASK GENMASK(7, 3)
|
||||
#define ST_LIS2DUXS12_FF_DUR5_MASK BIT(7)
|
||||
|
||||
#define ST_LIS2DUXS12_MD1_CFG_ADDR 0x1f
|
||||
#define ST_LIS2DUXS12_MD2_CFG_ADDR 0x20
|
||||
#define ST_LIS2DUXS12_INT_SLEEP_CHANGE_MASK BIT(7)
|
||||
#define ST_LIS2DUXS12_INT_WU_MASK BIT(5)
|
||||
#define ST_LIS2DUXS12_INT_FF_MASK BIT(4)
|
||||
#define ST_LIS2DUXS12_INT_TAP_MASK BIT(3)
|
||||
#define ST_LIS2DUXS12_INT_6D_MASK BIT(2)
|
||||
#define ST_LIS2DUXS12_INT_TIMESTAMP_MASK BIT(1)
|
||||
#define ST_LIS2DUXS12_INT_EMB_FUNC_MASK BIT(0)
|
||||
|
||||
#define ST_LIS2DUXS12_WAKE_UP_SRC_ADDR 0x21
|
||||
#define ST_LIS2DUXS12_WK_MASK GENMASK(2, 0)
|
||||
#define ST_LIS2DUXS12_WU_IA_MASK BIT(3)
|
||||
#define ST_LIS2DUXS12_SLEEP_STATE_MASK BIT(4)
|
||||
#define ST_LIS2DUXS12_FF_IA_MASK BIT(5)
|
||||
#define ST_LIS2DUXS12_SLEEP_CHANGE_IA_MASK BIT(6)
|
||||
|
||||
#define ST_LIS2DUXS12_TAP_SRC_ADDR 0x22
|
||||
#define ST_LIS2DUXS12_TRIPLE_TAP_IA_MASK BIT(4)
|
||||
#define ST_LIS2DUXS12_DOUBLE_TAP_IA_MASK BIT(5)
|
||||
#define ST_LIS2DUXS12_SINGLE_TAP_IA_MASK BIT(6)
|
||||
#define ST_LIS2DUXS12_TAP_IA_MASK BIT(7)
|
||||
|
||||
#define ST_LIS2DUXS12_SIXD_SRC_ADDR 0x23
|
||||
#define ST_LIS2DUXS12_X_Y_Z_MASK GENMASK(5, 0)
|
||||
#define ST_LIS2DUXS12_D6D_IA_MASK BIT(6)
|
||||
|
||||
#define ST_LIS2DUXS12_ALL_INT_SRC_ADDR 0x24
|
||||
#define ST_LIS2DUXS12_FF_IA_ALL_MASK BIT(0)
|
||||
#define ST_LIS2DUXS12_WU_IA_ALL_MASK BIT(1)
|
||||
#define ST_LIS2DUXS12_SINGLE_TAP_ALL_MASK BIT(2)
|
||||
#define ST_LIS2DUXS12_DOUBLE_TAP_ALL_MASK BIT(3)
|
||||
#define ST_LIS2DUXS12_TRIPLE_TAP_ALL_MASK BIT(4)
|
||||
#define ST_LIS2DUXS12_D6D_IA_ALL_MASK BIT(5)
|
||||
#define ST_LIS2DUXS12_SLEEP_CHANGE_ALL_MASK BIT(6)
|
||||
|
||||
#define ST_LIS2DUXS12_STATUS_ADDR 0x25
|
||||
#define ST_LIS2DUXS12_DRDY_MASK BIT(0)
|
||||
#define ST_LIS2DUXS12_INT_GLOBAL_MASK BIT(5)
|
||||
|
||||
#define ST_LIS2DUXS12_FIFO_STATUS1_ADDR 0x26
|
||||
#define ST_LIS2DUXS12_FIFO_WTM_IA_MASK BIT(7)
|
||||
|
||||
#define ST_LIS2DUXS12_FIFO_STATUS2_ADDR 0x27
|
||||
#define ST_LIS2DUXS12_FIFO_FSS_MASK GENMASK(7, 0)
|
||||
|
||||
#define ST_LIS2DUXS12_OUT_X_L_ADDR 0x28
|
||||
#define ST_LIS2DUXS12_OUT_Y_L_ADDR 0x2a
|
||||
#define ST_LIS2DUXS12_OUT_Z_L_ADDR 0x2c
|
||||
#define ST_LIS2DUXS12_OUT_T_L_ADDR 0x2e
|
||||
|
||||
#define ST_LIS2DUXS12_AH_QVAR_CFG_ADDR 0x31
|
||||
#define ST_LIS2DUXS12_AH_QVAR_EN_MASK BIT(7)
|
||||
#define ST_LIS2DUXS12_AH_QVAR_NOTCH_EN_MASK BIT(6)
|
||||
#define ST_LIS2DUXS12_AH_QVAR_NOTCH_CUTOFF_MASK BIT(5)
|
||||
#define ST_LIS2DUXS12_AH_QVAR_C_ZIN_MASK GENMASK(4, 3)
|
||||
#define ST_LIS2DUXS12_AH_QVAR_GAIN_MASK GENMASK(2, 1)
|
||||
|
||||
#define ST_LIS2DUXS12_SELF_TEST_ADDR 0x32
|
||||
#define ST_LIS2DUXS12_ST_MASK GENMASK(5, 4)
|
||||
|
||||
#define ST_LIS2DUXS12_EMB_FUNC_STATUS_MAINPAGE_ADDR 0x34
|
||||
#define ST_LIS2DUXS12_IS_STEP_DET_MASK BIT(3)
|
||||
#define ST_LIS2DUXS12_IS_TILT_MASK BIT(4)
|
||||
#define ST_LIS2DUXS12_IS_SIGMOT_MASK BIT(5)
|
||||
|
||||
#define ST_LIS2DUXS12_FSM_STATUS_MAINPAGE_ADDR 0x35
|
||||
#define ST_LIS2DUXS12_MLC_STATUS_MAINPAGE_ADDR 0x36
|
||||
|
||||
#define ST_LIS2DUXS12_FUNC_CFG_ACCESS_ADDR 0x3f
|
||||
#define ST_LIS2DUXS12_EMB_FUNC_REG_ACCESS_MASK BIT(7)
|
||||
#define ST_LIS2DUXS12_FSM_WR_CTRL_EN_MASK BIT(0)
|
||||
|
||||
#define ST_LIS2DUXS12_FIFO_DATA_OUT_TAG_ADDR 0x40
|
||||
|
||||
#define ST_LIS2DUXS12_FIFO_BATCH_DEC_ADDR 0x47
|
||||
#define ST_LIS2DUXS12_BDR_XL_MASK GENMASK(2, 0)
|
||||
#define ST_LIS2DUXS12_DEC_TS_MASK GENMASK(4, 3)
|
||||
|
||||
#define ST_LIS2DUXS12_TAP_CFG0_ADDR 0x6f
|
||||
#define ST_LIS2DUXS12_AXIS_MASK GENMASK(7, 6)
|
||||
#define ST_LIS2DUXS12_INVERT_T_MASK GENMASK(5, 0)
|
||||
|
||||
#define ST_LIS2DUXS12_TAP_CFG1_ADDR 0x70
|
||||
#define ST_LIS2DUXS12_POST_STILL_T_MASK GENMASK(3, 0)
|
||||
#define ST_LIS2DUXS12_PRE_STILL_THS_MASK GENMASK(7, 4)
|
||||
|
||||
#define ST_LIS2DUXS12_TAP_CFG2_ADDR 0x71
|
||||
#define ST_LIS2DUXS12_WAIT_T_MASK GENMASK(5, 0)
|
||||
#define ST_LIS2DUXS12_POST_STILL_TH_MASK GENMASK(7, 6)
|
||||
|
||||
#define ST_LIS2DUXS12_TAP_CFG3_ADDR 0x72
|
||||
#define ST_LIS2DUXS12_LATENCY_T_MASK GENMASK(3, 0)
|
||||
#define ST_LIS2DUXS12_POST_STILL_THS_MASK GENMASK(7, 4)
|
||||
|
||||
#define ST_LIS2DUXS12_TAP_CFG4_ADDR 0x73
|
||||
#define ST_LIS2DUXS12_PEAK_THS_MASK GENMASK(5, 0)
|
||||
#define ST_LIS2DUXS12_WAIT_END_LATENCY_MASK BIT(7)
|
||||
|
||||
#define ST_LIS2DUXS12_TAP_CFG5_ADDR 0x74
|
||||
#define ST_LIS2DUXS12_REBOUND_T_MASK GENMASK(4, 0)
|
||||
#define ST_LIS2DUXS12_SINGLE_TAP_EN_MASK BIT(5)
|
||||
#define ST_LIS2DUXS12_DOUBLE_TAP_EN_MASK BIT(6)
|
||||
#define ST_LIS2DUXS12_TRIPLE_TAP_EN_MASK BIT(7)
|
||||
|
||||
#define ST_LIS2DUXS12_TAP_CFG6_ADDR 0x75
|
||||
#define ST_LIS2DUXS12_PRE_STILL_N_MASK GENMASK(3, 0)
|
||||
#define ST_LIS2DUXS12_PRE_STILL_ST_MASK GENMASK(7, 4)
|
||||
|
||||
#define ST_LIS2DUXS12_TIMESTAMP2_ADDR 0x7c
|
||||
|
||||
#define ST_LIS2DUXS12_SELFTEST_ACCEL_MIN 204
|
||||
#define ST_LIS2DUXS12_SELFTEST_ACCEL_MAX 4918
|
||||
|
||||
/* embedded function registers */
|
||||
#define ST_LIS2DUXS12_PAGE_SEL_ADDR 0x02
|
||||
#define ST_LIS2DUXS12_EMB_FUNC_EN_A_ADDR 0x04
|
||||
#define ST_LIS2DUXS12_PEDO_EN_MASK BIT(3)
|
||||
#define ST_LIS2DUXS12_TILT_EN_MASK BIT(4)
|
||||
#define ST_LIS2DUXS12_SIGN_MOTION_EN_MASK BIT(5)
|
||||
#define ST_LIS2DUXS12_MLC_BEFORE_FSM_EN_MASK BIT(7)
|
||||
|
||||
#define ST_LIS2DUXS12_EMB_FUNC_EN_B_ADDR 0x05
|
||||
#define ST_LIS2DUXS12_FSM_EN_MASK BIT(0)
|
||||
#define ST_LIS2DUXS12_MLC_EN_MASK BIT(4)
|
||||
|
||||
#define ST_LIS2DUXS12_EMB_FUNC_EXEC_STATUS_ADDR 0x07
|
||||
#define ST_LIS2DUXS12_FUNC_ENDOP_MASK BIT(0)
|
||||
#define ST_LIS2DUXS12_FUNC_EXEC_OVR_MASK BIT(1)
|
||||
|
||||
#define ST_LIS2DUXS12_PAGE_ADDRESS_ADDR 0x08
|
||||
#define ST_LIS2DUXS12_PAGE_VALUE_ADDR 0x09
|
||||
|
||||
#define ST_LIS2DUXS12_EMB_FUNC_INT1_ADDR 0x0a
|
||||
#define ST_LIS2DUXS12_INT_STEP_DETECTOR_MASK BIT(3)
|
||||
#define ST_LIS2DUXS12_INT_TILT_MASK BIT(4)
|
||||
#define ST_LIS2DUXS12_INT_SIG_MOT_MASK BIT(5)
|
||||
|
||||
#define ST_LIS2DUXS12_FSM_INT1_ADDR 0x0b
|
||||
#define ST_LIS2DUXS12_MLC_INT1_ADDR 0x0d
|
||||
#define ST_LIS2DUXS12_EMB_FUNC_INT2_ADDR 0x0e
|
||||
#define ST_LIS2DUXS12_FSM_INT2_ADDR 0x0f
|
||||
#define ST_LIS2DUXS12_MLC_INT2_ADDR 0x11
|
||||
|
||||
#define ST_LIS2DUXS12_EMB_FUNC_STATUS_ADDR 0x12
|
||||
#define ST_LIS2DUXS12_EMB_IS_STEP_DET_MASK BIT(3)
|
||||
#define ST_LIS2DUXS12_EMB_IS_TILT_MASK BIT(4)
|
||||
#define ST_LIS2DUXS12_EMB_IS_SIGMOT_MASK BIT(5)
|
||||
|
||||
#define ST_LIS2DUXS12_FSM_STATUS_ADDR 0x13
|
||||
#define ST_LIS2DUXS12_MLC_STATUS_ADDR 0x15
|
||||
|
||||
#define ST_LIS2DUXS12_PAGE_RW_ADDR 0x17
|
||||
#define ST_LIS2DUXS12_EMB_FUNC_LIR_MASK BIT(7)
|
||||
|
||||
#define ST_LIS2DUXS12_EMB_FUNC_FIFO_EN_ADDR 0x18
|
||||
#define ST_LIS2DUXS12_STEP_COUNTER_FIFO_EN_MASK BIT(0)
|
||||
#define ST_LIS2DUXS12_MLC_FIFO_EN_MASK BIT(1)
|
||||
#define ST_LIS2DUXS12_MLC_FILTER_FEATURE_FIFO_EN_MASK BIT(2)
|
||||
#define ST_LIS2DUXS12_FSM_FIFO_EN_MASK BIT(3)
|
||||
|
||||
#define ST_LIS2DUXS12_FSM_ENABLE_ADDR 0x1a
|
||||
#define ST_LIS2DUXS12_FSM_OUTS1_ADDR 0x20
|
||||
|
||||
#define ST_LIS2DUXS12_STEP_COUNTER_L_ADDR 0x28
|
||||
#define ST_LIS2DUXS12_STEP_COUNTER_H_ADDR 0x29
|
||||
|
||||
#define ST_LIS2DUXS12_EMB_FUNC_SRC_ADDR 0x2a
|
||||
#define ST_LIS2DUXS12_STEP_DETECTED_MASK BIT(7)
|
||||
#define ST_LIS2DUXS12_PEDO_RST_STEP_MASK BIT(8)
|
||||
|
||||
#define ST_LIS2DUXS12_EMB_FUNC_INIT_A_ADDR 0x2c
|
||||
#define ST_LIS2DUXS12_STEP_DET_INIT_MASK BIT(3)
|
||||
#define ST_LIS2DUXS12_TILT_INIT_MASK BIT(4)
|
||||
#define ST_LIS2DUXS12_SIG_MOT_INIT_MASK BIT(5)
|
||||
#define ST_LIS2DUXS12_MLC_BEFORE_FSM_INIT_MASK BIT(7)
|
||||
|
||||
#define ST_LIS2DUXS12_EMB_FUNC_INIT_B_ADDR 0x2d
|
||||
#define ST_LIS2DUXS12_FSM_INIT_MASK BIT(0)
|
||||
#define ST_LIS2DUXS12_MLC_INIT_MASK BIT(4)
|
||||
|
||||
#define ST_LIS2DUXS12_MLC1_SRC_ADDR 0x34
|
||||
|
||||
#define ST_LIS2DUXS12_FSM_ODR_ADDR 0x39
|
||||
#define ST_LIS2DUXS12_FSM_ODR_MASK GENMASK(5, 3)
|
||||
|
||||
#define ST_LIS2DUXS12_MLC_ODR_ADDR 0x3a
|
||||
#define ST_LIS2DUXS12_MLC_ODR_MASK GENMASK(6, 4)
|
||||
|
||||
/* Timestamp Tick 10us/LSB */
|
||||
#define ST_LIS2DUXS12_TS_DELTA_NS 10000ULL
|
||||
|
||||
/* Temperature in uC */
|
||||
#define ST_LIS2DUXS12_TEMP_GAIN 256
|
||||
#define ST_LIS2DUXS12_TEMP_OFFSET 6400
|
||||
|
||||
/* FIFO simple size and depth */
|
||||
#define ST_LIS2DUXS12_SAMPLE_SIZE 6
|
||||
#define ST_LIS2DUXS12_TS_SAMPLE_SIZE 4
|
||||
#define ST_LIS2DUXS12_TAG_SIZE 1
|
||||
#define ST_LIS2DUXS12_FIFO_SAMPLE_SIZE (ST_LIS2DUXS12_SAMPLE_SIZE + \
|
||||
ST_LIS2DUXS12_TAG_SIZE)
|
||||
#define ST_LIS2DUXS12_MAX_FIFO_DEPTH 127
|
||||
|
||||
struct __packed raw_data_compact_t {
|
||||
__le16 x:12;
|
||||
__le16 y:12;
|
||||
__le16 z:12;
|
||||
__le16 t:12;
|
||||
};
|
||||
|
||||
struct __packed raw_data_t {
|
||||
__le16 x;
|
||||
__le16 y;
|
||||
__le16 z;
|
||||
};
|
||||
|
||||
#define ST_LIS2DUXS12_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, \
|
||||
.shift = sb - rb, \
|
||||
.endianness = IIO_LE, \
|
||||
}, \
|
||||
.ext_info = ext_inf, \
|
||||
}
|
||||
|
||||
static const struct iio_event_spec st_lis2duxs12_flush_event = {
|
||||
.type = IIO_EV_TYPE_FIFO_FLUSH,
|
||||
.dir = IIO_EV_DIR_EITHER,
|
||||
};
|
||||
|
||||
static const struct iio_event_spec st_lis2duxs12_thr_event = {
|
||||
.type = IIO_EV_TYPE_THRESH,
|
||||
.dir = IIO_EV_DIR_RISING,
|
||||
.mask_separate = BIT(IIO_EV_INFO_ENABLE),
|
||||
};
|
||||
|
||||
#define ST_LIS2DUXS12_EVENT_CHANNEL(ctype, etype) \
|
||||
{ \
|
||||
.type = ctype, \
|
||||
.modified = 0, \
|
||||
.scan_index = -1, \
|
||||
.indexed = -1, \
|
||||
.event_spec = &st_lis2duxs12_##etype##_event, \
|
||||
.num_event_specs = 1, \
|
||||
}
|
||||
|
||||
#define ST_LIS2DUXS12_SHIFT_VAL(val, mask) (((val) << __ffs(mask)) & (mask))
|
||||
#define ST_LIS2DUXS12_DESHIFT_VAL(val, mask) (((val) & (mask)) >> __ffs(mask))
|
||||
|
||||
enum st_lis2duxs12_pm_t {
|
||||
ST_LIS2DUXS12_LP_MODE = 0,
|
||||
ST_LIS2DUXS12_HP_MODE,
|
||||
ST_LIS2DUXS12_NO_MODE,
|
||||
};
|
||||
|
||||
enum st_lis2duxs12_fsm_mlc_enable_id {
|
||||
ST_LIS2DUXS12_MLC_FSM_DISABLED = 0,
|
||||
ST_LIS2DUXS12_MLC_ENABLED = BIT(0),
|
||||
ST_LIS2DUXS12_FSM_ENABLED = BIT(1),
|
||||
};
|
||||
|
||||
/**
|
||||
* struct mlc_config_t -
|
||||
* @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.
|
||||
* @requested_device: Device bitmask requested by firmware.
|
||||
* @status: MLC / FSM enabled status.
|
||||
*/
|
||||
struct st_lis2duxs12_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;
|
||||
uint32_t requested_device;
|
||||
enum st_lis2duxs12_fsm_mlc_enable_id status;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct st_lis2duxs12_ff_th - Free Fall threshold table
|
||||
* @mg: Threshold in mg.
|
||||
* @val: Register value.
|
||||
*/
|
||||
struct st_lis2duxs12_ff_th {
|
||||
u32 mg;
|
||||
u8 val;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct st_lis2duxs12_6D_th - 6D threshold table
|
||||
* @deg: Threshold in degrees.
|
||||
* @val: Register value.
|
||||
*/
|
||||
struct st_lis2duxs12_6D_th {
|
||||
u8 deg;
|
||||
u8 val;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct st_lis2duxs12_reg - Generic sensor register description addr +
|
||||
* mask
|
||||
* @addr: Address of register.
|
||||
* @mask: Bitmask register for proper usage.
|
||||
*/
|
||||
struct st_lis2duxs12_reg {
|
||||
u8 addr;
|
||||
u8 mask;
|
||||
};
|
||||
|
||||
/**
|
||||
* Define embedded functions register access
|
||||
*
|
||||
* FUNC_CFG_ACCESS_0 is default bank
|
||||
* FUNC_CFG_ACCESS_FUNC_CFG Enable access to the embedded functions
|
||||
* configuration registers.
|
||||
*/
|
||||
enum st_lis2duxs12_page_sel_register {
|
||||
FUNC_CFG_ACCESS_0 = 0,
|
||||
FUNC_CFG_ACCESS_FUNC_CFG,
|
||||
};
|
||||
|
||||
/**
|
||||
* struct st_lis2duxs12_odr - Single ODR entry
|
||||
* @hz: Most significant part of the sensor ODR (Hz).
|
||||
* @uhz: Less significant part of the sensor ODR (micro Hz).
|
||||
* @val: ODR register value.
|
||||
*/
|
||||
struct st_lis2duxs12_odr {
|
||||
u16 hz;
|
||||
u32 uhz;
|
||||
u8 val;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct st_lis2duxs12_odr_table_entry - Sensor ODR table
|
||||
* @size: Size of ODR table.
|
||||
* @reg: ODR register.
|
||||
* @pm: Power mode register.
|
||||
* @batching_reg: ODR register for batching on fifo.
|
||||
* @odr_avl: Array of supported ODR value.
|
||||
*/
|
||||
struct st_lis2duxs12_odr_table_entry {
|
||||
u8 size;
|
||||
struct st_lis2duxs12_reg reg;
|
||||
struct st_lis2duxs12_reg pm;
|
||||
struct st_lis2duxs12_odr odr_avl[10];
|
||||
};
|
||||
|
||||
/**
|
||||
* struct st_lis2duxs12_fs - Full Scale sensor table entry
|
||||
* @gain: Sensor sensitivity (mdps/LSB, mg/LSB and uC/LSB).
|
||||
* @val: FS register value.
|
||||
*/
|
||||
struct st_lis2duxs12_fs {
|
||||
u32 gain;
|
||||
u8 val;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct st_lis2duxs12_fs_table_entry - Full Scale sensor table
|
||||
* @size: Full Scale sensor table size.
|
||||
* @reg: Register description for FS settings.
|
||||
* @fs_avl: Full Scale list entries.
|
||||
*/
|
||||
struct st_lis2duxs12_fs_table_entry {
|
||||
u8 size;
|
||||
struct st_lis2duxs12_reg reg;
|
||||
struct st_lis2duxs12_fs fs_avl[4];
|
||||
};
|
||||
|
||||
enum st_lis2duxs12_sensor_id {
|
||||
ST_LIS2DUXS12_ID_ACC = 0,
|
||||
ST_LIS2DUXS12_ID_TEMP,
|
||||
ST_LIS2DUXS12_ID_STEP_COUNTER,
|
||||
ST_LIS2DUXS12_ID_STEP_DETECTOR,
|
||||
ST_LIS2DUXS12_ID_SIGN_MOTION,
|
||||
ST_LIS2DUXS12_ID_TILT,
|
||||
ST_LIS2DUXS12_ID_QVAR,
|
||||
ST_LIS2DUXS12_ID_FF,
|
||||
ST_LIS2DUXS12_ID_SC,
|
||||
ST_LIS2DUXS12_ID_WK,
|
||||
ST_LIS2DUXS12_ID_6D,
|
||||
ST_LIS2DUXS12_ID_TAP,
|
||||
ST_LIS2DUXS12_ID_DTAP,
|
||||
ST_LIS2DUXS12_ID_TTAP,
|
||||
ST_LIS2DUXS12_ID_MLC,
|
||||
ST_LIS2DUXS12_ID_MLC_0,
|
||||
ST_LIS2DUXS12_ID_MLC_1,
|
||||
ST_LIS2DUXS12_ID_MLC_2,
|
||||
ST_LIS2DUXS12_ID_MLC_3,
|
||||
ST_LIS2DUXS12_ID_FSM_0,
|
||||
ST_LIS2DUXS12_ID_FSM_1,
|
||||
ST_LIS2DUXS12_ID_FSM_2,
|
||||
ST_LIS2DUXS12_ID_FSM_3,
|
||||
ST_LIS2DUXS12_ID_FSM_4,
|
||||
ST_LIS2DUXS12_ID_FSM_5,
|
||||
ST_LIS2DUXS12_ID_FSM_6,
|
||||
ST_LIS2DUXS12_ID_FSM_7,
|
||||
ST_LIS2DUXS12_ID_MAX,
|
||||
};
|
||||
|
||||
static const enum
|
||||
st_lis2duxs12_sensor_id st_lis2duxs12_buffered_sensor_list[] = {
|
||||
[0] = ST_LIS2DUXS12_ID_ACC,
|
||||
[1] = ST_LIS2DUXS12_ID_TEMP,
|
||||
[2] = ST_LIS2DUXS12_ID_STEP_COUNTER,
|
||||
[3] = ST_LIS2DUXS12_ID_QVAR,
|
||||
};
|
||||
|
||||
#define ST_LIS2DUXS12_BUFFERED_ENABLED (BIT(ST_LIS2DUXS12_ID_ACC) | \
|
||||
BIT(ST_LIS2DUXS12_ID_TEMP) | \
|
||||
BIT(ST_LIS2DUXS12_ID_STEP_COUNTER) | \
|
||||
BIT(ST_LIS2DUXS12_ID_QVAR))
|
||||
|
||||
static const enum
|
||||
st_lis2duxs12_sensor_id st_lis2duxs12_mlc_sensor_list[] = {
|
||||
[0] = ST_LIS2DUXS12_ID_MLC_0,
|
||||
[1] = ST_LIS2DUXS12_ID_MLC_1,
|
||||
[2] = ST_LIS2DUXS12_ID_MLC_2,
|
||||
[3] = ST_LIS2DUXS12_ID_MLC_3,
|
||||
};
|
||||
|
||||
static const enum
|
||||
st_lis2duxs12_sensor_id st_lis2duxs12_fsm_sensor_list[] = {
|
||||
[0] = ST_LIS2DUXS12_ID_FSM_0,
|
||||
[1] = ST_LIS2DUXS12_ID_FSM_1,
|
||||
[2] = ST_LIS2DUXS12_ID_FSM_2,
|
||||
[3] = ST_LIS2DUXS12_ID_FSM_3,
|
||||
[4] = ST_LIS2DUXS12_ID_FSM_4,
|
||||
[5] = ST_LIS2DUXS12_ID_FSM_5,
|
||||
[6] = ST_LIS2DUXS12_ID_FSM_6,
|
||||
[7] = ST_LIS2DUXS12_ID_FSM_7,
|
||||
};
|
||||
|
||||
#define ST_LIS2DUXS12_EMB_FUNC_ENABLED (BIT(ST_LIS2DUXS12_ID_STEP_DETECTOR) | \
|
||||
BIT(ST_LIS2DUXS12_ID_SIGN_MOTION) | \
|
||||
BIT(ST_LIS2DUXS12_ID_TILT))
|
||||
|
||||
#define ST_LIS2DUXS12_BASIC_FUNC_ENABLED (GENMASK(ST_LIS2DUXS12_ID_TTAP, \
|
||||
ST_LIS2DUXS12_ID_FF))
|
||||
|
||||
/* HW devices that can wakeup the target */
|
||||
#define ST_LIS2DUXS12_WAKE_UP_SENSORS (BIT(ST_LIS2DUXS12_ID_ACC) | \
|
||||
BIT(ST_LIS2DUXS12_ID_MLC_0) | \
|
||||
BIT(ST_LIS2DUXS12_ID_MLC_1) | \
|
||||
BIT(ST_LIS2DUXS12_ID_MLC_2) | \
|
||||
BIT(ST_LIS2DUXS12_ID_MLC_3) | \
|
||||
BIT(ST_LIS2DUXS12_ID_FSM_0) | \
|
||||
BIT(ST_LIS2DUXS12_ID_FSM_1) | \
|
||||
BIT(ST_LIS2DUXS12_ID_FSM_2) | \
|
||||
BIT(ST_LIS2DUXS12_ID_FSM_3) | \
|
||||
BIT(ST_LIS2DUXS12_ID_FSM_4) | \
|
||||
BIT(ST_LIS2DUXS12_ID_FSM_5) | \
|
||||
BIT(ST_LIS2DUXS12_ID_FSM_6) | \
|
||||
BIT(ST_LIS2DUXS12_ID_FSM_7))
|
||||
|
||||
/* this is the minimal ODR for wake-up sensors and dependencies */
|
||||
#define ST_LIS2DUXS12_MIN_ODR_IN_WAKEUP 25
|
||||
|
||||
enum st_lis2duxs12_fifo_mode {
|
||||
ST_LIS2DUXS12_FIFO_BYPASS = 0x0,
|
||||
ST_LIS2DUXS12_FIFO_CONT = 0x6,
|
||||
};
|
||||
|
||||
enum {
|
||||
ST_LIS2DUXS12_HW_FLUSH,
|
||||
ST_LIS2DUXS12_HW_OPERATIONAL,
|
||||
};
|
||||
|
||||
/**
|
||||
* struct st_lis2duxs12_sensor - ST ACC sensor instance
|
||||
*/
|
||||
struct st_lis2duxs12_sensor {
|
||||
char name[32];
|
||||
enum st_lis2duxs12_sensor_id id;
|
||||
struct st_lis2duxs12_hw *hw;
|
||||
struct iio_trigger *trig;
|
||||
|
||||
int odr;
|
||||
int uodr;
|
||||
|
||||
union {
|
||||
/* sensor with odrs, gain and offset */
|
||||
struct {
|
||||
u32 gain;
|
||||
u32 offset;
|
||||
u8 decimator;
|
||||
u8 dec_counter;
|
||||
__le16 old_data;
|
||||
u8 max_watermark;
|
||||
u8 watermark;
|
||||
enum st_lis2duxs12_pm_t pm;
|
||||
|
||||
/* self test */
|
||||
int8_t selftest_status;
|
||||
int min_st;
|
||||
int max_st;
|
||||
};
|
||||
/* mlc/fsm event sensors */
|
||||
struct {
|
||||
uint8_t status_reg;
|
||||
uint8_t outreg_addr;
|
||||
enum st_lis2duxs12_fsm_mlc_enable_id status;
|
||||
};
|
||||
/* sensor specific data configuration */
|
||||
struct {
|
||||
u32 conf[6];
|
||||
/* Ensure natural alignment of timestamp */
|
||||
struct {
|
||||
u8 event;
|
||||
s64 ts __aligned(8);
|
||||
} scan;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* struct st_lis2duxs12_hw - ST ACC MEMS hw instance
|
||||
*/
|
||||
struct st_lis2duxs12_hw {
|
||||
struct device *dev;
|
||||
int irq;
|
||||
struct regmap *regmap;
|
||||
struct mutex page_lock;
|
||||
struct mutex fifo_lock;
|
||||
enum st_lis2duxs12_fifo_mode fifo_mode;
|
||||
unsigned long state;
|
||||
bool xl_only;
|
||||
bool timestamp;
|
||||
|
||||
u8 std_level;
|
||||
u64 samples;
|
||||
|
||||
u32 enable_mask;
|
||||
u32 requested_mask;
|
||||
|
||||
s64 ts_offset;
|
||||
s64 hw_ts;
|
||||
s64 tsample;
|
||||
s64 delta_ts;
|
||||
s64 ts;
|
||||
s64 last_fifo_timestamp;
|
||||
|
||||
struct iio_mount_matrix orientation;
|
||||
struct regulator *vdd_supply;
|
||||
struct regulator *vddio_supply;
|
||||
|
||||
struct st_lis2duxs12_mlc_config_t *mlc_config;
|
||||
const struct st_lis2duxs12_odr_table_entry *odr_table_entry;
|
||||
const struct st_lis2duxs12_fs_table_entry *fs_table_entry;
|
||||
|
||||
bool preload_mlc;
|
||||
|
||||
u8 int_pin;
|
||||
u8 ft_int_reg;
|
||||
u8 md_int_reg;
|
||||
u8 emb_int_reg;
|
||||
|
||||
struct iio_dev *iio_devs[ST_LIS2DUXS12_ID_MAX];
|
||||
};
|
||||
|
||||
extern const struct dev_pm_ops st_lis2duxs12_pm_ops;
|
||||
|
||||
static inline bool
|
||||
st_lis2duxs12_is_fifo_enabled(struct st_lis2duxs12_hw *hw)
|
||||
{
|
||||
return hw->enable_mask & (BIT(ST_LIS2DUXS12_ID_ACC) |
|
||||
BIT(ST_LIS2DUXS12_ID_TEMP));
|
||||
}
|
||||
|
||||
static inline int
|
||||
__st_lis2duxs12_write_with_mask(struct st_lis2duxs12_hw *hw,
|
||||
unsigned int addr,
|
||||
unsigned int mask,
|
||||
unsigned int data)
|
||||
{
|
||||
int err;
|
||||
unsigned int val = ST_LIS2DUXS12_SHIFT_VAL(data, mask);
|
||||
|
||||
err = regmap_update_bits(hw->regmap, addr, mask, val);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static inline int
|
||||
st_lis2duxs12_update_bits_locked(struct st_lis2duxs12_hw *hw,
|
||||
unsigned int addr, unsigned int mask,
|
||||
unsigned int val)
|
||||
{
|
||||
int err;
|
||||
|
||||
mutex_lock(&hw->page_lock);
|
||||
err = __st_lis2duxs12_write_with_mask(hw, addr, mask, val);
|
||||
mutex_unlock(&hw->page_lock);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/* use when mask is constant */
|
||||
static inline int
|
||||
st_lis2duxs12_write_with_mask_locked(struct st_lis2duxs12_hw *hw,
|
||||
unsigned int addr,
|
||||
unsigned int mask,
|
||||
unsigned int data)
|
||||
{
|
||||
int err;
|
||||
unsigned int val = FIELD_PREP(mask, data);
|
||||
|
||||
mutex_lock(&hw->page_lock);
|
||||
err = regmap_update_bits(hw->regmap, addr, mask, val);
|
||||
mutex_unlock(&hw->page_lock);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static inline int
|
||||
st_lis2duxs12_read_locked(struct st_lis2duxs12_hw *hw,
|
||||
unsigned int addr, void *val,
|
||||
unsigned int len)
|
||||
{
|
||||
int err;
|
||||
|
||||
mutex_lock(&hw->page_lock);
|
||||
err = regmap_bulk_read(hw->regmap, addr, val, len);
|
||||
mutex_unlock(&hw->page_lock);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static inline int
|
||||
st_lis2duxs12_read_with_mask(struct st_lis2duxs12_hw *hw,
|
||||
unsigned int addr, unsigned int mask,
|
||||
u8 *val)
|
||||
{
|
||||
unsigned int data;
|
||||
int err;
|
||||
|
||||
err = regmap_read(hw->regmap, addr, &data);
|
||||
*val = (u8)ST_LIS2DUXS12_DESHIFT_VAL(data, mask);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static inline int
|
||||
st_lis2duxs12_read_with_mask_locked(struct st_lis2duxs12_hw *hw,
|
||||
unsigned int addr,
|
||||
unsigned int mask, u8 *val)
|
||||
{
|
||||
unsigned int data;
|
||||
int err;
|
||||
|
||||
mutex_lock(&hw->page_lock);
|
||||
err = regmap_read(hw->regmap, addr, &data);
|
||||
mutex_unlock(&hw->page_lock);
|
||||
*val = (u8)ST_LIS2DUXS12_DESHIFT_VAL(data, mask);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static inline int
|
||||
st_lis2duxs12_write_locked(struct st_lis2duxs12_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_lis2duxs12_set_emb_access(struct st_lis2duxs12_hw *hw,
|
||||
unsigned int val)
|
||||
{
|
||||
return regmap_write(hw->regmap,
|
||||
ST_LIS2DUXS12_FUNC_CFG_ACCESS_ADDR,
|
||||
val ? ST_LIS2DUXS12_EMB_FUNC_REG_ACCESS_MASK : 0);
|
||||
}
|
||||
|
||||
static inline int
|
||||
st_lis2duxs12_read_page_locked(struct st_lis2duxs12_hw *hw,
|
||||
unsigned int addr, void *val,
|
||||
unsigned int len)
|
||||
{
|
||||
int err;
|
||||
|
||||
mutex_lock(&hw->page_lock);
|
||||
st_lis2duxs12_set_emb_access(hw, 1);
|
||||
err = regmap_bulk_read(hw->regmap, addr, val, len);
|
||||
st_lis2duxs12_set_emb_access(hw, 0);
|
||||
mutex_unlock(&hw->page_lock);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static inline int
|
||||
st_lis2duxs12_write_page_locked(struct st_lis2duxs12_hw *hw,
|
||||
unsigned int addr, unsigned int *val,
|
||||
unsigned int len)
|
||||
{
|
||||
int err;
|
||||
|
||||
mutex_lock(&hw->page_lock);
|
||||
st_lis2duxs12_set_emb_access(hw, 1);
|
||||
err = regmap_bulk_write(hw->regmap, addr, val, len);
|
||||
st_lis2duxs12_set_emb_access(hw, 0);
|
||||
mutex_unlock(&hw->page_lock);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static inline int
|
||||
st_lis2duxs12_update_page_bits_locked(struct st_lis2duxs12_hw *hw,
|
||||
unsigned int addr,
|
||||
unsigned int mask,
|
||||
unsigned int val)
|
||||
{
|
||||
int err;
|
||||
|
||||
mutex_lock(&hw->page_lock);
|
||||
st_lis2duxs12_set_emb_access(hw, 1);
|
||||
err = regmap_update_bits(hw->regmap, addr, mask, val);
|
||||
st_lis2duxs12_set_emb_access(hw, 0);
|
||||
mutex_unlock(&hw->page_lock);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
int st_lis2duxs12_probe(struct device *dev, int irq, struct regmap *regmap);
|
||||
int st_lis2duxs12_remove(struct device *dev);
|
||||
int st_lis2duxs12_sensor_set_enable(struct st_lis2duxs12_sensor *sensor,
|
||||
bool enable);
|
||||
int st_lis2duxs12_buffers_setup(struct st_lis2duxs12_hw *hw);
|
||||
ssize_t st_lis2duxs12_flush_fifo(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t size);
|
||||
ssize_t st_lis2duxs12_get_max_watermark(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf);
|
||||
ssize_t st_lis2duxs12_get_watermark(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf);
|
||||
ssize_t st_lis2duxs12_set_watermark(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t size);
|
||||
int st_lis2duxs12_suspend_fifo(struct st_lis2duxs12_hw *hw);
|
||||
int st_lis2duxs12_set_fifo_mode(struct st_lis2duxs12_hw *hw,
|
||||
enum st_lis2duxs12_fifo_mode fifo_mode);
|
||||
int st_lis2duxs12_update_batching(struct iio_dev *iio_dev, bool enable);
|
||||
|
||||
/* mlc / fsm */
|
||||
int st_lis2duxs12_mlc_probe(struct st_lis2duxs12_hw *hw);
|
||||
int st_lis2duxs12_mlc_remove(struct device *dev);
|
||||
int st_lis2duxs12_mlc_check_status(struct st_lis2duxs12_hw *hw);
|
||||
int st_lis2duxs12_mlc_init_preload(struct st_lis2duxs12_hw *hw);
|
||||
|
||||
int st_lis2duxs12_reset_step_counter(struct iio_dev *iio_dev);
|
||||
int st_lis2duxs12_embedded_function_init(struct st_lis2duxs12_hw *hw);
|
||||
int st_lis2duxs12_step_counter_set_enable(struct st_lis2duxs12_sensor *sensor,
|
||||
bool enable);
|
||||
int st_lis2duxs12_embfunc_sensor_set_enable(struct st_lis2duxs12_sensor *sensor,
|
||||
bool enable);
|
||||
int st_lis2duxs12_probe_basicfunc(struct st_lis2duxs12_hw *hw);
|
||||
int st_lis2duxs12_event_handler(struct st_lis2duxs12_hw *hw);
|
||||
|
||||
#ifdef CONFIG_IIO_ST_LIS2DUXS12_QVAR
|
||||
/* qvar */
|
||||
int st_lis2duxs12_qvar_probe(struct st_lis2duxs12_hw *hw);
|
||||
int st_lis2duxs12_qvar_set_enable(struct st_lis2duxs12_sensor *sensor,
|
||||
bool enable);
|
||||
#endif /* CONFIG_IIO_ST_LIS2DUXS12_QVAR */
|
||||
#endif /* ST_LIS2DUXS12_H */
|
1056
drivers/iio/stm/accel/st_lis2duxs12_basicfunc.c
Normal file
1056
drivers/iio/stm/accel/st_lis2duxs12_basicfunc.c
Normal file
File diff suppressed because it is too large
Load Diff
751
drivers/iio/stm/accel/st_lis2duxs12_buffer.c
Normal file
751
drivers/iio/stm/accel/st_lis2duxs12_buffer.c
Normal file
@ -0,0 +1,751 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* STMicroelectronics st_lis2duxs12 FIFO buffer library driver
|
||||
*
|
||||
* MEMS Software Solutions Team
|
||||
*
|
||||
* Copyright 2022 STMicroelectronics Inc.
|
||||
*/
|
||||
#include <linux/module.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/kfifo_buf.h>
|
||||
#include <linux/iio/events.h>
|
||||
#include <linux/iio/buffer.h>
|
||||
#include <asm/unaligned.h>
|
||||
#include <linux/iio/trigger_consumer.h>
|
||||
#include <linux/iio/triggered_buffer.h>
|
||||
#include <linux/iio/trigger.h>
|
||||
#include <linux/iio/buffer.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/version.h>
|
||||
|
||||
#include "st_lis2duxs12.h"
|
||||
|
||||
/* Timestamp convergence filter parameters */
|
||||
#define ST_LIS2DUXS12_EWMA_LEVEL 120
|
||||
#define ST_LIS2DUXS12_EWMA_DIV 128
|
||||
|
||||
/* FIFO tags */
|
||||
enum {
|
||||
ST_LIS2DUXS12_ACC_TEMP_TAG = 0x02,
|
||||
ST_LIS2DUXS12_TS_TAG = 0x04,
|
||||
ST_LIS2DUXS12_STEP_COUNTER_TAG = 0x12,
|
||||
ST_LIS2DUXS12_ACC_QVAR_TAG = 0x1f,
|
||||
};
|
||||
|
||||
static inline s64 st_lis2duxs12_ewma(s64 old, s64 new, int weight)
|
||||
{
|
||||
s64 diff, incr;
|
||||
|
||||
diff = new - old;
|
||||
incr = div_s64((ST_LIS2DUXS12_EWMA_DIV - weight) * diff,
|
||||
ST_LIS2DUXS12_EWMA_DIV);
|
||||
|
||||
return old + incr;
|
||||
}
|
||||
|
||||
static inline int st_lis2duxs12_reset_hwts(struct st_lis2duxs12_hw *hw)
|
||||
{
|
||||
u8 data = 0xaa;
|
||||
|
||||
hw->ts = iio_get_time_ns(hw->iio_devs[0]);
|
||||
hw->ts_offset = hw->ts;
|
||||
hw->tsample = 0ull;
|
||||
|
||||
return st_lis2duxs12_write_locked(hw, ST_LIS2DUXS12_TIMESTAMP2_ADDR,
|
||||
data);
|
||||
}
|
||||
|
||||
int st_lis2duxs12_set_fifo_mode(struct st_lis2duxs12_hw *hw,
|
||||
enum st_lis2duxs12_fifo_mode fifo_mode)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = st_lis2duxs12_write_with_mask_locked(hw,
|
||||
ST_LIS2DUXS12_FIFO_CTRL_ADDR,
|
||||
ST_LIS2DUXS12_FIFO_MODE_MASK,
|
||||
fifo_mode);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
hw->fifo_mode = fifo_mode;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int st_lis2duxs12_update_watermark(struct st_lis2duxs12_sensor *sensor,
|
||||
u8 watermark)
|
||||
{
|
||||
u8 fifo_watermark = ST_LIS2DUXS12_MAX_FIFO_DEPTH;
|
||||
struct st_lis2duxs12_hw *hw = sensor->hw;
|
||||
struct st_lis2duxs12_sensor *cur_sensor;
|
||||
u8 cur_watermark = 0;
|
||||
int err;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(st_lis2duxs12_buffered_sensor_list);
|
||||
i++) {
|
||||
enum st_lis2duxs12_sensor_id id =
|
||||
st_lis2duxs12_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(u8, fifo_watermark,
|
||||
cur_watermark);
|
||||
}
|
||||
|
||||
fifo_watermark = max_t(u8, fifo_watermark,
|
||||
hw->timestamp ? 2 : 1);
|
||||
|
||||
err = st_lis2duxs12_write_with_mask_locked(hw,
|
||||
ST_LIS2DUXS12_FIFO_WTM_ADDR,
|
||||
ST_LIS2DUXS12_FIFO_WTM_MASK,
|
||||
fifo_watermark);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int st_lis2duxs12_read_fifo(struct st_lis2duxs12_hw *hw)
|
||||
{
|
||||
u8 iio_buf[ALIGN(ST_LIS2DUXS12_SAMPLE_SIZE, sizeof(s64)) + sizeof(s64)];
|
||||
u8 buf[6 * ST_LIS2DUXS12_FIFO_SAMPLE_SIZE], tag, *ptr;
|
||||
int i, err, word_len, fifo_len, read_len;
|
||||
u8 fifo_status, fifo_depth;
|
||||
s64 ts_irq, hw_ts_old;
|
||||
u32 val;
|
||||
|
||||
if (hw->fifo_mode == ST_LIS2DUXS12_FIFO_BYPASS)
|
||||
return 0;
|
||||
|
||||
ts_irq = hw->ts -hw->delta_ts;
|
||||
|
||||
err = st_lis2duxs12_read_locked(hw,
|
||||
ST_LIS2DUXS12_FIFO_STATUS2_ADDR,
|
||||
&fifo_status,
|
||||
sizeof(fifo_status));
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
fifo_depth = fifo_status & ST_LIS2DUXS12_FIFO_FSS_MASK;
|
||||
if (!fifo_depth)
|
||||
return 0;
|
||||
|
||||
fifo_len = fifo_depth * ST_LIS2DUXS12_FIFO_SAMPLE_SIZE;
|
||||
read_len = 0;
|
||||
|
||||
while (read_len < fifo_len) {
|
||||
word_len = min_t(int, fifo_len - read_len, sizeof(buf));
|
||||
err = st_lis2duxs12_read_locked(hw,
|
||||
ST_LIS2DUXS12_FIFO_DATA_OUT_TAG_ADDR,
|
||||
buf, word_len);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
for (i = 0; i < word_len; i += ST_LIS2DUXS12_FIFO_SAMPLE_SIZE) {
|
||||
ptr = &buf[i + ST_LIS2DUXS12_TAG_SIZE];
|
||||
tag = buf[i] >> 3;
|
||||
|
||||
switch (tag) {
|
||||
case ST_LIS2DUXS12_TS_TAG:
|
||||
val = get_unaligned_le32(ptr + 2);
|
||||
hw_ts_old = hw->hw_ts;
|
||||
hw->hw_ts = val * ST_LIS2DUXS12_TS_DELTA_NS;
|
||||
hw->ts_offset =
|
||||
st_lis2duxs12_ewma(hw->ts_offset,
|
||||
ts_irq - hw->hw_ts,
|
||||
ST_LIS2DUXS12_EWMA_LEVEL);
|
||||
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;
|
||||
break;
|
||||
case ST_LIS2DUXS12_ACC_TEMP_TAG: {
|
||||
struct iio_dev *iio_dev =
|
||||
hw->iio_devs[ST_LIS2DUXS12_ID_ACC];
|
||||
|
||||
if (hw->timestamp)
|
||||
hw->tsample = min_t(s64,
|
||||
iio_get_time_ns(iio_dev),
|
||||
hw->tsample);
|
||||
else
|
||||
hw->tsample = iio_get_time_ns(iio_dev);
|
||||
|
||||
hw->last_fifo_timestamp = hw->tsample;
|
||||
|
||||
if (hw->xl_only) {
|
||||
/*
|
||||
* data representation in FIFO
|
||||
* when ACC only:
|
||||
* ----------- -----------
|
||||
* | LSBX | MSBX |
|
||||
* ----------- -----------
|
||||
* | LSBY | MSBY |
|
||||
* ----------- -----------
|
||||
* | LSBZ | MSBZ |
|
||||
* ----------- -----------
|
||||
*/
|
||||
memcpy(iio_buf,
|
||||
ptr, ST_LIS2DUXS12_SAMPLE_SIZE);
|
||||
if (unlikely(++hw->samples < hw->std_level))
|
||||
continue;
|
||||
|
||||
iio_push_to_buffers_with_timestamp(iio_dev,
|
||||
iio_buf, hw->tsample);
|
||||
} else {
|
||||
struct raw_data_compact_t *raw_data_c;
|
||||
struct iio_dev *iio_temp_dev;
|
||||
struct raw_data_t raw_data;
|
||||
__le16 temp;
|
||||
|
||||
raw_data_c = (struct raw_data_compact_t *)ptr;
|
||||
iio_temp_dev =
|
||||
hw->iio_devs[ST_LIS2DUXS12_ID_TEMP];
|
||||
|
||||
/*
|
||||
* data representation in FIFO
|
||||
* when ACC/Temp available:
|
||||
* ------------- -------------
|
||||
* | LSB0 | LSN1 | MSN0 |
|
||||
* ------------- -------------
|
||||
* | MSB1 | LSB2 |
|
||||
* ------------- -------------
|
||||
* | LSN3 | MSN2 | MSB3 |
|
||||
* ------------- -------------
|
||||
*/
|
||||
|
||||
/* extends to 16 bit */
|
||||
temp = cpu_to_le16(le16_to_cpu(raw_data_c->t) << 4);
|
||||
|
||||
memcpy(iio_buf, (u8 *)&temp, sizeof(temp));
|
||||
iio_push_to_buffers_with_timestamp(iio_temp_dev,
|
||||
iio_buf, hw->tsample);
|
||||
|
||||
if (unlikely(++hw->samples < hw->std_level))
|
||||
continue;
|
||||
|
||||
/* extends to 16 bit */
|
||||
raw_data.x = cpu_to_le16(le16_to_cpu(raw_data_c->x) << 4);
|
||||
raw_data.y = cpu_to_le16(le16_to_cpu(raw_data_c->y) << 4);
|
||||
raw_data.z = cpu_to_le16(le16_to_cpu(raw_data_c->z) << 4);
|
||||
|
||||
memcpy(iio_buf, (u8 *)&raw_data, sizeof(raw_data));
|
||||
iio_push_to_buffers_with_timestamp(iio_dev,
|
||||
iio_buf, hw->tsample);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ST_LIS2DUXS12_ACC_QVAR_TAG: {
|
||||
struct iio_dev *iio_dev =
|
||||
hw->iio_devs[ST_LIS2DUXS12_ID_ACC];
|
||||
struct raw_data_compact_t *raw_data_c;
|
||||
struct iio_dev *iio_qvar_dev;
|
||||
struct raw_data_t raw_data;
|
||||
__le16 qvar;
|
||||
|
||||
if (hw->timestamp)
|
||||
hw->tsample = min_t(s64,
|
||||
iio_get_time_ns(iio_dev),
|
||||
hw->tsample);
|
||||
else
|
||||
hw->tsample = iio_get_time_ns(iio_dev);
|
||||
|
||||
hw->last_fifo_timestamp = hw->tsample;
|
||||
|
||||
raw_data_c = (struct raw_data_compact_t *)ptr;
|
||||
iio_qvar_dev =
|
||||
hw->iio_devs[ST_LIS2DUXS12_ID_QVAR];
|
||||
|
||||
/*
|
||||
* data representation in FIFO
|
||||
* when ACC/Qvar available:
|
||||
* ------------- -------------
|
||||
* | LSB0 | LSN1 | MSN0 |
|
||||
* ------------- -------------
|
||||
* | MSB1 | LSB2 |
|
||||
* ------------- -------------
|
||||
* | LSN3 | MSN2 | MSB3 |
|
||||
* ------------- -------------
|
||||
*/
|
||||
|
||||
/* extends to 16 bit */
|
||||
qvar = cpu_to_le16(le16_to_cpu(raw_data_c->t) << 4);
|
||||
|
||||
memcpy(iio_buf, (u8 *)&qvar, sizeof(qvar));
|
||||
iio_push_to_buffers_with_timestamp(iio_qvar_dev,
|
||||
iio_buf,
|
||||
hw->tsample);
|
||||
|
||||
/* skip push acc if not enabled */
|
||||
if (!(hw->enable_mask & BIT(ST_LIS2DUXS12_ID_ACC)) ||
|
||||
unlikely(++hw->samples < hw->std_level))
|
||||
continue;
|
||||
|
||||
/* extends to 16 bit */
|
||||
raw_data.x = cpu_to_le16(le16_to_cpu(raw_data_c->x) << 4);
|
||||
raw_data.y = cpu_to_le16(le16_to_cpu(raw_data_c->y) << 4);
|
||||
raw_data.z = cpu_to_le16(le16_to_cpu(raw_data_c->z) << 4);
|
||||
|
||||
memcpy(iio_buf, (u8 *)&raw_data, sizeof(raw_data));
|
||||
iio_push_to_buffers_with_timestamp(iio_dev,
|
||||
iio_buf,
|
||||
hw->tsample);
|
||||
break;
|
||||
}
|
||||
case ST_LIS2DUXS12_STEP_COUNTER_TAG: {
|
||||
struct iio_dev *iio_dev =
|
||||
hw->iio_devs[ST_LIS2DUXS12_ID_STEP_COUNTER];
|
||||
|
||||
val = get_unaligned_le32(ptr + 2);
|
||||
hw->tsample = val * ST_LIS2DUXS12_TS_DELTA_NS;
|
||||
memcpy(iio_buf, ptr, ST_LIS2DUXS12_SAMPLE_SIZE);
|
||||
iio_push_to_buffers_with_timestamp(iio_dev,
|
||||
iio_buf,
|
||||
hw->tsample);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
read_len += word_len;
|
||||
}
|
||||
|
||||
return read_len;
|
||||
}
|
||||
|
||||
ssize_t st_lis2duxs12_get_max_watermark(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct iio_dev *iio_dev = dev_get_drvdata(dev);
|
||||
struct st_lis2duxs12_sensor *sensor = iio_priv(iio_dev);
|
||||
|
||||
return sprintf(buf, "%d\n", sensor->max_watermark);
|
||||
}
|
||||
|
||||
ssize_t st_lis2duxs12_get_watermark(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct iio_dev *iio_dev = dev_get_drvdata(dev);
|
||||
struct st_lis2duxs12_sensor *sensor = iio_priv(iio_dev);
|
||||
|
||||
return sprintf(buf, "%d\n", sensor->watermark);
|
||||
}
|
||||
|
||||
ssize_t st_lis2duxs12_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_lis2duxs12_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_lis2duxs12_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_lis2duxs12_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_lis2duxs12_sensor *sensor = iio_priv(iio_dev);
|
||||
struct st_lis2duxs12_hw *hw = sensor->hw;
|
||||
s64 type, event, fts, ts;
|
||||
int count;
|
||||
|
||||
mutex_lock(&hw->fifo_lock);
|
||||
ts = iio_get_time_ns(iio_dev);
|
||||
hw->delta_ts = ts -hw->ts;
|
||||
hw->ts = ts;
|
||||
set_bit(ST_LIS2DUXS12_HW_FLUSH, &hw->state);
|
||||
count = st_lis2duxs12_read_fifo(hw);
|
||||
sensor->dec_counter = 0;
|
||||
if (count > 0)
|
||||
fts = hw->last_fifo_timestamp;
|
||||
else
|
||||
fts = ts;
|
||||
mutex_unlock(&hw->fifo_lock);
|
||||
|
||||
type = count > 0 ? IIO_EV_DIR_FIFO_DATA : IIO_EV_DIR_FIFO_EMPTY;
|
||||
event = IIO_UNMOD_EVENT_CODE(iio_dev->channels[0].type, -1,
|
||||
IIO_EV_TYPE_FIFO_FLUSH, type);
|
||||
iio_push_event(iio_dev, event, fts);
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
int st_lis2duxs12_suspend_fifo(struct st_lis2duxs12_hw *hw)
|
||||
{
|
||||
int err;
|
||||
|
||||
mutex_lock(&hw->fifo_lock);
|
||||
st_lis2duxs12_read_fifo(hw);
|
||||
err = st_lis2duxs12_set_fifo_mode(hw, ST_LIS2DUXS12_FIFO_BYPASS);
|
||||
mutex_unlock(&hw->fifo_lock);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int st_lis2duxs12_update_fifo(struct iio_dev *iio_dev, bool enable)
|
||||
{
|
||||
struct st_lis2duxs12_sensor *sensor = iio_priv(iio_dev);
|
||||
struct st_lis2duxs12_hw *hw = sensor->hw;
|
||||
int err;
|
||||
|
||||
disable_irq(hw->irq);
|
||||
mutex_lock(&hw->fifo_lock);
|
||||
|
||||
switch (sensor->id) {
|
||||
|
||||
#ifdef CONFIG_IIO_ST_LIS2DUXS12_QVAR
|
||||
case ST_LIS2DUXS12_ID_QVAR: {
|
||||
u8 xl_only = enable ? 0 : 1;
|
||||
|
||||
/*
|
||||
* check consistency, temperature sensor need to be
|
||||
* disabled because share the same QVAR output registers
|
||||
*/
|
||||
if (hw->enable_mask & BIT(ST_LIS2DUXS12_ID_TEMP)) {
|
||||
err = -EBUSY;
|
||||
|
||||
goto out;
|
||||
}
|
||||
|
||||
err = st_lis2duxs12_qvar_set_enable(sensor, enable);
|
||||
if (err < 0)
|
||||
goto out;
|
||||
|
||||
/* enable XL and Temp */
|
||||
err = regmap_update_bits(hw->regmap,
|
||||
ST_LIS2DUXS12_FIFO_WTM_ADDR,
|
||||
ST_LIS2DUXS12_XL_ONLY_FIFO_MASK,
|
||||
FIELD_PREP(ST_LIS2DUXS12_XL_ONLY_FIFO_MASK,
|
||||
xl_only));
|
||||
if (err < 0)
|
||||
goto out;
|
||||
|
||||
hw->xl_only = !!xl_only;
|
||||
break;
|
||||
}
|
||||
#endif /* CONFIG_IIO_ST_LIS2DUXS12_QVAR */
|
||||
|
||||
case ST_LIS2DUXS12_ID_TEMP: {
|
||||
u8 xl_only = enable ? 0 : 1;
|
||||
|
||||
/*
|
||||
* check consistency, QVAR sensor need to be disabled
|
||||
* because share the same TEMP output registers
|
||||
*/
|
||||
if (hw->enable_mask & BIT(ST_LIS2DUXS12_ID_QVAR)) {
|
||||
err = -EBUSY;
|
||||
|
||||
goto out;
|
||||
}
|
||||
|
||||
err = st_lis2duxs12_sensor_set_enable(sensor, enable);
|
||||
if (err < 0)
|
||||
goto out;
|
||||
|
||||
/* enable XL and Temp */
|
||||
err = regmap_update_bits(hw->regmap,
|
||||
ST_LIS2DUXS12_FIFO_WTM_ADDR,
|
||||
ST_LIS2DUXS12_XL_ONLY_FIFO_MASK,
|
||||
FIELD_PREP(ST_LIS2DUXS12_XL_ONLY_FIFO_MASK,
|
||||
xl_only));
|
||||
if (err < 0)
|
||||
goto out;
|
||||
|
||||
hw->xl_only = !!xl_only;
|
||||
break;
|
||||
}
|
||||
case ST_LIS2DUXS12_ID_STEP_COUNTER:
|
||||
err = st_lis2duxs12_step_counter_set_enable(sensor, enable);
|
||||
if (err < 0)
|
||||
goto out;
|
||||
break;
|
||||
case ST_LIS2DUXS12_ID_ACC:
|
||||
err = st_lis2duxs12_sensor_set_enable(sensor, enable);
|
||||
if (err < 0)
|
||||
goto out;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
err = st_lis2duxs12_update_watermark(sensor, sensor->watermark);
|
||||
if (err < 0)
|
||||
goto out;
|
||||
|
||||
if (enable && hw->fifo_mode == ST_LIS2DUXS12_FIFO_BYPASS) {
|
||||
st_lis2duxs12_reset_hwts(hw);
|
||||
err = st_lis2duxs12_set_fifo_mode(hw,
|
||||
ST_LIS2DUXS12_FIFO_CONT);
|
||||
} else if (!(hw->enable_mask & ST_LIS2DUXS12_BUFFERED_ENABLED)) {
|
||||
err = st_lis2duxs12_set_fifo_mode(hw,
|
||||
ST_LIS2DUXS12_FIFO_BYPASS);
|
||||
}
|
||||
|
||||
out:
|
||||
mutex_unlock(&hw->fifo_lock);
|
||||
enable_irq(hw->irq);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static irqreturn_t st_lis2duxs12_handler_irq(int irq, void *private)
|
||||
{
|
||||
struct st_lis2duxs12_hw *hw = (struct st_lis2duxs12_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_lis2duxs12_handler_thread(int irq, void *private)
|
||||
{
|
||||
struct st_lis2duxs12_hw *hw = (struct st_lis2duxs12_hw *)private;
|
||||
|
||||
st_lis2duxs12_mlc_check_status(hw);
|
||||
|
||||
mutex_lock(&hw->fifo_lock);
|
||||
st_lis2duxs12_read_fifo(hw);
|
||||
clear_bit(ST_LIS2DUXS12_HW_FLUSH, &hw->state);
|
||||
mutex_unlock(&hw->fifo_lock);
|
||||
|
||||
if (hw->enable_mask & (BIT(ST_LIS2DUXS12_ID_STEP_DETECTOR) |
|
||||
BIT(ST_LIS2DUXS12_ID_TILT) |
|
||||
BIT(ST_LIS2DUXS12_ID_SIGN_MOTION))) {
|
||||
struct iio_dev *iio_dev;
|
||||
u8 status;
|
||||
s64 event;
|
||||
int err;
|
||||
|
||||
err = st_lis2duxs12_read_locked(hw,
|
||||
ST_LIS2DUXS12_EMB_FUNC_STATUS_MAINPAGE_ADDR,
|
||||
&status, sizeof(status));
|
||||
if (err < 0)
|
||||
goto out;
|
||||
|
||||
/* embedded function sensors */
|
||||
if (status & ST_LIS2DUXS12_IS_STEP_DET_MASK) {
|
||||
iio_dev = hw->iio_devs[ST_LIS2DUXS12_ID_STEP_DETECTOR];
|
||||
event = IIO_UNMOD_EVENT_CODE(IIO_STEP_DETECTOR, -1,
|
||||
IIO_EV_TYPE_THRESH,
|
||||
IIO_EV_DIR_RISING);
|
||||
iio_push_event(iio_dev, event,
|
||||
iio_get_time_ns(iio_dev));
|
||||
}
|
||||
|
||||
if (status & ST_LIS2DUXS12_IS_SIGMOT_MASK) {
|
||||
iio_dev = hw->iio_devs[ST_LIS2DUXS12_ID_SIGN_MOTION];
|
||||
event = IIO_UNMOD_EVENT_CODE(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_LIS2DUXS12_IS_TILT_MASK) {
|
||||
iio_dev = hw->iio_devs[ST_LIS2DUXS12_ID_TILT];
|
||||
event = IIO_UNMOD_EVENT_CODE(IIO_TILT, -1,
|
||||
IIO_EV_TYPE_THRESH,
|
||||
IIO_EV_DIR_RISING);
|
||||
iio_push_event(iio_dev, event,
|
||||
iio_get_time_ns(iio_dev));
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
return st_lis2duxs12_event_handler(hw);
|
||||
}
|
||||
|
||||
static int st_lis2duxs12_fifo_preenable(struct iio_dev *iio_dev)
|
||||
{
|
||||
return st_lis2duxs12_update_fifo(iio_dev, true);
|
||||
}
|
||||
|
||||
static int st_lis2duxs12_fifo_postdisable(struct iio_dev *iio_dev)
|
||||
{
|
||||
return st_lis2duxs12_update_fifo(iio_dev, false);
|
||||
}
|
||||
|
||||
static const struct iio_buffer_setup_ops st_lis2duxs12_fifo_ops = {
|
||||
.preenable = st_lis2duxs12_fifo_preenable,
|
||||
.postdisable = st_lis2duxs12_fifo_postdisable,
|
||||
};
|
||||
|
||||
int st_lis2duxs12_buffers_setup(struct st_lis2duxs12_hw *hw)
|
||||
{
|
||||
struct device_node *np = hw->dev->of_node;
|
||||
unsigned long irq_type;
|
||||
bool irq_active_low;
|
||||
int err;
|
||||
int i;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/* configure interrupt pin level */
|
||||
if (irq_active_low) {
|
||||
err = regmap_update_bits(hw->regmap,
|
||||
ST_LIS2DUXS12_PIN_CTRL_ADDR,
|
||||
ST_LIS2DUXS12_H_LACTIVE_MASK,
|
||||
FIELD_PREP(ST_LIS2DUXS12_H_LACTIVE_MASK, 1));
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
|
||||
if (np && of_property_read_bool(np, "drive-open-drain")) {
|
||||
err = regmap_update_bits(hw->regmap,
|
||||
ST_LIS2DUXS12_PIN_CTRL_ADDR,
|
||||
ST_LIS2DUXS12_PP_OD_MASK,
|
||||
FIELD_PREP(ST_LIS2DUXS12_PP_OD_MASK, 1));
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
irq_type |= IRQF_SHARED;
|
||||
}
|
||||
|
||||
/* check pull down disable on int1 pin property */
|
||||
if (np && of_property_read_bool(np, "pd_dis_int1")) {
|
||||
err = regmap_update_bits(hw->regmap,
|
||||
ST_LIS2DUXS12_PIN_CTRL_ADDR,
|
||||
ST_LIS2DUXS12_PD_DIS_INT1_MASK,
|
||||
FIELD_PREP(ST_LIS2DUXS12_PD_DIS_INT1_MASK, 1));
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_IIO_ST_LIS2DUXS12_QVAR
|
||||
if (hw->int_pin != 1) {
|
||||
dev_err(hw->dev,
|
||||
"if qvar enabled only irq pin 1 can be used\n");
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/* route on RES pin the interrupt pin configured */
|
||||
err = regmap_update_bits(hw->regmap,
|
||||
ST_LIS2DUXS12_CTRL1_ADDR,
|
||||
ST_LIS2DUXS12_INT1_ON_RES_MASK,
|
||||
FIELD_PREP(ST_LIS2DUXS12_INT1_ON_RES_MASK, 1));
|
||||
if (err < 0)
|
||||
return err;
|
||||
#else /* CONFIG_IIO_ST_LIS2DUXS12_QVAR */
|
||||
/*
|
||||
* check pull down disable on int2 pin property (not supported
|
||||
* when qvar enabled)
|
||||
*/
|
||||
if (np && of_property_read_bool(np, "pd_dis_int2")) {
|
||||
err = regmap_update_bits(hw->regmap,
|
||||
ST_LIS2DUXS12_PIN_CTRL_ADDR,
|
||||
ST_LIS2DUXS12_PD_DIS_INT2_MASK,
|
||||
FIELD_PREP(ST_LIS2DUXS12_PD_DIS_INT2_MASK, 1));
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
#endif /* CONFIG_IIO_ST_LIS2DUXS12_QVAR */
|
||||
|
||||
err = devm_request_threaded_irq(hw->dev, hw->irq,
|
||||
st_lis2duxs12_handler_irq,
|
||||
st_lis2duxs12_handler_thread,
|
||||
irq_type | IRQF_ONESHOT,
|
||||
ST_LIS2DUXS12_DEV_NAME, hw);
|
||||
if (err) {
|
||||
dev_err(hw->dev,
|
||||
"failed to request trigger irq %d\n",
|
||||
hw->irq);
|
||||
return err;
|
||||
}
|
||||
|
||||
/* allocate buffer for all buffered sensor type */
|
||||
for (i = 0; i < ARRAY_SIZE(st_lis2duxs12_buffered_sensor_list); i++) {
|
||||
enum st_lis2duxs12_sensor_id id =
|
||||
st_lis2duxs12_buffered_sensor_list[i];
|
||||
|
||||
#if KERNEL_VERSION(5, 13, 0) > LINUX_VERSION_CODE
|
||||
struct iio_buffer *buffer;
|
||||
#endif /* LINUX_VERSION_CODE */
|
||||
|
||||
if (!hw->iio_devs[id])
|
||||
continue;
|
||||
|
||||
#if KERNEL_VERSION(5, 13, 0) <= LINUX_VERSION_CODE
|
||||
err = devm_iio_kfifo_buffer_setup(hw->dev, hw->iio_devs[id],
|
||||
INDIO_BUFFER_SOFTWARE,
|
||||
&st_lis2duxs12_fifo_ops);
|
||||
if (err)
|
||||
return err;
|
||||
#else /* LINUX_VERSION_CODE */
|
||||
buffer = devm_iio_kfifo_allocate(hw->dev);
|
||||
if (!buffer)
|
||||
return -ENOMEM;
|
||||
|
||||
iio_device_attach_buffer(hw->iio_devs[id], buffer);
|
||||
hw->iio_devs[id]->modes |= INDIO_BUFFER_SOFTWARE;
|
||||
hw->iio_devs[id]->setup_ops = &st_lis2duxs12_fifo_ops;
|
||||
#endif /* LINUX_VERSION_CODE */
|
||||
}
|
||||
|
||||
if (hw->timestamp) {
|
||||
err = regmap_update_bits(hw->regmap,
|
||||
ST_LIS2DUXS12_FIFO_BATCH_DEC_ADDR,
|
||||
ST_LIS2DUXS12_DEC_TS_MASK,
|
||||
FIELD_PREP(ST_LIS2DUXS12_DEC_TS_MASK, 1));
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
1661
drivers/iio/stm/accel/st_lis2duxs12_core.c
Normal file
1661
drivers/iio/stm/accel/st_lis2duxs12_core.c
Normal file
File diff suppressed because it is too large
Load Diff
205
drivers/iio/stm/accel/st_lis2duxs12_embfunc.c
Normal file
205
drivers/iio/stm/accel/st_lis2duxs12_embfunc.c
Normal file
@ -0,0 +1,205 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* STMicroelectronics st_lis2duxs12 embedded function sensor driver
|
||||
*
|
||||
* MEMS Software Solutions Team
|
||||
*
|
||||
* Copyright 2022 STMicroelectronics Inc.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/iio/iio.h>
|
||||
|
||||
#include "st_lis2duxs12.h"
|
||||
|
||||
static int
|
||||
st_lis2duxs12_ef_pg1_sensor_set_enable(struct st_lis2duxs12_sensor *sensor,
|
||||
u8 mask, u8 irq_mask, bool enable)
|
||||
{
|
||||
struct st_lis2duxs12_hw *hw = sensor->hw;
|
||||
int err;
|
||||
|
||||
err = st_lis2duxs12_sensor_set_enable(sensor, enable);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
mutex_lock(&hw->page_lock);
|
||||
err = st_lis2duxs12_set_emb_access(hw, 1);
|
||||
if (err < 0)
|
||||
goto unlock;
|
||||
|
||||
err = __st_lis2duxs12_write_with_mask(hw,
|
||||
ST_LIS2DUXS12_EMB_FUNC_EN_A_ADDR,
|
||||
mask, enable);
|
||||
if (err < 0)
|
||||
goto reset_page;
|
||||
|
||||
err = __st_lis2duxs12_write_with_mask(hw, hw->emb_int_reg,
|
||||
irq_mask, enable);
|
||||
|
||||
reset_page:
|
||||
st_lis2duxs12_set_emb_access(hw, 0);
|
||||
|
||||
if (err < 0)
|
||||
goto unlock;
|
||||
|
||||
if (((hw->enable_mask & ST_LIS2DUXS12_EMB_FUNC_ENABLED) && enable) ||
|
||||
(!(hw->enable_mask & ST_LIS2DUXS12_EMB_FUNC_ENABLED) && !enable)) {
|
||||
err = __st_lis2duxs12_write_with_mask(hw, hw->md_int_reg,
|
||||
ST_LIS2DUXS12_INT_EMB_FUNC_MASK,
|
||||
enable);
|
||||
}
|
||||
|
||||
unlock:
|
||||
mutex_unlock(&hw->page_lock);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable Embedded Function sensor [EMB_FUN]
|
||||
*
|
||||
* @param sensor: ST ACC sensor instance
|
||||
* @param enable: Enable/Disable sensor
|
||||
* @return < 0 if error, 0 otherwise
|
||||
*/
|
||||
int st_lis2duxs12_embfunc_sensor_set_enable(struct st_lis2duxs12_sensor *sensor,
|
||||
bool enable)
|
||||
{
|
||||
int err;
|
||||
|
||||
switch (sensor->id) {
|
||||
case ST_LIS2DUXS12_ID_STEP_DETECTOR:
|
||||
err = st_lis2duxs12_ef_pg1_sensor_set_enable(sensor,
|
||||
ST_LIS2DUXS12_PEDO_EN_MASK,
|
||||
ST_LIS2DUXS12_INT_STEP_DETECTOR_MASK,
|
||||
enable);
|
||||
break;
|
||||
case ST_LIS2DUXS12_ID_SIGN_MOTION:
|
||||
err = st_lis2duxs12_ef_pg1_sensor_set_enable(sensor,
|
||||
ST_LIS2DUXS12_SIGN_MOTION_EN_MASK,
|
||||
ST_LIS2DUXS12_INT_SIG_MOT_MASK,
|
||||
enable);
|
||||
break;
|
||||
case ST_LIS2DUXS12_ID_TILT:
|
||||
err = st_lis2duxs12_ef_pg1_sensor_set_enable(sensor,
|
||||
ST_LIS2DUXS12_TILT_EN_MASK,
|
||||
ST_LIS2DUXS12_INT_TILT_MASK,
|
||||
enable);
|
||||
break;
|
||||
default:
|
||||
err = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
int
|
||||
st_lis2duxs12_step_counter_set_enable(struct st_lis2duxs12_sensor *sensor,
|
||||
bool enable)
|
||||
{
|
||||
struct st_lis2duxs12_hw *hw = sensor->hw;
|
||||
bool run_enable = false;
|
||||
int err;
|
||||
|
||||
mutex_lock(&hw->page_lock);
|
||||
err = st_lis2duxs12_set_emb_access(hw, 1);
|
||||
if (err < 0)
|
||||
goto unlock;
|
||||
|
||||
err = __st_lis2duxs12_write_with_mask(hw,
|
||||
ST_LIS2DUXS12_EMB_FUNC_EN_A_ADDR,
|
||||
ST_LIS2DUXS12_PEDO_EN_MASK,
|
||||
enable);
|
||||
if (err < 0)
|
||||
goto reset_page;
|
||||
|
||||
err = __st_lis2duxs12_write_with_mask(hw,
|
||||
ST_LIS2DUXS12_EMB_FUNC_FIFO_EN_ADDR,
|
||||
ST_LIS2DUXS12_STEP_COUNTER_FIFO_EN_MASK,
|
||||
enable);
|
||||
if (err < 0)
|
||||
goto reset_page;
|
||||
|
||||
run_enable = true;
|
||||
|
||||
reset_page:
|
||||
st_lis2duxs12_set_emb_access(hw, 0);
|
||||
|
||||
unlock:
|
||||
mutex_unlock(&hw->page_lock);
|
||||
|
||||
if (run_enable)
|
||||
err = st_lis2duxs12_sensor_set_enable(sensor, enable);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
int st_lis2duxs12_reset_step_counter(struct iio_dev *iio_dev)
|
||||
{
|
||||
struct st_lis2duxs12_sensor *sensor = iio_priv(iio_dev);
|
||||
struct st_lis2duxs12_hw *hw = sensor->hw;
|
||||
__le16 data;
|
||||
int err;
|
||||
|
||||
err = iio_device_claim_direct_mode(iio_dev);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = st_lis2duxs12_step_counter_set_enable(sensor, true);
|
||||
if (err < 0)
|
||||
goto unlock_iio_dev;
|
||||
|
||||
mutex_lock(&hw->page_lock);
|
||||
err = st_lis2duxs12_set_emb_access(hw, 1);
|
||||
if (err < 0)
|
||||
goto unlock_page;
|
||||
|
||||
err = __st_lis2duxs12_write_with_mask(hw,
|
||||
ST_LIS2DUXS12_EMB_FUNC_SRC_ADDR,
|
||||
ST_LIS2DUXS12_PEDO_RST_STEP_MASK, 1);
|
||||
if (err < 0)
|
||||
goto reset_page;
|
||||
|
||||
msleep(100);
|
||||
|
||||
regmap_bulk_read(hw->regmap, ST_LIS2DUXS12_STEP_COUNTER_L_ADDR,
|
||||
(u8 *)&data, sizeof(data));
|
||||
|
||||
reset_page:
|
||||
st_lis2duxs12_set_emb_access(hw, 0);
|
||||
|
||||
unlock_page:
|
||||
mutex_unlock(&hw->page_lock);
|
||||
|
||||
err = st_lis2duxs12_step_counter_set_enable(sensor, false);
|
||||
|
||||
unlock_iio_dev:
|
||||
iio_device_release_direct_mode(iio_dev);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
int st_lis2duxs12_embedded_function_init(struct st_lis2duxs12_hw *hw)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = st_lis2duxs12_update_bits_locked(hw,
|
||||
ST_LIS2DUXS12_CTRL4_ADDR,
|
||||
ST_LIS2DUXS12_EMB_FUNC_EN_MASK,
|
||||
1);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
usleep_range(5000, 6000);
|
||||
|
||||
/* enable latched interrupts */
|
||||
err = st_lis2duxs12_update_page_bits_locked(hw,
|
||||
ST_LIS2DUXS12_PAGE_RW_ADDR,
|
||||
ST_LIS2DUXS12_EMB_FUNC_LIR_MASK,
|
||||
1);
|
||||
|
||||
return err;
|
||||
}
|
73
drivers/iio/stm/accel/st_lis2duxs12_i2c.c
Normal file
73
drivers/iio/stm/accel/st_lis2duxs12_i2c.c
Normal file
@ -0,0 +1,73 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* STMicroelectronics st_lis2duxs12 i2c driver
|
||||
*
|
||||
* MEMS Software Solutions Team
|
||||
*
|
||||
* Copyright 2022 STMicroelectronics Inc.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/of.h>
|
||||
|
||||
#include "st_lis2duxs12.h"
|
||||
|
||||
static const struct regmap_config st_lis2duxs12_i2c_regmap_config = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
};
|
||||
|
||||
static int st_lis2duxs12_i2c_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct regmap *regmap;
|
||||
|
||||
regmap = devm_regmap_init_i2c(client,
|
||||
&st_lis2duxs12_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_lis2duxs12_probe(&client->dev, client->irq, regmap);
|
||||
}
|
||||
|
||||
static int st_lis2duxs12_i2c_remove(struct i2c_client *client)
|
||||
{
|
||||
return st_lis2duxs12_remove(&client->dev);
|
||||
}
|
||||
|
||||
static const struct of_device_id st_lis2duxs12_i2c_of_match[] = {
|
||||
{
|
||||
.compatible = "st," ST_LIS2DUXS12_DEV_NAME,
|
||||
},
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, st_lis2duxs12_i2c_of_match);
|
||||
|
||||
static const struct i2c_device_id st_lis2duxs12_i2c_id_table[] = {
|
||||
{ ST_LIS2DUXS12_DEV_NAME },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, st_lis2duxs12_i2c_id_table);
|
||||
|
||||
static struct i2c_driver st_lis2duxs12_driver = {
|
||||
.driver = {
|
||||
.name = "st_" ST_LIS2DUXS12_DEV_NAME "_i2c",
|
||||
.pm = &st_lis2duxs12_pm_ops,
|
||||
.of_match_table = of_match_ptr(st_lis2duxs12_i2c_of_match),
|
||||
},
|
||||
.probe = st_lis2duxs12_i2c_probe,
|
||||
.remove = st_lis2duxs12_i2c_remove,
|
||||
.id_table = st_lis2duxs12_i2c_id_table,
|
||||
};
|
||||
module_i2c_driver(st_lis2duxs12_driver);
|
||||
|
||||
MODULE_AUTHOR("MEMS Software Solutions Team");
|
||||
MODULE_DESCRIPTION("STMicroelectronics st_lis2duxs12 i2c driver");
|
||||
MODULE_LICENSE("GPL v2");
|
61
drivers/iio/stm/accel/st_lis2duxs12_i3c.c
Normal file
61
drivers/iio/stm/accel/st_lis2duxs12_i3c.c
Normal file
@ -0,0 +1,61 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* STMicroelectronics st_lis2duxs12 i3c driver
|
||||
*
|
||||
* MEMS Software Solutions Team
|
||||
*
|
||||
* Copyright 2022 STMicroelectronics Inc.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/i3c/device.h>
|
||||
#include <linux/i3c/master.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
#include "st_lis2duxs12.h"
|
||||
|
||||
static const struct i3c_device_id st_lis2duxs12_i3c_ids[] = {
|
||||
I3C_DEVICE(0x0104, ST_LIS2DUXS12_WHOAMI_VAL, NULL),
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i3c, st_lis2duxs12_i3c_ids);
|
||||
|
||||
static int st_lis2duxs12_i3c_probe(struct i3c_device *i3cdev)
|
||||
{
|
||||
struct regmap_config st_lis2duxs12_i3c_regmap_config = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
};
|
||||
const struct i3c_device_id *id = i3c_device_match_id(i3cdev,
|
||||
st_lis2duxs12_i3c_ids);
|
||||
struct regmap *regmap;
|
||||
|
||||
regmap = devm_regmap_init_i3c(i3cdev,
|
||||
&st_lis2duxs12_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_lis2duxs12_probe(&i3cdev->dev, 0, regmap);
|
||||
}
|
||||
|
||||
static struct i3c_driver st_lis2duxs12_driver = {
|
||||
.driver = {
|
||||
.name = "st_" ST_LIS2DUXS12_DEV_NAME "_i3c",
|
||||
.pm = &st_lis2duxs12_pm_ops,
|
||||
},
|
||||
.probe = st_lis2duxs12_i3c_probe,
|
||||
.id_table = st_lis2duxs12_i3c_ids,
|
||||
};
|
||||
module_i3c_driver(st_lis2duxs12_driver);
|
||||
|
||||
MODULE_AUTHOR("MEMS Software Solutions Team");
|
||||
MODULE_DESCRIPTION("STMicroelectronics st_lis2duxs12 i3c driver");
|
||||
MODULE_LICENSE("GPL v2");
|
806
drivers/iio/stm/accel/st_lis2duxs12_mlc.c
Normal file
806
drivers/iio/stm/accel/st_lis2duxs12_mlc.c
Normal file
@ -0,0 +1,806 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* STMicroelectronics st_lis2duxs12 machine learning core driver
|
||||
*
|
||||
* MEMS Software Solutions Team
|
||||
*
|
||||
* Copyright 2022 STMicroelectronics Inc.
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/firmware.h>
|
||||
#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/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/version.h>
|
||||
|
||||
#include <linux/platform_data/st_sensors_pdata.h>
|
||||
|
||||
#include "st_lis2duxs12.h"
|
||||
|
||||
#define ST_LIS2DUXS12_MLC_LOADER_VERSION "0.4"
|
||||
|
||||
/* number of machine learning core available on device hardware */
|
||||
#define ST_LIS2DUXS12_MLC_MAX_NUMBER 4
|
||||
#define ST_LIS2DUXS12_FSM_MAX_NUMBER 8
|
||||
|
||||
#define ST_LIS2DUXS12_LOADER_CMD_WAIT 0xff
|
||||
|
||||
#ifdef CONFIG_IIO_LIS2DUXS12_MLC_BUILTIN_FIRMWARE
|
||||
static const u8 st_lis2duxs12_mlc_fw[] = {
|
||||
#include "st_lis2duxs12_mlc.fw"
|
||||
};
|
||||
DECLARE_BUILTIN_FIRMWARE(LIS2DUXS12_MLC_FIRMWARE_NAME,
|
||||
st_lis2duxs12_mlc_fw);
|
||||
#else /* CONFIG_IIO_LIS2DUXS12_MLC_BUILTIN_FIRMWARE */
|
||||
#define LIS2DUXS12_MLC_FIRMWARE_NAME "st_lis2duxs12_mlc.bin"
|
||||
#endif /* CONFIG_IIO_LIS2DUXS12_MLC_BUILTIN_FIRMWARE */
|
||||
|
||||
static const u8 mlcdata[] = {
|
||||
/* lis2duxs12_mlc_fsm_hpd.ucf */
|
||||
0x14, 0x00, 0x13, 0x10, 0xff, 0x05, 0x3f, 0x80, 0x04, 0x00,
|
||||
0x05, 0x00, 0x39, 0x4b, 0x1a, 0x01, 0x0a, 0x00, 0x0b, 0x01,
|
||||
0x0e, 0x00, 0x0f, 0x00, 0x17, 0x40, 0x02, 0x01, 0x08, 0x54,
|
||||
0x09, 0x00, 0x09, 0x00, 0x09, 0x01, 0x09, 0x01, 0x09, 0x00,
|
||||
0x09, 0x02, 0x02, 0x21, 0x08, 0x00, 0x09, 0x24, 0x09, 0x08,
|
||||
0x09, 0x18, 0x09, 0x00, 0x09, 0x0f, 0x09, 0x00, 0x09, 0x01,
|
||||
0x09, 0x00, 0x09, 0x00, 0x09, 0x00, 0x09, 0x00, 0x09, 0x00,
|
||||
0x09, 0xee, 0x09, 0x02, 0x09, 0x04, 0x09, 0x0f, 0x09, 0x66,
|
||||
0x09, 0x99, 0x09, 0x33, 0x09, 0xf1, 0x09, 0x44, 0x09, 0x77,
|
||||
0x09, 0x22, 0x09, 0x00, 0x04, 0x00, 0x05, 0x11, 0x17, 0x80,
|
||||
0x3f, 0x00, 0x1f, 0x01, 0x14, 0x92, 0x14, 0x00, 0x13, 0x10,
|
||||
0xff, 0x05, 0x3f, 0x80, 0x04, 0x00, 0x05, 0x00, 0x39, 0x4b,
|
||||
0x1a, 0x01, 0x0a, 0x00, 0x0b, 0x01, 0x0e, 0x00, 0x0f, 0x00,
|
||||
0x17, 0x40, 0x02, 0x01, 0x08, 0x54, 0x09, 0x00, 0x09, 0x00,
|
||||
0x09, 0x01, 0x09, 0x01, 0x09, 0x00, 0x09, 0x02, 0x02, 0x21,
|
||||
0x08, 0x00, 0x09, 0x24, 0x09, 0x08, 0x09, 0x18, 0x09, 0x00,
|
||||
0x09, 0x0f, 0x09, 0x00, 0x09, 0x01, 0x09, 0x00, 0x09, 0x00,
|
||||
0x09, 0x00, 0x09, 0x00, 0x09, 0x00, 0x09, 0xee, 0x09, 0x02,
|
||||
0x09, 0x04, 0x09, 0x0f, 0x09, 0x66, 0x09, 0x99, 0x09, 0x33,
|
||||
0x09, 0xf1, 0x09, 0x44, 0x09, 0x77, 0x09, 0x22, 0x09, 0x00,
|
||||
0x04, 0x00, 0x05, 0x11, 0x17, 0x80, 0x3f, 0x00, 0x1f, 0x01,
|
||||
0x14, 0x92, 0x13, 0x10, 0xff, 0x05, 0x14, 0x00, 0x3f, 0x80,
|
||||
0x04, 0x00, 0x05, 0x00, 0x17, 0x40, 0x02, 0x01, 0x08, 0xb6,
|
||||
0x09, 0x00, 0x09, 0x3c, 0x09, 0xfe, 0x09, 0x00, 0x09, 0x18,
|
||||
0x09, 0x01, 0x09, 0x01, 0x09, 0x00, 0x09, 0x14, 0x09, 0x01,
|
||||
0x09, 0x0a, 0x02, 0x01, 0x08, 0xc8, 0x09, 0xdc, 0x09, 0x00,
|
||||
0x09, 0x1a, 0x09, 0x01, 0x09, 0x26, 0x09, 0x01, 0x02, 0x01,
|
||||
0x08, 0xdc, 0x09, 0x1c, 0x09, 0x00, 0x09, 0x00, 0x09, 0x00,
|
||||
0x09, 0x00, 0x09, 0x00, 0x09, 0x00, 0x09, 0x00, 0x09, 0x00,
|
||||
0x09, 0x00, 0x09, 0x00, 0x09, 0x00, 0x09, 0x00, 0x09, 0x00,
|
||||
0x09, 0xa2, 0x09, 0x1d, 0x09, 0xaf, 0x09, 0x21, 0x09, 0xa2,
|
||||
0x09, 0x1d, 0x09, 0x1d, 0x09, 0xbf, 0x09, 0x68, 0x09, 0x3a,
|
||||
0x09, 0x3f, 0x09, 0x00, 0x09, 0x03, 0x09, 0x2c, 0x09, 0x00,
|
||||
0x09, 0xfc, 0x09, 0x00, 0x09, 0x7c, 0x09, 0x1f, 0x09, 0x00,
|
||||
0x02, 0x11, 0x08, 0x1a, 0x09, 0x00, 0x09, 0x00, 0x09, 0x00,
|
||||
0x09, 0x00, 0x09, 0x00, 0x09, 0x00, 0x09, 0x00, 0x09, 0x00,
|
||||
0x09, 0x00, 0x09, 0x00, 0x09, 0x00, 0x3f, 0x00, 0x3f, 0x80,
|
||||
0x17, 0x40, 0x02, 0x11, 0x08, 0x26, 0x09, 0x00, 0x09, 0x58,
|
||||
0x09, 0x40, 0x09, 0xe0, 0x3f, 0x80, 0x17, 0x00, 0x04, 0x00,
|
||||
0x05, 0x10, 0x02, 0x01, 0x3f, 0x00, 0x1f, 0x01, 0x3f, 0x80,
|
||||
0x3a, 0x41, 0x17, 0x80, 0x04, 0x00, 0x05, 0x11, 0x02, 0x01,
|
||||
0x3f, 0x00, 0x0c, 0x00, 0x0e, 0x00, 0x10, 0x10, 0x11, 0x00,
|
||||
0x12, 0x00, 0x13, 0x10, 0x14, 0x92, 0x15, 0x00, 0x16, 0x00,
|
||||
0x17, 0x00, 0x18, 0x00, 0x1c, 0x00, 0x1d, 0x00, 0x1e, 0x00,
|
||||
0x1f, 0x01, 0x20, 0x00, 0x31, 0x9a, 0x32, 0x00, 0x33, 0x00,
|
||||
0x3d, 0x00, 0x3f, 0x00, 0x47, 0x00, 0x6f, 0x00, 0x70, 0x00,
|
||||
0x71, 0x00, 0x72, 0x00, 0x73, 0x00, 0x74, 0x00, 0x75, 0x00,
|
||||
0x3f, 0x80, 0x17, 0x80, 0x04, 0x00, 0x05, 0x11, 0x02, 0x01,
|
||||
0x3f, 0x00,
|
||||
};
|
||||
|
||||
static const struct firmware st_lis2duxs12_mlc_preload = {
|
||||
.size = sizeof(mlcdata),
|
||||
.data = mlcdata
|
||||
};
|
||||
|
||||
static struct
|
||||
iio_dev *st_lis2duxs12_mlc_alloc_iio_dev(struct st_lis2duxs12_hw *hw,
|
||||
enum st_lis2duxs12_sensor_id id);
|
||||
|
||||
static const unsigned long st_lis2duxs12_mlc_available_scan_masks[] = {
|
||||
BIT(1), 0x0
|
||||
};
|
||||
|
||||
static int
|
||||
st_lis2duxs12_mlc_enable_sensor(struct st_lis2duxs12_sensor *sensor,
|
||||
bool enable)
|
||||
{
|
||||
struct st_lis2duxs12_hw *hw = sensor->hw;
|
||||
int i, id, err = 0;
|
||||
|
||||
/* enable acc sensor as trigger */
|
||||
err = st_lis2duxs12_sensor_set_enable(sensor, enable);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
if (sensor->status == ST_LIS2DUXS12_MLC_ENABLED) {
|
||||
int value;
|
||||
|
||||
value = enable ? hw->mlc_config->mlc_int_mask : 0;
|
||||
err = st_lis2duxs12_write_page_locked(hw,
|
||||
hw->mlc_config->mlc_int_addr,
|
||||
&value, 1);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
/*
|
||||
* enable mlc core
|
||||
* only one mlc enable bit so not need to check if
|
||||
* other running
|
||||
*/
|
||||
err = st_lis2duxs12_update_page_bits_locked(hw,
|
||||
ST_LIS2DUXS12_EMB_FUNC_EN_B_ADDR,
|
||||
ST_LIS2DUXS12_MLC_EN_MASK,
|
||||
ST_LIS2DUXS12_SHIFT_VAL(enable,
|
||||
ST_LIS2DUXS12_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_LIS2DUXS12_FSM_ENABLED) {
|
||||
int value;
|
||||
|
||||
value = enable ? hw->mlc_config->fsm_int_mask : 0;
|
||||
err = st_lis2duxs12_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_LIS2DUXS12_FSM_MAX_NUMBER; i++) {
|
||||
id = st_lis2duxs12_fsm_sensor_list[i];
|
||||
if (hw->enable_mask & BIT(id))
|
||||
break;
|
||||
}
|
||||
|
||||
/* check for any other fsm already enabled */
|
||||
if (enable || i == ST_LIS2DUXS12_FSM_MAX_NUMBER) {
|
||||
err = st_lis2duxs12_update_page_bits_locked(hw,
|
||||
ST_LIS2DUXS12_EMB_FUNC_EN_B_ADDR,
|
||||
ST_LIS2DUXS12_FSM_EN_MASK,
|
||||
ST_LIS2DUXS12_SHIFT_VAL(enable,
|
||||
ST_LIS2DUXS12_FSM_EN_MASK));
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
/* force mlc enable */
|
||||
err = st_lis2duxs12_update_page_bits_locked(hw,
|
||||
ST_LIS2DUXS12_EMB_FUNC_EN_B_ADDR,
|
||||
ST_LIS2DUXS12_MLC_EN_MASK,
|
||||
ST_LIS2DUXS12_SHIFT_VAL(enable,
|
||||
ST_LIS2DUXS12_MLC_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_lis2duxs12_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_lis2duxs12_sensor *sensor = iio_priv(iio_dev);
|
||||
|
||||
return st_lis2duxs12_mlc_enable_sensor(sensor, state);
|
||||
}
|
||||
|
||||
static int
|
||||
st_lis2duxs12_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_lis2duxs12_sensor *sensor = iio_priv(iio_dev);
|
||||
struct st_lis2duxs12_hw *hw = sensor->hw;
|
||||
|
||||
return !!(hw->enable_mask & BIT(sensor->id));
|
||||
}
|
||||
|
||||
static int
|
||||
st_lis2duxs12_get_mlc_odr_val(struct st_lis2duxs12_hw *hw, u8 val_odr)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < hw->odr_table_entry[ST_LIS2DUXS12_ID_ACC].size; i++) {
|
||||
if (hw->odr_table_entry[ST_LIS2DUXS12_ID_ACC].odr_avl[i].val == val_odr)
|
||||
return hw->odr_table_entry[ST_LIS2DUXS12_ID_ACC].odr_avl[i].hz;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* parse and program mlc fragments */
|
||||
static int st_lis2duxs12_program_mlc(const struct firmware *fw,
|
||||
struct st_lis2duxs12_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;
|
||||
|
||||
mutex_lock(&hw->page_lock);
|
||||
while (i < fw->size) {
|
||||
reg = fw->data[i++];
|
||||
val = fw->data[i++];
|
||||
if ((reg == ST_LIS2DUXS12_FUNC_CFG_ACCESS_ADDR) &&
|
||||
(val & ST_LIS2DUXS12_EMB_FUNC_REG_ACCESS_MASK)) {
|
||||
stmc_page = true;
|
||||
} else if ((reg == ST_LIS2DUXS12_FUNC_CFG_ACCESS_ADDR) &&
|
||||
(val & ST_LIS2DUXS12_EMB_FUNC_REG_ACCESS_MASK) == 0) {
|
||||
stmc_page = false;
|
||||
} else if (reg == ST_LIS2DUXS12_LOADER_CMD_WAIT) {
|
||||
msleep(val);
|
||||
} else if (stmc_page) {
|
||||
switch (reg) {
|
||||
case ST_LIS2DUXS12_MLC_INT1_ADDR:
|
||||
case ST_LIS2DUXS12_MLC_INT2_ADDR:
|
||||
mlc_int |= val;
|
||||
mlc_num++;
|
||||
skip = 1;
|
||||
break;
|
||||
case ST_LIS2DUXS12_FSM_INT1_ADDR:
|
||||
case ST_LIS2DUXS12_FSM_INT2_ADDR:
|
||||
fsm_int |= val;
|
||||
fsm_num++;
|
||||
skip = 1;
|
||||
break;
|
||||
case ST_LIS2DUXS12_EMB_FUNC_EN_B_ADDR:
|
||||
skip = 1;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
switch (reg) {
|
||||
case ST_LIS2DUXS12_CTRL5_ADDR:
|
||||
/* save requested odr and skip write to reg */
|
||||
req_odr = max_t(u8, req_odr,
|
||||
(val >> __ffs(ST_LIS2DUXS12_ODR_MASK)) & 0x0f);
|
||||
skip = 1;
|
||||
break;
|
||||
case ST_LIS2DUXS12_AH_QVAR_CFG_ADDR:
|
||||
/* check qvar requirement */
|
||||
if (val & ST_LIS2DUXS12_AH_QVAR_EN_MASK)
|
||||
hw->mlc_config->requested_device |=
|
||||
BIT(ST_LIS2DUXS12_ID_QVAR);
|
||||
|
||||
/* remove qvar enable flag */
|
||||
val &= ~ST_LIS2DUXS12_AH_QVAR_EN_MASK;
|
||||
skip = 1;
|
||||
break;
|
||||
case ST_LIS2DUXS12_MD1_CFG_ADDR:
|
||||
case ST_LIS2DUXS12_MD2_CFG_ADDR:
|
||||
/* just write int on emb functions */
|
||||
val &= ST_LIS2DUXS12_INT_EMB_FUNC_MASK;
|
||||
break;
|
||||
default:
|
||||
skip = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!skip) {
|
||||
ret = regmap_write(hw->regmap, reg, val);
|
||||
if (ret) {
|
||||
dev_err(hw->dev, "regmap_write fails\n");
|
||||
|
||||
mutex_unlock(&hw->page_lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
skip = 0;
|
||||
|
||||
if (mlc_num >= ST_LIS2DUXS12_MLC_MAX_NUMBER ||
|
||||
fsm_num >= ST_LIS2DUXS12_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_LIS2DUXS12_MLC_INT1_ADDR :
|
||||
ST_LIS2DUXS12_MLC_INT2_ADDR);
|
||||
|
||||
hw->mlc_config->status |= ST_LIS2DUXS12_MLC_ENABLED;
|
||||
hw->mlc_config->mlc_configured += mlc_num;
|
||||
hw->mlc_config->requested_odr = st_lis2duxs12_get_mlc_odr_val(hw, req_odr);
|
||||
}
|
||||
|
||||
if (fsm_num) {
|
||||
hw->mlc_config->fsm_int_mask = fsm_int;
|
||||
hw->mlc_config->fsm_int_addr = (hw->int_pin == 1 ?
|
||||
ST_LIS2DUXS12_FSM_INT1_ADDR :
|
||||
ST_LIS2DUXS12_FSM_INT2_ADDR);
|
||||
|
||||
hw->mlc_config->status |= ST_LIS2DUXS12_FSM_ENABLED;
|
||||
hw->mlc_config->fsm_configured += fsm_num;
|
||||
hw->mlc_config->requested_odr = st_lis2duxs12_get_mlc_odr_val(hw, req_odr);
|
||||
}
|
||||
|
||||
mutex_unlock(&hw->page_lock);
|
||||
|
||||
return fsm_num + mlc_num;
|
||||
}
|
||||
|
||||
static void st_lis2duxs12_mlc_update(const struct firmware *fw, void *context)
|
||||
{
|
||||
struct st_lis2duxs12_hw *hw = context;
|
||||
enum st_lis2duxs12_sensor_id id;
|
||||
int ret, i;
|
||||
|
||||
if (!fw) {
|
||||
dev_err(hw->dev, "could not get binary firmware\n");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
ret = st_lis2duxs12_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_LIS2DUXS12_MLC_MAX_NUMBER; i++) {
|
||||
if (mlc_mask & BIT(i)) {
|
||||
id = st_lis2duxs12_mlc_sensor_list[i];
|
||||
hw->iio_devs[id] =
|
||||
st_lis2duxs12_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_LIS2DUXS12_FSM_MAX_NUMBER; i++) {
|
||||
if (fsm_mask & BIT(i)) {
|
||||
id = st_lis2duxs12_fsm_sensor_list[i];
|
||||
hw->iio_devs[id] =
|
||||
st_lis2duxs12_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:
|
||||
if (hw->preload_mlc) {
|
||||
hw->preload_mlc = 0;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
release_firmware(fw);
|
||||
}
|
||||
|
||||
static int st_lis2duxs12_mlc_flush_all(struct st_lis2duxs12_hw *hw)
|
||||
{
|
||||
struct st_lis2duxs12_sensor *sensor_mlc;
|
||||
struct iio_dev *iio_dev;
|
||||
int ret = 0, id;
|
||||
|
||||
for (id = ST_LIS2DUXS12_ID_MLC_0; id < ST_LIS2DUXS12_ID_MAX; id++) {
|
||||
iio_dev = hw->iio_devs[id];
|
||||
if (!iio_dev)
|
||||
continue;
|
||||
|
||||
sensor_mlc = iio_priv(iio_dev);
|
||||
ret = st_lis2duxs12_mlc_enable_sensor(sensor_mlc, false);
|
||||
if (ret < 0)
|
||||
break;
|
||||
|
||||
iio_device_unregister(iio_dev);
|
||||
kfree(iio_dev->channels);
|
||||
iio_device_free(iio_dev);
|
||||
hw->iio_devs[id] = NULL;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ssize_t st_lis2duxs12_mlc_info(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct st_lis2duxs12_sensor *sensor = iio_priv(dev_get_drvdata(dev));
|
||||
struct st_lis2duxs12_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_lis2duxs12_mlc_get_version(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
return scnprintf(buf, PAGE_SIZE, "mlc loader Version %s\n",
|
||||
ST_LIS2DUXS12_MLC_LOADER_VERSION);
|
||||
}
|
||||
|
||||
static ssize_t st_lis2duxs12_mlc_flush(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t size)
|
||||
{
|
||||
struct st_lis2duxs12_sensor *sensor = iio_priv(dev_get_drvdata(dev));
|
||||
struct st_lis2duxs12_hw *hw = sensor->hw;
|
||||
int ret;
|
||||
|
||||
ret = st_lis2duxs12_mlc_flush_all(hw);
|
||||
memset(hw->mlc_config, 0, sizeof(*hw->mlc_config));
|
||||
|
||||
return ret < 0 ? ret : size;
|
||||
}
|
||||
|
||||
static ssize_t st_lis2duxs12_mlc_upload(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t size)
|
||||
{
|
||||
struct st_lis2duxs12_sensor *sensor = iio_priv(dev_get_drvdata(dev));
|
||||
int err;
|
||||
|
||||
err = request_firmware_nowait(THIS_MODULE, true,
|
||||
LIS2DUXS12_MLC_FIRMWARE_NAME,
|
||||
dev, GFP_KERNEL,
|
||||
sensor->hw,
|
||||
st_lis2duxs12_mlc_update);
|
||||
|
||||
return err < 0 ? err : size;
|
||||
}
|
||||
|
||||
static ssize_t st_lis2duxs12_mlc_odr(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct st_lis2duxs12_sensor *sensor = iio_priv(dev_get_drvdata(dev));
|
||||
struct st_lis2duxs12_hw *hw = sensor->hw;
|
||||
|
||||
return scnprintf(buf, PAGE_SIZE, "%d\n", hw->mlc_config->requested_odr);
|
||||
}
|
||||
|
||||
static IIO_DEVICE_ATTR(mlc_info, 0444, st_lis2duxs12_mlc_info, NULL, 0);
|
||||
static IIO_DEVICE_ATTR(mlc_flush, 0200, NULL, st_lis2duxs12_mlc_flush, 0);
|
||||
static IIO_DEVICE_ATTR(mlc_version, 0444, st_lis2duxs12_mlc_get_version,
|
||||
NULL, 0);
|
||||
static IIO_DEVICE_ATTR(load_mlc, 0200, NULL, st_lis2duxs12_mlc_upload, 0);
|
||||
static IIO_DEVICE_ATTR(mlc_odr, 0444, st_lis2duxs12_mlc_odr, NULL, 0);
|
||||
|
||||
static struct attribute *st_lis2duxs12_mlc_event_attributes[] = {
|
||||
&iio_dev_attr_mlc_info.dev_attr.attr,
|
||||
&iio_dev_attr_mlc_version.dev_attr.attr,
|
||||
&iio_dev_attr_load_mlc.dev_attr.attr,
|
||||
&iio_dev_attr_mlc_flush.dev_attr.attr,
|
||||
&iio_dev_attr_mlc_odr.dev_attr.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static const struct
|
||||
attribute_group st_lis2duxs12_mlc_event_attribute_group = {
|
||||
.attrs = st_lis2duxs12_mlc_event_attributes,
|
||||
};
|
||||
|
||||
static const struct iio_info st_lis2duxs12_mlc_event_info = {
|
||||
.attrs = &st_lis2duxs12_mlc_event_attribute_group,
|
||||
.read_event_config = st_lis2duxs12_mlc_read_event_config,
|
||||
.write_event_config = st_lis2duxs12_mlc_write_event_config,
|
||||
};
|
||||
|
||||
static ssize_t st_lis2duxs12_mlc_x_odr(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct st_lis2duxs12_sensor *sensor = iio_priv(dev_get_drvdata(dev));
|
||||
|
||||
return scnprintf(buf, PAGE_SIZE, "%d.%02d\n",
|
||||
sensor->odr, sensor->uodr);
|
||||
}
|
||||
|
||||
static IIO_DEVICE_ATTR(mlc_x_odr, 0444, st_lis2duxs12_mlc_x_odr, NULL, 0);
|
||||
|
||||
static struct attribute *st_lis2duxs12_mlc_x_event_attributes[] = {
|
||||
&iio_dev_attr_mlc_x_odr.dev_attr.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static const struct
|
||||
attribute_group st_lis2duxs12_mlc_x_event_attribute_group = {
|
||||
.attrs = st_lis2duxs12_mlc_x_event_attributes,
|
||||
};
|
||||
|
||||
static const struct iio_info st_lis2duxs12_mlc_x_event_info = {
|
||||
.attrs = &st_lis2duxs12_mlc_x_event_attribute_group,
|
||||
.read_event_config = st_lis2duxs12_mlc_read_event_config,
|
||||
.write_event_config = st_lis2duxs12_mlc_write_event_config,
|
||||
};
|
||||
|
||||
static struct
|
||||
iio_dev *st_lis2duxs12_mlc_alloc_iio_dev(struct st_lis2duxs12_hw *hw,
|
||||
enum st_lis2duxs12_sensor_id id)
|
||||
{
|
||||
struct st_lis2duxs12_sensor *sensor;
|
||||
struct iio_chan_spec *channels;
|
||||
struct iio_dev *iio_dev;
|
||||
|
||||
/* devm management only for ST_LIS2DUXS12_ID_MLC */
|
||||
if (id == ST_LIS2DUXS12_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;
|
||||
sensor->pm = ST_LIS2DUXS12_NO_MODE;
|
||||
|
||||
switch (id) {
|
||||
case ST_LIS2DUXS12_ID_MLC: {
|
||||
const struct iio_chan_spec st_lis2duxs12_mlc_channels[] = {
|
||||
ST_LIS2DUXS12_EVENT_CHANNEL(IIO_ACTIVITY, thr),
|
||||
};
|
||||
|
||||
channels = devm_kzalloc(hw->dev,
|
||||
sizeof(st_lis2duxs12_mlc_channels),
|
||||
GFP_KERNEL);
|
||||
if (!channels)
|
||||
return NULL;
|
||||
|
||||
memcpy(channels, st_lis2duxs12_mlc_channels,
|
||||
sizeof(st_lis2duxs12_mlc_channels));
|
||||
|
||||
iio_dev->available_scan_masks =
|
||||
st_lis2duxs12_mlc_available_scan_masks;
|
||||
iio_dev->channels = channels;
|
||||
iio_dev->num_channels = ARRAY_SIZE(st_lis2duxs12_mlc_channels);
|
||||
iio_dev->info = &st_lis2duxs12_mlc_event_info;
|
||||
scnprintf(sensor->name, sizeof(sensor->name),
|
||||
ST_LIS2DUXS12_DEV_NAME "_mlc");
|
||||
break;
|
||||
}
|
||||
case ST_LIS2DUXS12_ID_MLC_0:
|
||||
case ST_LIS2DUXS12_ID_MLC_1:
|
||||
case ST_LIS2DUXS12_ID_MLC_2:
|
||||
case ST_LIS2DUXS12_ID_MLC_3: {
|
||||
const struct iio_chan_spec st_lis2duxs12_mlc_x_ch[] = {
|
||||
ST_LIS2DUXS12_EVENT_CHANNEL(IIO_ACTIVITY, thr),
|
||||
};
|
||||
|
||||
channels = kzalloc(sizeof(st_lis2duxs12_mlc_x_ch),
|
||||
GFP_KERNEL);
|
||||
if (!channels)
|
||||
return NULL;
|
||||
|
||||
memcpy(channels, st_lis2duxs12_mlc_x_ch,
|
||||
sizeof(st_lis2duxs12_mlc_x_ch));
|
||||
|
||||
iio_dev->available_scan_masks =
|
||||
st_lis2duxs12_mlc_available_scan_masks;
|
||||
iio_dev->channels = channels;
|
||||
iio_dev->num_channels = ARRAY_SIZE(st_lis2duxs12_mlc_x_ch);
|
||||
iio_dev->info = &st_lis2duxs12_mlc_x_event_info;
|
||||
scnprintf(sensor->name, sizeof(sensor->name),
|
||||
ST_LIS2DUXS12_DEV_NAME "_mlc_%d",
|
||||
id - ST_LIS2DUXS12_ID_MLC_0);
|
||||
sensor->outreg_addr = ST_LIS2DUXS12_MLC1_SRC_ADDR + id -
|
||||
ST_LIS2DUXS12_ID_MLC_0;
|
||||
sensor->status = ST_LIS2DUXS12_MLC_ENABLED;
|
||||
sensor->odr = hw->mlc_config->requested_odr;
|
||||
sensor->uodr = 0;
|
||||
break;
|
||||
}
|
||||
case ST_LIS2DUXS12_ID_FSM_0:
|
||||
case ST_LIS2DUXS12_ID_FSM_1:
|
||||
case ST_LIS2DUXS12_ID_FSM_2:
|
||||
case ST_LIS2DUXS12_ID_FSM_3:
|
||||
case ST_LIS2DUXS12_ID_FSM_4:
|
||||
case ST_LIS2DUXS12_ID_FSM_5:
|
||||
case ST_LIS2DUXS12_ID_FSM_6:
|
||||
case ST_LIS2DUXS12_ID_FSM_7: {
|
||||
const struct iio_chan_spec st_lis2duxs12_fsm_x_ch[] = {
|
||||
ST_LIS2DUXS12_EVENT_CHANNEL(IIO_ACTIVITY, thr),
|
||||
};
|
||||
|
||||
channels = kzalloc(sizeof(st_lis2duxs12_fsm_x_ch),
|
||||
GFP_KERNEL);
|
||||
if (!channels)
|
||||
return NULL;
|
||||
|
||||
memcpy(channels, st_lis2duxs12_fsm_x_ch,
|
||||
sizeof(st_lis2duxs12_fsm_x_ch));
|
||||
|
||||
iio_dev->available_scan_masks =
|
||||
st_lis2duxs12_mlc_available_scan_masks;
|
||||
iio_dev->channels = channels;
|
||||
iio_dev->num_channels = ARRAY_SIZE(st_lis2duxs12_fsm_x_ch);
|
||||
iio_dev->info = &st_lis2duxs12_mlc_x_event_info;
|
||||
scnprintf(sensor->name, sizeof(sensor->name),
|
||||
ST_LIS2DUXS12_DEV_NAME "_fsm_%d",
|
||||
id - ST_LIS2DUXS12_ID_FSM_0);
|
||||
sensor->outreg_addr = ST_LIS2DUXS12_FSM_OUTS1_ADDR +
|
||||
id - ST_LIS2DUXS12_ID_FSM_0;
|
||||
sensor->status = ST_LIS2DUXS12_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_lis2duxs12_mlc_check_status(struct st_lis2duxs12_hw *hw)
|
||||
{
|
||||
struct st_lis2duxs12_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_LIS2DUXS12_MLC_ENABLED) {
|
||||
err = st_lis2duxs12_read_locked(hw,
|
||||
ST_LIS2DUXS12_MLC_STATUS_MAINPAGE_ADDR,
|
||||
(void *)&mlc_status, 1);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (mlc_status) {
|
||||
for (i = 0; i < ST_LIS2DUXS12_MLC_MAX_NUMBER; i++) {
|
||||
id = st_lis2duxs12_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_lis2duxs12_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_LIS2DUXS12_FSM_ENABLED) {
|
||||
err = st_lis2duxs12_read_locked(hw,
|
||||
ST_LIS2DUXS12_FSM_STATUS_MAINPAGE_ADDR,
|
||||
(void *)&fsm_status, 1);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (fsm_status) {
|
||||
for (i = 0; i < ST_LIS2DUXS12_FSM_MAX_NUMBER; i++) {
|
||||
id = st_lis2duxs12_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_lis2duxs12_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, fsm_status, (u64)event[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
int st_lis2duxs12_mlc_init_preload(struct st_lis2duxs12_hw *hw)
|
||||
{
|
||||
hw->preload_mlc = 1;
|
||||
st_lis2duxs12_mlc_update(&st_lis2duxs12_mlc_preload, hw);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int st_lis2duxs12_mlc_probe(struct st_lis2duxs12_hw *hw)
|
||||
{
|
||||
hw->iio_devs[ST_LIS2DUXS12_ID_MLC] =
|
||||
st_lis2duxs12_mlc_alloc_iio_dev(hw, ST_LIS2DUXS12_ID_MLC);
|
||||
if (!hw->iio_devs[ST_LIS2DUXS12_ID_MLC])
|
||||
return -ENOMEM;
|
||||
|
||||
hw->mlc_config = devm_kzalloc(hw->dev,
|
||||
sizeof(struct st_lis2duxs12_mlc_config_t),
|
||||
GFP_KERNEL);
|
||||
if (!hw->mlc_config)
|
||||
return -ENOMEM;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int st_lis2duxs12_mlc_remove(struct device *dev)
|
||||
{
|
||||
struct st_lis2duxs12_hw *hw = dev_get_drvdata(dev);
|
||||
|
||||
return st_lis2duxs12_mlc_flush_all(hw);
|
||||
}
|
||||
EXPORT_SYMBOL(st_lis2duxs12_mlc_remove);
|
264
drivers/iio/stm/accel/st_lis2duxs12_qvar.c
Normal file
264
drivers/iio/stm/accel/st_lis2duxs12_qvar.c
Normal file
@ -0,0 +1,264 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* STMicroelectronics st_lis2duxs12 qvar sensor driver
|
||||
*
|
||||
* MEMS Software Solutions Team
|
||||
*
|
||||
* Copyright 2022 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_lis2duxs12.h"
|
||||
|
||||
#define ST_LIS2DUXS12_REG_OUT_T_AH_QVAR_L_ADDR 0x2e
|
||||
|
||||
static const struct iio_chan_spec st_lis2duxs12_qvar_channels[] = {
|
||||
ST_LIS2DUXS12_DATA_CHANNEL(IIO_ALTVOLTAGE,
|
||||
ST_LIS2DUXS12_REG_OUT_T_AH_QVAR_L_ADDR,
|
||||
0, 0, 0, 12, 16, 's',
|
||||
NULL),
|
||||
IIO_CHAN_SOFT_TIMESTAMP(1),
|
||||
};
|
||||
|
||||
static int st_lis2duxs12_qvar_init(struct st_lis2duxs12_hw *hw)
|
||||
{
|
||||
/* impedance selection */
|
||||
return st_lis2duxs12_write_with_mask_locked(hw,
|
||||
ST_LIS2DUXS12_AH_QVAR_CFG_ADDR,
|
||||
ST_LIS2DUXS12_AH_QVAR_C_ZIN_MASK, 3);
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
st_lis2duxs12_sysfs_qvar_sampling_freq_avail(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct iio_dev *iio_dev = dev_get_drvdata(dev);
|
||||
struct st_lis2duxs12_sensor *sensor = iio_priv(iio_dev);
|
||||
struct st_lis2duxs12_hw *hw = sensor->hw;
|
||||
int len = 0;
|
||||
int i;
|
||||
|
||||
/* qvar share the same XL odr table */
|
||||
for (i = 0; i < hw->odr_table_entry[ST_LIS2DUXS12_ID_ACC].size; i++) {
|
||||
if (!hw->odr_table_entry[ST_LIS2DUXS12_ID_ACC].odr_avl[i].hz)
|
||||
continue;
|
||||
|
||||
len += scnprintf(buf + len, PAGE_SIZE - len, "%d.%06d ",
|
||||
hw->odr_table_entry[ST_LIS2DUXS12_ID_ACC].odr_avl[i].hz,
|
||||
hw->odr_table_entry[ST_LIS2DUXS12_ID_ACC].odr_avl[i].uhz);
|
||||
}
|
||||
|
||||
buf[len - 1] = '\n';
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static int
|
||||
st_lis2duxs12_get_qvar_odr_val(struct st_lis2duxs12_hw *hw,
|
||||
int odr, int uodr,
|
||||
struct st_lis2duxs12_odr *oe)
|
||||
{
|
||||
int req_odr = ST_LIS2DUXS12_ODR_EXPAND(odr, uodr);
|
||||
int sensor_odr;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < hw->odr_table_entry[ST_LIS2DUXS12_ID_ACC].size; i++) {
|
||||
sensor_odr = ST_LIS2DUXS12_ODR_EXPAND(
|
||||
hw->odr_table_entry[ST_LIS2DUXS12_ID_ACC].odr_avl[i].hz,
|
||||
hw->odr_table_entry[ST_LIS2DUXS12_ID_ACC].odr_avl[i].uhz);
|
||||
if (sensor_odr >= req_odr) {
|
||||
oe->hz = hw->odr_table_entry[ST_LIS2DUXS12_ID_ACC].odr_avl[i].hz;
|
||||
oe->uhz = hw->odr_table_entry[ST_LIS2DUXS12_ID_ACC].odr_avl[i].uhz;
|
||||
oe->val = hw->odr_table_entry[ST_LIS2DUXS12_ID_ACC].odr_avl[i].val;
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int
|
||||
st_lis2duxs12_qvar_read_oneshot(struct st_lis2duxs12_sensor *sensor,
|
||||
u8 addr, int *val)
|
||||
{
|
||||
struct st_lis2duxs12_hw *hw = sensor->hw;
|
||||
int err, delay;
|
||||
__le16 data;
|
||||
|
||||
err = st_lis2duxs12_sensor_set_enable(sensor, true);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
delay = 1000000 / sensor->odr;
|
||||
usleep_range(delay, 2 * delay);
|
||||
err = st_lis2duxs12_read_locked(hw, addr, &data, sizeof(data));
|
||||
|
||||
st_lis2duxs12_sensor_set_enable(sensor, false);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
*val = (s16)le16_to_cpu(data);
|
||||
|
||||
return IIO_VAL_INT;
|
||||
}
|
||||
|
||||
static int st_lis2duxs12_qvar_read_raw(struct iio_dev *iio_dev,
|
||||
struct iio_chan_spec const *ch,
|
||||
int *val, int *val2, long mask)
|
||||
{
|
||||
struct st_lis2duxs12_sensor *sensor = iio_priv(iio_dev);
|
||||
int ret;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
*val = 1;
|
||||
ret = IIO_VAL_INT;
|
||||
break;
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
ret = iio_device_claim_direct_mode(iio_dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = st_lis2duxs12_qvar_read_oneshot(sensor,
|
||||
ch->address, val);
|
||||
iio_device_release_direct_mode(iio_dev);
|
||||
break;
|
||||
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||
*val = (int)sensor->odr;
|
||||
*val2 = (int)sensor->uodr;
|
||||
ret = IIO_VAL_INT_PLUS_MICRO;
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
st_lis2duxs12_qvar_write_raw(struct iio_dev *iio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int val, int val2, long mask)
|
||||
{
|
||||
struct st_lis2duxs12_sensor *sensor = iio_priv(iio_dev);
|
||||
int err;
|
||||
|
||||
err = iio_device_claim_direct_mode(iio_dev);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_SAMP_FREQ: {
|
||||
struct st_lis2duxs12_odr oe = { 0 };
|
||||
|
||||
err = st_lis2duxs12_get_qvar_odr_val(sensor->hw,
|
||||
val, val2, &oe);
|
||||
if (!err) {
|
||||
sensor->odr = oe.hz;
|
||||
sensor->uodr = oe.uhz;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
err = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
iio_device_release_direct_mode(iio_dev);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static IIO_DEV_ATTR_SAMP_FREQ_AVAIL(st_lis2duxs12_sysfs_qvar_sampling_freq_avail);
|
||||
|
||||
static struct attribute *st_lis2duxs12_qvar_attributes[] = {
|
||||
&iio_dev_attr_sampling_frequency_available.dev_attr.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static const struct attribute_group st_lis2duxs12_qvar_attribute_group = {
|
||||
.attrs = st_lis2duxs12_qvar_attributes,
|
||||
};
|
||||
|
||||
static const struct iio_info st_lis2duxs12_qvar_info = {
|
||||
.attrs = &st_lis2duxs12_qvar_attribute_group,
|
||||
.read_raw = st_lis2duxs12_qvar_read_raw,
|
||||
.write_raw = st_lis2duxs12_qvar_write_raw,
|
||||
};
|
||||
|
||||
static const unsigned long st_lis2duxs12_qvar_available_scan_masks[] = {
|
||||
0x1, 0x0
|
||||
};
|
||||
|
||||
int st_lis2duxs12_qvar_set_enable(struct st_lis2duxs12_sensor *sensor,
|
||||
bool enable)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = st_lis2duxs12_sensor_set_enable(sensor, enable);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
return st_lis2duxs12_write_with_mask_locked(sensor->hw,
|
||||
ST_LIS2DUXS12_AH_QVAR_CFG_ADDR,
|
||||
ST_LIS2DUXS12_AH_QVAR_EN_MASK,
|
||||
enable ? 1 : 0);
|
||||
}
|
||||
|
||||
struct iio_dev *
|
||||
st_lis2duxs12_alloc_qvar_iiodev(struct st_lis2duxs12_hw *hw)
|
||||
{
|
||||
struct st_lis2duxs12_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_LIS2DUXS12_ID_QVAR;
|
||||
sensor->hw = hw;
|
||||
|
||||
iio_dev->channels = st_lis2duxs12_qvar_channels;
|
||||
iio_dev->num_channels = ARRAY_SIZE(st_lis2duxs12_qvar_channels);
|
||||
iio_dev->name = "lis2duxs12_qvar";
|
||||
iio_dev->info = &st_lis2duxs12_qvar_info;
|
||||
iio_dev->available_scan_masks =
|
||||
st_lis2duxs12_qvar_available_scan_masks;
|
||||
|
||||
sensor->odr = hw->odr_table_entry[ST_LIS2DUXS12_ID_ACC].odr_avl[0].hz;
|
||||
sensor->uodr = hw->odr_table_entry[ST_LIS2DUXS12_ID_ACC].odr_avl[0].uhz;
|
||||
sensor->gain = 1;
|
||||
sensor->watermark = 1;
|
||||
|
||||
return iio_dev;
|
||||
}
|
||||
|
||||
int st_lis2duxs12_qvar_probe(struct st_lis2duxs12_hw *hw)
|
||||
{
|
||||
int err;
|
||||
|
||||
hw->iio_devs[ST_LIS2DUXS12_ID_QVAR] =
|
||||
st_lis2duxs12_alloc_qvar_iiodev(hw);
|
||||
if (!hw->iio_devs[ST_LIS2DUXS12_ID_QVAR])
|
||||
return -ENOMEM;
|
||||
|
||||
err = st_lis2duxs12_qvar_init(hw);
|
||||
|
||||
return err < 0 ? err : 0;
|
||||
}
|
72
drivers/iio/stm/accel/st_lis2duxs12_spi.c
Normal file
72
drivers/iio/stm/accel/st_lis2duxs12_spi.c
Normal file
@ -0,0 +1,72 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* STMicroelectronics st_lis2duxs12 spi driver
|
||||
*
|
||||
* MEMS Software Solutions Team
|
||||
*
|
||||
* Copyright 2022 STMicroelectronics Inc.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/of.h>
|
||||
|
||||
#include "st_lis2duxs12.h"
|
||||
|
||||
static const struct regmap_config st_lis2duxs12_spi_regmap_config = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
};
|
||||
|
||||
static int st_lis2duxs12_spi_probe(struct spi_device *spi)
|
||||
{
|
||||
struct regmap *regmap;
|
||||
|
||||
regmap = devm_regmap_init_spi(spi,
|
||||
&st_lis2duxs12_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_lis2duxs12_probe(&spi->dev, spi->irq, regmap);
|
||||
}
|
||||
|
||||
static int st_lis2duxs12_spi_remove(struct spi_device *spi)
|
||||
{
|
||||
return st_lis2duxs12_remove(&spi->dev);
|
||||
}
|
||||
|
||||
static const struct of_device_id st_lis2duxs12_spi_of_match[] = {
|
||||
{
|
||||
.compatible = "st," ST_LIS2DUXS12_DEV_NAME,
|
||||
},
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, st_lis2duxs12_spi_of_match);
|
||||
|
||||
static const struct spi_device_id st_lis2duxs12_spi_id_table[] = {
|
||||
{ ST_LIS2DUXS12_DEV_NAME },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, st_lis2duxs12_spi_id_table);
|
||||
|
||||
static struct spi_driver st_lis2duxs12_driver = {
|
||||
.driver = {
|
||||
.name = "st_" ST_LIS2DUXS12_DEV_NAME "_spi",
|
||||
.pm = &st_lis2duxs12_pm_ops,
|
||||
.of_match_table = of_match_ptr(st_lis2duxs12_spi_of_match),
|
||||
},
|
||||
.probe = st_lis2duxs12_spi_probe,
|
||||
.remove = st_lis2duxs12_spi_remove,
|
||||
.id_table = st_lis2duxs12_spi_id_table,
|
||||
};
|
||||
module_spi_driver(st_lis2duxs12_driver);
|
||||
|
||||
MODULE_AUTHOR("MEMS Software Solutions Team");
|
||||
MODULE_DESCRIPTION("STMicroelectronics st_lis2duxs12 spi driver");
|
||||
MODULE_LICENSE("GPL v2");
|
5
stm_iio_configs/lis2duxs12_defconfig
Normal file
5
stm_iio_configs/lis2duxs12_defconfig
Normal file
@ -0,0 +1,5 @@
|
||||
CONFIG_IIO_ST_LIS2DUXS12=m
|
||||
CONFIG_IIO_ST_LIS2DUXS12_I2C=m
|
||||
CONFIG_IIO_ST_LIS2DUXS12_SPI=m
|
||||
CONFIG_IIO_ST_LIS2DUXS12_I3C=m
|
||||
CONFIG_IIO_ST_LIS2DUXS12_QVAR=y
|
Loading…
Reference in New Issue
Block a user