drivers:iio:imu:Added support to LSM9DS1 IMU sensor

List of supported features:
 - Acc / Gyro sensors
    > Configurable ODRs [Power_Off ... 476] Hz
    > Configurable Full Scales
    > Sensor Event Timestamp HW support
    > Read single raw data values
 - FIFO:
    > HW Watermark configuration
    > Custom Flush command / event support
 - Triggered buffer support for external interrupt sources

Signed-off-by: mario tesi <mario.tesi@st.com>
Change-Id: I85048099848da44179b6f59366452b2b6bdbd989
This commit is contained in:
mario tesi 2021-11-08 13:04:14 +01:00 committed by Mario Tesi
parent a1c75d18df
commit 30ef4f8618
10 changed files with 1130 additions and 0 deletions

View File

@ -12,5 +12,6 @@ source "drivers/iio/stm/imu/st_ism330dhcx/Kconfig"
source "drivers/iio/stm/imu/st_asm330lhh/Kconfig"
source "drivers/iio/stm/imu/st_lsm6dso32x/Kconfig"
source "drivers/iio/stm/imu/st_lsm6dso/Kconfig"
source "drivers/iio/stm/imu/st_imu68/Kconfig"
endmenu

View File

@ -10,3 +10,4 @@ obj-y += st_ism330dhcx/
obj-y += st_asm330lhh/
obj-y += st_lsm6dso32x/
obj-y += st_lsm6dso/
obj-y += st_imu68/

View File

@ -0,0 +1,24 @@
# SPDX-License-Identifier: GPL-2.0-only
config IIO_ST_IMU68
tristate "STMicroelectronics LSM9DS1 IMU sensor"
depends on (I2C || SPI)
select IIO_BUFFER
select IIO_TRIGGERED_BUFFER
select IIO_ST_IMU68_I2C if (I2C)
select IIO_ST_IMU68_SPI if (SPI_MASTER)
help
Say yes here to build support for STMicroelectronics IMU sensors:
LSM9DS1
To compile this driver as a module, choose M here: the module
will be called st_imu68.
config IIO_ST_IMU68_I2C
tristate
depends on IIO_ST_IMU68
config IIO_ST_IMU68_SPI
tristate
depends on IIO_ST_IMU68

View File

@ -0,0 +1,6 @@
# SPDX-License-Identifier: GPL-2.0-only
st_imu68-y := st_imu68_core.o st_imu68_buffer.o
obj-$(CONFIG_IIO_ST_IMU68) += st_imu68.o
obj-$(CONFIG_IIO_ST_IMU68_I2C) += st_imu68_i2c.o
obj-$(CONFIG_IIO_ST_IMU68_SPI) += st_imu68_spi.o

View File

@ -0,0 +1,86 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* STMicroelectronics st_imu68 sensor driver
*
* Copyright 2016 STMicroelectronics Inc.
*
* Lorenzo Bianconi <lorenzo.bianconi@st.com>
*/
#ifndef ST_IMU68_H
#define ST_IMU68_H
#include <linux/device.h>
#define ST_LSM9DS1_DEV_NAME "lsm9ds1"
#define ST_IMU68_OUT_LEN 6
#if defined(CONFIG_SPI_MASTER)
#define ST_IMU68_RX_MAX_LENGTH 8
#define ST_IMU68_TX_MAX_LENGTH 8
struct st_imu68_transfer_buffer {
u8 rx_buf[ST_IMU68_RX_MAX_LENGTH];
u8 tx_buf[ST_IMU68_TX_MAX_LENGTH] ____cacheline_aligned;
};
#endif /* CONFIG_SPI_MASTER */
struct st_imu68_transfer_function {
int (*read)(struct device *dev, u8 addr, int len, u8 *data);
int (*write)(struct device *dev, u8 addr, int len, u8 *data);
};
enum st_imu68_sensor_id {
ST_IMU68_ID_ACC,
ST_IMU68_ID_GYRO,
ST_IMU68_ID_MAX,
};
struct st_imu68_reg {
u8 addr;
u8 mask;
};
struct st_imu68_sensor {
enum st_imu68_sensor_id id;
struct st_imu68_hw *hw;
struct iio_trigger *trigger;
u32 gain;
u16 odr;
u8 drdy_mask;
u8 status_mask;
};
struct st_imu68_hw {
const char *name;
struct device *dev;
int irq;
struct mutex lock;
s64 timestamp;
u8 enabled_mask;
struct iio_dev *iio_devs[ST_IMU68_ID_MAX];
const struct st_imu68_transfer_function *tf;
#if defined(CONFIG_SPI_MASTER)
struct st_imu68_transfer_buffer tb;
#endif /* CONFIG_SPI_MASTER */
};
int st_imu68_write_with_mask(struct st_imu68_hw *hw, u8 addr, u8 mask,
u8 val);
int st_imu68_probe(struct device *dev, int irq, const char *name,
const struct st_imu68_transfer_function *tf_ops);
int st_imu68_remove(struct device *dev);
int st_imu68_sensor_enable(struct st_imu68_sensor *sensor, bool enable);
int st_imu68_allocate_buffers(struct st_imu68_hw *hw);
void st_imu68_deallocate_buffers(struct st_imu68_hw *hw);
int st_imu68_allocate_triggers(struct st_imu68_hw *hw);
void st_imu68_deallocate_triggers(struct st_imu68_hw *hw);
#endif /* ST_IMU68_H */

View File

@ -0,0 +1,196 @@
/*
* STMicroelectronics st_imu68 buffer library driver
*
* Copyright 2016 STMicroelectronics Inc.
*
* Lorenzo Bianconi <lorenzo.bianconi@st.com>
*
* Licensed under the GPL-2.
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/iio/iio.h>
#include <linux/iio/buffer.h>
#include <linux/iio/trigger.h>
#include <linux/iio/trigger_consumer.h>
#include <linux/iio/triggered_buffer.h>
#include "st_imu68.h"
#define ST_IMU68_REG_INT1_CTRL_ADDR 0x0c
#define ST_IMU68_REG_INT2_CTRL_ADDR 0x0d
#define ST_IMU68_REG_STATUS_ADDR 0x17
static int st_imu68_trig_set_state(struct iio_trigger *trig, bool state)
{
struct iio_dev *iio_dev = iio_trigger_get_drvdata(trig);
struct st_imu68_sensor *sensor = iio_priv(iio_dev);
int err;
err = st_imu68_write_with_mask(sensor->hw, ST_IMU68_REG_INT1_CTRL_ADDR,
sensor->drdy_mask, state);
return err < 0 ? err : 0;
}
static const struct iio_trigger_ops st_imu68_trigger_ops = {
.set_trigger_state = st_imu68_trig_set_state,
};
static irqreturn_t st_imu68_trigger_irq_handler(int irq, void *p)
{
struct st_imu68_hw *hw = (struct st_imu68_hw *)p;
hw->timestamp = iio_get_time_ns(hw->iio_devs[0]);
return IRQ_WAKE_THREAD;
}
static irqreturn_t st_imu68_trigger_thread_handler(int irq, void *p)
{
struct st_imu68_hw *hw = (struct st_imu68_hw *)p;
struct st_imu68_sensor *sensor;
int i, err, count = 0;
u8 status;
err = hw->tf->read(hw->dev, ST_IMU68_REG_STATUS_ADDR, sizeof(status),
&status);
if (err < 0)
return IRQ_HANDLED;
for (i = 0; i < ST_IMU68_ID_MAX; i++) {
sensor = iio_priv(hw->iio_devs[i]);
if (status & sensor->status_mask) {
iio_trigger_poll_chained(sensor->trigger);
count++;
}
}
return count > 0 ? IRQ_HANDLED : IRQ_NONE;
}
int st_imu68_allocate_triggers(struct st_imu68_hw *hw)
{
struct st_imu68_sensor *sensor;
int i, err;
err = devm_request_threaded_irq(hw->dev, hw->irq,
st_imu68_trigger_irq_handler,
st_imu68_trigger_thread_handler,
IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
hw->name, hw);
if (err)
return err;
for (i = 0; i < ST_IMU68_ID_MAX; i++) {
sensor = iio_priv(hw->iio_devs[i]);
sensor->trigger = devm_iio_trigger_alloc(hw->dev, "%s-trigger",
hw->iio_devs[i]->name);
if (!sensor->trigger) {
dev_err(hw->dev, "failed to allocate iio trigger.\n");
err = -ENOMEM;
goto err;
}
iio_trigger_set_drvdata(sensor->trigger, hw->iio_devs[i]);
sensor->trigger->ops = &st_imu68_trigger_ops;
sensor->trigger->dev.parent = hw->dev;
err = iio_trigger_register(sensor->trigger);
if (err < 0) {
dev_err(hw->dev, "failed to register iio trigger.\n");
goto err;
}
hw->iio_devs[i]->trig = sensor->trigger;
}
return 0;
err:
for (i--; i >= 0; i--) {
sensor = iio_priv(hw->iio_devs[i]);
iio_trigger_unregister(sensor->trigger);
}
return err;
}
void st_imu68_deallocate_triggers(struct st_imu68_hw *hw)
{
struct st_imu68_sensor *sensor;
int i;
for (i = 0; i < ST_IMU68_ID_MAX; i++) {
sensor = iio_priv(hw->iio_devs[i]);
iio_trigger_unregister(sensor->trigger);
}
}
static int st_imu68_buffer_preenable(struct iio_dev *iio_dev)
{
return st_imu68_sensor_enable(iio_priv(iio_dev), true);
}
static int st_imu68_buffer_postdisable(struct iio_dev *iio_dev)
{
return st_imu68_sensor_enable(iio_priv(iio_dev), false);
}
static const struct iio_buffer_setup_ops st_imu68_buffer_setup_ops = {
.preenable = st_imu68_buffer_preenable,
.postdisable = st_imu68_buffer_postdisable,
};
static irqreturn_t st_imu68_buffer_thread_handler(int irq, void *p)
{
u8 buffer[ALIGN(ST_IMU68_OUT_LEN, sizeof(s64)) + sizeof(s64)];
struct iio_poll_func *pf = p;
struct iio_chan_spec const *ch = pf->indio_dev->channels;
struct st_imu68_sensor *sensor = iio_priv(pf->indio_dev);
struct st_imu68_hw *hw = sensor->hw;
int err;
err = hw->tf->read(hw->dev, ch->address, ST_IMU68_OUT_LEN, buffer);
if (err < 0)
goto out;
iio_push_to_buffers_with_timestamp(pf->indio_dev, buffer,
hw->timestamp);
out:
iio_trigger_notify_done(sensor->trigger);
return IRQ_HANDLED;
}
int st_imu68_allocate_buffers(struct st_imu68_hw *hw)
{
int err, i;
for (i = 0; i < ST_IMU68_ID_MAX; i++) {
err = iio_triggered_buffer_setup(hw->iio_devs[i], NULL,
st_imu68_buffer_thread_handler,
&st_imu68_buffer_setup_ops);
if (err)
goto err;
}
return 0;
err:
for (i--; i >= 0; i--)
iio_triggered_buffer_cleanup(hw->iio_devs[i]);
return err;
}
void st_imu68_deallocate_buffers(struct st_imu68_hw *hw)
{
int i;
for (i = 0; i < ST_IMU68_ID_MAX; i++)
iio_triggered_buffer_cleanup(hw->iio_devs[i]);
}

View File

@ -0,0 +1,625 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* STMicroelectronics st_imu68 sensor driver
*
* Copyright 2019 STMicroelectronics Inc.
*
* Lorenzo Bianconi <lorenzo.bianconi@st.com>
* Mario Tesi <mario.tesi@st.com>
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
#include "st_imu68.h"
#define ST_IMU68_WHOAMI_ADDR 0x0f
#define ST_IMU68_WHOAMI 0x68
#define ST_IMU68_REG_GYRO_ODR_ADDR 0x10
#define ST_IMU68_REG_GYRO_ODR_MASK GENMASK(7, 5)
#define ST_IMU68_REG_GYRO_FS_ADDR 0x10
#define ST_IMU68_REG_GYRO_FS_MASK GENMASK(4, 3)
#define ST_IMU68_REG_CTRL4_ADDR 0x1e
#define ST_IMU68_GYRO_EN_MASK GENMASK(5, 3)
#define ST_IMU68_REG_CTRL5_ADDR 0x1f
#define ST_IMU68_ACC_EN_MASK GENMASK(5, 3)
#define ST_IMU68_REG_ACC_ODR_ADDR 0x20
#define ST_IMU68_REG_ACC_ODR_MASK GENMASK(7, 5)
#define ST_IMU68_REG_ACC_FS_ADDR 0x20
#define ST_IMU68_REG_ACC_FS_MASK GENMASK(4, 3)
#define ST_IMU68_INT_ACC_DRDY_MASK BIT(0)
#define ST_IMU68_ACC_STATUS_MASK BIT(0)
#define ST_IMU68_INT_GYRO_DRDY_MASK BIT(1)
#define ST_IMU68_GYRO_STATUS_MASK BIT(1)
#define ST_IMU68_REG_GYRO_OUT_X_L_ADDR 0x18
#define ST_IMU68_REG_GYRO_OUT_Y_L_ADDR 0x1a
#define ST_IMU68_REG_GYRO_OUT_Z_L_ADDR 0x1c
#define ST_IMU68_REG_ACC_OUT_X_L_ADDR 0x28
#define ST_IMU68_REG_ACC_OUT_Y_L_ADDR 0x2a
#define ST_IMU68_REG_ACC_OUT_Z_L_ADDR 0x2c
#define ST_IMU68_ACC_FS_2G_GAIN IIO_G_TO_M_S_2(61)
#define ST_IMU68_ACC_FS_4G_GAIN IIO_G_TO_M_S_2(122)
#define ST_IMU68_ACC_FS_8G_GAIN IIO_G_TO_M_S_2(244)
#define ST_IMU68_ACC_FS_16G_GAIN IIO_G_TO_M_S_2(732)
#define ST_IMU68_GYRO_FS_250_GAIN IIO_DEGREE_TO_RAD(8750)
#define ST_IMU68_GYRO_FS_500_GAIN IIO_DEGREE_TO_RAD(17500)
#define ST_IMU68_GYRO_FS_2000_GAIN IIO_DEGREE_TO_RAD(70000)
struct st_imu68_odr {
u16 hz;
u8 val;
};
#define ST_IMU68_ODR_LIST_SIZE 5
struct st_imu68_odr_table_entry {
struct st_imu68_reg reg;
struct st_imu68_odr odr_avl[ST_IMU68_ODR_LIST_SIZE];
};
static const struct st_imu68_odr_table_entry st_imu68_odr_table[] = {
[ST_IMU68_ID_ACC] = {
.reg = {
.addr = ST_IMU68_REG_ACC_ODR_ADDR,
.mask = ST_IMU68_REG_ACC_ODR_MASK,
},
.odr_avl[0] = { 10, 0x01 },
.odr_avl[1] = { 50, 0x02 },
.odr_avl[2] = { 119, 0x03 },
.odr_avl[3] = { 238, 0x04 },
.odr_avl[4] = { 476, 0x05 },
},
[ST_IMU68_ID_GYRO] = {
.reg = {
.addr = ST_IMU68_REG_GYRO_ODR_ADDR,
.mask = ST_IMU68_REG_GYRO_ODR_MASK,
},
.odr_avl[0] = { 15, 0x01 },
.odr_avl[1] = { 60, 0x02 },
.odr_avl[2] = { 119, 0x03 },
.odr_avl[3] = { 238, 0x04 },
.odr_avl[4] = { 476, 0x05 },
}
};
struct st_imu68_fs {
u32 gain;
u8 val;
};
#define ST_IMU68_FS_LIST_SIZE 4
struct st_imu68_fs_table_entry {
struct st_imu68_reg reg;
struct st_imu68_fs fs_avl[ST_IMU68_FS_LIST_SIZE];
u8 depth;
};
static const struct st_imu68_fs_table_entry st_imu68_fs_table[] = {
[ST_IMU68_ID_ACC] = {
.reg = {
.addr = ST_IMU68_REG_ACC_FS_ADDR,
.mask = ST_IMU68_REG_ACC_FS_MASK,
},
.fs_avl[0] = { ST_IMU68_ACC_FS_2G_GAIN, 0x0 },
.fs_avl[1] = { ST_IMU68_ACC_FS_4G_GAIN, 0x2 },
.fs_avl[2] = { ST_IMU68_ACC_FS_8G_GAIN, 0x3 },
.fs_avl[3] = { ST_IMU68_ACC_FS_16G_GAIN, 0x1 },
},
[ST_IMU68_ID_GYRO] = {
.reg = {
.addr = ST_IMU68_REG_GYRO_FS_ADDR,
.mask = ST_IMU68_REG_GYRO_FS_MASK,
},
.fs_avl[0] = { ST_IMU68_GYRO_FS_250_GAIN, 0x0 },
.fs_avl[1] = { ST_IMU68_GYRO_FS_500_GAIN, 0x1 },
.fs_avl[2] = { ST_IMU68_GYRO_FS_2000_GAIN, 0x3 },
}
};
#define ST_IMU68_CHANNEL(chan_type, addr, mod, scan_idx) \
{ \
.type = chan_type, \
.address = addr, \
.modified = 1, \
.channel2 = mod, \
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
BIT(IIO_CHAN_INFO_SCALE), \
.scan_index = scan_idx, \
.scan_type = { \
.sign = 's', \
.realbits = 16, \
.storagebits = 16, \
.endianness = IIO_LE, \
}, \
}
static const struct iio_chan_spec st_imu68_acc_channels[] = {
ST_IMU68_CHANNEL(IIO_ACCEL, ST_IMU68_REG_ACC_OUT_X_L_ADDR,
IIO_MOD_X, 0),
ST_IMU68_CHANNEL(IIO_ACCEL, ST_IMU68_REG_ACC_OUT_Y_L_ADDR,
IIO_MOD_Y, 1),
ST_IMU68_CHANNEL(IIO_ACCEL, ST_IMU68_REG_ACC_OUT_Z_L_ADDR,
IIO_MOD_Z, 2),
IIO_CHAN_SOFT_TIMESTAMP(3),
};
static const struct iio_chan_spec st_imu68_gyro_channels[] = {
ST_IMU68_CHANNEL(IIO_ANGL_VEL, ST_IMU68_REG_GYRO_OUT_X_L_ADDR,
IIO_MOD_X, 0),
ST_IMU68_CHANNEL(IIO_ANGL_VEL, ST_IMU68_REG_GYRO_OUT_Y_L_ADDR,
IIO_MOD_Y, 1),
ST_IMU68_CHANNEL(IIO_ANGL_VEL, ST_IMU68_REG_GYRO_OUT_Z_L_ADDR,
IIO_MOD_Z, 2),
IIO_CHAN_SOFT_TIMESTAMP(3),
};
static inline int st_imu68_claim_direct_mode(struct iio_dev *iio_dev)
{
mutex_lock(&iio_dev->mlock);
if (iio_buffer_enabled(iio_dev)) {
mutex_unlock(&iio_dev->mlock);
return -EBUSY;
}
return 0;
}
static inline void st_imu68_release_direct_mode(struct iio_dev *iio_dev)
{
mutex_unlock(&iio_dev->mlock);
}
int st_imu68_write_with_mask(struct st_imu68_hw *hw, u8 addr, u8 mask, u8 val)
{
u8 data;
int err;
mutex_lock(&hw->lock);
err = hw->tf->read(hw->dev, addr, sizeof(data), &data);
if (err < 0) {
dev_err(hw->dev, "failed to read %02x register\n", addr);
goto out;
}
data = (data & ~mask) | ((val << __ffs(mask)) & mask);
err = hw->tf->write(hw->dev, addr, sizeof(data), &data);
if (err < 0)
dev_err(hw->dev, "failed to write %02x register\n", addr);
out:
mutex_unlock(&hw->lock);
return err;
}
static int st_imu68_check_whoami(struct st_imu68_hw *hw)
{
int err;
u8 data;
err = hw->tf->read(hw->dev, ST_IMU68_WHOAMI_ADDR, sizeof(data),
&data);
if (err < 0) {
dev_err(hw->dev, "failed to read whoami register\n");
return err;
}
if (data != ST_IMU68_WHOAMI) {
dev_err(hw->dev, "unsupported whoami [%02x]\n", data);
return -ENODEV;
}
return 0;
}
static int st_imu68_set_full_scale(struct st_imu68_sensor *sensor, u32 gain)
{
enum st_imu68_sensor_id id = sensor->id;
int i, err;
u8 val;
for (i = 0; i < ST_IMU68_FS_LIST_SIZE; i++)
if ((st_imu68_fs_table[id].fs_avl[i].gain == gain) &&
st_imu68_fs_table[id].fs_avl[i].gain)
break;
if (i == ST_IMU68_FS_LIST_SIZE)
return -EINVAL;
val = st_imu68_fs_table[id].fs_avl[i].val;
err = st_imu68_write_with_mask(sensor->hw,
st_imu68_fs_table[id].reg.addr,
st_imu68_fs_table[id].reg.mask, val);
if (err < 0)
return err;
sensor->gain = gain;
return 0;
}
static int st_imu68_set_odr(struct st_imu68_sensor *sensor, u16 odr)
{
enum st_imu68_sensor_id id = sensor->id;
int i, err;
u8 val;
for (i = 0; i < ST_IMU68_ODR_LIST_SIZE; i++)
if (st_imu68_odr_table[id].odr_avl[i].hz >= odr)
break;
if (i == ST_IMU68_ODR_LIST_SIZE)
return -EINVAL;
val = st_imu68_odr_table[id].odr_avl[i].val;
err = st_imu68_write_with_mask(sensor->hw,
st_imu68_odr_table[id].reg.addr,
st_imu68_odr_table[id].reg.mask, val);
if (err < 0)
return err;
sensor->odr = st_imu68_odr_table[id].odr_avl[i].hz;
return 0;
}
static ssize_t
st_imu68_sysfs_sampling_frequency_avail(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct st_imu68_sensor *sensor = iio_priv(dev_get_drvdata(dev));
enum st_imu68_sensor_id id = sensor->id;
int i, len = 0;
for (i = 0; i < ST_IMU68_ODR_LIST_SIZE; i++)
len += scnprintf(buf + len, PAGE_SIZE - len, "%d ",
st_imu68_odr_table[id].odr_avl[i].hz);
buf[len - 1] = '\n';
return len;
}
static ssize_t st_imu68_sysfs_scale_avail(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct st_imu68_sensor *sensor = iio_priv(dev_get_drvdata(dev));
enum st_imu68_sensor_id id = sensor->id;
int i, len = 0;
for (i = 0; i < ST_IMU68_FS_LIST_SIZE; i++)
len += scnprintf(buf + len, PAGE_SIZE - len, "0.%06u ",
st_imu68_fs_table[id].fs_avl[i].gain);
buf[len - 1] = '\n';
return len;
}
static int st_imu68_read_oneshot(struct st_imu68_sensor *sensor, u8 addr,
int *val)
{
int err, delay;
__le16 data;
err = st_imu68_sensor_enable(sensor, true);
if (err < 0)
return err;
delay = 1000000 / sensor->odr;
usleep_range(delay, 2 * delay);
err = sensor->hw->tf->read(sensor->hw->dev, addr, sizeof(data),
(u8 *)&data);
if (err < 0)
return err;
st_imu68_sensor_enable(sensor, false);
*val = (s16)data;
return IIO_VAL_INT;
}
static int st_imu68_read_raw(struct iio_dev *iio_dev,
struct iio_chan_spec const *ch,
int *val, int *val2, long mask)
{
struct st_imu68_sensor *sensor = iio_priv(iio_dev);
int ret;
switch (mask) {
case IIO_CHAN_INFO_RAW:
ret = st_imu68_claim_direct_mode(iio_dev);
if (ret)
break;
ret = st_imu68_read_oneshot(sensor, ch->address, val);
st_imu68_release_direct_mode(iio_dev);
break;
case IIO_CHAN_INFO_SCALE:
*val = 0;
*val2 = sensor->gain;
ret = IIO_VAL_INT_PLUS_MICRO;
break;
default:
ret = -EINVAL;
break;
}
return ret;
}
static int st_imu68_write_raw(struct iio_dev *iio_dev,
struct iio_chan_spec const *chan,
int val, int val2, long mask)
{
struct st_imu68_sensor *sensor = iio_priv(iio_dev);
int err;
err = st_imu68_claim_direct_mode(iio_dev);
if (err)
return err;
switch (mask) {
case IIO_CHAN_INFO_SCALE:
err = st_imu68_set_full_scale(sensor, val2);
break;
default:
err = -EINVAL;
break;
}
st_imu68_release_direct_mode(iio_dev);
return err;
}
int st_imu68_sensor_enable(struct st_imu68_sensor *sensor, bool enable)
{
int err;
enum st_imu68_sensor_id id = sensor->id;
if (enable) {
u16 odr;
struct st_imu68_sensor *sensor_acc =
iio_priv(sensor->hw->iio_devs[ST_IMU68_ID_ACC]);
/* Check if Gyro enabling with Acc already on */
if ((id == ST_IMU68_ID_GYRO) &&
(sensor->hw->enabled_mask & BIT(ST_IMU68_ID_ACC))) {
odr = max(sensor->odr, sensor_acc->odr);
} else {
odr = sensor->odr;
}
err = st_imu68_set_odr(sensor, odr);
if (err < 0)
return err;
sensor->hw->enabled_mask |= BIT(id);
} else {
err = st_imu68_write_with_mask(sensor->hw,
st_imu68_odr_table[id].reg.addr,
st_imu68_odr_table[id].reg.mask,
0);
if (err < 0)
return err;
sensor->hw->enabled_mask &= ~BIT(id);
}
return 0;
}
static ssize_t st_imu68_get_sampling_frequency(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct st_imu68_sensor *sensor = iio_priv(dev_get_drvdata(dev));
return sprintf(buf, "%d\n", sensor->odr);
}
static ssize_t st_imu68_set_sampling_frequency(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct st_imu68_sensor *sensor = iio_priv(dev_get_drvdata(dev));
int err, odr;
err = kstrtoint(buf, 10, &odr);
if (err < 0)
return err;
err = st_imu68_set_odr(sensor, odr);
return err < 0 ? err : count;
}
static IIO_DEV_ATTR_SAMP_FREQ_AVAIL(st_imu68_sysfs_sampling_frequency_avail);
static IIO_DEVICE_ATTR(in_accel_scale_available, 0444,
st_imu68_sysfs_scale_avail, NULL, 0);
static IIO_DEVICE_ATTR(in_anglvel_scale_available, 0444,
st_imu68_sysfs_scale_avail, NULL, 0);
static IIO_DEV_ATTR_SAMP_FREQ(S_IWUSR | S_IRUGO,
st_imu68_get_sampling_frequency,
st_imu68_set_sampling_frequency);
static struct attribute *st_imu68_acc_attributes[] = {
&iio_dev_attr_sampling_frequency.dev_attr.attr,
&iio_dev_attr_sampling_frequency_available.dev_attr.attr,
&iio_dev_attr_in_accel_scale_available.dev_attr.attr,
NULL,
};
static const struct attribute_group st_imu68_acc_attribute_group = {
.attrs = st_imu68_acc_attributes,
};
static const struct iio_info st_imu68_acc_info = {
.attrs = &st_imu68_acc_attribute_group,
.read_raw = st_imu68_read_raw,
.write_raw = st_imu68_write_raw,
};
static struct attribute *st_imu68_gyro_attributes[] = {
&iio_dev_attr_sampling_frequency.dev_attr.attr,
&iio_dev_attr_sampling_frequency_available.dev_attr.attr,
&iio_dev_attr_in_anglvel_scale_available.dev_attr.attr,
NULL,
};
static const struct attribute_group st_imu68_gyro_attribute_group = {
.attrs = st_imu68_gyro_attributes,
};
static const struct iio_info st_imu68_gyro_info = {
.attrs = &st_imu68_gyro_attribute_group,
.read_raw = st_imu68_read_raw,
.write_raw = st_imu68_write_raw,
};
static const unsigned long st_imu68_available_scan_masks[] = {0x7, 0x0};
static struct iio_dev *st_imu68_alloc_iiodev(struct st_imu68_hw *hw,
enum st_imu68_sensor_id id)
{
struct st_imu68_sensor *sensor;
struct iio_dev *iio_dev;
iio_dev = devm_iio_device_alloc(hw->dev, sizeof(*sensor));
if (!iio_dev)
return NULL;
iio_dev->modes = INDIO_DIRECT_MODE;
iio_dev->dev.parent = hw->dev;
iio_dev->available_scan_masks = st_imu68_available_scan_masks;
sensor = iio_priv(iio_dev);
sensor->id = id;
sensor->hw = hw;
sensor->odr = st_imu68_odr_table[id].odr_avl[0].hz;
sensor->gain = st_imu68_fs_table[id].fs_avl[0].gain;
switch (id) {
case ST_IMU68_ID_ACC:
iio_dev->channels = st_imu68_acc_channels;
iio_dev->num_channels = ARRAY_SIZE(st_imu68_acc_channels);
iio_dev->name = kasprintf(GFP_KERNEL, "%s_accel", hw->name);
iio_dev->info = &st_imu68_acc_info;
sensor->drdy_mask = ST_IMU68_INT_ACC_DRDY_MASK;
sensor->status_mask = ST_IMU68_ACC_STATUS_MASK;
break;
case ST_IMU68_ID_GYRO:
iio_dev->channels = st_imu68_gyro_channels;
iio_dev->num_channels = ARRAY_SIZE(st_imu68_gyro_channels);
iio_dev->name = kasprintf(GFP_KERNEL, "%s_gyro", hw->name);
iio_dev->info = &st_imu68_gyro_info;
sensor->drdy_mask = ST_IMU68_INT_GYRO_DRDY_MASK;
sensor->status_mask = ST_IMU68_GYRO_STATUS_MASK;
break;
default:
return NULL;
}
return iio_dev;
}
int st_imu68_probe(struct device *dev, int irq, const char *name,
const struct st_imu68_transfer_function *tf_ops)
{
struct st_imu68_hw *hw;
int err, i;
hw = devm_kzalloc(dev, sizeof(*hw), GFP_KERNEL);
if (!hw)
return -ENOMEM;
dev_set_drvdata(dev, (void *)hw);
mutex_init(&hw->lock);
hw->name = name;
hw->dev = dev;
hw->irq = irq;
hw->tf = tf_ops;
hw->enabled_mask = 0;
err = st_imu68_check_whoami(hw);
if (err < 0)
return err;
err = st_imu68_write_with_mask(hw, ST_IMU68_REG_CTRL4_ADDR,
ST_IMU68_GYRO_EN_MASK, 0x7);
if (err < 0)
return err;
err = st_imu68_write_with_mask(hw, ST_IMU68_REG_CTRL5_ADDR,
ST_IMU68_ACC_EN_MASK, 0x7);
if (err < 0)
return err;
for (i = 0; i < ST_IMU68_ID_MAX; i++) {
hw->iio_devs[i] = st_imu68_alloc_iiodev(hw, i);
if (!hw->iio_devs[i])
return -ENOMEM;
}
if (irq > 0) {
err = st_imu68_allocate_buffers(hw);
if (err)
return err;
err = st_imu68_allocate_triggers(hw);
if (err)
goto deallocate_iio_buffers;
}
for (i = 0; i < ST_IMU68_ID_MAX; i++) {
err = devm_iio_device_register(hw->dev, hw->iio_devs[i]);
if (err)
goto deallocate_iio_triggers;
}
return 0;
deallocate_iio_triggers:
if (irq > 0)
st_imu68_deallocate_triggers(hw);
deallocate_iio_buffers:
if (irq > 0)
st_imu68_deallocate_buffers(hw);
return err;
}
EXPORT_SYMBOL(st_imu68_probe);
int st_imu68_remove(struct device *dev)
{
struct st_imu68_hw *hw = dev_get_drvdata(dev);
if (hw->irq) {
st_imu68_deallocate_triggers(hw);
st_imu68_deallocate_buffers(hw);
}
return 0;
}
EXPORT_SYMBOL(st_imu68_remove);
MODULE_AUTHOR("Lorenzo Bianconi <lorenzo.bianconi@st.com>");
MODULE_AUTHOR("Mario Tesi <mario.tesi@st.com>");
MODULE_DESCRIPTION("STMicroelectronics st_imu68 driver");
MODULE_LICENSE("GPL v2");

View File

@ -0,0 +1,76 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* STMicroelectronics st_imu68 i2c driver
*
* Copyright 2016 STMicroelectronics Inc.
*
* Lorenzo Bianconi <lorenzo.bianconi@st.com>
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/slab.h>
#include <linux/of.h>
#include "st_imu68.h"
static int st_imu68_i2c_read(struct device *dev, u8 addr, int len, u8 *data)
{
return i2c_smbus_read_i2c_block_data_or_emulated(to_i2c_client(dev),
addr, len, data);
}
static int st_imu68_i2c_write(struct device *dev, u8 addr, int len, u8 *data)
{
return i2c_smbus_write_i2c_block_data(to_i2c_client(dev), addr, len,
data);
}
static const struct st_imu68_transfer_function st_imu68_transfer_fn = {
.read = st_imu68_i2c_read,
.write = st_imu68_i2c_write,
};
static int st_imu68_i2c_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
return st_imu68_probe(&client->dev, client->irq, client->name,
&st_imu68_transfer_fn);
}
static int st_imu68_i2c_remove(struct i2c_client *client)
{
st_imu68_remove(&client->dev);
return 0;
}
static const struct of_device_id st_imu68_i2c_of_match[] = {
{
.compatible = "st,lsm9ds1",
},
{},
};
MODULE_DEVICE_TABLE(of, st_imu68_i2c_of_match);
static const struct i2c_device_id st_imu68_i2c_id_table[] = {
{ ST_LSM9DS1_DEV_NAME },
{},
};
MODULE_DEVICE_TABLE(i2c, st_imu68_i2c_id_table);
static struct i2c_driver st_imu68_driver = {
.driver = {
.name = "st_imu68_i2c",
.of_match_table = of_match_ptr(st_imu68_i2c_of_match),
},
.probe = st_imu68_i2c_probe,
.remove = st_imu68_i2c_remove,
.id_table = st_imu68_i2c_id_table,
};
module_i2c_driver(st_imu68_driver);
MODULE_AUTHOR("Lorenzo Bianconi <lorenzo.bianconi@st.com>");
MODULE_DESCRIPTION("STMicroelectronics st_imu68 i2c driver");
MODULE_LICENSE("GPL v2");

View File

@ -0,0 +1,112 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* STMicroelectronics st_imu68 spi driver
*
* Copyright 2016 STMicroelectronics Inc.
*
* Lorenzo Bianconi <lorenzo.bianconi@st.com>
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/spi/spi.h>
#include <linux/slab.h>
#include <linux/of.h>
#include "st_imu68.h"
#define SENSORS_SPI_READ BIT(7)
static int st_imu68_spi_read(struct device *dev, u8 addr, int len, u8 *data)
{
struct spi_device *spi = to_spi_device(dev);
struct st_imu68_hw *hw = spi_get_drvdata(spi);
int err;
struct spi_transfer xfers[] = {
{
.tx_buf = hw->tb.tx_buf,
.bits_per_word = 8,
.len = 1,
},
{
.rx_buf = hw->tb.rx_buf,
.bits_per_word = 8,
.len = len,
}
};
hw->tb.tx_buf[0] = addr | SENSORS_SPI_READ;
err = spi_sync_transfer(spi, xfers, ARRAY_SIZE(xfers));
if (err < 0)
return err;
memcpy(data, hw->tb.rx_buf, len * sizeof(u8));
return len;
}
static int st_imu68_spi_write(struct device *dev, u8 addr, int len, u8 *data)
{
struct st_imu68_hw *hw;
struct spi_device *spi;
if (len >= ST_IMU68_TX_MAX_LENGTH)
return -ENOMEM;
spi = to_spi_device(dev);
hw = spi_get_drvdata(spi);
hw->tb.tx_buf[0] = addr;
memcpy(&hw->tb.tx_buf[1], data, len);
return spi_write(spi, hw->tb.tx_buf, len + 1);
}
static const struct st_imu68_transfer_function st_imu68_transfer_fn = {
.read = st_imu68_spi_read,
.write = st_imu68_spi_write,
};
static int st_imu68_spi_probe(struct spi_device *spi)
{
return st_imu68_probe(&spi->dev, spi->irq, spi->modalias,
&st_imu68_transfer_fn);
}
static int st_imu68_spi_remove(struct spi_device *spi)
{
st_imu68_remove(&spi->dev);
return 0;
}
static const struct of_device_id st_imu68_spi_of_match[] = {
{
.compatible = "st,lsm9ds1",
},
{},
};
MODULE_DEVICE_TABLE(of, st_imu68_spi_of_match);
static const struct spi_device_id st_imu68_spi_id_table[] = {
{ ST_LSM9DS1_DEV_NAME },
{},
};
MODULE_DEVICE_TABLE(spi, st_imu68_spi_id_table);
static struct spi_driver st_imu68_driver = {
.driver = {
.name = "st_imu68_spi",
.of_match_table = of_match_ptr(st_imu68_spi_of_match),
},
.probe = st_imu68_spi_probe,
.remove = st_imu68_spi_remove,
.id_table = st_imu68_spi_id_table,
};
module_spi_driver(st_imu68_driver);
MODULE_AUTHOR("Lorenzo Bianconi <lorenzo.bianconi@st.com>");
MODULE_DESCRIPTION("STMicroelectronics st_imu68 spi driver");
MODULE_LICENSE("GPL v2");

View File

@ -0,0 +1,3 @@
CONFIG_IIO_ST_IMU68=m
CONFIG_IIO_ST_IMU68_I2C=m
CONFIG_IIO_ST_IMU68_SPI=m