drivers:iio:stm:accel: Add support to ST MEMS ISM303DAC

List of supported features:
     - Configurable ODRs [Power_Off ... 800] Hz
     - Sensor Event Timestamp support
     - Read single accel raw data values
     - FIFO:
      > Accel data stored in FIFO
      > HW Watermark configuration
      > Custom Flush command / event support
     - TAP and DTAP event recognition
     - External trigger support

Signed-off-by: mario tesi <mario.tesi@st.com>
Change-Id: I948c93516fa83c7068e894a1fcc14f265b71067a
Reviewed-on: https://sczcxd1104.scz.st.com/gerrit/c/linux/stm-ldd-iio/+/332
Tested-by: aosp-ger <aosp-ger@st.com>
Reviewed-by: Denis CIOCCA <denis.ciocca@st.com>
This commit is contained in:
mario tesi 2021-11-25 14:08:53 +01:00 committed by Mario Tesi
parent a276e09d44
commit 863aa3c1a2
9 changed files with 2349 additions and 0 deletions

View File

@ -96,4 +96,35 @@ config IIO_ST_LIS2DW12_SPI
depends on IIO_ST_LIS2DW12
depends on SPI
menuconfig IIO_ST_ISM303DAC_ACCEL
tristate "STMicroelectronics ISM303DAC Accelerometer Driver"
depends on (I2C || SPI_MASTER) && SYSFS
select IIO_BUFFER
select IIO_TRIGGERED_BUFFER
select IIO_ST_ISM303DAC_ACCEL_I2C if (I2C)
select IIO_ST_ISM303DAC_ACCEL_SPI if (SPI)
help
Say yes here to build support for the ISM303DAC
accelerometers.
config IIO_ST_ISM303DAC_ACCEL_I2C
tristate
depends on IIO_ST_ISM303DAC_ACCEL
depends on I2C
config IIO_ST_ISM303DAC_ACCEL_SPI
tristate
depends on IIO_ST_ISM303DAC_ACCEL
depends on SPI
config ST_ISM303DAC_ACCEL_IIO_LIMIT_FIFO
int "Limit fifo read lenght (#n byte)"
depends on IIO_ST_ISM303DAC_ACCEL
range 0 1536
default 0
help
Limit atomic fifo read to #n byte. In some platform i2c/spi read
can be limited by software or hardware.
Set 0 to disable the limit.
endmenu

View File

@ -19,3 +19,10 @@ st_lis2dw12-y:= st_lis2dw12_core.o st_lis2dw12_buffer.o
obj-$(CONFIG_IIO_ST_LIS2DW12) += st_lis2dw12.o
obj-$(CONFIG_IIO_ST_LIS2DW12_I2C) += st_lis2dw12_i2c.o
obj-$(CONFIG_IIO_ST_LIS2DW12_SPI) += st_lis2dw12_spi.o
ism303dac_accel-y += st_ism303dac_accel_core.o st_ism303dac_accel_buffer.o \
st_ism303dac_accel_trigger.o
obj-$(CONFIG_IIO_ST_ISM303DAC_ACCEL) += ism303dac_accel.o
obj-$(CONFIG_IIO_ST_ISM303DAC_ACCEL_I2C) += st_ism303dac_accel_i2c.o
obj-$(CONFIG_IIO_ST_ISM303DAC_ACCEL_SPI) += st_ism303dac_accel_spi.o

View File

@ -0,0 +1,303 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* STMicroelectronics ism303dac driver
*
* Copyright 2018 STMicroelectronics Inc.
*
* Giuseppe Barba <giuseppe.barba@st.com>
*/
#ifndef __ISM303DAC_H
#define __ISM303DAC_H
#include <linux/types.h>
#include <linux/iio/iio.h>
#include <linux/iio/trigger.h>
#ifndef CONFIG_OF
#include <linux/platform_data/ism303dac.h>
#endif
#define ISM303DAC_WHO_AM_I_ADDR 0x0f
#define ISM303DAC_WHO_AM_I_DEF 0x43
#define ISM303DAC_CTRL1_ADDR 0x20
#define ISM303DAC_CTRL2_ADDR 0x21
#define ISM303DAC_CTRL3_ADDR 0x22
#define ISM303DAC_CTRL4_INT1_PAD_ADDR 0x23
#define ISM303DAC_CTRL5_INT2_PAD_ADDR 0x24
#define ISM303DAC_FIFO_CTRL_ADDR 0x25
#define ISM303DAC_OUTX_L_ADDR 0x28
#define ISM303DAC_OUTY_L_ADDR 0x2a
#define ISM303DAC_OUTZ_L_ADDR 0x2c
#define ISM303DAC_TAP_THS_6D_ADDR 0x31
#define ISM303DAC_WAKE_UP_THS_ADDR 0x33
#define ISM303DAC_FREE_FALL_ADDR 0x35
#define ISM303DAC_TAP_SRC_ADDR 0x38
#define ISM303DAC_FUNC_CTRL_ADDR 0x3f
#define ISM303DAC_FIFO_THS_ADDR 0x2e
#define ISM303DAC_FIFO_THS_MASK 0xff
#define ISM303DAC_ODR_ADDR ISM303DAC_CTRL1_ADDR
#define ISM303DAC_ODR_MASK 0xf0
#define ISM303DAC_ODR_POWER_OFF_VAL 0x00
#define ISM303DAC_ODR_1HZ_LP_VAL 0x08
#define ISM303DAC_ODR_12HZ_LP_VAL 0x09
#define ISM303DAC_ODR_25HZ_LP_VAL 0x0a
#define ISM303DAC_ODR_50HZ_LP_VAL 0x0b
#define ISM303DAC_ODR_100HZ_LP_VAL 0x0c
#define ISM303DAC_ODR_200HZ_LP_VAL 0x0d
#define ISM303DAC_ODR_400HZ_LP_VAL 0x0e
#define ISM303DAC_ODR_800HZ_LP_VAL 0x0f
#define ISM303DAC_ODR_LP_LIST_NUM 9
#define ISM303DAC_ODR_12_5HZ_HR_VAL 0x01
#define ISM303DAC_ODR_25HZ_HR_VAL 0x02
#define ISM303DAC_ODR_50HZ_HR_VAL 0x03
#define ISM303DAC_ODR_100HZ_HR_VAL 0x04
#define ISM303DAC_ODR_200HZ_HR_VAL 0x05
#define ISM303DAC_ODR_400HZ_HR_VAL 0x06
#define ISM303DAC_ODR_800HZ_HR_VAL 0x07
#define ISM303DAC_ODR_HR_LIST_NUM 8
#define ISM303DAC_FS_ADDR ISM303DAC_CTRL1_ADDR
#define ISM303DAC_FS_MASK 0x0c
#define ISM303DAC_FS_2G_VAL 0x00
#define ISM303DAC_FS_4G_VAL 0x02
#define ISM303DAC_FS_8G_VAL 0x03
#define ISM303DAC_FS_16G_VAL 0x01
/* Advanced Configuration Registers */
#define ISM303DAC_FUNC_CFG_ENTER_ADDR ISM303DAC_CTRL2_ADDR
#define ISM303DAC_FUNC_CFG_EXIT_ADDR 0x3F
#define ISM303DAC_FUNC_CFG_EN_MASK 0x10
#define ISM303DAC_SIM_ADDR ISM303DAC_CTRL2_ADDR
#define ISM303DAC_SIM_MASK 0x01
#define ISM303DAC_ADD_INC_MASK 0x04
/* Sensitivity for the 16-bit data */
#define ISM303DAC_FS_2G_GAIN IIO_G_TO_M_S_2(61)
#define ISM303DAC_FS_4G_GAIN IIO_G_TO_M_S_2(122)
#define ISM303DAC_FS_8G_GAIN IIO_G_TO_M_S_2(244)
#define ISM303DAC_FS_16G_GAIN IIO_G_TO_M_S_2(488)
#define ISM303DAC_MODE_DEFAULT ISM303DAC_HR_MODE
#define ISM303DAC_INT1_S_TAP_MASK 0x40
#define ISM303DAC_INT1_WAKEUP_MASK 0x20
#define ISM303DAC_INT1_FREE_FALL_MASK 0x10
#define ISM303DAC_INT1_TAP_MASK 0x08
#define ISM303DAC_INT1_6D_MASK 0x04
#define ISM303DAC_INT1_FTH_MASK 0x02
#define ISM303DAC_INT1_DRDY_MASK 0x01
#define ISM303DAC_INT1_EVENTS_MASK (ISM303DAC_INT1_S_TAP_MASK | \
ISM303DAC_INT1_WAKEUP_MASK | \
ISM303DAC_INT1_FREE_FALL_MASK | \
ISM303DAC_INT1_TAP_MASK | \
ISM303DAC_INT1_6D_MASK | \
ISM303DAC_INT1_FTH_MASK | \
ISM303DAC_INT1_DRDY_MASK)
#define ISM303DAC_INT2_ON_INT1_MASK 0x20
#define ISM303DAC_INT2_FTH_MASK 0x02
#define ISM303DAC_INT2_DRDY_MASK 0x01
#define ISM303DAC_INT2_EVENTS_MASK (ISM303DAC_INT2_FTH_MASK | \
ISM303DAC_INT2_DRDY_MASK)
#define ISM303DAC_WAKE_UP_THS_WU_MASK 0x3f
#define ISM303DAC_WAKE_UP_THS_WU_DEFAULT 0x02
#define ISM303DAC_FREE_FALL_THS_MASK 0x07
#define ISM303DAC_FREE_FALL_DUR_MASK 0xF8
#define ISM303DAC_FREE_FALL_THS_DEFAULT 0x01
#define ISM303DAC_FREE_FALL_DUR_DEFAULT 0x01
#define ISM303DAC_BDU_ADDR ISM303DAC_CTRL1_ADDR
#define ISM303DAC_BDU_MASK 0x01
#define ISM303DAC_SOFT_RESET_ADDR ISM303DAC_CTRL2_ADDR
#define ISM303DAC_SOFT_RESET_MASK 0x40
#define ISM303DAC_LIR_ADDR ISM303DAC_CTRL3_ADDR
#define ISM303DAC_LIR_MASK 0x04
#define ISM303DAC_TAP_AXIS_ADDR ISM303DAC_CTRL3_ADDR
#define ISM303DAC_TAP_AXIS_MASK 0x38
#define ISM303DAC_TAP_AXIS_ANABLE_ALL 0x07
#define ISM303DAC_TAP_THS_ADDR ISM303DAC_TAP_THS_6D_ADDR
#define ISM303DAC_TAP_THS_MASK 0x1f
#define ISM303DAC_TAP_THS_DEFAULT 0x09
#define ISM303DAC_INT2_ON_INT1_ADDR ISM303DAC_CTRL5_INT2_PAD_ADDR
#define ISM303DAC_INT2_ON_INT1_MASK 0x20
#define ISM303DAC_FIFO_MODE_ADDR ISM303DAC_FIFO_CTRL_ADDR
#define ISM303DAC_FIFO_MODE_MASK 0xe0
#define ISM303DAC_FIFO_MODE_BYPASS 0x00
#define ISM303DAC_FIFO_MODE_CONTINUOS 0x06
#define ISM303DAC_OUT_XYZ_SIZE 8
#define ISM303DAC_SELFTEST_ADDR ISM303DAC_CTRL3_ADDR
#define ISM303DAC_SELFTEST_MASK 0xc0
#define ISM303DAC_SELFTEST_NORMAL 0x00
#define ISM303DAC_SELFTEST_POS_SIGN 0x01
#define ISM303DAC_SELFTEST_NEG_SIGN 0x02
#define ISM303DAC_FIFO_SRC 0x2f
#define ISM303DAC_FIFO_SRC_DIFF_MASK 0x20
#define ISM303DAC_FIFO_NUM_AXIS 3
#define ISM303DAC_FIFO_BYTE_X_AXIS 2
#define ISM303DAC_FIFO_BYTE_FOR_SAMPLE (ISM303DAC_FIFO_NUM_AXIS * \
ISM303DAC_FIFO_BYTE_X_AXIS)
#define ISM303DAC_TIMESTAMP_SIZE 8
#define ISM303DAC_STATUS_ADDR 0x27
#define ISM303DAC_STATUS_DUP_ADDR 0x36
#define ISM303DAC_WAKE_UP_IA_MASK 0x40
#define ISM303DAC_DOUBLE_TAP_MASK 0x10
#define ISM303DAC_TAP_MASK 0x08
#define ISM303DAC_6D_IA_MASK 0x04
#define ISM303DAC_FF_IA_MASK 0x02
#define ISM303DAC_DRDY_MASK 0x01
#define ISM303DAC_EVENT_MASK (ISM303DAC_WAKE_UP_IA_MASK | \
ISM303DAC_DOUBLE_TAP_MASK | \
ISM303DAC_TAP_MASK | \
ISM303DAC_6D_IA_MASK | \
ISM303DAC_FF_IA_MASK)
#define ISM303DAC_FIFO_SRC_ADDR 0x2f
#define ISM303DAC_FIFO_SRC_FTH_MASK 0x80
#define ISM303DAC_EN_BIT 0x01
#define ISM303DAC_DIS_BIT 0x00
#define ISM303DAC_ACCEL_ODR 1
#define ISM303DAC_DEFAULT_ACCEL_FS 2
#define ISM303DAC_FF_ODR 25
#define ISM303DAC_TAP_ODR 400
#define ISM303DAC_WAKEUP_ODR 25
#define ISM303DAC_ACTIVITY_ODR 12
#define ISM303DAC_MAX_FIFO_LENGHT 256
#define ISM303DAC_MAX_FIFO_THS (ISM303DAC_MAX_FIFO_LENGHT - 1)
#define ISM303DAC_MAX_CHANNEL_SPEC 5
#define ISM303DAC_EVENT_CHANNEL_SPEC_SIZE 2
#define ISM303DAC_MIN_DURATION_MS 1638
#define ISM303DAC_DEV_NAME "ism303dac_accel"
#define SET_BIT(a, b) {a |= (1 << b);}
#define RESET_BIT(a, b) {a &= ~(1 << b);}
#define CHECK_BIT(a, b) (a & (1 << b))
enum {
ISM303DAC_ACCEL = 0,
ISM303DAC_TAP,
ISM303DAC_DOUBLE_TAP,
ISM303DAC_SENSORS_NUMB,
};
#define ST_ISM303DAC_FLUSH_CHANNEL(device_type) \
{ \
.type = device_type, \
.modified = 0, \
.scan_index = -1, \
.indexed = -1, \
.event_spec = &ism303dac_fifo_flush_event,\
.num_event_specs = 1, \
}
#define ST_ISM303DAC_HWFIFO_ENABLED() \
IIO_DEVICE_ATTR(hwfifo_enabled, S_IWUSR | S_IRUGO, \
ism303dac_sysfs_get_hwfifo_enabled,\
ism303dac_sysfs_set_hwfifo_enabled, 0);
#define ST_ISM303DAC_HWFIFO_WATERMARK() \
IIO_DEVICE_ATTR(hwfifo_watermark, S_IWUSR | S_IRUGO, \
ism303dac_sysfs_get_hwfifo_watermark,\
ism303dac_sysfs_set_hwfifo_watermark, 0);
#define ST_ISM303DAC_HWFIFO_WATERMARK_MIN() \
IIO_DEVICE_ATTR(hwfifo_watermark_min, S_IRUGO, \
ism303dac_sysfs_get_hwfifo_watermark_min, NULL, 0);
#define ST_ISM303DAC_HWFIFO_WATERMARK_MAX() \
IIO_DEVICE_ATTR(hwfifo_watermark_max, S_IRUGO, \
ism303dac_sysfs_get_hwfifo_watermark_max, NULL, 0);
#define ST_ISM303DAC_HWFIFO_FLUSH() \
IIO_DEVICE_ATTR(hwfifo_flush, S_IWUSR, NULL, \
ism303dac_sysfs_flush_fifo, 0);
enum fifo_mode {
BYPASS = 0,
CONTINUOS,
};
#define ISM303DAC_TX_MAX_LENGTH 12
#define ISM303DAC_RX_MAX_LENGTH 8193
#define ISM303DAC_EWMA_DIV 128
struct ism303dac_transfer_buffer {
struct mutex buf_lock;
u8 rx_buf[ISM303DAC_RX_MAX_LENGTH];
u8 tx_buf[ISM303DAC_TX_MAX_LENGTH] ____cacheline_aligned;
};
struct ism303dac_data;
struct ism303dac_transfer_function {
int (*write)(struct ism303dac_data *cdata, u8 reg_addr, int len,
u8 *data, bool b_lock);
int (*read)(struct ism303dac_data *cdata, u8 reg_addr, int len,
u8 *data, bool b_lock);
};
struct ism303dac_sensor_data {
struct ism303dac_data *cdata;
const char *name;
s64 timestamp;
u8 enabled;
u32 odr;
u32 gain;
u8 sindex;
u8 sample_to_discard;
};
struct ism303dac_data {
const char *name;
u8 drdy_int_pin;
bool spi_3wire;
u8 selftest_status;
u8 hwfifo_enabled;
u8 hwfifo_watermark;
u8 power_mode;
u8 enabled_sensor;
u32 common_odr;
int irq;
s64 timestamp;
s64 accel_deltatime;
s64 sample_timestamp;
u8 *fifo_data;
u16 fifo_size;
u64 samples;
u8 std_level;
struct mutex fifo_lock;
struct device *dev;
struct iio_dev *iio_sensors_dev[ISM303DAC_SENSORS_NUMB];
struct iio_trigger *iio_trig[ISM303DAC_SENSORS_NUMB];
struct mutex regs_lock;
const struct ism303dac_transfer_function *tf;
struct ism303dac_transfer_buffer tb;
};
static inline s64 ism303dac_get_time_ns(struct iio_dev *iio_sensors_dev)
{
return iio_get_time_ns(iio_sensors_dev);
}
int ism303dac_common_probe(struct ism303dac_data *cdata, int irq);
#ifdef CONFIG_PM
int ism303dac_common_suspend(struct ism303dac_data *cdata);
int ism303dac_common_resume(struct ism303dac_data *cdata);
#endif
int ism303dac_allocate_rings(struct ism303dac_data *cdata);
int ism303dac_allocate_triggers(struct ism303dac_data *cdata,
const struct iio_trigger_ops *trigger_ops);
int ism303dac_trig_set_state(struct iio_trigger *trig, bool state);
int ism303dac_read_register(struct ism303dac_data *cdata, u8 reg_addr,
int data_len, u8 *data, bool b_lock);
int ism303dac_update_drdy_irq(struct ism303dac_sensor_data *sdata, bool state);
int ism303dac_set_enable(struct ism303dac_sensor_data *sdata, bool enable);
void ism303dac_common_remove(struct ism303dac_data *cdata, int irq);
void ism303dac_read_xyz(struct ism303dac_data *cdata);
void ism303dac_read_fifo(struct ism303dac_data *cdata, bool check_fifo_len);
void ism303dac_deallocate_rings(struct ism303dac_data *cdata);
void ism303dac_deallocate_triggers(struct ism303dac_data *cdata);
#endif /* __ISM303DAC_H */

View File

@ -0,0 +1,212 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* STMicroelectronics ism303dac driver
*
* Copyright 2018 STMicroelectronics Inc.
*
* Giuseppe Barba <giuseppe.barba@st.com>
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/stat.h>
#include <linux/interrupt.h>
#include <linux/i2c.h>
#include <linux/delay.h>
#include <linux/iio/iio.h>
#include <linux/iio/buffer.h>
#include <linux/iio/trigger_consumer.h>
#include <linux/iio/triggered_buffer.h>
#include "st_ism303dac_accel.h"
#define ISM303DAC_ACCEL_BUFFER_SIZE \
ALIGN(ISM303DAC_FIFO_BYTE_FOR_SAMPLE + ISM303DAC_TIMESTAMP_SIZE, \
ISM303DAC_TIMESTAMP_SIZE)
static void ism303dac_push_accel_data(struct ism303dac_data *cdata,
u8 *acc_buf, u16 read_length)
{
size_t offset;
uint16_t i, j, k;
u8 buffer[ISM303DAC_ACCEL_BUFFER_SIZE], out_buf_index;
struct iio_dev *indio_dev = cdata->iio_sensors_dev[ISM303DAC_ACCEL];
u32 delta_ts = div_s64(cdata->accel_deltatime, cdata->hwfifo_watermark);
for (i = 0; i < read_length; i += ISM303DAC_FIFO_BYTE_FOR_SAMPLE) {
/* Skip first samples. */
if (unlikely(++cdata->samples <= cdata->std_level)) {
cdata->sample_timestamp += delta_ts;
continue;
}
for (j = 0, out_buf_index = 0; j < ISM303DAC_FIFO_NUM_AXIS;
j++) {
k = i + ISM303DAC_FIFO_BYTE_X_AXIS * j;
if (test_bit(j, indio_dev->active_scan_mask)) {
memcpy(&buffer[out_buf_index],
&acc_buf[k],
ISM303DAC_FIFO_BYTE_X_AXIS);
out_buf_index += ISM303DAC_FIFO_BYTE_X_AXIS;
}
}
if (indio_dev->scan_timestamp) {
offset = indio_dev->scan_bytes / sizeof(s64) - 1;
((s64 *)buffer)[offset] = cdata->sample_timestamp;
cdata->sample_timestamp += delta_ts;
}
iio_push_to_buffers(indio_dev, buffer);
}
}
void ism303dac_read_xyz(struct ism303dac_data *cdata)
{
int err;
u8 xyz_buf[ISM303DAC_FIFO_BYTE_FOR_SAMPLE];
err = ism303dac_read_register(cdata, ISM303DAC_OUTX_L_ADDR,
ISM303DAC_FIFO_BYTE_FOR_SAMPLE, xyz_buf, true);
if (err < 0)
return;
cdata->sample_timestamp = cdata->timestamp;
ism303dac_push_accel_data(cdata, xyz_buf, ISM303DAC_FIFO_BYTE_FOR_SAMPLE);
}
void ism303dac_read_fifo(struct ism303dac_data *cdata, bool check_fifo_len)
{
int err;
u8 fifo_src[2];
u16 read_len;
#if (CONFIG_ST_ISM303DAC_ACCEL_IIO_LIMIT_FIFO > 0)
u16 data_remaining, data_to_read, extra_bytes;
#endif /* CONFIG_ST_ISM303DAC_ACCEL_IIO_LIMIT_FIFO */
err = ism303dac_read_register(cdata, ISM303DAC_FIFO_SRC, 2,
fifo_src, true);
if (err < 0)
return;
read_len = (fifo_src[0] & ISM303DAC_FIFO_SRC_DIFF_MASK) ? (1 << 8) : 0;
read_len |= fifo_src[1];
read_len *= ISM303DAC_FIFO_BYTE_FOR_SAMPLE;
if (read_len == 0)
return;
#if (CONFIG_ST_ISM303DAC_ACCEL_IIO_LIMIT_FIFO == 0)
err = ism303dac_read_register(cdata, ISM303DAC_OUTX_L_ADDR, read_len,
cdata->fifo_data, true);
if (err < 0)
return;
#else /* CONFIG_ST_ISM303DAC_ACCEL_IIO_LIMIT_FIFO */
data_remaining = read_len;
do {
if (data_remaining > CONFIG_ST_ISM303DAC_ACCEL_IIO_LIMIT_FIFO)
data_to_read = CONFIG_ST_ISM303DAC_ACCEL_IIO_LIMIT_FIFO;
else
data_to_read = data_remaining;
extra_bytes = (data_to_read % ISM303DAC_FIFO_BYTE_FOR_SAMPLE);
if (extra_bytes != 0) {
data_to_read -= extra_bytes;
if (data_to_read < ISM303DAC_FIFO_BYTE_FOR_SAMPLE)
data_to_read = ISM303DAC_FIFO_BYTE_FOR_SAMPLE;
}
err = ism303dac_read_register(cdata, ISM303DAC_OUTX_L_ADDR, data_to_read,
&cdata->fifo_data[read_len - data_remaining], true);
if (err < 0)
return;
data_remaining -= data_to_read;
} while (data_remaining > 0);
#endif /* CONFIG_ST_ISM303DAC_ACCEL_IIO_LIMIT_FIFO */
ism303dac_push_accel_data(cdata, cdata->fifo_data, read_len);
}
static inline irqreturn_t ism303dac_handler_empty(int irq, void *p)
{
return IRQ_HANDLED;
}
int ism303dac_trig_set_state(struct iio_trigger *trig, bool state)
{
int err;
struct ism303dac_sensor_data *sdata;
sdata = iio_priv(iio_trigger_get_drvdata(trig));
err = ism303dac_update_drdy_irq(sdata, state);
return (err < 0) ? err : 0;
}
static int ism303dac_buffer_preenable(struct iio_dev *indio_dev)
{
int err;
struct ism303dac_sensor_data *sdata = iio_priv(indio_dev);
err = ism303dac_set_enable(sdata, true);
if (err < 0)
return err;
return 0;
}
static int ism303dac_buffer_postdisable(struct iio_dev *indio_dev)
{
int err;
struct ism303dac_sensor_data *sdata = iio_priv(indio_dev);
err = ism303dac_set_enable(sdata, false);
if (err < 0)
return err;
return 0;
}
static const struct iio_buffer_setup_ops ism303dac_buffer_setup_ops = {
.preenable = &ism303dac_buffer_preenable,
.postdisable = &ism303dac_buffer_postdisable,
};
int ism303dac_allocate_rings(struct ism303dac_data *cdata)
{
int err, i;
for (i = 0; i < ISM303DAC_SENSORS_NUMB; i++) {
err = iio_triggered_buffer_setup(
cdata->iio_sensors_dev[i],
&ism303dac_handler_empty,
NULL,
&ism303dac_buffer_setup_ops);
if (err < 0)
goto buffer_cleanup;
}
return 0;
buffer_cleanup:
for (i--; i >= 0; i--)
iio_triggered_buffer_cleanup(cdata->iio_sensors_dev[i]);
return err;
}
void ism303dac_deallocate_rings(struct ism303dac_data *cdata)
{
int i;
for (i = 0; i < ISM303DAC_SENSORS_NUMB; i++)
iio_triggered_buffer_cleanup(cdata->iio_sensors_dev[i]);
}
MODULE_DESCRIPTION("STMicroelectronics ism303dac iio buffer driver");
MODULE_AUTHOR("Giuseppe Barba");
MODULE_LICENSE("GPL v2");

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,171 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* STMicroelectronics ism303dac i2c driver
*
* Copyright 2018 STMicroelectronics Inc.
*
* Giuseppe Barba <giuseppe.barba@st.com>
*/
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/i2c.h>
#include <linux/hrtimer.h>
#include <linux/types.h>
#include "st_ism303dac_accel.h"
static int ism303dac_i2c_read(struct ism303dac_data *cdata, u8 reg_addr, int len,
u8 * data, bool b_lock)
{
int err = 0;
struct i2c_msg msg[2];
struct i2c_client *client = to_i2c_client(cdata->dev);
msg[0].addr = client->addr;
msg[0].flags = client->flags;
msg[0].len = 1;
msg[0].buf = &reg_addr;
msg[1].addr = client->addr;
msg[1].flags = client->flags | I2C_M_RD;
msg[1].len = len;
msg[1].buf = data;
if (b_lock) {
mutex_lock(&cdata->regs_lock);
err = i2c_transfer(client->adapter, msg, 2);
mutex_unlock(&cdata->regs_lock);
} else
err = i2c_transfer(client->adapter, msg, 2);
return err;
}
static int ism303dac_i2c_write(struct ism303dac_data *cdata, u8 reg_addr, int len,
u8 * data, bool b_lock)
{
int err = 0;
u8 send[len + 1];
struct i2c_msg msg;
struct i2c_client *client = to_i2c_client(cdata->dev);
send[0] = reg_addr;
memcpy(&send[1], data, len * sizeof(u8));
len++;
msg.addr = client->addr;
msg.flags = client->flags;
msg.len = len;
msg.buf = send;
if (b_lock) {
mutex_lock(&cdata->regs_lock);
err = i2c_transfer(client->adapter, &msg, 1);
mutex_unlock(&cdata->regs_lock);
} else
err = i2c_transfer(client->adapter, &msg, 1);
return err;
}
static const struct ism303dac_transfer_function ism303dac_tf_i2c = {
.write = ism303dac_i2c_write,
.read = ism303dac_i2c_read,
};
static int ism303dac_i2c_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
int err;
struct ism303dac_data *cdata;
cdata = kzalloc(sizeof(*cdata), GFP_KERNEL);
if (!cdata)
return -ENOMEM;
cdata->dev = &client->dev;
cdata->name = client->name;
cdata->tf = &ism303dac_tf_i2c;
i2c_set_clientdata(client, cdata);
err = ism303dac_common_probe(cdata, client->irq);
if (err < 0)
goto free_data;
return 0;
free_data:
kfree(cdata);
return err;
}
static int ism303dac_i2c_remove(struct i2c_client *client)
{
struct ism303dac_data *cdata = i2c_get_clientdata(client);
ism303dac_common_remove(cdata, client->irq);
dev_info(cdata->dev, "%s: removed\n", ISM303DAC_DEV_NAME);
kfree(cdata);
return 0;
}
#ifdef CONFIG_PM
static int __maybe_unused ism303dac_suspend(struct device *dev)
{
struct ism303dac_data *cdata = i2c_get_clientdata(to_i2c_client(dev));
return ism303dac_common_suspend(cdata);
}
static int __maybe_unused ism303dac_resume(struct device *dev)
{
struct ism303dac_data *cdata = i2c_get_clientdata(to_i2c_client(dev));
return ism303dac_common_resume(cdata);
}
static const struct dev_pm_ops ism303dac_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(ism303dac_suspend, ism303dac_resume)
};
#define ISM303DAC_PM_OPS (&ism303dac_pm_ops)
#else /* CONFIG_PM */
#define ISM303DAC_PM_OPS NULL
#endif /* CONFIG_PM */
static const struct i2c_device_id ism303dac_ids[] = {
{ ISM303DAC_DEV_NAME, 0 },
{}
};
MODULE_DEVICE_TABLE(i2c, ism303dac_ids);
#ifdef CONFIG_OF
static const struct of_device_id ism303dac_id_table[] = {
{.compatible = "st,ism303dac_accel",},
{},
};
MODULE_DEVICE_TABLE(of, ism303dac_id_table);
#endif
static struct i2c_driver ism303dac_i2c_driver = {
.driver = {
.owner = THIS_MODULE,
.name = ISM303DAC_DEV_NAME,
.pm = ISM303DAC_PM_OPS,
#ifdef CONFIG_OF
.of_match_table = ism303dac_id_table,
#endif
},
.probe = ism303dac_i2c_probe,
.remove = ism303dac_i2c_remove,
.id_table = ism303dac_ids,
};
module_i2c_driver(ism303dac_i2c_driver);
MODULE_DESCRIPTION("STMicroelectronics ism303dac i2c driver");
MODULE_AUTHOR("Giuseppe Barba");
MODULE_LICENSE("GPL v2");

View File

@ -0,0 +1,193 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* STMicroelectronics ism303dac spi driver
*
* Copyright 2018 STMicroelectronics Inc.
*
* Armando Visconti <armando.visconti@st.com>
* Giuseppe Barba <giuseppe.barba@st.com>
*/
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/spi/spi.h>
#include <linux/types.h>
#include "st_ism303dac_accel.h"
#define ST_SENSORS_SPI_READ 0x80
static int ism303dac_spi_read(struct ism303dac_data *cdata,
u8 reg_addr, int len, u8 *data, bool b_lock)
{
int err;
struct spi_transfer xfers[] = {
{
.tx_buf = cdata->tb.tx_buf,
.bits_per_word = 8,
.len = 1,
},
{
.rx_buf = cdata->tb.rx_buf,
.bits_per_word = 8,
.len = len,
}
};
if (b_lock)
mutex_lock(&cdata->regs_lock);
mutex_lock(&cdata->tb.buf_lock);
cdata->tb.tx_buf[0] = reg_addr | ST_SENSORS_SPI_READ;
err = spi_sync_transfer(to_spi_device(cdata->dev),
xfers, ARRAY_SIZE(xfers));
if (err)
goto acc_spi_read_error;
memcpy(data, cdata->tb.rx_buf, len*sizeof(u8));
mutex_unlock(&cdata->tb.buf_lock);
if (b_lock)
mutex_unlock(&cdata->regs_lock);
return len;
acc_spi_read_error:
mutex_unlock(&cdata->tb.buf_lock);
if (b_lock)
mutex_unlock(&cdata->regs_lock);
return err;
}
static int ism303dac_spi_write(struct ism303dac_data *cdata,
u8 reg_addr, int len, u8 *data, bool b_lock)
{
int err;
struct spi_transfer xfers = {
.tx_buf = cdata->tb.tx_buf,
.bits_per_word = 8,
.len = len + 1,
};
if (len >= ISM303DAC_RX_MAX_LENGTH)
return -ENOMEM;
if (b_lock)
mutex_lock(&cdata->regs_lock);
mutex_lock(&cdata->tb.buf_lock);
cdata->tb.tx_buf[0] = reg_addr;
memcpy(&cdata->tb.tx_buf[1], data, len);
err = spi_sync_transfer(to_spi_device(cdata->dev), &xfers, 1);
mutex_unlock(&cdata->tb.buf_lock);
if (b_lock)
mutex_unlock(&cdata->regs_lock);
return err;
}
static const struct ism303dac_transfer_function ism303dac_tf_spi = {
.write = ism303dac_spi_write,
.read = ism303dac_spi_read,
};
static int ism303dac_spi_probe(struct spi_device *spi)
{
int err;
struct ism303dac_data *cdata;
cdata = kzalloc(sizeof(*cdata), GFP_KERNEL);
if (!cdata)
return -ENOMEM;
cdata->dev = &spi->dev;
cdata->name = spi->modalias;
cdata->tf = &ism303dac_tf_spi;
spi_set_drvdata(spi, cdata);
err = ism303dac_common_probe(cdata, spi->irq);
if (err < 0)
goto free_data;
return 0;
free_data:
kfree(cdata);
return err;
}
static int ism303dac_spi_remove(struct spi_device *spi)
{
struct ism303dac_data *cdata = spi_get_drvdata(spi);
ism303dac_common_remove(cdata, spi->irq);
dev_info(cdata->dev, "%s: removed\n", ISM303DAC_DEV_NAME);
kfree(cdata);
return 0;
}
#ifdef CONFIG_PM
static int __maybe_unused ism303dac_suspend(struct device *dev)
{
struct ism303dac_data *cdata = spi_get_drvdata(to_spi_device(dev));
return ism303dac_common_suspend(cdata);
}
static int __maybe_unused ism303dac_resume(struct device *dev)
{
struct ism303dac_data *cdata = spi_get_drvdata(to_spi_device(dev));
return ism303dac_common_resume(cdata);
}
static const struct dev_pm_ops ism303dac_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(ism303dac_suspend, ism303dac_resume)
};
#define ISM303DAC_PM_OPS (&ism303dac_pm_ops)
#else /* CONFIG_PM */
#define ISM303DAC_PM_OPS NULL
#endif /* CONFIG_PM */
static const struct spi_device_id ism303dac_ids[] = {
{ ISM303DAC_DEV_NAME, 0 },
{}
};
MODULE_DEVICE_TABLE(spi, ism303dac_ids);
#ifdef CONFIG_OF
static const struct of_device_id ism303dac_id_table[] = {
{.compatible = "st,ism303dac_accel",},
{},
};
MODULE_DEVICE_TABLE(of, ism303dac_id_table);
#endif /* CONFIG_OF */
static struct spi_driver ism303dac_spi_driver = {
.driver = {
.owner = THIS_MODULE,
.name = ISM303DAC_DEV_NAME,
.pm = ISM303DAC_PM_OPS,
#ifdef CONFIG_OF
.of_match_table = ism303dac_id_table,
#endif /* CONFIG_OF */
},
.probe = ism303dac_spi_probe,
.remove = ism303dac_spi_remove,
.id_table = ism303dac_ids,
};
module_spi_driver(ism303dac_spi_driver);
MODULE_DESCRIPTION("STMicroelectronics ism303dac spi driver");
MODULE_AUTHOR("Armando Visconti <armando.visconti@st.com>");
MODULE_LICENSE("GPL v2");

View File

@ -0,0 +1,162 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* STMicroelectronics ism303dac driver
*
* Copyright 2018 STMicroelectronics Inc.
*
* Giuseppe Barba <giuseppe.barba@st.com>
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/iio/iio.h>
#include <linux/iio/trigger.h>
#include <linux/interrupt.h>
#include <linux/iio/events.h>
#include "st_ism303dac_accel.h"
static void ism303dac_event_management(struct ism303dac_data *cdata,
u8 int_reg_val)
{
u8 status;
/* Must read TAP_SRC to remove irq bits */
cdata->tf->read(cdata, ISM303DAC_TAP_SRC_ADDR, 1, &status, true);
if (CHECK_BIT(cdata->enabled_sensor, ISM303DAC_TAP) &&
(int_reg_val & ISM303DAC_TAP_MASK))
iio_push_event(cdata->iio_sensors_dev[ISM303DAC_TAP],
IIO_UNMOD_EVENT_CODE(IIO_TAP, 0,
IIO_EV_TYPE_THRESH,
IIO_EV_DIR_EITHER),
cdata->timestamp);
if (CHECK_BIT(cdata->enabled_sensor, ISM303DAC_DOUBLE_TAP) &&
(int_reg_val & ISM303DAC_DOUBLE_TAP_MASK))
iio_push_event(cdata->iio_sensors_dev[ISM303DAC_DOUBLE_TAP],
IIO_UNMOD_EVENT_CODE(IIO_TAP_TAP, 0,
IIO_EV_TYPE_THRESH,
IIO_EV_DIR_EITHER),
cdata->timestamp);
}
static inline s64 st_ism303dac_ewma(s64 old, s64 new, int weight)
{
s64 diff, incr;
diff = new - old;
incr = div_s64((ISM303DAC_EWMA_DIV - weight) *
diff, ISM303DAC_EWMA_DIV);
return old + incr;
}
static irqreturn_t ism303dac_irq_handler(int irq, void *private)
{
u8 ewma_level;
struct ism303dac_data *cdata = private;
s64 ts;
ewma_level = (cdata->common_odr >= 100) ? 120 : 96;
ts = ism303dac_get_time_ns(cdata->iio_sensors_dev[ISM303DAC_ACCEL]);
cdata->accel_deltatime = st_ism303dac_ewma(cdata->accel_deltatime,
ts - cdata->timestamp,
ewma_level);
cdata->timestamp = ts;
return IRQ_WAKE_THREAD;
}
static irqreturn_t ism303dac_irq_thread(int irq, void *private)
{
u8 status;
struct ism303dac_data *cdata = private;
if (CHECK_BIT(cdata->enabled_sensor, ISM303DAC_ACCEL)) {
if (cdata->hwfifo_enabled) {
mutex_lock(&cdata->fifo_lock);
ism303dac_read_fifo(cdata, true);
mutex_unlock(&cdata->fifo_lock);
} else {
cdata->tf->read(cdata, ISM303DAC_STATUS_DUP_ADDR, 1, &status, true);
if (status & (ISM303DAC_DRDY_MASK))
ism303dac_read_xyz(cdata);
}
}
if (cdata->enabled_sensor & ~(1 << ISM303DAC_ACCEL)) {
cdata->tf->read(cdata, ISM303DAC_STATUS_DUP_ADDR, 1, &status, true);
if (status & ISM303DAC_EVENT_MASK)
ism303dac_event_management(cdata, status);
}
return IRQ_HANDLED;
}
int ism303dac_allocate_triggers(struct ism303dac_data *cdata,
const struct iio_trigger_ops *trigger_ops)
{
int err, i, n;
for (i = 0; i < ISM303DAC_SENSORS_NUMB; i++) {
cdata->iio_trig[i] = iio_trigger_alloc("%s-trigger",
cdata->iio_sensors_dev[i]->name);
if (!cdata->iio_trig[i]) {
dev_err(cdata->dev, "failed to allocate iio trigger.\n");
err = -ENOMEM;
goto deallocate_trigger;
}
iio_trigger_set_drvdata(cdata->iio_trig[i],
cdata->iio_sensors_dev[i]);
cdata->iio_trig[i]->ops = trigger_ops;
cdata->iio_trig[i]->dev.parent = cdata->dev;
}
err = request_threaded_irq(cdata->irq,
ism303dac_irq_handler,
ism303dac_irq_thread,
IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
cdata->name, cdata);
if (err)
goto deallocate_trigger;
for (n = 0; n < ISM303DAC_SENSORS_NUMB; n++) {
err = iio_trigger_register(cdata->iio_trig[n]);
if (err < 0) {
dev_err(cdata->dev, "failed to register iio trigger.\n");
goto free_irq;
}
cdata->iio_sensors_dev[n]->trig = cdata->iio_trig[n];
}
return 0;
free_irq:
free_irq(cdata->irq, cdata);
for (n--; n >= 0; n--)
iio_trigger_unregister(cdata->iio_trig[n]);
deallocate_trigger:
for (i--; i >= 0; i--)
iio_trigger_free(cdata->iio_trig[i]);
return err;
}
void ism303dac_deallocate_triggers(struct ism303dac_data *cdata)
{
int i;
free_irq(cdata->irq, cdata);
for (i = 0; i < ISM303DAC_SENSORS_NUMB; i++)
iio_trigger_unregister(cdata->iio_trig[i]);
}
MODULE_DESCRIPTION("STMicroelectronics ism303dac iio trigger driver");
MODULE_AUTHOR("Giuseppe Barba");
MODULE_LICENSE("GPL v2");

View File

@ -0,0 +1,4 @@
CONFIG_IIO_ST_ISM303DAC_ACCEL=m
CONFIG_IIO_ST_ISM303DAC_ACCEL_I2C=m
CONFIG_IIO_ST_ISM303DAC_ACCEL_SPI=m
CONFIG_ST_ISM303DAC_ACCEL_IIO_LIMIT_FIFO=0