drivers:iio:stm:accel: Add support to LIS2DU12 STMEMS accel
Added support to STMEMS acc LIS2DU12 sensor. The following features has been included: - Acc and Temp sensor in FIFO - Embedded features event detection supported: - Wake-up - Free fall - Tap - Double Tap - 6D / 4D - activity / inactivity - Embedded features parameters configuration - Self test procedure Signed-off-by: Mario Tesi <mario.tesi@st.com> Change-Id: I89df1f65ca14e01bc424b8e26de2c972cf71b999
This commit is contained in:
parent
b314796715
commit
115ea4079c
@ -0,0 +1,38 @@
|
|||||||
|
* lis2du12 driver for accel MEMS sensors
|
||||||
|
|
||||||
|
Required properties for all bus drivers:
|
||||||
|
- compatible: must be one of:
|
||||||
|
"st,lis2du12"
|
||||||
|
|
||||||
|
Required properties for the i2c/i3c bindings:
|
||||||
|
- reg: i2c/i3c address of the sensor (for i3c is the static sddress)
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
- pp_od_int: set int pin to open drain.
|
||||||
|
|
||||||
|
Example for an spi device node:
|
||||||
|
|
||||||
|
lis2du12-accel@0 {
|
||||||
|
compatible = "st,lis2du12";
|
||||||
|
reg = <0x0>;
|
||||||
|
spi-max-frequency = <1000000>;
|
||||||
|
interrupt-parent = <&gpio0>;
|
||||||
|
interrupts = <0 IRQ_TYPE_LEVEL_HIGH>;
|
||||||
|
st,int-pin = <1>;
|
||||||
|
};
|
@ -214,4 +214,39 @@ config IIO_ST_H3LIS331DL_SPI
|
|||||||
select REGMAP_SPI
|
select REGMAP_SPI
|
||||||
depends on IIO_ST_H3LIS331DL
|
depends on IIO_ST_H3LIS331DL
|
||||||
|
|
||||||
|
config IIO_ST_LIS2DU12
|
||||||
|
tristate "STMicroelectronics LIS2DU12 Accelerometer Driver"
|
||||||
|
depends on (I2C || SPI || I3C)
|
||||||
|
select IIO_BUFFER
|
||||||
|
select IIO_KFIFO_BUF
|
||||||
|
select IIO_ST_LIS2DU12_I2C if (I2C)
|
||||||
|
select IIO_ST_LIS2DU12_SPI if (SPI)
|
||||||
|
select IIO_ST_LIS2DU12_I3C if (I3C)
|
||||||
|
help
|
||||||
|
Say yes here to build support for the LIS2DU12 accelerometer.
|
||||||
|
|
||||||
|
This driver can also be built as a module. If so, will be created
|
||||||
|
these modules:
|
||||||
|
- st_lisdu12 (core functions for the driver [it is mandatory]);
|
||||||
|
- st_lisdu12_i2c (necessary for the I2C devices [optional*]);
|
||||||
|
- st_lisdu12_spi (necessary for the SPI devices [optional*]);
|
||||||
|
- st_lisdu12_i3c (necessary for the I3C devices [optional*]);
|
||||||
|
|
||||||
|
(*) one of these is necessary to do something.
|
||||||
|
|
||||||
|
config IIO_ST_LIS2DU12_I2C
|
||||||
|
tristate
|
||||||
|
depends on IIO_ST_LIS2DU12
|
||||||
|
depends on I2C
|
||||||
|
|
||||||
|
config IIO_ST_LIS2DU12_SPI
|
||||||
|
tristate
|
||||||
|
depends on IIO_ST_LIS2DU12
|
||||||
|
depends on SPI
|
||||||
|
|
||||||
|
config IIO_ST_LIS2DU12_I3C
|
||||||
|
tristate
|
||||||
|
depends on IIO_ST_LIS2DU12
|
||||||
|
depends on I3C
|
||||||
|
|
||||||
endmenu
|
endmenu
|
||||||
|
@ -48,3 +48,10 @@ st_h3lis331dl-y := st_h3lis331dl_core.o st_h3lis331dl_buffer.o
|
|||||||
obj-$(CONFIG_IIO_ST_H3LIS331DL) += st_h3lis331dl.o
|
obj-$(CONFIG_IIO_ST_H3LIS331DL) += st_h3lis331dl.o
|
||||||
obj-$(CONFIG_IIO_ST_H3LIS331DL_I2C) += st_h3lis331dl_i2c.o
|
obj-$(CONFIG_IIO_ST_H3LIS331DL_I2C) += st_h3lis331dl_i2c.o
|
||||||
obj-$(CONFIG_IIO_ST_H3LIS331DL_SPI) += st_h3lis331dl_spi.o
|
obj-$(CONFIG_IIO_ST_H3LIS331DL_SPI) += st_h3lis331dl_spi.o
|
||||||
|
|
||||||
|
st_lis2du12-y:= st_lis2du12_core.o st_lis2du12_buffer.o
|
||||||
|
obj-$(CONFIG_IIO_ST_LIS2DU12) += st_lis2du12.o
|
||||||
|
obj-$(CONFIG_IIO_ST_LIS2DU12_I2C) += st_lis2du12_i2c.o
|
||||||
|
obj-$(CONFIG_IIO_ST_LIS2DU12_SPI) += st_lis2du12_spi.o
|
||||||
|
obj-$(CONFIG_IIO_ST_LIS2DU12_I3C) += st_lis2du12_i3c.o
|
||||||
|
|
||||||
|
364
drivers/iio/stm/accel/st_lis2du12.h
Normal file
364
drivers/iio/stm/accel/st_lis2du12.h
Normal file
@ -0,0 +1,364 @@
|
|||||||
|
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||||
|
/*
|
||||||
|
* STMicroelectronics lis2du12 driver
|
||||||
|
*
|
||||||
|
* MEMS Software Solutions Team
|
||||||
|
*
|
||||||
|
* Copyright 2022 STMicroelectronics Inc.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef ST_LIS2DU12_H
|
||||||
|
#define ST_LIS2DU12_H
|
||||||
|
|
||||||
|
#include <linux/device.h>
|
||||||
|
#include <linux/iio/events.h>
|
||||||
|
#include <linux/iio/iio.h>
|
||||||
|
#include <linux/regmap.h>
|
||||||
|
#include <linux/bitfield.h>
|
||||||
|
|
||||||
|
#include "../common/stm_iio_types.h"
|
||||||
|
|
||||||
|
#define ST_LIS2DU12_DEV_NAME "lis2du12"
|
||||||
|
#define ST_LIS2DU12_MAX_WATERMARK 127
|
||||||
|
#define ST_LIS2DU12_ACC_DATA_SIZE 6
|
||||||
|
#define ST_LIS2DU12_TEMP_DATA_SIZE 2
|
||||||
|
#define ST_LIS2DU12_DATA_SIZE (ST_LIS2DU12_ACC_DATA_SIZE + \
|
||||||
|
ST_LIS2DU12_TEMP_DATA_SIZE)
|
||||||
|
#define ST_LIS2DU12_ODR_EXPAND(odr, uodr) ((odr * 1000000) + uodr)
|
||||||
|
|
||||||
|
#define ST_LIS2DU12_IF_CTRL_ADDR 0x0e
|
||||||
|
#define ST_LIS2DU12_PD_DIS_INT1_MASK BIT(2)
|
||||||
|
|
||||||
|
#define ST_LIS2DU12_CTRL1_ADDR 0x10
|
||||||
|
#define ST_LIS2DU12_WU_EN_MASK GENMASK(2, 0)
|
||||||
|
#define ST_LIS2DU12_IF_ADD_INC_MASK BIT(4)
|
||||||
|
#define ST_LIS2DU12_SW_RESET_MASK BIT(5)
|
||||||
|
#define ST_LIS2DU12_PP_OD_MASK BIT(7)
|
||||||
|
|
||||||
|
#define ST_LIS2DU12_CTRL2_ADDR 0x11
|
||||||
|
#define ST_LIS2DU12_INT_F_FTH_MASK BIT(5)
|
||||||
|
|
||||||
|
#define ST_LIS2DU12_CTRL3_ADDR 0x12
|
||||||
|
#define ST_LIS2DU12_ST_MASK GENMASK(1, 0)
|
||||||
|
|
||||||
|
#define ST_LIS2DU12_CTRL4_ADDR 0x13
|
||||||
|
#define ST_LIS2DU12_BOOT_MASK BIT(0)
|
||||||
|
#define ST_LIS2DU12_BDU_MASK BIT(5)
|
||||||
|
|
||||||
|
#define ST_LIS2DU12_CTRL5_ADDR 0x14
|
||||||
|
#define ST_LIS2DU12_ODR_MASK GENMASK(7, 4)
|
||||||
|
#define ST_LIS2DU12_FS_MASK GENMASK(1, 0)
|
||||||
|
|
||||||
|
#define ST_LIS2DU12_FIFO_CTRL_ADDR 0x15
|
||||||
|
#define ST_LIS2DU12_FIFOMODE_MASK GENMASK(3, 0)
|
||||||
|
#define ST_LIS2DU12_ROUNDING_XYZ_MASK BIT(7)
|
||||||
|
|
||||||
|
#define ST_LIS2DU12_FIFO_WTM_ADDR 0x16
|
||||||
|
#define ST_LIS2DU12_FTH_MASK GENMASK(6, 0)
|
||||||
|
|
||||||
|
#define ST_LIS2DU12_INTERRUPT_CFG_ADDR 0x17
|
||||||
|
#define ST_LIS2DU12_INTERRUPTS_ENABLE_MASK BIT(0)
|
||||||
|
#define ST_LIS2DU12_LIR_MASK BIT(1)
|
||||||
|
#define ST_LIS2DU12_H_LACTIVE_MASK BIT(2)
|
||||||
|
#define ST_LIS2DU12_SLEEP_STATUS_ON_INT_MASK BIT(3)
|
||||||
|
#define ST_LIS2DU12_INT_SHORT_EN_MASK BIT(6)
|
||||||
|
|
||||||
|
#define ST_LIS2DU12_TAP_THS_X_ADDR 0x18
|
||||||
|
#define ST_LIS2DU12_D4D_EN_MASK BIT(7)
|
||||||
|
#define ST_LIS2DU12_D6D_THS_MASK GENMASK(6, 5)
|
||||||
|
#define ST_LIS2DU12_TAP_THS_X_MASK GENMASK(4, 0)
|
||||||
|
|
||||||
|
#define ST_LIS2DU12_TAP_THS_Y_ADDR 0x19
|
||||||
|
#define ST_LIS2DU12_TAP_PRIORITY_MASK GENMASK(7, 5)
|
||||||
|
#define ST_LIS2DU12_TAP_THS_Y_MASK GENMASK(4, 0)
|
||||||
|
|
||||||
|
#define ST_LIS2DU12_TAP_THS_Z_ADDR 0x1a
|
||||||
|
#define ST_LIS2DU12_TAP_EN_MASK GENMASK(7, 5)
|
||||||
|
#define ST_LIS2DU12_TAP_THS_Z_MASK GENMASK(4, 0)
|
||||||
|
|
||||||
|
#define ST_LIS2DU12_INT_DUR_ADDR 0x1b
|
||||||
|
#define ST_LIS2DU12_SHOCK_MASK GENMASK(1, 0)
|
||||||
|
#define ST_LIS2DU12_QUIET_MASK GENMASK(3, 2)
|
||||||
|
#define ST_LIS2DU12_LATENCY_MASK GENMASK(7, 4)
|
||||||
|
|
||||||
|
#define ST_LIS2DU12_WAKE_UP_THS_ADDR 0x1c
|
||||||
|
#define ST_LIS2DU12_WK_THS_MASK GENMASK(5, 0)
|
||||||
|
#define ST_LIS2DU12_SLEEP_ON_MASK BIT(6)
|
||||||
|
#define ST_LIS2DU12_SINGLE_DOUBLE_TAP_MASK BIT(7)
|
||||||
|
|
||||||
|
#define ST_LIS2DU12_WAKE_UP_DUR_ADDR 0x1d
|
||||||
|
#define ST_LIS2DU12_SLEEP_DUR_MASK GENMASK(3, 0)
|
||||||
|
#define ST_LIS2DU12_WAKE_DUR_MASK GENMASK(6, 5)
|
||||||
|
#define ST_LIS2DU12_FF_DUR5_MASK BIT(7)
|
||||||
|
|
||||||
|
#define ST_LIS2DU12_FREE_FALL_ADDR 0x1e
|
||||||
|
#define ST_LIS2DU12_FF_THS_MASK GENMASK(2, 0)
|
||||||
|
#define ST_LIS2DU12_FF_DUR_MASK GENMASK(7, 3)
|
||||||
|
|
||||||
|
#define ST_LIS2DU12_MD1_CFG_ADDR 0x1f
|
||||||
|
#define ST_LIS2DU12_MD2_CFG_ADDR 0x20
|
||||||
|
#define ST_LIS2DU12_MD_INT_MASK GENMASK(7, 2)
|
||||||
|
#define ST_LIS2DU12_INT_6D_MASK BIT(2)
|
||||||
|
#define ST_LIS2DU12_INT_DOUBLE_TAP_MASK BIT(3)
|
||||||
|
#define ST_LIS2DU12_INT_FF_MASK BIT(4)
|
||||||
|
#define ST_LIS2DU12_INT_WU_MASK BIT(5)
|
||||||
|
#define ST_LIS2DU12_INT_SINGLE_TAP_MASK BIT(6)
|
||||||
|
#define ST_LIS2DU12_INT_SLEEP_CHANGE_MASK BIT(7)
|
||||||
|
|
||||||
|
#define ST_LIS2DU12_WAKE_UP_SRC_ADDR 0x21
|
||||||
|
#define ST_LIS2DU12_WU_MASK GENMASK(3, 0)
|
||||||
|
#define ST_LIS2DU12_WU_IA_MASK BIT(3)
|
||||||
|
#define ST_LIS2DU12_SLEEP_STATE_MASK BIT(4)
|
||||||
|
#define ST_LIS2DU12_FF_IA_MASK BIT(5)
|
||||||
|
#define ST_LIS2DU12_SLEEP_CHANGE_IA_MASK BIT(6)
|
||||||
|
|
||||||
|
#define ST_LIS2DU12_TAP_SRC_ADDR 0x22
|
||||||
|
#define ST_LIS2DU12_DOUBLE_TAP_IA_MASK BIT(4)
|
||||||
|
#define ST_LIS2DU12_SINGLE_TAP_IA_MASK BIT(5)
|
||||||
|
|
||||||
|
#define ST_LIS2DU12_SIXD_SRC_ADDR 0x23
|
||||||
|
#define ST_LIS2DU12_OVERTHRESHOLD_MASK GENMASK(5, 0)
|
||||||
|
#define ST_LIS2DU12_D6D_IA_MASK BIT(6)
|
||||||
|
|
||||||
|
#define ST_LIS2DU12_ALL_INT_SRC_ADDR 0x24
|
||||||
|
#define ST_LIS2DU12_FF_IA_ALL_MASK BIT(0)
|
||||||
|
#define ST_LIS2DU12_WU_IA_ALL_MASK BIT(1)
|
||||||
|
#define ST_LIS2DU12_SINGLE_TAP_ALL_MASK BIT(2)
|
||||||
|
#define ST_LIS2DU12_DOUBLE_TAP_ALL_MASK BIT(3)
|
||||||
|
#define ST_LIS2DU12_D6D_IA_ALL_MASK BIT(4)
|
||||||
|
#define ST_LIS2DU12_SLEEP_CHANGE_IA_ALL_MASK BIT(5)
|
||||||
|
#define ST_LIS2DU12_INT_GLOBAL_MASK BIT(6)
|
||||||
|
|
||||||
|
#define ST_LIS2DU12_STATUS_ADDR 0x25
|
||||||
|
#define ST_LIS2DU12_DRDY_MASK BIT(0)
|
||||||
|
|
||||||
|
#define ST_LIS2DU12_FIFO_STATUS1_ADDR 0x26
|
||||||
|
#define ST_LIS2DU12_FTH_WTM_MASK BIT(7)
|
||||||
|
|
||||||
|
#define ST_LIS2DU12_FIFO_STATUS2_ADDR 0x27
|
||||||
|
#define ST_LIS2DU12_FSS_MASK GENMASK(7, 0)
|
||||||
|
|
||||||
|
#define ST_LIS2DU12_OUT_X_L_ADDR 0x28
|
||||||
|
#define ST_LIS2DU12_OUT_Y_L_ADDR 0x2a
|
||||||
|
#define ST_LIS2DU12_OUT_Z_L_ADDR 0x2c
|
||||||
|
|
||||||
|
#define ST_LIS2DU12_TEMP_L_ADDR 0x30
|
||||||
|
|
||||||
|
#define ST_LIS2DU12_WHOAMI_ADDR 0x43
|
||||||
|
#define ST_LIS2DU12_WHOAMI_VAL 0x45
|
||||||
|
|
||||||
|
#define ST_LIS2DU12_ST_SIGN_ADDR 0x58
|
||||||
|
#define ST_LIS2DU12_STSIGN_MASK GENMASK(7, 5)
|
||||||
|
|
||||||
|
#define ST_LIS2DU12_SHIFT_VAL(val, mask) (((val) << __ffs(mask)) & (mask))
|
||||||
|
|
||||||
|
#define ST_LIS2DU12_ACC_CHAN(addr, ch2, idx) \
|
||||||
|
{ \
|
||||||
|
.type = IIO_ACCEL, \
|
||||||
|
.address = addr, \
|
||||||
|
.modified = 1, \
|
||||||
|
.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 = idx, \
|
||||||
|
.scan_type = { \
|
||||||
|
.sign = 's', \
|
||||||
|
.realbits = 12, \
|
||||||
|
.storagebits = 16, \
|
||||||
|
.shift = 4, \
|
||||||
|
.endianness = IIO_LE, \
|
||||||
|
}, \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define ST_LIS2DU12_TEMP_CHAN(addr, ch2) \
|
||||||
|
{ \
|
||||||
|
.type = IIO_TEMP, \
|
||||||
|
.address = addr, \
|
||||||
|
.modified = 1, \
|
||||||
|
.channel2 = ch2, \
|
||||||
|
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
|
||||||
|
BIT(IIO_CHAN_INFO_OFFSET) | \
|
||||||
|
BIT(IIO_CHAN_INFO_SCALE), \
|
||||||
|
.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), \
|
||||||
|
.scan_index = 0, \
|
||||||
|
.scan_type = { \
|
||||||
|
.sign = 's', \
|
||||||
|
.realbits = 12, \
|
||||||
|
.storagebits = 16, \
|
||||||
|
.shift = 4, \
|
||||||
|
.endianness = IIO_LE, \
|
||||||
|
}, \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define ST_LIS2DU12_EVENT_CHANNEL(chan_type, evt_spec) \
|
||||||
|
{ \
|
||||||
|
.type = chan_type, \
|
||||||
|
.modified = 0, \
|
||||||
|
.scan_index = -1, \
|
||||||
|
.indexed = -1, \
|
||||||
|
.event_spec = evt_spec, \
|
||||||
|
.num_event_specs = 1, \
|
||||||
|
}
|
||||||
|
|
||||||
|
enum st_lis2du12_fifo_mode {
|
||||||
|
ST_LIS2DU12_FIFO_BYPASS = 0x0,
|
||||||
|
ST_LIS2DU12_FIFO_CONTINUOUS = 0x6,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum st_lis2du12_selftest_status {
|
||||||
|
ST_LIS2DU12_ST_RESET,
|
||||||
|
ST_LIS2DU12_ST_PASS,
|
||||||
|
ST_LIS2DU12_ST_FAIL,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum st_lis2du12_sensor_id {
|
||||||
|
ST_LIS2DU12_ID_ACC,
|
||||||
|
ST_LIS2DU12_ID_TEMP,
|
||||||
|
ST_LIS2DU12_ID_TAP_TAP,
|
||||||
|
ST_LIS2DU12_ID_TAP,
|
||||||
|
ST_LIS2DU12_ID_WU,
|
||||||
|
ST_LIS2DU12_ID_FF,
|
||||||
|
ST_LIS2DU12_ID_6D,
|
||||||
|
ST_LIS2DU12_ID_ACT,
|
||||||
|
ST_LIS2DU12_ID_MAX,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum st_lis2du12_attr_id {
|
||||||
|
ST_LIS2DU12_WK_THS_ATTR_ID = 0x0,
|
||||||
|
ST_LIS2DU12_WK_DUR_ATTR_ID,
|
||||||
|
ST_LIS2DU12_FF_THS_ATTR_ID,
|
||||||
|
ST_LIS2DU12_FF_DUR_ATTR_ID,
|
||||||
|
ST_LIS2DU12_6D_THS_ATTR_ID,
|
||||||
|
ST_LIS2DU12_LATENCY_ATTR_ID,
|
||||||
|
ST_LIS2DU12_QUIET_ATTR_ID,
|
||||||
|
ST_LIS2DU12_SHOCK_ATTR_ID,
|
||||||
|
ST_LIS2DU12_TAP_PRIORITY_ATTR_ID,
|
||||||
|
ST_LIS2DU12_TAP_THRESHOLD_X_ATTR_ID,
|
||||||
|
ST_LIS2DU12_TAP_THRESHOLD_Y_ATTR_ID,
|
||||||
|
ST_LIS2DU12_TAP_THRESHOLD_Z_ATTR_ID,
|
||||||
|
ST_LIS2DU12_TAP_ENABLE_ATTR_ID,
|
||||||
|
ST_LIS2DU12_SLEEP_DUR_ATTR_ID,
|
||||||
|
};
|
||||||
|
|
||||||
|
#define ST_LIS2DU12_MAX_BUFFER ST_LIS2DU12_ID_TEMP
|
||||||
|
|
||||||
|
struct st_lis2du12_sensor {
|
||||||
|
enum st_lis2du12_sensor_id id;
|
||||||
|
struct st_lis2du12_hw *hw;
|
||||||
|
|
||||||
|
u16 odr;
|
||||||
|
u32 uodr;
|
||||||
|
|
||||||
|
union {
|
||||||
|
struct {
|
||||||
|
u16 gain;
|
||||||
|
u32 offset;
|
||||||
|
u8 watermark;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct {
|
||||||
|
u8 wk_en;
|
||||||
|
u8 d6d_ths;
|
||||||
|
u8 tap_ths_x;
|
||||||
|
u8 tap_ths_y;
|
||||||
|
u8 tap_ths_z;
|
||||||
|
u8 tap_priority;
|
||||||
|
u8 tap_en;
|
||||||
|
u8 latency;
|
||||||
|
u8 quiet;
|
||||||
|
u8 shock;
|
||||||
|
u8 wh_ths;
|
||||||
|
u8 wh_dur;
|
||||||
|
u8 sleep_dur;
|
||||||
|
u8 ff_dur;
|
||||||
|
u8 ff_ths;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
struct st_lis2du12_hw {
|
||||||
|
struct device *dev;
|
||||||
|
int irq;
|
||||||
|
|
||||||
|
struct regmap *regmap;
|
||||||
|
struct mutex fifo_lock;
|
||||||
|
struct mutex lock;
|
||||||
|
|
||||||
|
struct iio_dev *iio_devs[ST_LIS2DU12_ID_MAX];
|
||||||
|
|
||||||
|
enum st_lis2du12_selftest_status st_status;
|
||||||
|
enum st_lis2du12_fifo_mode fifo_mode;
|
||||||
|
u16 enable_mask;
|
||||||
|
|
||||||
|
u8 fifo_watermark;
|
||||||
|
bool round_xl_xyz;
|
||||||
|
u8 std_level;
|
||||||
|
u64 samples;
|
||||||
|
|
||||||
|
s64 delta_ts;
|
||||||
|
s64 ts_irq;
|
||||||
|
s64 ts;
|
||||||
|
|
||||||
|
u8 drdy_reg;
|
||||||
|
u8 md_reg;
|
||||||
|
bool fourd_enabled;
|
||||||
|
};
|
||||||
|
|
||||||
|
extern const struct dev_pm_ops st_lis2du12_pm_ops;
|
||||||
|
|
||||||
|
static inline s64 st_lis2du12_get_timestamp(struct st_lis2du12_hw *hw)
|
||||||
|
{
|
||||||
|
return iio_get_time_ns(hw->iio_devs[ST_LIS2DU12_ID_ACC]);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool
|
||||||
|
st_lis2du12_interrupts_enabled(struct st_lis2du12_hw *hw)
|
||||||
|
{
|
||||||
|
return hw->enable_mask & (BIT(ST_LIS2DU12_ID_FF) |
|
||||||
|
BIT(ST_LIS2DU12_ID_TAP_TAP) |
|
||||||
|
BIT(ST_LIS2DU12_ID_TAP) |
|
||||||
|
BIT(ST_LIS2DU12_ID_WU) |
|
||||||
|
BIT(ST_LIS2DU12_ID_6D) |
|
||||||
|
BIT(ST_LIS2DU12_ID_ACT));
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool
|
||||||
|
st_lis2du12_fifo_enabled(struct st_lis2du12_hw *hw)
|
||||||
|
{
|
||||||
|
return hw->enable_mask & (BIT(ST_LIS2DU12_ID_ACC) |
|
||||||
|
BIT(ST_LIS2DU12_ID_TEMP));
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int
|
||||||
|
st_lis2du12_read_locked(struct st_lis2du12_hw *hw, unsigned int addr,
|
||||||
|
void *val, unsigned int len)
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
|
||||||
|
mutex_lock(&hw->lock);
|
||||||
|
err = regmap_bulk_read(hw->regmap, addr, val, len);
|
||||||
|
mutex_unlock(&hw->lock);
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline struct st_lis2du12_sensor *
|
||||||
|
st_lis2du12_get_sensor_from_id(struct st_lis2du12_hw *hw,
|
||||||
|
enum st_lis2du12_sensor_id id)
|
||||||
|
{
|
||||||
|
return iio_priv(hw->iio_devs[id]);
|
||||||
|
}
|
||||||
|
|
||||||
|
int st_lis2du12_probe(struct device *dev, int irq,
|
||||||
|
struct regmap *regmap);
|
||||||
|
int st_lis2du12_buffer_setup(struct st_lis2du12_hw *hw);
|
||||||
|
ssize_t st_lis2du12_flush_fifo(struct device *dev,
|
||||||
|
struct device_attribute *attr,
|
||||||
|
const char *buf, size_t size);
|
||||||
|
ssize_t st_lis2du12_set_hwfifo_watermark(struct device *device,
|
||||||
|
struct device_attribute *attr,
|
||||||
|
const char *buf, size_t size);
|
||||||
|
int st_lis2du12_sensor_set_enable(struct st_lis2du12_sensor *sensor,
|
||||||
|
bool enable);
|
||||||
|
#endif /* ST_LIS2DU12_H */
|
555
drivers/iio/stm/accel/st_lis2du12_buffer.c
Normal file
555
drivers/iio/stm/accel/st_lis2du12_buffer.c
Normal file
@ -0,0 +1,555 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-2.0-only
|
||||||
|
/*
|
||||||
|
* STMicroelectronics lis2du12 fifo driver
|
||||||
|
*
|
||||||
|
* MEMS Software Solutions Team
|
||||||
|
*
|
||||||
|
* Copyright 2022 STMicroelectronics Inc.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/interrupt.h>
|
||||||
|
#include <linux/irq.h>
|
||||||
|
#include <linux/iio/kfifo_buf.h>
|
||||||
|
#include <linux/iio/events.h>
|
||||||
|
#include <linux/iio/iio.h>
|
||||||
|
#include <linux/iio/buffer.h>
|
||||||
|
#include <linux/of_device.h>
|
||||||
|
#include <linux/version.h>
|
||||||
|
|
||||||
|
#include "st_lis2du12.h"
|
||||||
|
|
||||||
|
#define ST_LIS2DU12_EWMA_LEVEL 120
|
||||||
|
#define ST_LIS2DU12_EWMA_DIV 128
|
||||||
|
static inline s64 st_lis2du12_ewma(s64 old, s64 new, int weight)
|
||||||
|
{
|
||||||
|
s64 diff, incr;
|
||||||
|
|
||||||
|
diff = new - old;
|
||||||
|
incr = div_s64((ST_LIS2DU12_EWMA_DIV - weight) * diff,
|
||||||
|
ST_LIS2DU12_EWMA_DIV);
|
||||||
|
|
||||||
|
return old + incr;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int st_lis2du12_set_fifo_mode(struct st_lis2du12_hw *hw,
|
||||||
|
enum st_lis2du12_fifo_mode mode)
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
|
||||||
|
err = regmap_update_bits(hw->regmap,
|
||||||
|
ST_LIS2DU12_FIFO_CTRL_ADDR,
|
||||||
|
ST_LIS2DU12_FIFOMODE_MASK,
|
||||||
|
FIELD_PREP(ST_LIS2DU12_FIFOMODE_MASK, mode));
|
||||||
|
if (err < 0)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
hw->fifo_mode = mode;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int st_lis2du12_update_fifo_watermark(struct st_lis2du12_sensor *sensor,
|
||||||
|
u8 watermark)
|
||||||
|
{
|
||||||
|
u16 fifo_watermark = ST_LIS2DU12_MAX_WATERMARK;
|
||||||
|
struct st_lis2du12_hw *hw = sensor->hw;
|
||||||
|
struct st_lis2du12_sensor *cur_sensor;
|
||||||
|
u16 cur_watermark = 0;
|
||||||
|
int i, err;
|
||||||
|
|
||||||
|
for (i = ST_LIS2DU12_ID_ACC; i <= ST_LIS2DU12_MAX_BUFFER; i++) {
|
||||||
|
if (!hw->iio_devs[i])
|
||||||
|
continue;
|
||||||
|
|
||||||
|
cur_sensor = iio_priv(hw->iio_devs[i]);
|
||||||
|
|
||||||
|
if (!(hw->enable_mask & BIT(cur_sensor->id)))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
cur_watermark = (cur_sensor == sensor) ? watermark :
|
||||||
|
cur_sensor->watermark;
|
||||||
|
|
||||||
|
fifo_watermark = min_t(u8, fifo_watermark,
|
||||||
|
cur_watermark);
|
||||||
|
}
|
||||||
|
|
||||||
|
mutex_lock(&hw->fifo_lock);
|
||||||
|
err = regmap_update_bits(hw->regmap,
|
||||||
|
ST_LIS2DU12_FIFO_WTM_ADDR,
|
||||||
|
ST_LIS2DU12_FTH_MASK,
|
||||||
|
FIELD_PREP(ST_LIS2DU12_FTH_MASK, fifo_watermark));
|
||||||
|
if (err < 0)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
hw->fifo_watermark = fifo_watermark;
|
||||||
|
|
||||||
|
out:
|
||||||
|
mutex_unlock(&hw->fifo_lock);
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int st_lis2du12_update_fifo(struct iio_dev *iio_dev, bool enable)
|
||||||
|
{
|
||||||
|
struct st_lis2du12_sensor *sensor = iio_priv(iio_dev);
|
||||||
|
struct st_lis2du12_hw *hw = sensor->hw;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
if (enable) {
|
||||||
|
hw->ts_irq = hw->ts = st_lis2du12_get_timestamp(hw);
|
||||||
|
hw->delta_ts = div_s64(1000000000LL, sensor->odr) *
|
||||||
|
hw->fifo_watermark;
|
||||||
|
hw->samples = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
disable_irq(hw->irq);
|
||||||
|
|
||||||
|
err = st_lis2du12_sensor_set_enable(sensor, enable);
|
||||||
|
if (err < 0)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
if (sensor->id == ST_LIS2DU12_ID_TEMP) {
|
||||||
|
u8 round = enable ? false : true;
|
||||||
|
|
||||||
|
/* configure rounding accordingly */
|
||||||
|
err = regmap_update_bits(hw->regmap,
|
||||||
|
ST_LIS2DU12_FIFO_CTRL_ADDR,
|
||||||
|
ST_LIS2DU12_ROUNDING_XYZ_MASK,
|
||||||
|
FIELD_PREP(ST_LIS2DU12_ROUNDING_XYZ_MASK, round));
|
||||||
|
if (err < 0)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
hw->round_xl_xyz = round;
|
||||||
|
}
|
||||||
|
|
||||||
|
st_lis2du12_update_fifo_watermark(sensor, sensor->watermark);
|
||||||
|
|
||||||
|
if (enable && (hw->fifo_mode == ST_LIS2DU12_FIFO_BYPASS))
|
||||||
|
err = st_lis2du12_set_fifo_mode(hw, ST_LIS2DU12_FIFO_CONTINUOUS);
|
||||||
|
else if (!st_lis2du12_fifo_enabled(hw))
|
||||||
|
err = st_lis2du12_set_fifo_mode(hw, ST_LIS2DU12_FIFO_BYPASS);
|
||||||
|
|
||||||
|
out:
|
||||||
|
enable_irq(hw->irq);
|
||||||
|
|
||||||
|
return err < 0 ? err : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
ssize_t st_lis2du12_set_hwfifo_watermark(struct device *dev,
|
||||||
|
struct device_attribute *attr,
|
||||||
|
const char *buf, size_t size)
|
||||||
|
{
|
||||||
|
struct iio_dev *iio_dev = dev_to_iio_dev(dev);
|
||||||
|
struct st_lis2du12_sensor *sensor = iio_priv(iio_dev);
|
||||||
|
int err, val;
|
||||||
|
|
||||||
|
mutex_lock(&iio_dev->mlock);
|
||||||
|
if (iio_buffer_enabled(iio_dev)) {
|
||||||
|
err = -EBUSY;
|
||||||
|
goto unlock;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = kstrtoint(buf, 10, &val);
|
||||||
|
if (err < 0)
|
||||||
|
goto unlock;
|
||||||
|
|
||||||
|
if (val < 1 || val > ST_LIS2DU12_MAX_WATERMARK) {
|
||||||
|
err = -EINVAL;
|
||||||
|
goto unlock;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = st_lis2du12_update_fifo_watermark(sensor, val);
|
||||||
|
if (err < 0)
|
||||||
|
goto unlock;
|
||||||
|
|
||||||
|
sensor->watermark = val;
|
||||||
|
|
||||||
|
unlock:
|
||||||
|
mutex_unlock(&iio_dev->mlock);
|
||||||
|
|
||||||
|
return err < 0 ? err : size;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int st_lis2du12_buffer_preenable(struct iio_dev *iio_dev)
|
||||||
|
{
|
||||||
|
return st_lis2du12_update_fifo(iio_dev, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int st_lis2du12_buffer_postdisable(struct iio_dev *iio_dev)
|
||||||
|
{
|
||||||
|
return st_lis2du12_update_fifo(iio_dev, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct
|
||||||
|
iio_buffer_setup_ops st_lis2du12_buffer_setup_ops = {
|
||||||
|
.preenable = st_lis2du12_buffer_preenable,
|
||||||
|
.postdisable = st_lis2du12_buffer_postdisable,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int st_lis2du12_read_fifo(struct st_lis2du12_hw *hw)
|
||||||
|
{
|
||||||
|
u8 iio_buff[ALIGN(ST_LIS2DU12_ACC_DATA_SIZE, sizeof(s64)) + sizeof(s64)];
|
||||||
|
u8 buff[16 * ST_LIS2DU12_ACC_DATA_SIZE], samples, data_size;
|
||||||
|
struct iio_dev *iio_acc_dev = hw->iio_devs[ST_LIS2DU12_ID_ACC];
|
||||||
|
struct iio_dev *iio_temp_dev = hw->iio_devs[ST_LIS2DU12_ID_TEMP];
|
||||||
|
struct iio_chan_spec const *ch = iio_acc_dev->channels;
|
||||||
|
int i, err, word_len, fifo_len, read_len = 0;
|
||||||
|
s64 delta_ts;
|
||||||
|
int status;
|
||||||
|
|
||||||
|
err = regmap_read(hw->regmap, ST_LIS2DU12_FIFO_STATUS2_ADDR, &status);
|
||||||
|
if (err < 0)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
samples = status & ST_LIS2DU12_FSS_MASK;
|
||||||
|
delta_ts = div_s64(hw->delta_ts, hw->fifo_watermark);
|
||||||
|
|
||||||
|
if (hw->round_xl_xyz)
|
||||||
|
data_size = ST_LIS2DU12_ACC_DATA_SIZE;
|
||||||
|
else
|
||||||
|
data_size = ST_LIS2DU12_DATA_SIZE;
|
||||||
|
|
||||||
|
fifo_len = samples * data_size;
|
||||||
|
|
||||||
|
while (read_len < fifo_len) {
|
||||||
|
word_len = min_t(int, fifo_len - read_len,
|
||||||
|
sizeof(buff));
|
||||||
|
err = st_lis2du12_read_locked(hw, ch[0].address,
|
||||||
|
buff, word_len);
|
||||||
|
if (err < 0)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
for (i = 0; i < word_len; i += data_size) {
|
||||||
|
if (unlikely(++hw->samples < hw->std_level)) {
|
||||||
|
hw->ts += delta_ts;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
hw->ts = min_t(s64,
|
||||||
|
st_lis2du12_get_timestamp(hw),
|
||||||
|
hw->ts);
|
||||||
|
|
||||||
|
if (hw->enable_mask & BIT(ST_LIS2DU12_ID_ACC)) {
|
||||||
|
memcpy(iio_buff, &buff[i],
|
||||||
|
ST_LIS2DU12_ACC_DATA_SIZE);
|
||||||
|
iio_push_to_buffers_with_timestamp(iio_acc_dev,
|
||||||
|
iio_buff,
|
||||||
|
hw->ts);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!hw->round_xl_xyz &&
|
||||||
|
(hw->enable_mask & BIT(ST_LIS2DU12_ID_TEMP))) {
|
||||||
|
memcpy(iio_buff,
|
||||||
|
&buff[i + ST_LIS2DU12_ACC_DATA_SIZE],
|
||||||
|
ST_LIS2DU12_TEMP_DATA_SIZE);
|
||||||
|
iio_push_to_buffers_with_timestamp(iio_temp_dev,
|
||||||
|
iio_buff,
|
||||||
|
hw->ts);
|
||||||
|
}
|
||||||
|
|
||||||
|
hw->ts += delta_ts;
|
||||||
|
}
|
||||||
|
|
||||||
|
read_len += word_len;
|
||||||
|
}
|
||||||
|
|
||||||
|
return read_len;
|
||||||
|
}
|
||||||
|
|
||||||
|
ssize_t st_lis2du12_flush_fifo(struct device *dev,
|
||||||
|
struct device_attribute *attr,
|
||||||
|
const char *buf, size_t size)
|
||||||
|
{
|
||||||
|
struct iio_dev *iio_dev = dev_to_iio_dev(dev);
|
||||||
|
struct st_lis2du12_sensor *sensor = iio_priv(iio_dev);
|
||||||
|
struct st_lis2du12_hw *hw = sensor->hw;
|
||||||
|
s64 code;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
mutex_lock(&hw->fifo_lock);
|
||||||
|
|
||||||
|
err = st_lis2du12_read_fifo(hw);
|
||||||
|
hw->ts_irq = st_lis2du12_get_timestamp(hw);
|
||||||
|
|
||||||
|
mutex_unlock(&hw->fifo_lock);
|
||||||
|
|
||||||
|
code = IIO_UNMOD_EVENT_CODE(IIO_ACCEL, -1,
|
||||||
|
STM_IIO_EV_TYPE_FIFO_FLUSH,
|
||||||
|
IIO_EV_DIR_EITHER);
|
||||||
|
iio_push_event(iio_dev, code, hw->ts_irq);
|
||||||
|
|
||||||
|
return err < 0 ? err : size;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static irqreturn_t st_lis2du12_handler_irq(int irq, void *private)
|
||||||
|
{
|
||||||
|
struct st_lis2du12_hw *hw = private;
|
||||||
|
s64 ts;
|
||||||
|
|
||||||
|
ts = st_lis2du12_get_timestamp(hw);
|
||||||
|
hw->delta_ts = st_lis2du12_ewma(hw->delta_ts, ts - hw->ts_irq,
|
||||||
|
ST_LIS2DU12_EWMA_LEVEL);
|
||||||
|
hw->ts_irq = ts;
|
||||||
|
|
||||||
|
return IRQ_WAKE_THREAD;
|
||||||
|
}
|
||||||
|
|
||||||
|
static irqreturn_t st_lis2du12_handler_thread(int irq, void *private)
|
||||||
|
{
|
||||||
|
int status, all_int_source, wk_source, sixd_source;
|
||||||
|
struct st_lis2du12_hw *hw = private;
|
||||||
|
s64 code;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
err = regmap_read(hw->regmap, ST_LIS2DU12_FIFO_STATUS1_ADDR,
|
||||||
|
&status);
|
||||||
|
if (err < 0)
|
||||||
|
return IRQ_HANDLED;
|
||||||
|
|
||||||
|
if (status & ST_LIS2DU12_FTH_WTM_MASK) {
|
||||||
|
mutex_lock(&hw->fifo_lock);
|
||||||
|
st_lis2du12_read_fifo(hw);
|
||||||
|
mutex_unlock(&hw->fifo_lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
err = regmap_read(hw->regmap, ST_LIS2DU12_ALL_INT_SRC_ADDR,
|
||||||
|
&all_int_source);
|
||||||
|
if (err < 0)
|
||||||
|
return IRQ_HANDLED;
|
||||||
|
|
||||||
|
if (((all_int_source & ST_LIS2DU12_SINGLE_TAP_ALL_MASK) &&
|
||||||
|
(hw->enable_mask & BIT(ST_LIS2DU12_ID_TAP))) ||
|
||||||
|
((all_int_source & ST_LIS2DU12_DOUBLE_TAP_ALL_MASK) &&
|
||||||
|
(hw->enable_mask & BIT(ST_LIS2DU12_ID_TAP_TAP)))) {
|
||||||
|
struct iio_dev *iio_dev;
|
||||||
|
enum iio_chan_type type;
|
||||||
|
int source;
|
||||||
|
|
||||||
|
err = regmap_read(hw->regmap, ST_LIS2DU12_TAP_SRC_ADDR,
|
||||||
|
&source);
|
||||||
|
if (err < 0)
|
||||||
|
return IRQ_HANDLED;
|
||||||
|
|
||||||
|
if (source & ST_LIS2DU12_DOUBLE_TAP_IA_MASK) {
|
||||||
|
iio_dev = hw->iio_devs[ST_LIS2DU12_ID_TAP_TAP];
|
||||||
|
type = STM_IIO_TAP_TAP;
|
||||||
|
code = IIO_UNMOD_EVENT_CODE(type, -1,
|
||||||
|
IIO_EV_TYPE_THRESH,
|
||||||
|
IIO_EV_DIR_RISING);
|
||||||
|
iio_push_event(iio_dev, code,
|
||||||
|
st_lis2du12_get_timestamp(hw));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (source & ST_LIS2DU12_SINGLE_TAP_IA_MASK) {
|
||||||
|
iio_dev = hw->iio_devs[ST_LIS2DU12_ID_TAP];
|
||||||
|
type = STM_IIO_TAP;
|
||||||
|
code = IIO_UNMOD_EVENT_CODE(type, -1,
|
||||||
|
IIO_EV_TYPE_THRESH,
|
||||||
|
IIO_EV_DIR_RISING);
|
||||||
|
iio_push_event(iio_dev, code,
|
||||||
|
st_lis2du12_get_timestamp(hw));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((all_int_source & ST_LIS2DU12_WU_IA_ALL_MASK) &&
|
||||||
|
(hw->enable_mask & BIT(ST_LIS2DU12_ID_WU))) {
|
||||||
|
struct iio_dev *iio_dev;
|
||||||
|
enum iio_chan_type type;
|
||||||
|
u8 wu_src;
|
||||||
|
|
||||||
|
err = regmap_read(hw->regmap, ST_LIS2DU12_WAKE_UP_SRC_ADDR,
|
||||||
|
&wk_source);
|
||||||
|
if (err < 0)
|
||||||
|
return IRQ_HANDLED;
|
||||||
|
|
||||||
|
wu_src = wk_source & ST_LIS2DU12_WU_MASK;
|
||||||
|
iio_dev = hw->iio_devs[ST_LIS2DU12_ID_WU];
|
||||||
|
/* use STM_IIO_GESTURE event type for custom events */
|
||||||
|
type = STM_IIO_GESTURE;
|
||||||
|
code = IIO_UNMOD_EVENT_CODE(type, wu_src,
|
||||||
|
IIO_EV_TYPE_THRESH,
|
||||||
|
IIO_EV_DIR_RISING);
|
||||||
|
iio_push_event(iio_dev, code,
|
||||||
|
st_lis2du12_get_timestamp(hw));
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((all_int_source & ST_LIS2DU12_FF_IA_ALL_MASK) &&
|
||||||
|
(hw->enable_mask & BIT(ST_LIS2DU12_ID_FF))) {
|
||||||
|
struct iio_dev *iio_dev;
|
||||||
|
enum iio_chan_type type;
|
||||||
|
|
||||||
|
iio_dev = hw->iio_devs[ST_LIS2DU12_ID_FF];
|
||||||
|
/* use STM_IIO_GESTURE event type for custom events */
|
||||||
|
type = STM_IIO_GESTURE;
|
||||||
|
code = IIO_UNMOD_EVENT_CODE(type, 1,
|
||||||
|
IIO_EV_TYPE_THRESH,
|
||||||
|
IIO_EV_DIR_RISING);
|
||||||
|
iio_push_event(iio_dev, code,
|
||||||
|
st_lis2du12_get_timestamp(hw));
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((all_int_source & ST_LIS2DU12_D6D_IA_ALL_MASK) &&
|
||||||
|
(hw->enable_mask & BIT(ST_LIS2DU12_ID_6D))) {
|
||||||
|
struct iio_dev *iio_dev;
|
||||||
|
enum iio_chan_type type;
|
||||||
|
u8 sixd_src;
|
||||||
|
|
||||||
|
err = regmap_read(hw->regmap, ST_LIS2DU12_SIXD_SRC_ADDR,
|
||||||
|
&sixd_source);
|
||||||
|
if (err < 0)
|
||||||
|
return IRQ_HANDLED;
|
||||||
|
|
||||||
|
sixd_src = sixd_source & ST_LIS2DU12_OVERTHRESHOLD_MASK;
|
||||||
|
iio_dev = hw->iio_devs[ST_LIS2DU12_ID_6D];
|
||||||
|
/* use IIO_GESTURE event type for custom events */
|
||||||
|
type = STM_IIO_GESTURE;
|
||||||
|
code = IIO_UNMOD_EVENT_CODE(type, sixd_src,
|
||||||
|
IIO_EV_TYPE_THRESH,
|
||||||
|
IIO_EV_DIR_RISING);
|
||||||
|
iio_push_event(iio_dev, code,
|
||||||
|
st_lis2du12_get_timestamp(hw));
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((all_int_source & ST_LIS2DU12_SLEEP_CHANGE_IA_ALL_MASK) &&
|
||||||
|
(hw->enable_mask & BIT(ST_LIS2DU12_ID_ACT))) {
|
||||||
|
struct iio_dev *iio_dev;
|
||||||
|
enum iio_chan_type type;
|
||||||
|
u8 sleep_state;
|
||||||
|
|
||||||
|
err = regmap_read(hw->regmap, ST_LIS2DU12_WAKE_UP_SRC_ADDR,
|
||||||
|
&wk_source);
|
||||||
|
if (err < 0)
|
||||||
|
return IRQ_HANDLED;
|
||||||
|
|
||||||
|
sleep_state = wk_source & ST_LIS2DU12_SLEEP_STATE_MASK;
|
||||||
|
iio_dev = hw->iio_devs[ST_LIS2DU12_ID_ACT];
|
||||||
|
/* use IIO_GESTURE event type for custom events */
|
||||||
|
type = STM_IIO_GESTURE;
|
||||||
|
code = IIO_UNMOD_EVENT_CODE(type, sleep_state,
|
||||||
|
IIO_EV_TYPE_THRESH,
|
||||||
|
IIO_EV_DIR_RISING);
|
||||||
|
iio_push_event(iio_dev, code,
|
||||||
|
st_lis2du12_get_timestamp(hw));
|
||||||
|
}
|
||||||
|
|
||||||
|
return IRQ_HANDLED;
|
||||||
|
}
|
||||||
|
|
||||||
|
int st_lis2du12_buffer_setup(struct st_lis2du12_hw *hw)
|
||||||
|
{
|
||||||
|
struct device_node *np = hw->dev->of_node;
|
||||||
|
|
||||||
|
#if KERNEL_VERSION(5, 13, 0) > LINUX_VERSION_CODE
|
||||||
|
struct iio_buffer *buffer;
|
||||||
|
#endif /* LINUX_VERSION_CODE */
|
||||||
|
|
||||||
|
unsigned long irq_type;
|
||||||
|
u8 irq_active_low, i;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
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 = 0;
|
||||||
|
break;
|
||||||
|
case IRQF_TRIGGER_LOW:
|
||||||
|
case IRQF_TRIGGER_FALLING:
|
||||||
|
irq_active_low = 1;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
dev_info(hw->dev, "mode %lx unsupported\n", irq_type);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (irq_active_low) {
|
||||||
|
/* configure interrupts to low level */
|
||||||
|
ret = regmap_update_bits(hw->regmap,
|
||||||
|
ST_LIS2DU12_INTERRUPT_CFG_ADDR,
|
||||||
|
ST_LIS2DU12_H_LACTIVE_MASK,
|
||||||
|
FIELD_PREP(ST_LIS2DU12_H_LACTIVE_MASK, 1));
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* check pull down disable on int1 pin property */
|
||||||
|
if (np && of_property_read_bool(np, "pd_dis_int1")) {
|
||||||
|
ret = regmap_update_bits(hw->regmap,
|
||||||
|
ST_LIS2DU12_IF_CTRL_ADDR,
|
||||||
|
ST_LIS2DU12_PD_DIS_INT1_MASK,
|
||||||
|
FIELD_PREP(ST_LIS2DU12_PD_DIS_INT1_MASK, 1));
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* check push pull / open drain int pin property */
|
||||||
|
if (np && of_property_read_bool(np, "pp_od_int")) {
|
||||||
|
ret = regmap_update_bits(hw->regmap,
|
||||||
|
ST_LIS2DU12_CTRL1_ADDR,
|
||||||
|
ST_LIS2DU12_PP_OD_MASK,
|
||||||
|
FIELD_PREP(ST_LIS2DU12_PP_OD_MASK, 1));
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = devm_request_threaded_irq(hw->dev, hw->irq,
|
||||||
|
st_lis2du12_handler_irq,
|
||||||
|
st_lis2du12_handler_thread,
|
||||||
|
irq_type | IRQF_ONESHOT,
|
||||||
|
"st_lis2du12", hw);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(hw->dev, "failed to request trigger irq %d\n",
|
||||||
|
hw->irq);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* configure rounding to read only acc data from FIFO */
|
||||||
|
ret = regmap_update_bits(hw->regmap,
|
||||||
|
ST_LIS2DU12_FIFO_CTRL_ADDR,
|
||||||
|
ST_LIS2DU12_ROUNDING_XYZ_MASK,
|
||||||
|
FIELD_PREP(ST_LIS2DU12_ROUNDING_XYZ_MASK, 1));
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
hw->round_xl_xyz = true;
|
||||||
|
|
||||||
|
for (i = ST_LIS2DU12_ID_ACC; i <= ST_LIS2DU12_MAX_BUFFER; i++) {
|
||||||
|
if (!hw->iio_devs[i])
|
||||||
|
continue;
|
||||||
|
|
||||||
|
#if KERNEL_VERSION(5, 19, 0) <= LINUX_VERSION_CODE
|
||||||
|
ret = devm_iio_kfifo_buffer_setup(hw->dev, hw->iio_devs[i],
|
||||||
|
&st_lis2du12_buffer_setup_ops);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
#elif KERNEL_VERSION(5, 13, 0) <= LINUX_VERSION_CODE
|
||||||
|
ret = devm_iio_kfifo_buffer_setup(hw->dev, hw->iio_devs[i],
|
||||||
|
INDIO_BUFFER_SOFTWARE,
|
||||||
|
&st_lis2du12_buffer_setup_ops);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
#else /* LINUX_VERSION_CODE */
|
||||||
|
buffer = devm_iio_kfifo_allocate(hw->dev);
|
||||||
|
if (!buffer)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
iio_device_attach_buffer(hw->iio_devs[i], buffer);
|
||||||
|
hw->iio_devs[i]->modes |= INDIO_BUFFER_SOFTWARE;
|
||||||
|
hw->iio_devs[i]->setup_ops = &st_lis2du12_buffer_setup_ops;
|
||||||
|
#endif /* LINUX_VERSION_CODE */
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = st_lis2du12_set_fifo_mode(hw, ST_LIS2DU12_FIFO_BYPASS);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
return regmap_update_bits(hw->regmap,
|
||||||
|
hw->drdy_reg,
|
||||||
|
ST_LIS2DU12_INT_F_FTH_MASK,
|
||||||
|
FIELD_PREP(ST_LIS2DU12_INT_F_FTH_MASK, 1));
|
||||||
|
}
|
1555
drivers/iio/stm/accel/st_lis2du12_core.c
Normal file
1555
drivers/iio/stm/accel/st_lis2du12_core.c
Normal file
File diff suppressed because it is too large
Load Diff
67
drivers/iio/stm/accel/st_lis2du12_i2c.c
Normal file
67
drivers/iio/stm/accel/st_lis2du12_i2c.c
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-2.0-only
|
||||||
|
/*
|
||||||
|
* STMicroelectronics st_lis2du12 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_lis2du12.h"
|
||||||
|
|
||||||
|
static const struct regmap_config st_lis2du12_i2c_regmap_config = {
|
||||||
|
.reg_bits = 8,
|
||||||
|
.val_bits = 8,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int st_lis2du12_i2c_probe(struct i2c_client *client,
|
||||||
|
const struct i2c_device_id *id)
|
||||||
|
{
|
||||||
|
struct regmap *regmap;
|
||||||
|
|
||||||
|
regmap = devm_regmap_init_i2c(client,
|
||||||
|
&st_lis2du12_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_lis2du12_probe(&client->dev, client->irq, regmap);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct of_device_id st_lis2du12_i2c_of_match[] = {
|
||||||
|
{
|
||||||
|
.compatible = "st," ST_LIS2DU12_DEV_NAME,
|
||||||
|
},
|
||||||
|
{},
|
||||||
|
};
|
||||||
|
MODULE_DEVICE_TABLE(of, st_lis2du12_i2c_of_match);
|
||||||
|
|
||||||
|
static const struct i2c_device_id st_lis2du12_i2c_id_table[] = {
|
||||||
|
{ ST_LIS2DU12_DEV_NAME },
|
||||||
|
{},
|
||||||
|
};
|
||||||
|
MODULE_DEVICE_TABLE(i2c, st_lis2du12_i2c_id_table);
|
||||||
|
|
||||||
|
static struct i2c_driver st_lis2du12_driver = {
|
||||||
|
.driver = {
|
||||||
|
.name = "st_" ST_LIS2DU12_DEV_NAME "_i2c",
|
||||||
|
.pm = &st_lis2du12_pm_ops,
|
||||||
|
.of_match_table = of_match_ptr(st_lis2du12_i2c_of_match),
|
||||||
|
},
|
||||||
|
.probe = st_lis2du12_i2c_probe,
|
||||||
|
.id_table = st_lis2du12_i2c_id_table,
|
||||||
|
};
|
||||||
|
module_i2c_driver(st_lis2du12_driver);
|
||||||
|
|
||||||
|
MODULE_AUTHOR("MEMS Software Solutions Team");
|
||||||
|
MODULE_DESCRIPTION("STMicroelectronics st_lis2du12 i2c driver");
|
||||||
|
MODULE_LICENSE("GPL v2");
|
58
drivers/iio/stm/accel/st_lis2du12_i3c.c
Normal file
58
drivers/iio/stm/accel/st_lis2du12_i3c.c
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-2.0-only
|
||||||
|
/*
|
||||||
|
* STMicroelectronics st_lis2du12 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_lis2du12.h"
|
||||||
|
|
||||||
|
static const struct i3c_device_id st_lis2du12_i3c_ids[] = {
|
||||||
|
I3C_DEVICE(0x0104, ST_LIS2DU12_WHOAMI_VAL, NULL),
|
||||||
|
{},
|
||||||
|
};
|
||||||
|
MODULE_DEVICE_TABLE(i3c, st_lis2du12_i3c_ids);
|
||||||
|
|
||||||
|
static int st_lis2du12_i3c_probe(struct i3c_device *i3cdev)
|
||||||
|
{
|
||||||
|
struct regmap_config st_lis2du12_i3c_regmap_config = {
|
||||||
|
.reg_bits = 8,
|
||||||
|
.val_bits = 8,
|
||||||
|
};
|
||||||
|
struct regmap *regmap;
|
||||||
|
|
||||||
|
regmap = devm_regmap_init_i3c(i3cdev, &st_lis2du12_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_lis2du12_probe(&i3cdev->dev, 0, regmap);
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct i3c_driver st_lis2du12_driver = {
|
||||||
|
.driver = {
|
||||||
|
.name = "st_" ST_LIS2DU12_DEV_NAME "_i3c",
|
||||||
|
.pm = &st_lis2du12_pm_ops,
|
||||||
|
},
|
||||||
|
.probe = st_lis2du12_i3c_probe,
|
||||||
|
.id_table = st_lis2du12_i3c_ids,
|
||||||
|
};
|
||||||
|
module_i3c_driver(st_lis2du12_driver);
|
||||||
|
|
||||||
|
MODULE_AUTHOR("MEMS Software Solutions Team");
|
||||||
|
MODULE_DESCRIPTION("STMicroelectronics st_lis2du12 i3c driver");
|
||||||
|
MODULE_LICENSE("GPL v2");
|
64
drivers/iio/stm/accel/st_lis2du12_spi.c
Normal file
64
drivers/iio/stm/accel/st_lis2du12_spi.c
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-2.0-only
|
||||||
|
/*
|
||||||
|
* STMicroelectronics lis2du12 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_lis2du12.h"
|
||||||
|
|
||||||
|
static const struct regmap_config st_lis2du12_spi_regmap_config = {
|
||||||
|
.reg_bits = 8,
|
||||||
|
.val_bits = 8,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int st_lis2du12_spi_probe(struct spi_device *spi)
|
||||||
|
{
|
||||||
|
struct regmap *regmap;
|
||||||
|
|
||||||
|
regmap = devm_regmap_init_spi(spi,
|
||||||
|
&st_lis2du12_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_lis2du12_probe(&spi->dev, spi->irq, regmap);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct of_device_id st_lis2du12_spi_of_match[] = {
|
||||||
|
{
|
||||||
|
.compatible = "st," ST_LIS2DU12_DEV_NAME,
|
||||||
|
},
|
||||||
|
{},
|
||||||
|
};
|
||||||
|
MODULE_DEVICE_TABLE(of, st_lis2du12_spi_of_match);
|
||||||
|
|
||||||
|
static const struct spi_device_id st_lis2du12_spi_id_table[] = {
|
||||||
|
{ ST_LIS2DU12_DEV_NAME },
|
||||||
|
{},
|
||||||
|
};
|
||||||
|
MODULE_DEVICE_TABLE(spi, st_lis2du12_spi_id_table);
|
||||||
|
|
||||||
|
static struct spi_driver st_lis2du12_driver = {
|
||||||
|
.driver = {
|
||||||
|
.name = "st_" ST_LIS2DU12_DEV_NAME "_spi",
|
||||||
|
.pm = &st_lis2du12_pm_ops,
|
||||||
|
.of_match_table = of_match_ptr(st_lis2du12_spi_of_match),
|
||||||
|
},
|
||||||
|
.probe = st_lis2du12_spi_probe,
|
||||||
|
.id_table = st_lis2du12_spi_id_table,
|
||||||
|
};
|
||||||
|
module_spi_driver(st_lis2du12_driver);
|
||||||
|
|
||||||
|
MODULE_AUTHOR("MEMS Software Solutions Team");
|
||||||
|
MODULE_DESCRIPTION("STMicroelectronics st_lis2du12 spi driver");
|
||||||
|
MODULE_LICENSE("GPL v2");
|
4
stm_iio_configs/lis2du12_defconfig
Normal file
4
stm_iio_configs/lis2du12_defconfig
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
CONFIG_IIO_ST_LIS2DU12=m
|
||||||
|
CONFIG_IIO_ST_LIS2DU12_I2C=m
|
||||||
|
CONFIG_IIO_ST_LIS2DU12_SPI=m
|
||||||
|
CONFIG_IIO_ST_LIS2DU12_I3C=m
|
Loading…
Reference in New Issue
Block a user