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:
Mario Tesi 2022-07-21 16:30:19 +02:00 committed by Matteo DAMENO
parent d93ddd10b6
commit 52224a7247
14 changed files with 5934 additions and 0 deletions

View File

@ -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>;
};

View File

@ -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

View File

@ -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

View 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 */

File diff suppressed because it is too large Load Diff

View 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;
}

File diff suppressed because it is too large Load Diff

View 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;
}

View 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");

View 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");

View 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);

View 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;
}

View 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");

View 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