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:
Mario Tesi 2022-03-14 12:43:36 +01:00 committed by mariotesi
parent b314796715
commit 115ea4079c
No known key found for this signature in database
GPG Key ID: 0B6EF815710A402D
10 changed files with 2747 additions and 0 deletions

View File

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

View File

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

View File

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

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

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

File diff suppressed because it is too large Load Diff

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

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

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

View 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