drivers:iio:stm:accel:Add support to LIS2HH12 STMEMS accel

Added support to LIS2HH12 STMEMS accel.
This device was missing from the list of supported devices for
kernel 5.4 despite being supported in older version kernels.
This commit aligns device supported to previous kernel versions.

Signed-off-by: Mario Tesi <mario.tesi@st.com>
Change-Id: I3508361e425a74a8f3fa6073feeae82b5c0dadaf
Reviewed-on: https://sczcxd1104.scz.st.com/gerrit/c/linux/stm-ldd-iio/+/629
Tested-by: CI STM MSD <aosp-ger@st.com>
Reviewed-by: Denis CIOCCA <denis.ciocca@st.com>
This commit is contained in:
Mario Tesi 2022-04-27 15:06:14 +02:00 committed by Denis CIOCCA
parent 89831e476b
commit e2adc33a17
10 changed files with 1808 additions and 0 deletions

View File

@ -0,0 +1,34 @@
* lis2hh12 driver for accel MEMS sensors
Required properties for all bus drivers:
- compatible: must be one of:
"st,lis2hh12"
Required properties for the i2c bindings:
- reg: i2c slave address
Required properties for the spi bindings:
- reg: the chipselect index
- spi-max-frequency: maximal bus speed, should be set to 1000000 unless
constrained by external circuitry
Optional properties for all bus drivers:
- st,drdy-int-pin: the pin on the package that will be used to signal
"data ready" (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.
Example for an spi device node:
lis2hh12-accel@0 {
compatible = "st,lis2hh12";
reg = <0x0>;
spi-max-frequency = <1000000>;
interrupt-parent = <&gpio0>;
interrupts = <0 IRQ_TYPE_LEVEL_HIGH>;
st,drdy-int-pin = <1>;
};

View File

@ -140,4 +140,23 @@ config IIO_ST_LIS3DHH
This driver can also be built as a module. If so, will be named
st_lis3dhh
config IIO_ST_LIS2HH12
tristate "STMicroelectronics LIS2HH12 Accelerometer driver"
depends on (I2C || SPI_MASTER) && SYSFS
select IIO_BUFFER
select IIO_TRIGGERED_BUFFER
select IIO_ST_LIS2HH12_I2C if (I2C)
select IIO_ST_LIS2HH12_SPI if (SPI)
help
Say yes here to build support for the LIS2HH12 accelerometer.
config IIO_ST_LIS2HH12_I2C
tristate
depends on IIO_ST_LIS2HH12
depends on I2C
config IIO_ST_LIS2HH12_SPI
tristate
depends on IIO_ST_LIS2HH12
endmenu

View File

@ -28,3 +28,9 @@ obj-$(CONFIG_IIO_ST_ISM303DAC_ACCEL_SPI) += st_ism303dac_accel_spi.o
st-lis3dhh-y := st_lis3dhh.o st_lis3dhh_buffer.o
obj-$(CONFIG_IIO_ST_LIS3DHH) += st-lis3dhh.o
lis2hh12-y += st_lis2hh12_core.o st_lis2hh12_buffer.o st_lis2hh12_trigger.o
obj-$(CONFIG_IIO_ST_LIS2HH12) += lis2hh12.o
obj-$(CONFIG_IIO_ST_LIS2HH12_I2C) += st_lis2hh12_i2c.o
obj-$(CONFIG_IIO_ST_LIS2HH12_SPI) += st_lis2hh12_spi.o

View File

@ -0,0 +1,215 @@
/*
* STMicroelectronics lis2hh12 driver
*
* Copyright 2016 STMicroelectronics Inc.
*
* Armando Visconti <armando.visconti@st.com>
*
* Licensed under the GPL-2.
*/
#ifndef __LIS2HH12_H
#define __LIS2HH12_H
#include <linux/types.h>
#include <linux/iio/iio.h>
#include <linux/iio/trigger.h>
#define LIS2HH12_WHO_AM_I_ADDR 0x0f
#define LIS2HH12_WHO_AM_I_DEF 0x41
#define LIS2HH12_CTRL1_ADDR 0x20
#define LIS2HH12_CTRL2_ADDR 0x21
#define LIS2HH12_CTRL3_ADDR 0x22
#define LIS2HH12_CTRL4_ADDR 0x23
#define LIS2HH12_CTRL5_ADDR 0x24
#define LIS2HH12_CTRL6_ADDR 0x25
#define LIS2HH12_CTRL7_ADDR 0x26
#define LIS2HH12_STATUS_ADDR 0x27
#define LIS2HH12_DATA_XYZ_RDY 0x08
#define LIS2HH12_FIFO_CTRL_ADDR 0x2E
#define LIS2HH12_OUTX_L_ADDR 0x28
#define LIS2HH12_OUTY_L_ADDR 0x2A
#define LIS2HH12_OUTZ_L_ADDR 0x2C
#define LIS2HH12_FIFO_THS_ADDR LIS2HH12_FIFO_CTRL_ADDR
#define LIS2HH12_FIFO_THS_MASK 0x1f
#define LIS2HH12_FIFO_MODE_ADDR LIS2HH12_FIFO_CTRL_ADDR
#define LIS2HH12_FIFO_MODE_MASK 0xe0
#define LIS2HH12_FIFO_MODE_BYPASS 0x00
#define LIS2HH12_FIFO_MODE_STREAM 0x02
#define LIS2HH12_FIFO_SRC_ADDR 0x2F
#define LIS2HH12_FIFO_STATUS_ADDR LIS2HH12_FIFO_SRC_ADDR
#define LIS2HH12_FIFO_FSS_MASK 0x1F
#define LIS2HH12_FIFO_SRC_FTH_MASK 0x80
#define LIS2HH12_ODR_ADDR LIS2HH12_CTRL1_ADDR
#define LIS2HH12_ODR_MASK 0x70
#define LIS2HH12_ODR_POWER_DOWN_VAL 0x00
#define LIS2HH12_ODR_10HZ_VAL 0x01
#define LIS2HH12_ODR_50HZ_VAL 0x02
#define LIS2HH12_ODR_100HZ_VAL 0x03
#define LIS2HH12_ODR_200HZ_VAL 0x04
#define LIS2HH12_ODR_400HZ_VAL 0x05
#define LIS2HH12_ODR_800HZ_VAL 0x06
#define LIS2HH12_ODR_LIST_NUM 7
#define LIS2HH12_FS_ADDR LIS2HH12_CTRL4_ADDR
#define LIS2HH12_FS_MASK 0x30
#define LIS2HH12_FS_2G_VAL 0x00
#define LIS2HH12_FS_4G_VAL 0x02
#define LIS2HH12_FS_8G_VAL 0x03
#define LIS2HH12_FS_LIST_NUM 3
#define LIS2HH12_FS_2G_GAIN IIO_G_TO_M_S_2(61)
#define LIS2HH12_FS_4G_GAIN IIO_G_TO_M_S_2(122)
#define LIS2HH12_FS_8G_GAIN IIO_G_TO_M_S_2(244)
#define LIS2HH12_INT_CFG_ADDR LIS2HH12_CTRL3_ADDR
#define LIS2HH12_INT_DRDY_MASK 0x01
#define LIS2HH12_INT_FTH_MASK 0x02
#define LIS2HH12_INT_FOVR_MASK 0x04
#define LIS2HH12_FIFO_EN_ADDR LIS2HH12_CTRL3_ADDR
#define LIS2HH12_FIFO_EN_MASK 0x80
#define LIS2HH12_BDU_ADDR LIS2HH12_CTRL1_ADDR
#define LIS2HH12_BDU_MASK 0x08
#define LIS2HH12_SOFT_RESET_ADDR LIS2HH12_CTRL5_ADDR
#define LIS2HH12_SOFT_RESET_MASK 0x40
#define LIS2HH12_LIR_ADDR LIS2HH12_CTRL7_ADDR
#define LIS2HH12_LIR1_MASK 0x04
#define LIS2HH12_LIR2_MASK 0x08
#define LIS2HH12_MAX_FIFO_LENGHT 32
#define LIS2HH12_MAX_FIFO_THS (LIS2HH12_MAX_FIFO_LENGHT - 1)
#define LIS2HH12_FIFO_NUM_AXIS 3
#define LIS2HH12_FIFO_BYTE_X_AXIS 2
#define LIS2HH12_FIFO_BYTE_FOR_SAMPLE (LIS2HH12_FIFO_NUM_AXIS * \
LIS2HH12_FIFO_BYTE_X_AXIS)
#define LIS2HH12_TIMESTAMP_SIZE 8
#define LIS2HH12_EN_BIT 0x01
#define LIS2HH12_DIS_BIT 0x00
#define LIS2HH12_MAX_CHANNEL_SPEC 5
#define LIS2HH12_ACCEL 0
#define LIS2HH12_SENSORS_NUMB 1
#define LIS2HH12_DEV_NAME "lis2hh12"
#define SET_BIT(a, b) {a |= (1 << b);}
#define RESET_BIT(a, b) {a &= ~(1 << b);}
#define CHECK_BIT(a, b) (a & (1 << b))
#define ST_LIS2HH12_FLUSH_CHANNEL(device_type) \
{ \
.type = device_type, \
.modified = 0, \
.scan_index = -1, \
.indexed = -1, \
.event_spec = &lis2hh12_fifo_flush_event,\
.num_event_specs = 1, \
}
#define ST_LIS2HH12_HWFIFO_ENABLED() \
IIO_DEVICE_ATTR(hwfifo_enabled, S_IWUSR | S_IRUGO, \
lis2hh12_sysfs_get_hwfifo_enabled,\
lis2hh12_sysfs_set_hwfifo_enabled, 0);
#define ST_LIS2HH12_HWFIFO_WATERMARK() \
IIO_DEVICE_ATTR(hwfifo_watermark, S_IWUSR | S_IRUGO, \
lis2hh12_sysfs_get_hwfifo_watermark,\
lis2hh12_sysfs_set_hwfifo_watermark, 0);
#define ST_LIS2HH12_HWFIFO_WATERMARK_MIN() \
IIO_DEVICE_ATTR(hwfifo_watermark_min, S_IRUGO, \
lis2hh12_sysfs_get_hwfifo_watermark_min, NULL, 0);
#define ST_LIS2HH12_HWFIFO_WATERMARK_MAX() \
IIO_DEVICE_ATTR(hwfifo_watermark_max, S_IRUGO, \
lis2hh12_sysfs_get_hwfifo_watermark_max, NULL, 0);
#define ST_LIS2HH12_HWFIFO_FLUSH() \
IIO_DEVICE_ATTR(hwfifo_flush, S_IWUSR, NULL, \
lis2hh12_sysfs_flush_fifo, 0);
enum fifo_mode {
BYPASS = 0,
STREAM,
};
#define LIS2HH12_TX_MAX_LENGTH 12
#define LIS2HH12_RX_MAX_LENGTH 8193
struct lis2hh12_transfer_buffer {
struct mutex buf_lock;
u8 rx_buf[LIS2HH12_RX_MAX_LENGTH];
u8 tx_buf[LIS2HH12_TX_MAX_LENGTH] ____cacheline_aligned;
};
struct lis2hh12_data;
struct lis2hh12_transfer_function {
int (*write)(struct lis2hh12_data *cdata, u8 reg_addr, int len, u8 *data);
int (*read)(struct lis2hh12_data *cdata, u8 reg_addr, int len, u8 *data);
};
struct lis2hh12_sensor_data {
struct lis2hh12_data *cdata;
const char *name;
s64 timestamp;
u8 enabled;
u32 odr;
u32 gain;
u8 sindex;
u8 sample_to_discard;
};
struct lis2hh12_data {
const char *name;
u8 drdy_int_pin;
u8 enabled_sensor;
u8 hwfifo_enabled;
u8 hwfifo_watermark;
u32 common_odr;
int irq;
s64 timestamp;
s64 sensor_deltatime;
s64 sensor_timestamp;
u8 *fifo_data;
u16 fifo_size;
struct device *dev;
struct iio_dev *iio_sensors_dev[LIS2HH12_SENSORS_NUMB];
struct iio_trigger *iio_trig[LIS2HH12_SENSORS_NUMB];
const struct lis2hh12_transfer_function *tf;
struct lis2hh12_transfer_buffer tb;
};
int lis2hh12_common_probe(struct lis2hh12_data *cdata, int irq);
#ifdef CONFIG_PM
int lis2hh12_common_suspend(struct lis2hh12_data *cdata);
int lis2hh12_common_resume(struct lis2hh12_data *cdata);
#endif
int lis2hh12_allocate_rings(struct lis2hh12_data *cdata);
int lis2hh12_allocate_triggers(struct lis2hh12_data *cdata,
const struct iio_trigger_ops *trigger_ops);
int lis2hh12_trig_set_state(struct iio_trigger *trig, bool state);
int lis2hh12_read_register(struct lis2hh12_data *cdata, u8 reg_addr, int data_len,
u8 *data);
int lis2hh12_update_drdy_irq(struct lis2hh12_sensor_data *sdata, bool state);
int lis2hh12_set_enable(struct lis2hh12_sensor_data *sdata, bool enable);
int lis2hh12_update_fifo_ths(struct lis2hh12_data *cdata, u8 fifo_len);
void lis2hh12_common_remove(struct lis2hh12_data *cdata, int irq);
void lis2hh12_read_fifo(struct lis2hh12_data *cdata, bool check_fifo_len);
void lis2hh12_deallocate_rings(struct lis2hh12_data *cdata);
void lis2hh12_deallocate_triggers(struct lis2hh12_data *cdata);
int lis2hh12_set_fifo_mode(struct lis2hh12_data *cdata, enum fifo_mode fm);
void lis2hh12_read_xyz(struct lis2hh12_data *cdata);
#endif /* __LIS2HH12_H */

View File

@ -0,0 +1,174 @@
/*
* STMicroelectronics lis2hh12 driver
*
* Copyright 2016 STMicroelectronics Inc.
*
* Armando Visconti <armando.visconti@st.com>
*
* Licensed under the GPL-2.
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/stat.h>
#include <linux/interrupt.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_lis2hh12.h"
#define LIS2HH12_ACCEL_BUFFER_SIZE \
ALIGN(LIS2HH12_FIFO_BYTE_FOR_SAMPLE + LIS2HH12_TIMESTAMP_SIZE, \
LIS2HH12_TIMESTAMP_SIZE)
static void lis2hh12_push_fifo_data(struct lis2hh12_data *cdata, u16 fifo_ptr)
{
size_t offset;
u8 buffer[LIS2HH12_ACCEL_BUFFER_SIZE], out_buf_index;
struct iio_dev *indio_dev = cdata->iio_sensors_dev[LIS2HH12_ACCEL];
struct lis2hh12_sensor_data *sdata;
out_buf_index = 0;
/* Accelerometer data */
sdata = iio_priv(indio_dev);
if (sdata->enabled) {
if (indio_dev->active_scan_mask &&
test_bit(0, indio_dev->active_scan_mask)) {
memcpy(&buffer[out_buf_index], &cdata->fifo_data[fifo_ptr],
LIS2HH12_FIFO_BYTE_FOR_SAMPLE);
out_buf_index += LIS2HH12_FIFO_BYTE_FOR_SAMPLE;
}
if (indio_dev->scan_timestamp) {
offset = indio_dev->scan_bytes / sizeof(s64) - 1;
((s64 *)buffer)[offset] = cdata->sensor_timestamp;
}
iio_push_to_buffers(indio_dev, buffer);
}
}
void lis2hh12_read_xyz(struct lis2hh12_data *cdata)
{
int err;
err = lis2hh12_read_register(cdata, LIS2HH12_OUTX_L_ADDR,
LIS2HH12_FIFO_BYTE_FOR_SAMPLE, cdata->fifo_data);
if (err < 0)
return;
cdata->sensor_timestamp = cdata->timestamp;
lis2hh12_push_fifo_data(cdata, 0);
}
void lis2hh12_read_fifo(struct lis2hh12_data *cdata, bool check_fifo_len)
{
int err;
u8 fifo_src;
u16 read_len = cdata->fifo_size;
uint16_t i;
if (!cdata->fifo_data)
return;
if (check_fifo_len) {
err = lis2hh12_read_register(cdata, LIS2HH12_FIFO_STATUS_ADDR, 1, &fifo_src);
if (err < 0)
return;
read_len = (fifo_src & LIS2HH12_FIFO_FSS_MASK);
read_len *= LIS2HH12_FIFO_BYTE_FOR_SAMPLE;
if (read_len > cdata->fifo_size)
read_len = cdata->fifo_size;
}
err = lis2hh12_read_register(cdata, LIS2HH12_OUTX_L_ADDR, read_len,
cdata->fifo_data);
if (err < 0)
return;
for (i = 0; i < read_len; i += LIS2HH12_FIFO_BYTE_FOR_SAMPLE) {
lis2hh12_push_fifo_data(cdata, i);
cdata->sensor_timestamp += cdata->sensor_deltatime;
}
}
static inline irqreturn_t lis2hh12_handler_empty(int irq, void *p)
{
return IRQ_HANDLED;
}
int lis2hh12_trig_set_state(struct iio_trigger *trig, bool state)
{
return 0;
}
static int lis2hh12_buffer_preenable(struct iio_dev *indio_dev)
{
int err;
struct lis2hh12_sensor_data *sdata = iio_priv(indio_dev);
err = lis2hh12_set_enable(sdata, true);
if (err < 0)
return err;
return 0;
}
static int lis2hh12_buffer_postdisable(struct iio_dev *indio_dev)
{
int err;
struct lis2hh12_sensor_data *sdata = iio_priv(indio_dev);
err = lis2hh12_set_enable(sdata, false);
if (err < 0)
return err;
return 0;
}
static const struct iio_buffer_setup_ops lis2hh12_buffer_setup_ops = {
.preenable = &lis2hh12_buffer_preenable,
.postdisable = &lis2hh12_buffer_postdisable,
};
int lis2hh12_allocate_rings(struct lis2hh12_data *cdata)
{
int err, i;
for (i = 0; i < LIS2HH12_SENSORS_NUMB; i++) {
err = iio_triggered_buffer_setup(
cdata->iio_sensors_dev[i],
&lis2hh12_handler_empty,
NULL,
&lis2hh12_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 lis2hh12_deallocate_rings(struct lis2hh12_data *cdata)
{
int i;
for (i = 0; i < LIS2HH12_SENSORS_NUMB; i++)
iio_triggered_buffer_cleanup(cdata->iio_sensors_dev[i]);
}
MODULE_DESCRIPTION("STMicroelectronics lis2hh12 driver");
MODULE_AUTHOR("Armando Visconti <armando.visconti@st.com>");
MODULE_LICENSE("GPL v2");

View File

@ -0,0 +1,911 @@
/*
* STMicroelectronics lis2hh12 driver
*
* Copyright 2016 STMicroelectronics Inc.
*
* Armando Visconti <armando.visconti@st.com>
*
* Licensed under the GPL-2.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/errno.h>
#include <linux/types.h>
#include <linux/mutex.h>
#include <linux/interrupt.h>
#include <linux/gpio.h>
#include <linux/irq.h>
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
#include <linux/iio/trigger.h>
#include <linux/delay.h>
#include <linux/iio/buffer.h>
#include <linux/iio/events.h>
#include <asm/unaligned.h>
#include "st_lis2hh12.h"
#ifndef CONFIG_OF
#include <linux/platform_data/st_lis2hh12.h>
#endif
#define ST_LIS2HH12_DEV_ATTR_SAMP_FREQ() \
IIO_DEV_ATTR_SAMP_FREQ(S_IWUSR | S_IRUGO, \
lis2hh12_sysfs_get_sampling_frequency, \
lis2hh12_sysfs_set_sampling_frequency)
#define ST_LIS2HH12_DEV_ATTR_SAMP_FREQ_AVAIL() \
IIO_DEV_ATTR_SAMP_FREQ_AVAIL( \
lis2hh12_sysfs_sampling_frequency_avail)
#define ST_LIS2HH12_DEV_ATTR_SCALE_AVAIL(name) \
IIO_DEVICE_ATTR(name, S_IRUGO, \
lis2hh12_sysfs_scale_avail, NULL , 0);
#define LIS2HH12_ADD_CHANNEL(device_type, modif, index, mod, endian, sbits,\
rbits, addr, s) \
{ \
.type = device_type, \
.modified = modif, \
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
BIT(IIO_CHAN_INFO_SCALE), \
.scan_index = index, \
.channel2 = mod, \
.address = addr, \
.scan_type = { \
.sign = s, \
.realbits = rbits, \
.shift = sbits - rbits, \
.storagebits = sbits, \
.endianness = endian, \
}, \
}
struct lis2hh12_odr_reg {
u32 hz;
u8 value;
};
static const struct lis2hh12_odr_table_t {
u8 addr;
u8 mask;
struct lis2hh12_odr_reg odr_avl[LIS2HH12_ODR_LIST_NUM];
} lis2hh12_odr_table = {
.addr = LIS2HH12_ODR_ADDR,
.mask = LIS2HH12_ODR_MASK,
.odr_avl[0] = {.hz = 0,.value = LIS2HH12_ODR_POWER_DOWN_VAL,},
.odr_avl[1] = {.hz = 10,.value = LIS2HH12_ODR_10HZ_VAL,},
.odr_avl[2] = {.hz = 50,.value = LIS2HH12_ODR_50HZ_VAL,},
.odr_avl[3] = {.hz = 100,.value = LIS2HH12_ODR_100HZ_VAL,},
.odr_avl[4] = {.hz = 200,.value = LIS2HH12_ODR_200HZ_VAL,},
.odr_avl[5] = {.hz = 400,.value = LIS2HH12_ODR_400HZ_VAL,},
.odr_avl[6] = {.hz = 800,.value = LIS2HH12_ODR_800HZ_VAL,},
};
struct lis2hh12_fs_reg {
unsigned int gain;
u8 value;
};
static struct lis2hh12_fs_table {
u8 addr;
u8 mask;
struct lis2hh12_fs_reg fs_avl[LIS2HH12_FS_LIST_NUM];
} lis2hh12_fs_table = {
.addr = LIS2HH12_FS_ADDR,
.mask = LIS2HH12_FS_MASK,
.fs_avl[0] = {
.gain = LIS2HH12_FS_2G_GAIN,
.value = LIS2HH12_FS_2G_VAL,
},
.fs_avl[1] = {
.gain = LIS2HH12_FS_4G_GAIN,
.value = LIS2HH12_FS_4G_VAL,
},
.fs_avl[2] = {
.gain = LIS2HH12_FS_8G_GAIN,
.value = LIS2HH12_FS_8G_VAL,
},
};
const struct iio_event_spec lis2hh12_fifo_flush_event = {
.type = IIO_EV_TYPE_FIFO_FLUSH,
.dir = IIO_EV_DIR_EITHER,
};
static const struct lis2hh12_sensors_table {
const char *name;
const char *description;
const u32 min_odr_hz;
const u8 iio_channel_size;
const struct iio_chan_spec iio_channel[LIS2HH12_MAX_CHANNEL_SPEC];
} lis2hh12_sensors_table[LIS2HH12_SENSORS_NUMB] = {
[LIS2HH12_ACCEL] = {
.name = "accel",
.description = "ST LIS2HH12 Accelerometer Sensor",
.min_odr_hz = 10,
.iio_channel = {
LIS2HH12_ADD_CHANNEL(IIO_ACCEL, 1, 0, IIO_MOD_X, IIO_LE,
16, 16, LIS2HH12_OUTX_L_ADDR, 's'),
LIS2HH12_ADD_CHANNEL(IIO_ACCEL, 1, 1, IIO_MOD_Y, IIO_LE,
16, 16, LIS2HH12_OUTY_L_ADDR, 's'),
LIS2HH12_ADD_CHANNEL(IIO_ACCEL, 1, 2, IIO_MOD_Z, IIO_LE,
16, 16, LIS2HH12_OUTZ_L_ADDR, 's'),
ST_LIS2HH12_FLUSH_CHANNEL(IIO_ACCEL),
IIO_CHAN_SOFT_TIMESTAMP(3)
},
.iio_channel_size = LIS2HH12_MAX_CHANNEL_SPEC,
},
};
inline int lis2hh12_read_register(struct lis2hh12_data *cdata, u8 reg_addr, int data_len,
u8 *data)
{
return cdata->tf->read(cdata, reg_addr, data_len, data);
}
static int lis2hh12_write_register(struct lis2hh12_data *cdata, u8 reg_addr,
u8 mask, u8 data)
{
int err;
u8 new_data = 0x00, old_data = 0x00;
err = lis2hh12_read_register(cdata, reg_addr, 1, &old_data);
if (err < 0)
return err;
new_data = ((old_data & (~mask)) | ((data << __ffs(mask)) & mask));
if (new_data == old_data)
return 1;
return cdata->tf->write(cdata, reg_addr, 1, &new_data);
}
int lis2hh12_set_fifo_mode(struct lis2hh12_data *cdata, enum fifo_mode fm)
{
int err;
u8 reg_value;
u8 set_bit = LIS2HH12_DIS_BIT;
switch (fm) {
case BYPASS:
reg_value = LIS2HH12_FIFO_MODE_BYPASS;
set_bit = LIS2HH12_DIS_BIT;
break;
case STREAM:
reg_value = LIS2HH12_FIFO_MODE_STREAM;
set_bit = LIS2HH12_EN_BIT;
break;
default:
return -EINVAL;
}
err = lis2hh12_write_register(cdata, LIS2HH12_FIFO_MODE_ADDR,
LIS2HH12_FIFO_MODE_MASK, reg_value);
if (err < 0)
return err;
cdata->sensor_timestamp =
iio_get_time_ns(cdata->iio_sensors_dev[LIS2HH12_ACCEL]);
err = lis2hh12_write_register(cdata, LIS2HH12_FIFO_EN_ADDR,
LIS2HH12_FIFO_EN_MASK, set_bit);
if (err < 0)
return err;
return 0;
}
EXPORT_SYMBOL(lis2hh12_set_fifo_mode);
int lis2hh12_write_max_odr(struct lis2hh12_sensor_data *sdata)
{
int err, i;
u32 max_odr = 0;
struct lis2hh12_sensor_data *t_sdata;
for (i = 0; i < LIS2HH12_SENSORS_NUMB; i++)
if (CHECK_BIT(sdata->cdata->enabled_sensor, i)) {
t_sdata = iio_priv(sdata->cdata->iio_sensors_dev[i]);
if (t_sdata->odr > max_odr)
max_odr = t_sdata->odr;
}
if (max_odr != sdata->cdata->common_odr) {
for (i = 0; i < LIS2HH12_ODR_LIST_NUM; i++) {
if (lis2hh12_odr_table.odr_avl[i].hz >= max_odr)
break;
}
if (i == LIS2HH12_ODR_LIST_NUM)
return -EINVAL;
err = lis2hh12_write_register(sdata->cdata,
lis2hh12_odr_table.addr,
lis2hh12_odr_table.mask,
lis2hh12_odr_table.odr_avl[i].value);
if (err < 0)
return err;
sdata->cdata->common_odr = max_odr;
sdata->cdata->sensor_deltatime = (max_odr) ? 1000000000L / max_odr : 0;
}
return 0;
}
int lis2hh12_set_fs(struct lis2hh12_sensor_data *sdata, unsigned int gain)
{
int err, i;
for (i = 0; i < LIS2HH12_FS_LIST_NUM; i++) {
if (lis2hh12_fs_table.fs_avl[i].gain == gain)
break;
}
if (i == LIS2HH12_FS_LIST_NUM)
return -EINVAL;
err = lis2hh12_write_register(sdata->cdata,
lis2hh12_fs_table.addr, lis2hh12_fs_table.mask,
lis2hh12_fs_table.fs_avl[i].value);
if (err < 0)
return err;
sdata->gain = lis2hh12_fs_table.fs_avl[i].gain;
return 0;
}
int lis2hh12_update_drdy_irq(struct lis2hh12_sensor_data *sdata, bool state)
{
u8 reg_addr, reg_val, reg_mask;
switch (sdata->sindex) {
case LIS2HH12_ACCEL:
reg_addr = LIS2HH12_INT_CFG_ADDR;
if (sdata->cdata->hwfifo_enabled)
reg_mask = (LIS2HH12_INT_FTH_MASK);
else
reg_mask = (LIS2HH12_INT_DRDY_MASK);
if (state)
reg_val = LIS2HH12_EN_BIT;
else
reg_val = LIS2HH12_DIS_BIT;
break;
default:
return -EINVAL;
}
return lis2hh12_write_register(sdata->cdata, reg_addr, reg_mask, reg_val);
}
EXPORT_SYMBOL(lis2hh12_update_drdy_irq);
static int lis2hh12_alloc_fifo(struct lis2hh12_data *cdata)
{
int fifo_size;
fifo_size = LIS2HH12_MAX_FIFO_LENGHT * LIS2HH12_FIFO_BYTE_FOR_SAMPLE;
cdata->fifo_data = kmalloc(fifo_size, GFP_KERNEL);
if (!cdata->fifo_data)
return -ENOMEM;
cdata->fifo_size = fifo_size;
return 0;
}
int lis2hh12_update_fifo_ths(struct lis2hh12_data *cdata, u8 fifo_len)
{
int err;
struct iio_dev *indio_dev;
indio_dev = cdata->iio_sensors_dev[LIS2HH12_ACCEL];
err = lis2hh12_write_register(cdata, LIS2HH12_FIFO_THS_ADDR,
LIS2HH12_FIFO_THS_MASK,
fifo_len);
if (err < 0)
return err;
return 0;
}
EXPORT_SYMBOL(lis2hh12_update_fifo_ths);
int lis2hh12_set_enable(struct lis2hh12_sensor_data *sdata, bool state)
{
int err = 0;
u8 mode;
if (sdata->enabled == state)
return 0;
/*
* Start assuming the sensor enabled if state == true.
* It will be restored if an error occur.
*/
if (state) {
SET_BIT(sdata->cdata->enabled_sensor, sdata->sindex);
mode = STREAM;
} else {
RESET_BIT(sdata->cdata->enabled_sensor, sdata->sindex);
mode = BYPASS;
}
/* Program the device */
err = lis2hh12_update_drdy_irq(sdata, state);
if (err < 0)
goto enable_sensor_error;
err = lis2hh12_set_fifo_mode(sdata->cdata, mode);
if (err < 0)
goto enable_sensor_error;
err = lis2hh12_write_max_odr(sdata);
if (err < 0)
goto enable_sensor_error;
sdata->enabled = state;
return 0;
enable_sensor_error:
if (state) {
RESET_BIT(sdata->cdata->enabled_sensor, sdata->sindex);
} else
SET_BIT(sdata->cdata->enabled_sensor, sdata->sindex);
return err;
}
EXPORT_SYMBOL(lis2hh12_set_enable);
int lis2hh12_init_sensors(struct lis2hh12_data *cdata)
{
int err;
/*
* Soft reset the device on power on.
*/
err = lis2hh12_write_register(cdata, LIS2HH12_SOFT_RESET_ADDR,
LIS2HH12_SOFT_RESET_MASK,
LIS2HH12_EN_BIT);
if (err < 0)
return err;
mdelay(40);
/*
* Enable latched interrupt mode on INT1.
*/
err = lis2hh12_write_register(cdata, LIS2HH12_LIR_ADDR,
LIS2HH12_LIR1_MASK,
LIS2HH12_EN_BIT);
if (err < 0)
return err;
/*
* Enable block data update feature.
*/
err = lis2hh12_write_register(cdata, LIS2HH12_BDU_ADDR,
LIS2HH12_BDU_MASK,
LIS2HH12_EN_BIT);
if (err < 0)
return err;
return 0;
}
static ssize_t lis2hh12_sysfs_get_sampling_frequency(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct lis2hh12_sensor_data *sdata = iio_priv(dev_get_drvdata(dev));
return sprintf(buf, "%d\n", sdata->odr);
}
ssize_t lis2hh12_sysfs_set_sampling_frequency(struct device * dev,
struct device_attribute * attr, const char *buf, size_t count)
{
int err;
u8 mode_count;
unsigned int odr, i;
struct iio_dev *indio_dev = dev_get_drvdata(dev);
struct lis2hh12_sensor_data *sdata = iio_priv(indio_dev);
err = kstrtoint(buf, 10, &odr);
if (err < 0)
return err;
if (sdata->odr == odr)
return count;
mode_count = LIS2HH12_ODR_LIST_NUM;
for (i = 0; i < mode_count; i++) {
if (lis2hh12_odr_table.odr_avl[i].hz >= odr)
break;
}
if (i == LIS2HH12_ODR_LIST_NUM)
return -EINVAL;
mutex_lock(&indio_dev->mlock);
sdata->odr = lis2hh12_odr_table.odr_avl[i].hz;
mutex_unlock(&indio_dev->mlock);
err = lis2hh12_write_max_odr(sdata);
if (err < 0)
return err;
return (err < 0) ? err : count;
}
static ssize_t lis2hh12_sysfs_sampling_frequency_avail(struct device *dev,
struct device_attribute
*attr, char *buf)
{
int i, len = 0;
for (i = 1; i < LIS2HH12_ODR_LIST_NUM; i++) {
len += scnprintf(buf + len, PAGE_SIZE - len, "%d ",
lis2hh12_odr_table.odr_avl[i].hz);
}
buf[len - 1] = '\n';
return len;
}
static ssize_t lis2hh12_sysfs_scale_avail(struct device *dev,
struct device_attribute *attr, char *buf)
{
int i, len = 0;
for (i = 0; i < LIS2HH12_FS_LIST_NUM; i++) {
len += scnprintf(buf + len, PAGE_SIZE - len, "0.%06u ",
lis2hh12_fs_table.fs_avl[i].gain);
}
buf[len - 1] = '\n';
return len;
}
ssize_t lis2hh12_sysfs_flush_fifo(struct device *dev,
struct device_attribute *attr, const char *buf, size_t size)
{
u64 event_type;
int64_t sensor_last_timestamp;
struct iio_dev *indio_dev = dev_get_drvdata(dev);
struct lis2hh12_sensor_data *sdata = iio_priv(indio_dev);
mutex_lock(&indio_dev->mlock);
if (indio_dev->currentmode == INDIO_BUFFER_TRIGGERED) {
disable_irq(sdata->cdata->irq);
} else {
mutex_unlock(&indio_dev->mlock);
return -EINVAL;
}
sensor_last_timestamp = sdata->cdata->sensor_timestamp;
lis2hh12_read_fifo(sdata->cdata, true);
if (sensor_last_timestamp == sdata->cdata->sensor_timestamp)
event_type = IIO_EV_DIR_FIFO_EMPTY;
else
event_type = IIO_EV_DIR_FIFO_DATA;
iio_push_event(indio_dev, IIO_UNMOD_EVENT_CODE(IIO_ACCEL,
-1, IIO_EV_TYPE_FIFO_FLUSH, event_type),
sdata->cdata->sensor_timestamp);
enable_irq(sdata->cdata->irq);
mutex_unlock(&indio_dev->mlock);
return size;
}
static ssize_t lis2hh12_sysfs_get_hwfifo_enabled(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct iio_dev *indio_dev = dev_get_drvdata(dev);
struct lis2hh12_sensor_data *sdata = iio_priv(indio_dev);
return sprintf(buf, "%d\n", sdata->cdata->hwfifo_enabled);
}
ssize_t lis2hh12_sysfs_set_hwfifo_enabled(struct device * dev,
struct device_attribute * attr, const char *buf, size_t count)
{
int err = 0, enable = 0;
u8 mode = BYPASS;
struct iio_dev *indio_dev = dev_get_drvdata(dev);
struct lis2hh12_sensor_data *sdata = iio_priv(indio_dev);
err = kstrtoint(buf, 10, &enable);
if (err < 0)
return err;
if (enable != 0x0 && enable != 0x1)
return -EINVAL;
mode = (enable == 0x0) ? BYPASS : STREAM;
err = lis2hh12_set_fifo_mode(sdata->cdata, mode);
if (err < 0)
return err;
sdata->cdata->hwfifo_enabled = enable;
return count;
}
static ssize_t lis2hh12_sysfs_get_hwfifo_watermark(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct iio_dev *indio_dev = dev_get_drvdata(dev);
struct lis2hh12_sensor_data *sdata = iio_priv(indio_dev);
return sprintf(buf, "%d\n", sdata->cdata->hwfifo_watermark);
}
ssize_t lis2hh12_sysfs_set_hwfifo_watermark(struct device * dev,
struct device_attribute * attr, const char *buf, size_t count)
{
int err = 0, watermark = 0;
struct iio_dev *indio_dev = dev_get_drvdata(dev);
struct lis2hh12_sensor_data *sdata = iio_priv(indio_dev);
err = kstrtoint(buf, 10, &watermark);
if (err < 0)
return err;
if ((watermark < 1) || (watermark > LIS2HH12_MAX_FIFO_THS))
return -EINVAL;
err = lis2hh12_update_fifo_ths(sdata->cdata, watermark);
if (err < 0)
return err;
sdata->cdata->hwfifo_watermark = watermark;
return count;
}
static ssize_t lis2hh12_sysfs_get_hwfifo_watermark_min(struct device *dev,
struct device_attribute *attr, char *buf)
{
return sprintf(buf, "%d\n", 1);
}
static ssize_t lis2hh12_sysfs_get_hwfifo_watermark_max(struct device *dev,
struct device_attribute *attr, char *buf)
{
return sprintf(buf, "%d\n", LIS2HH12_MAX_FIFO_THS);
}
static int lis2hh12_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *ch, int *val,
int *val2, long mask)
{
int err;
u8 outdata[2], nbytes;
struct lis2hh12_sensor_data *sdata = iio_priv(indio_dev);
switch (mask) {
case IIO_CHAN_INFO_RAW:
mutex_lock(&indio_dev->mlock);
if (indio_dev->currentmode == INDIO_BUFFER_TRIGGERED) {
mutex_unlock(&indio_dev->mlock);
return -EBUSY;
}
err = lis2hh12_set_enable(sdata, true);
if (err < 0) {
mutex_unlock(&indio_dev->mlock);
return -EBUSY;
}
msleep(40);
nbytes = ch->scan_type.realbits / 8;
err = lis2hh12_read_register(sdata->cdata, ch->address, nbytes, outdata);
if (err < 0) {
mutex_unlock(&indio_dev->mlock);
return err;
}
*val = (s16)get_unaligned_le16(outdata);
*val = *val >> ch->scan_type.shift;
err = lis2hh12_set_enable(sdata, false);
mutex_unlock(&indio_dev->mlock);
if (err < 0)
return err;
return IIO_VAL_INT;
case IIO_CHAN_INFO_SCALE:
*val = 0;
*val2 = sdata->gain;
return IIO_VAL_INT_PLUS_MICRO;
default:
return -EINVAL;
}
return 0;
}
static int lis2hh12_write_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan, int val, int val2, long mask)
{
int err, i;
struct lis2hh12_sensor_data *sdata = iio_priv(indio_dev);
switch (mask) {
case IIO_CHAN_INFO_SCALE:
mutex_lock(&indio_dev->mlock);
if (indio_dev->currentmode == INDIO_BUFFER_TRIGGERED) {
mutex_unlock(&indio_dev->mlock);
return -EBUSY;
}
for (i = 0; i < LIS2HH12_FS_LIST_NUM; i++) {
if (lis2hh12_fs_table.fs_avl[i].gain == val2)
break;
}
err = lis2hh12_set_fs(sdata, lis2hh12_fs_table.fs_avl[i].gain);
mutex_unlock(&indio_dev->mlock);
break;
default:
return -EINVAL;
}
return err;
}
static ST_LIS2HH12_DEV_ATTR_SAMP_FREQ();
static ST_LIS2HH12_DEV_ATTR_SAMP_FREQ_AVAIL();
static ST_LIS2HH12_DEV_ATTR_SCALE_AVAIL(in_accel_scale_available);
static ST_LIS2HH12_HWFIFO_ENABLED();
static ST_LIS2HH12_HWFIFO_WATERMARK();
static ST_LIS2HH12_HWFIFO_WATERMARK_MIN();
static ST_LIS2HH12_HWFIFO_WATERMARK_MAX();
static ST_LIS2HH12_HWFIFO_FLUSH();
static struct attribute *lis2hh12_accel_attributes[] = {
&iio_dev_attr_sampling_frequency_available.dev_attr.attr,
&iio_dev_attr_in_accel_scale_available.dev_attr.attr,
&iio_dev_attr_sampling_frequency.dev_attr.attr,
&iio_dev_attr_hwfifo_enabled.dev_attr.attr,
&iio_dev_attr_hwfifo_watermark.dev_attr.attr,
&iio_dev_attr_hwfifo_watermark_min.dev_attr.attr,
&iio_dev_attr_hwfifo_watermark_max.dev_attr.attr,
&iio_dev_attr_hwfifo_flush.dev_attr.attr,
NULL,
};
static const struct attribute_group lis2hh12_accel_attribute_group = {
.attrs = lis2hh12_accel_attributes,
};
static const struct iio_info lis2hh12_info[LIS2HH12_SENSORS_NUMB] = {
[LIS2HH12_ACCEL] = {
.attrs = &lis2hh12_accel_attribute_group,
.read_raw = &lis2hh12_read_raw,
.write_raw = &lis2hh12_write_raw,
},
};
#ifdef CONFIG_IIO_TRIGGER
static const struct iio_trigger_ops lis2hh12_trigger_ops = {
.set_trigger_state = (&lis2hh12_trig_set_state),
};
#define LIS2HH12_TRIGGER_OPS (&lis2hh12_trigger_ops)
#else /*CONFIG_IIO_TRIGGER */
#define LIS2HH12_TRIGGER_OPS NULL
#endif /*CONFIG_IIO_TRIGGER */
#ifdef CONFIG_OF
static const struct of_device_id lis2hh12_dt_id[] = {
{.compatible = "st,lis2hh12",},
{},
};
MODULE_DEVICE_TABLE(of, lis2hh12_dt_id);
static u32 lis2hh12_parse_dt(struct lis2hh12_data *cdata)
{
u32 val;
struct device_node *np;
np = cdata->dev->of_node;
if (!np)
return -EINVAL;
/*TODO for this device interrupt pin is only one!!*/
if (!of_property_read_u32(np, "st,drdy-int-pin", &val) &&
(val <= 1) && (val > 0))
cdata->drdy_int_pin = (u8) val;
else
cdata->drdy_int_pin = 1;
return 0;
}
#endif /*CONFIG_OF */
int lis2hh12_common_probe(struct lis2hh12_data *cdata, int irq)
{
u8 wai = 0;
int32_t err, i, n;
struct iio_dev *piio_dev;
struct lis2hh12_sensor_data *sdata;
mutex_init(&cdata->tb.buf_lock);
cdata->fifo_data = 0;
cdata->hwfifo_enabled = 0;
cdata->hwfifo_watermark = 0;
err = lis2hh12_read_register(cdata, LIS2HH12_WHO_AM_I_ADDR, 1, &wai);
if (err < 0) {
dev_err(cdata->dev, "failed to read Who-Am-I register.\n");
return err;
}
if (wai != LIS2HH12_WHO_AM_I_DEF) {
dev_err(cdata->dev, "Who-Am-I value not valid.\n");
return -ENODEV;
}
if (irq > 0) {
cdata->irq = irq;
#ifdef CONFIG_OF
err = lis2hh12_parse_dt(cdata);
if (err < 0)
return err;
#else /* CONFIG_OF */
if (cdata->dev->platform_data) {
cdata->drdy_int_pin = ((struct lis2hh12_platform_data *)
cdata->dev->platform_data)->drdy_int_pin;
if ((cdata->drdy_int_pin > 1) || (cdata->drdy_int_pin < 1))
cdata->drdy_int_pin = 1;
} else
cdata->drdy_int_pin = 1;
#endif /* CONFIG_OF */
dev_info(cdata->dev, "driver use DRDY int pin %d\n",
cdata->drdy_int_pin);
}
cdata->common_odr = 0;
cdata->enabled_sensor = 0;
err = lis2hh12_alloc_fifo(cdata);
if (err)
return err;
for (i = 0; i < LIS2HH12_SENSORS_NUMB; i++) {
piio_dev = devm_iio_device_alloc(cdata->dev,
sizeof(struct lis2hh12_sensor_data *));
if (piio_dev == NULL) {
err = -ENOMEM;
goto iio_device_free;
}
cdata->iio_sensors_dev[i] = piio_dev;
sdata = iio_priv(piio_dev);
sdata->enabled = false;
sdata->cdata = cdata;
sdata->sindex = i;
sdata->name = lis2hh12_sensors_table[i].name;
sdata->odr = lis2hh12_sensors_table[i].min_odr_hz;
sdata->gain = lis2hh12_fs_table.fs_avl[0].gain;
piio_dev->channels = lis2hh12_sensors_table[i].iio_channel;
piio_dev->num_channels = lis2hh12_sensors_table[i].iio_channel_size;
piio_dev->info = &lis2hh12_info[i];
piio_dev->modes = INDIO_DIRECT_MODE;
piio_dev->name = kasprintf(GFP_KERNEL, "%s_%s", cdata->name,
sdata->name);
}
err = lis2hh12_set_fifo_mode(sdata->cdata, BYPASS);
if (err < 0)
goto iio_device_free;
err = lis2hh12_init_sensors(cdata);
if (err < 0)
goto iio_device_free;
err = lis2hh12_allocate_rings(cdata);
if (err < 0)
goto iio_device_free;
if (irq > 0) {
err = lis2hh12_allocate_triggers(cdata, LIS2HH12_TRIGGER_OPS);
if (err < 0)
goto deallocate_ring;
}
for (n = 0; n < LIS2HH12_SENSORS_NUMB; n++) {
err = iio_device_register(cdata->iio_sensors_dev[n]);
if (err)
goto iio_device_unregister_and_trigger_deallocate;
}
dev_info(cdata->dev, "%s: probed\n", LIS2HH12_DEV_NAME);
return 0;
iio_device_unregister_and_trigger_deallocate:
for (n--; n >= 0; n--)
iio_device_unregister(cdata->iio_sensors_dev[n]);
deallocate_ring:
lis2hh12_deallocate_rings(cdata);
iio_device_free:
for (i--; i >= 0; i--)
iio_device_free(cdata->iio_sensors_dev[i]);
return err;
}
EXPORT_SYMBOL(lis2hh12_common_probe);
void lis2hh12_common_remove(struct lis2hh12_data *cdata, int irq)
{
int i;
if (cdata->fifo_data) {
kfree(cdata->fifo_data);
cdata->fifo_size = 0;
}
for (i = 0; i < LIS2HH12_SENSORS_NUMB; i++)
iio_device_unregister(cdata->iio_sensors_dev[i]);
if (irq > 0)
lis2hh12_deallocate_triggers(cdata);
lis2hh12_deallocate_rings(cdata);
for (i = 0; i < LIS2HH12_SENSORS_NUMB; i++)
iio_device_free(cdata->iio_sensors_dev[i]);
}
EXPORT_SYMBOL(lis2hh12_common_remove);
#ifdef CONFIG_PM
int lis2hh12_common_suspend(struct lis2hh12_data *cdata)
{
return 0;
}
EXPORT_SYMBOL(lis2hh12_common_suspend);
int lis2hh12_common_resume(struct lis2hh12_data *cdata)
{
return 0;
}
EXPORT_SYMBOL(lis2hh12_common_resume);
#endif /* CONFIG_PM */
MODULE_DESCRIPTION("STMicroelectronics lis2hh12 driver");
MODULE_AUTHOR("Armando Visconti <armando.visconti@st.com>");
MODULE_LICENSE("GPL v2");

View File

@ -0,0 +1,155 @@
/*
* STMicroelectronics lis2hh12 driver
*
* Copyright 2016 STMicroelectronics Inc.
*
* Armando Visconti <armando.visconti@st.com>
*
* Licensed under the GPL-2.
*/
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/i2c.h>
#include <linux/types.h>
#include "st_lis2hh12.h"
static int lis2hh12_i2c_read(struct lis2hh12_data *cdata, u8 reg_addr, int len,
u8 * data)
{
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;
return(i2c_transfer(client->adapter, msg, 2));
}
static int lis2hh12_i2c_write(struct lis2hh12_data *cdata, u8 reg_addr, int len,
u8 * data)
{
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;
return(i2c_transfer(client->adapter, &msg, 1));
}
static const struct lis2hh12_transfer_function lis2hh12_tf_i2c = {
.write = lis2hh12_i2c_write,
.read = lis2hh12_i2c_read,
};
static int lis2hh12_i2c_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
int err;
struct lis2hh12_data *cdata;
cdata = kmalloc(sizeof(*cdata), GFP_KERNEL);
if (!cdata)
return -ENOMEM;
cdata->dev = &client->dev;
cdata->name = client->name;
cdata->tf = &lis2hh12_tf_i2c;
i2c_set_clientdata(client, cdata);
err = lis2hh12_common_probe(cdata, client->irq);
if (err < 0)
goto free_data;
return 0;
free_data:
kfree(cdata);
return err;
}
static int lis2hh12_i2c_remove(struct i2c_client *client)
{
struct lis2hh12_data *cdata = i2c_get_clientdata(client);
lis2hh12_common_remove(cdata, client->irq);
dev_info(cdata->dev, "%s: removed\n", LIS2HH12_DEV_NAME);
kfree(cdata);
return 0;
}
#ifdef CONFIG_PM
static int lis2hh12_suspend(struct device *dev)
{
struct lis2hh12_data *cdata = i2c_get_clientdata(to_i2c_client(dev));
return lis2hh12_common_suspend(cdata);
}
static int lis2hh12_resume(struct device *dev)
{
struct lis2hh12_data *cdata = i2c_get_clientdata(to_i2c_client(dev));
return lis2hh12_common_resume(cdata);
}
static const struct dev_pm_ops lis2hh12_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(lis2hh12_suspend, lis2hh12_resume)
};
#define LIS2HH12_PM_OPS (&lis2hh12_pm_ops)
#else /* CONFIG_PM */
#define LIS2HH12_PM_OPS NULL
#endif /* CONFIG_PM */
static const struct i2c_device_id lis2hh12_ids[] = {
{"lis2hh12", 0},
{}
};
MODULE_DEVICE_TABLE(i2c, lis2hh12_ids);
#ifdef CONFIG_OF
static const struct of_device_id lis2hh12_id_table[] = {
{.compatible = "st,lis2hh12",},
{},
};
MODULE_DEVICE_TABLE(of, lis2hh12_id_table);
#endif /* CONFIG_OF */
static struct i2c_driver lis2hh12_i2c_driver = {
.driver = {
.owner = THIS_MODULE,
.name = LIS2HH12_DEV_NAME,
.pm = LIS2HH12_PM_OPS,
#ifdef CONFIG_OF
.of_match_table = lis2hh12_id_table,
#endif /* CONFIG_OF */
},
.probe = lis2hh12_i2c_probe,
.remove = lis2hh12_i2c_remove,
.id_table = lis2hh12_ids,
};
module_i2c_driver(lis2hh12_i2c_driver);
MODULE_DESCRIPTION("STMicroelectronics lis2hh12 i2c driver");
MODULE_AUTHOR("Armando Visconti <armando.visconti@st.com>");
MODULE_LICENSE("GPL v2");

View File

@ -0,0 +1,185 @@
/*
* STMicroelectronics lis2hh12 driver
*
* Copyright 2016 STMicroelectronics Inc.
*
* Armando Visconti <armando.visconti@st.com>
*
* Licensed under the GPL-2.
*/
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/spi/spi.h>
#include <linux/types.h>
#include "st_lis2hh12.h"
#define ST_SENSORS_SPI_READ 0x80
static int lis2hh12_spi_read(struct lis2hh12_data *cdata,
u8 reg_addr, int len, u8 *data)
{
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,
}
};
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);
return len;
acc_spi_read_error:
mutex_unlock(&cdata->tb.buf_lock);
return err;
}
static int lis2hh12_spi_write(struct lis2hh12_data *cdata,
u8 reg_addr, int len, u8 *data)
{
int err;
struct spi_transfer xfers = {
.tx_buf = cdata->tb.tx_buf,
.bits_per_word = 8,
.len = len + 1,
};
if (len >= LIS2HH12_RX_MAX_LENGTH)
return -ENOMEM;
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);
return err;
}
static const struct lis2hh12_transfer_function lis2hh12_tf_spi = {
.write = lis2hh12_spi_write,
.read = lis2hh12_spi_read,
};
static int lis2hh12_spi_probe(struct spi_device *spi)
{
int err;
struct lis2hh12_data *cdata;
cdata = kmalloc(sizeof(*cdata), GFP_KERNEL);
if (!cdata)
return -ENOMEM;
cdata->dev = &spi->dev;
cdata->name = spi->modalias;
cdata->tf = &lis2hh12_tf_spi;
spi_set_drvdata(spi, cdata);
err = lis2hh12_common_probe(cdata, spi->irq);
if (err < 0)
goto free_data;
return 0;
free_data:
kfree(cdata);
return err;
}
static int lis2hh12_spi_remove(struct spi_device *spi)
{
struct lis2hh12_data *cdata = spi_get_drvdata(spi);
lis2hh12_common_remove(cdata, spi->irq);
dev_info(cdata->dev, "%s: removed\n", LIS2HH12_DEV_NAME);
kfree(cdata);
return 0;
}
#ifdef CONFIG_PM
static int lis2hh12_suspend(struct device *dev)
{
struct lis2hh12_data *cdata = spi_get_drvdata(to_spi_device(dev));
return lis2hh12_common_suspend(cdata);
}
static int lis2hh12_resume(struct device *dev)
{
struct lis2hh12_data *cdata = spi_get_drvdata(to_spi_device(dev));
return lis2hh12_common_resume(cdata);
}
static const struct dev_pm_ops lis2hh12_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(lis2hh12_suspend, lis2hh12_resume)
};
#define LIS2HH12_PM_OPS (&lis2hh12_pm_ops)
#else /* CONFIG_PM */
#define LIS2HH12_PM_OPS NULL
#endif /* CONFIG_PM */
static const struct spi_device_id lis2hh12_ids[] = {
{"lis2hh12", 0},
{}
};
MODULE_DEVICE_TABLE(spi, lis2hh12_ids);
#ifdef CONFIG_OF
static const struct of_device_id lis2hh12_id_table[] = {
{ .compatible = "st,lis2hh12"},
{},
};
MODULE_DEVICE_TABLE(of, lis2hh12_id_table);
#endif /* CONFIG_OF */
static struct spi_driver lis2hh12_spi_driver = {
.driver = {
.owner = THIS_MODULE,
.name = LIS2HH12_DEV_NAME,
.pm = LIS2HH12_PM_OPS,
#ifdef CONFIG_OF
.of_match_table = lis2hh12_id_table,
#endif /* CONFIG_OF */
},
.probe = lis2hh12_spi_probe,
.remove = lis2hh12_spi_remove,
.id_table = lis2hh12_ids,
};
module_spi_driver(lis2hh12_spi_driver);
MODULE_DESCRIPTION("STMicroelectronics lis2hh12 spi driver");
MODULE_AUTHOR("Armando Visconti <armando.visconti@st.com>");
MODULE_LICENSE("GPL v2");

View File

@ -0,0 +1,106 @@
/*
* STMicroelectronics lis2hh12 driver
*
* Copyright 2016 STMicroelectronics Inc.
*
* Armando Visconti <armando.visconti@st.com>
*
* Licensed under the GPL-2.
*/
#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_lis2hh12.h"
static irqreturn_t lis2hh12_irq_management(int irq, void *private)
{
struct lis2hh12_data *cdata = private;
u8 status;
cdata->timestamp =
iio_get_time_ns(cdata->iio_sensors_dev[LIS2HH12_ACCEL]);
if (cdata->hwfifo_enabled) {
cdata->tf->read(cdata, LIS2HH12_FIFO_STATUS_ADDR, 1, &status);
if (status & LIS2HH12_FIFO_SRC_FTH_MASK)
lis2hh12_read_fifo(cdata, true);
} else {
cdata->tf->read(cdata, LIS2HH12_STATUS_ADDR, 1, &status);
if (status & LIS2HH12_DATA_XYZ_RDY)
lis2hh12_read_xyz(cdata);
}
return IRQ_HANDLED;
}
int lis2hh12_allocate_triggers(struct lis2hh12_data *cdata,
const struct iio_trigger_ops *trigger_ops)
{
int err, i, n;
for (i = 0; i < LIS2HH12_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, NULL, lis2hh12_irq_management,
IRQF_TRIGGER_HIGH | IRQF_ONESHOT, cdata->name, cdata);
if (err)
goto deallocate_trigger;
for (n = 0; n < LIS2HH12_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;
}
EXPORT_SYMBOL(lis2hh12_allocate_triggers);
void lis2hh12_deallocate_triggers(struct lis2hh12_data *cdata)
{
int i;
free_irq(cdata->irq, cdata);
for (i = 0; i < LIS2HH12_SENSORS_NUMB; i++)
iio_trigger_unregister(cdata->iio_trig[i]);
}
EXPORT_SYMBOL(lis2hh12_deallocate_triggers);
MODULE_DESCRIPTION("STMicroelectronics lis2hh12 driver");
MODULE_AUTHOR("Armando Visconti <armando.visconti@st.com>");
MODULE_LICENSE("GPL v2");

View File

@ -0,0 +1,3 @@
CONFIG_IIO_ST_LIS2HH12=m
CONFIG_IIO_ST_LIS2HH12_I2C=m
CONFIG_IIO_ST_LIS2HH12_SPI=m