drivers:iio:stm:pressure: Add support to ST MEMS LPS22DF pressure sensor

Added support to LPS22DF ST MEMS pressure and temperature sensor

List of supported features:
 - Configurable ODRs [Power_Off ... 200] Hz
 - Sensor Event Timestamp support
 - Read single raw data values for pressure and temperature
 - FIFO:
    > Pressure data stored in FIFO
    > HW Watermark configuration
    > Custom Flush command / event support

Signed-off-by: mario tesi <mario.tesi@st.com>
Change-Id: Ia3be1f93498c6c46345a067c6323968a94014662
This commit is contained in:
mario tesi 2021-09-14 18:45:54 +02:00 committed by Mario Tesi
parent d5a9842abc
commit 28089ed3d7
8 changed files with 1133 additions and 0 deletions

View File

@ -25,4 +25,26 @@ config ST_LPS22HH_SPI_IIO
tristate
depends on ST_LPS22HH_IIO
config ST_LPS22DF_IIO
tristate "STMicroelectronics LPS22DF sensor"
depends on (I2C || SPI_MASTER) && SYSFS
select IIO_BUFFER
select IIO_KFIFO_BUF
select ST_LPS22DF_I2C_IIO if (I2C)
select ST_LPS22DF_SPI_IIO if (SPI)
help
Say yes here to build support for the ST MEMS LPS22DF pressure and
temperature sensor.
This driver can be built as a module. The module will be called
st-lps22df.
config ST_LPS22DF_I2C_IIO
tristate
depends on ST_LPS22DF_IIO
config ST_LPS22DF_SPI_IIO
tristate
depends on ST_LPS22DF_IIO
endmenu

View File

@ -9,3 +9,10 @@ obj-$(CONFIG_ST_LPS22HH_I2C_IIO) += st_lps22hh_i2c.o
obj-$(CONFIG_ST_LPS22HH_SPI_IIO) += st_lps22hh_spi.o
st_lps22hh-y += st_lps22hh_core.o st_lps22hh_buffer.o
obj-$(CONFIG_ST_LPS22DF_IIO) += st_lps22df.o
obj-$(CONFIG_ST_LPS22DF_I2C_IIO) += st_lps22df_i2c.o
obj-$(CONFIG_ST_LPS22DF_SPI_IIO) += st_lps22df_spi.o
st_lps22df-y += st_lps22df_core.o st_lps22df_buffer.o

View File

@ -0,0 +1,138 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* STMicroelectronics lps22df driver
*
* Copyright 2021 STMicroelectronics Inc.
*
* Mario Tesi <mario.tesi@st.com>
*/
#ifndef __ST_LPS22DF_H
#define __ST_LPS22DF_H
#include <linux/module.h>
#include <linux/types.h>
#include <linux/iio/iio.h>
#include <linux/iio/trigger.h>
#define ST_LPS22DF_MAX_FIFO_LENGTH 127
#define ST_LPS22DF_INTERRUPT_CFG_ADDR 0x0b
#define ST_LPS22DF_LIR_MASK BIT(2)
#define ST_LPS22DF_WHO_AM_I_ADDR 0x0f
#define ST_LPS22DF_WHO_AM_I_VAL 0xb4
#define ST_LPS22DF_CTRL_REG1_ADDR 0x10
#define ST_LPS22DF_AVG_MASK GENMASK(2, 0)
#define ST_LPS22DF_ODR_MASK GENMASK(6, 3)
#define ST_LPS22DF_CTRL_REG2_ADDR 0x11
#define ST_LPS22DF_SWRESET_MASK BIT(2)
#define ST_LPS22DF_BDU_MASK BIT(3)
#define ST_LPS22DF_EN_LPFP_MASK BIT(4)
#define ST_LPS22DF_BOOT_MASK BIT(7)
#define ST_LPS22DF_CTRL3_ADDR 0x12
#define ST_LPS22DF_IF_ADD_INC_MASK BIT(0)
#define ST_LPS22DF_PP_OD_MASK BIT(1)
#define ST_LPS22DF_INT_H_L_MASK BIT(3)
#define ST_LPS22DF_CTRL4_ADDR 0x13
#define ST_LPS22DF_INT_F_WTM_MASK BIT(1)
#define ST_LPS22DF_FIFO_CTRL_ADDR 0x14
#define ST_LPS22DF_FIFO_MODE_MASK GENMASK(1, 0)
#define ST_LPS22DF_FIFO_WTM_ADDR 0x15
#define ST_LPS22DF_FIFO_THS_MASK GENMASK(6, 0)
#define ST_LPS22DF_FIFO_STATUS1_ADDR 0x25
#define ST_LPS22DF_FIFO_SRC_DIFF_MASK GENMASK(7, 0)
#define ST_LPS22DF_FIFO_STATUS2_ADDR 0x26
#define ST_LPS22DF_FIFO_WTM_IA_MASK BIT(7)
#define ST_LPS22DF_PRESS_OUT_XL_ADDR 0x28
#define ST_LPS22DF_TEMP_OUT_L_ADDR 0x2b
#define ST_LPS22DF_FIFO_DATA_OUT_PRESS_XL_ADDR 0x78
#define ST_LPS22DF_PRESS_FS_AVL_GAIN (1000000000UL / 4096UL)
#define ST_LPS22DF_TEMP_FS_AVL_GAIN (1000000000UL / 100UL)
#define ST_LPS22DF_ODR_LIST_NUM 9
enum st_lps22df_sensor_type {
ST_LPS22DF_PRESS = 0,
ST_LPS22DF_TEMP,
ST_LPS22DF_SENSORS_NUMB,
};
enum st_lps22df_fifo_mode {
ST_LPS22DF_BYPASS = 0x0,
ST_LPS22DF_STREAM = 0x2,
};
#define ST_LPS22DF_PRESS_SAMPLE_LEN 3
#define ST_LPS22DF_TEMP_SAMPLE_LEN 2
#define ST_LPS22DF_TX_MAX_LENGTH 64
#define ST_LPS22DF_RX_MAX_LENGTH ((ST_LPS22DF_MAX_FIFO_LENGTH + 1) * \
ST_LPS22DF_PRESS_SAMPLE_LEN)
struct st_lps22df_transfer_buffer {
u8 rx_buf[ST_LPS22DF_RX_MAX_LENGTH];
u8 tx_buf[ST_LPS22DF_TX_MAX_LENGTH] ____cacheline_aligned;
};
struct st_lps22df_transfer_function {
int (*write)(struct device *dev, u8 addr, int len, u8 *data);
int (*read)(struct device *dev, u8 addr, int len, u8 *data);
};
struct st_lps22df_hw {
struct device *dev;
int irq;
struct mutex fifo_lock;
struct mutex lock;
u8 watermark;
struct iio_dev *iio_devs[ST_LPS22DF_SENSORS_NUMB];
u8 enable_mask;
u8 odr;
s64 last_fifo_ts;
s64 delta_ts;
s64 ts_irq;
s64 ts;
const struct st_lps22df_transfer_function *tf;
struct st_lps22df_transfer_buffer tb;
};
struct st_lps22df_sensor {
struct st_lps22df_hw *hw;
enum st_lps22df_sensor_type type;
char name[32];
u32 gain;
u8 odr;
};
int st_lps22df_common_probe(struct device *dev, int irq, const char *name,
const struct st_lps22df_transfer_function *tf_ops);
int st_lps22df_write_with_mask(struct st_lps22df_hw *hw, u8 addr, u8 mask,
u8 data);
int st_lps22df_allocate_buffers(struct st_lps22df_hw *hw);
int st_lps22df_set_enable(struct st_lps22df_sensor *sensor, bool enable);
ssize_t st_lps22df_sysfs_set_hwfifo_watermark(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count);
ssize_t st_lps22df_sysfs_flush_fifo(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size);
#endif /* __ST_LPS22DF_H */

View File

@ -0,0 +1,299 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* STMicroelectronics lps22df buffer driver
*
* Copyright 2021 STMicroelectronics Inc.
*
* Mario Tesi <mario.tesi@st.com>
*/
#include <linux/interrupt.h>
#include <linux/iio/iio.h>
#include <linux/iio/buffer.h>
#include <linux/iio/kfifo_buf.h>
#include <linux/iio/events.h>
#include "st_lps22df.h"
#define ST_LPS22DF_EWMA_LEVEL 96
#define ST_LPS22DF_EWMA_DIV 128
static inline s64 st_lps22df_ewma(s64 old, s64 new, int weight)
{
s64 diff, incr;
diff = new - old;
incr = div_s64((ST_LPS22DF_EWMA_DIV - weight) * diff,
ST_LPS22DF_EWMA_DIV);
return old + incr;
}
static inline s64 st_lps22df_get_time_ns(struct st_lps22df_hw *hw)
{
return iio_get_time_ns(hw->iio_devs[ST_LPS22DF_PRESS]);
}
static int st_lps22df_set_fifo_mode(struct st_lps22df_hw *hw,
enum st_lps22df_fifo_mode mode)
{
switch (mode) {
case ST_LPS22DF_BYPASS:
case ST_LPS22DF_STREAM:
break;
default:
return -EINVAL;
}
return st_lps22df_write_with_mask(hw, ST_LPS22DF_FIFO_CTRL_ADDR,
ST_LPS22DF_FIFO_MODE_MASK, mode);
}
static int st_lps22df_update_fifo_watermark(struct st_lps22df_hw *hw, u8 val)
{
int err;
err = st_lps22df_write_with_mask(hw, ST_LPS22DF_FIFO_WTM_ADDR,
ST_LPS22DF_FIFO_THS_MASK, val);
if (err < 0)
return err;
hw->watermark = val;
return 0;
}
ssize_t st_lps22df_sysfs_set_hwfifo_watermark(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct st_lps22df_sensor *sensor = iio_priv(dev_get_drvdata(dev));
int err, watermark;
err = kstrtoint(buf, 10, &watermark);
if (err < 0)
return err;
if (watermark < 1 || watermark > ST_LPS22DF_MAX_FIFO_LENGTH)
return -EINVAL;
err = st_lps22df_update_fifo_watermark(sensor->hw, watermark);
return err < 0 ? err : count;
}
static int st_lps22df_read_fifo(struct st_lps22df_hw *hw, s64 delta_ts)
{
u8 iio_buff[ALIGN(sizeof(u32) + sizeof(s64), sizeof(s64))];
u8 buff[ST_LPS22DF_RX_MAX_LENGTH];
int err, i, read_len;
__le16 fifo_status;
err = hw->tf->read(hw->dev, ST_LPS22DF_FIFO_STATUS1_ADDR,
sizeof(fifo_status), (u8 *)&fifo_status);
if (err < 0)
return err;
read_len = (le16_to_cpu(fifo_status) & ST_LPS22DF_FIFO_SRC_DIFF_MASK) *
ST_LPS22DF_PRESS_SAMPLE_LEN;
if (!read_len)
return 0;
err = hw->tf->read(hw->dev, ST_LPS22DF_FIFO_DATA_OUT_PRESS_XL_ADDR,
read_len, buff);
if (err < 0)
return err;
for (i = 0; i < read_len; i += ST_LPS22DF_PRESS_SAMPLE_LEN) {
memcpy(iio_buff, buff + i, ST_LPS22DF_PRESS_SAMPLE_LEN);
iio_push_to_buffers_with_timestamp(
hw->iio_devs[ST_LPS22DF_PRESS],
iio_buff, hw->ts);
hw->ts += delta_ts;
}
hw->last_fifo_ts = hw->ts;
return read_len;
}
ssize_t st_lps22df_sysfs_flush_fifo(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
{
struct iio_dev *indio_dev = dev_get_drvdata(dev);
struct st_lps22df_sensor *sensor = iio_priv(indio_dev);
struct st_lps22df_hw *hw = sensor->hw;
int len, dir;
s64 fts;
mutex_lock(&hw->fifo_lock);
len = st_lps22df_read_fifo(hw, hw->delta_ts);
hw->ts = st_lps22df_get_time_ns(hw);
hw->ts_irq = hw->ts;
/* flush event timestamp must match with last sample pushed in fifo */
if (len)
fts = hw->ts;
else
fts = hw->last_fifo_ts;
mutex_unlock(&hw->fifo_lock);
dir = len > 0 ? IIO_EV_DIR_FIFO_DATA : IIO_EV_DIR_FIFO_EMPTY;
iio_push_event(indio_dev,
IIO_UNMOD_EVENT_CODE(IIO_PRESSURE, -1,
IIO_EV_TYPE_FIFO_FLUSH, dir),
fts);
return size;
}
static irqreturn_t st_lps22df_irq_handler(int irq, void *private)
{
struct st_lps22df_hw *hw = private;
s64 delta_ts, ts = st_lps22df_get_time_ns(hw);
delta_ts = div_s64((ts - hw->ts_irq), hw->watermark);
if (hw->odr >= 50)
hw->delta_ts = st_lps22df_ewma(hw->delta_ts, delta_ts,
ST_LPS22DF_EWMA_LEVEL);
else
hw->delta_ts = delta_ts;
hw->ts_irq = ts;
return IRQ_WAKE_THREAD;
}
static irqreturn_t st_lps22df_irq_thread(int irq, void *private)
{
struct st_lps22df_hw *hw = private;
mutex_lock(&hw->fifo_lock);
st_lps22df_read_fifo(hw, hw->delta_ts);
mutex_unlock(&hw->fifo_lock);
return IRQ_HANDLED;
}
static int st_lps22df_buffer_preenable(struct iio_dev *indio_dev)
{
struct st_lps22df_sensor *sensor = iio_priv(indio_dev);
struct st_lps22df_hw *hw = sensor->hw;
int err;
err = st_lps22df_set_fifo_mode(sensor->hw, ST_LPS22DF_STREAM);
if (err < 0)
return err;
err = st_lps22df_update_fifo_watermark(hw, hw->watermark);
if (err < 0)
return err;
err = st_lps22df_write_with_mask(sensor->hw, ST_LPS22DF_CTRL4_ADDR,
ST_LPS22DF_INT_F_WTM_MASK, true);
if (err < 0)
return err;
err = st_lps22df_set_enable(sensor, true);
if (err < 0)
return err;
hw->delta_ts = div_s64(1000000000UL, hw->odr);
hw->ts = st_lps22df_get_time_ns(hw);
hw->ts_irq = hw->ts;
return 0;
}
static int st_lps22df_buffer_postdisable(struct iio_dev *indio_dev)
{
struct st_lps22df_sensor *sensor = iio_priv(indio_dev);
int err;
err = st_lps22df_set_fifo_mode(sensor->hw, ST_LPS22DF_BYPASS);
if (err < 0)
return err;
err = st_lps22df_write_with_mask(sensor->hw, ST_LPS22DF_CTRL4_ADDR,
ST_LPS22DF_INT_F_WTM_MASK, false);
if (err < 0)
return err;
return st_lps22df_set_enable(sensor, false);
}
static const struct iio_buffer_setup_ops st_lps22df_buffer_ops = {
.preenable = st_lps22df_buffer_preenable,
.postdisable = st_lps22df_buffer_postdisable,
};
int st_lps22df_allocate_buffers(struct st_lps22df_hw *hw)
{
struct iio_buffer *buffer;
unsigned long irq_type;
u8 int_active = 0;
int err;
irq_type = irqd_get_trigger_type(irq_get_irq_data(hw->irq));
switch (irq_type) {
case IRQF_TRIGGER_HIGH:
case IRQF_TRIGGER_RISING:
int_active = 0;
break;
case IRQF_TRIGGER_LOW:
case IRQF_TRIGGER_FALLING:
int_active = 1;
break;
default:
dev_info(hw->dev, "mode %lx unsupported\n", irq_type);
return -EINVAL;
}
/* int pin active low */
if (device_property_read_bool(hw->dev, "int-active-low")) {
err = st_lps22df_write_with_mask(hw, ST_LPS22DF_CTRL3_ADDR,
ST_LPS22DF_INT_H_L_MASK, 1);
if (err < 0)
return err;
}
/* int pin open drain configuration */
if (device_property_read_bool(hw->dev, "int-open-drain")) {
err = st_lps22df_write_with_mask(hw, ST_LPS22DF_CTRL3_ADDR,
ST_LPS22DF_PP_OD_MASK, 1);
if (err < 0)
return err;
}
err = st_lps22df_write_with_mask(hw, ST_LPS22DF_CTRL3_ADDR,
ST_LPS22DF_INT_H_L_MASK,
int_active);
if (err < 0)
return err;
err = devm_request_threaded_irq(hw->dev, hw->irq,
st_lps22df_irq_handler,
st_lps22df_irq_thread,
irq_type | IRQF_ONESHOT,
"lps22df", hw);
if (err)
return err;
buffer = devm_iio_kfifo_allocate(hw->dev);
if (!buffer)
return -ENOMEM;
iio_device_attach_buffer(hw->iio_devs[ST_LPS22DF_PRESS], buffer);
hw->iio_devs[ST_LPS22DF_PRESS]->modes |= INDIO_BUFFER_SOFTWARE;
hw->iio_devs[ST_LPS22DF_PRESS]->setup_ops = &st_lps22df_buffer_ops;
return 0;
}
MODULE_DESCRIPTION("STMicroelectronics lps22df buffer driver");
MODULE_AUTHOR("Mario Tesi <mario.tesi@st.com>");
MODULE_LICENSE("GPL v2");

View File

@ -0,0 +1,461 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* STMicroelectronics lps22df driver
*
* Copyright 2021 STMicroelectronics Inc.
*
* Mario Tesi <mario.tesi@st.com>
*/
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/types.h>
#include <linux/mutex.h>
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
#include <linux/iio/trigger.h>
#include <linux/delay.h>
#include <asm/unaligned.h>
#include "st_lps22df.h"
struct st_lps22df_odr_table_t {
u8 addr;
u8 mask;
u8 odr_avl[ST_LPS22DF_ODR_LIST_NUM];
};
const static struct st_lps22df_odr_table_t st_lps22df_odr_table = {
.addr = ST_LPS22DF_CTRL_REG1_ADDR,
.mask = ST_LPS22DF_ODR_MASK,
.odr_avl = { 0, 1, 4, 10, 25, 50, 75, 100, 200 },
};
const struct iio_event_spec st_lps22df_fifo_flush_event = {
.type = IIO_EV_TYPE_FIFO_FLUSH,
.dir = IIO_EV_DIR_EITHER,
};
static const struct iio_chan_spec st_lps22df_press_channels[] = {
{
.type = IIO_PRESSURE,
.address = ST_LPS22DF_PRESS_OUT_XL_ADDR,
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
BIT(IIO_CHAN_INFO_SCALE),
.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),
.channel2 = IIO_NO_MOD,
.scan_index = 0,
.scan_type = {
.sign = 'u',
.realbits = 24,
.storagebits = 32,
.endianness = IIO_LE,
},
},
{
.type = IIO_PRESSURE,
.scan_index = -1,
.indexed = -1,
.event_spec = &st_lps22df_fifo_flush_event,
.num_event_specs = 1,
},
IIO_CHAN_SOFT_TIMESTAMP(1)
};
static const struct iio_chan_spec st_lps22df_temp_channels[] = {
{
.type = IIO_TEMP,
.address = ST_LPS22DF_TEMP_OUT_L_ADDR,
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
BIT(IIO_CHAN_INFO_SCALE),
.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),
.channel2 = IIO_NO_MOD,
.scan_index = 0,
.scan_type = {
.sign = 's',
.realbits = 16,
.storagebits = 16,
.endianness = IIO_LE,
},
},
};
int st_lps22df_write_with_mask(struct st_lps22df_hw *hw, u8 addr, u8 mask,
u8 val)
{
int err;
u8 data;
mutex_lock(&hw->lock);
err = hw->tf->read(hw->dev, addr, sizeof(data), &data);
if (err < 0)
goto unlock;
data = (data & ~mask) | ((val << __ffs(mask)) & mask);
err = hw->tf->write(hw->dev, addr, sizeof(data), &data);
unlock:
mutex_unlock(&hw->lock);
return err;
}
static int st_lps22df_check_whoami(struct st_lps22df_hw *hw)
{
int err;
u8 data;
err = hw->tf->read(hw->dev, ST_LPS22DF_WHO_AM_I_ADDR, sizeof(data),
&data);
if (err < 0) {
dev_err(hw->dev, "failed to read Who-Am-I register\n");
return err;
}
if (data != ST_LPS22DF_WHO_AM_I_VAL) {
dev_err(hw->dev, "Who-Am-I value not valid (%x)\n", data);
return -ENODEV;
}
return 0;
}
static int st_lps22df_get_odr(struct st_lps22df_sensor *sensor, u8 odr)
{
int i;
for (i = 0; i < ST_LPS22DF_ODR_LIST_NUM; i++) {
if (st_lps22df_odr_table.odr_avl[i] == odr)
break;
}
return i == ST_LPS22DF_ODR_LIST_NUM ? -EINVAL : i;
}
int st_lps22df_set_enable(struct st_lps22df_sensor *sensor, bool enable)
{
struct st_lps22df_hw *hw = sensor->hw;
u32 max_odr = enable ? sensor->odr : 0;
int i;
for (i = 0; i < ST_LPS22DF_SENSORS_NUMB; i++) {
if (sensor->type == i)
continue;
if (hw->enable_mask & BIT(i)) {
struct st_lps22df_sensor *temp;
temp = iio_priv(hw->iio_devs[i]);
max_odr = max_t(u32, max_odr, temp->odr);
}
}
if (max_odr != hw->odr) {
int err, ret;
ret = st_lps22df_get_odr(sensor, max_odr);
if (ret < 0)
return ret;
err = st_lps22df_write_with_mask(hw, st_lps22df_odr_table.addr,
st_lps22df_odr_table.mask,
ret);
if (err < 0)
return err;
hw->odr = max_odr;
}
if (enable)
hw->enable_mask |= BIT(sensor->type);
else
hw->enable_mask &= ~BIT(sensor->type);
return 0;
}
int st_lps22df_init_sensors(struct st_lps22df_hw *hw)
{
int err;
/* reboot memory content */
err = st_lps22df_write_with_mask(hw, ST_LPS22DF_CTRL_REG2_ADDR,
ST_LPS22DF_BOOT_MASK, 1);
if (err < 0)
return err;
usleep_range(8000, 10000);
/* soft reset the device on power on */
err = st_lps22df_write_with_mask(hw, ST_LPS22DF_CTRL_REG2_ADDR,
ST_LPS22DF_SWRESET_MASK, 1);
if (err < 0)
return err;
usleep_range(100, 200);
/* enable latched interrupt mode */
err = st_lps22df_write_with_mask(hw, ST_LPS22DF_INTERRUPT_CFG_ADDR,
ST_LPS22DF_LIR_MASK, 1);
if (err < 0)
return err;
/* enable BDU */
return st_lps22df_write_with_mask(hw, ST_LPS22DF_CTRL_REG2_ADDR,
ST_LPS22DF_BDU_MASK, 1);
}
static ssize_t
st_lps22df_get_sampling_frequency_avail(struct device *dev,
struct device_attribute *attr,
char *buf)
{
int i, len = 0;
for (i = 1; i < ST_LPS22DF_ODR_LIST_NUM; i++) {
len += scnprintf(buf + len, PAGE_SIZE - len, "%d ",
st_lps22df_odr_table.odr_avl[i]);
}
buf[len - 1] = '\n';
return len;
}
static ssize_t
st_lps22df_sysfs_get_hwfifo_watermark(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct st_lps22df_sensor *sensor = iio_priv(dev_get_drvdata(dev));
return sprintf(buf, "%d\n", sensor->hw->watermark);
}
static ssize_t
st_lps22df_sysfs_get_hwfifo_watermark_max(struct device *dev,
struct device_attribute *attr,
char *buf)
{
return sprintf(buf, "%d\n", ST_LPS22DF_MAX_FIFO_LENGTH);
}
static int st_lps22df_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *ch,
int *val, int *val2, long mask)
{
struct st_lps22df_sensor *sensor = iio_priv(indio_dev);
struct st_lps22df_hw *hw = sensor->hw;
int ret, delay;
switch (mask) {
case IIO_CHAN_INFO_RAW: {
u8 data[4] = {};
u8 len;
ret = iio_device_claim_direct_mode(indio_dev);
if (ret)
return ret;
ret = st_lps22df_set_enable(sensor, true);
if (ret < 0)
goto unlock;
/* wait at least 10% more than one odr */
delay = 1100000 / sensor->odr;
usleep_range(delay, 2 * delay);
len = ch->scan_type.realbits >> 3;
ret = hw->tf->read(hw->dev, ch->address, len, data);
if (ret < 0)
goto unlock;
if (sensor->type == ST_LPS22DF_PRESS)
*val = (s32)get_unaligned_le32(data);
else if (sensor->type == ST_LPS22DF_TEMP)
*val = (s16)get_unaligned_le16(data);
unlock:
ret = st_lps22df_set_enable(sensor, false);
iio_device_release_direct_mode(indio_dev);
ret = ret < 0 ? ret : IIO_VAL_INT;
break;
}
case IIO_CHAN_INFO_SCALE:
switch (ch->type) {
case IIO_TEMP:
*val = 1000;
*val2 = sensor->gain;
ret = IIO_VAL_FRACTIONAL;
break;
case IIO_PRESSURE:
*val = 0;
*val2 = sensor->gain;
ret = IIO_VAL_INT_PLUS_NANO;
break;
default:
ret = -ENODEV;
break;
}
break;
case IIO_CHAN_INFO_SAMP_FREQ:
*val = sensor->odr;
ret = IIO_VAL_INT;
break;
default:
ret = -EINVAL;
break;
}
return ret;
}
static int st_lps22df_write_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *ch,
int val, int val2, long mask)
{
struct st_lps22df_sensor *sensor = iio_priv(indio_dev);
int ret;
switch (mask) {
case IIO_CHAN_INFO_SAMP_FREQ:
ret = st_lps22df_get_odr(sensor, val);
if (ret > 0)
sensor->odr = val;
break;
default:
ret = -EINVAL;
break;
}
return ret;
}
static IIO_DEV_ATTR_SAMP_FREQ_AVAIL(st_lps22df_get_sampling_frequency_avail);
static IIO_DEVICE_ATTR(hwfifo_watermark, 0644,
st_lps22df_sysfs_get_hwfifo_watermark,
st_lps22df_sysfs_set_hwfifo_watermark, 0);
static IIO_DEVICE_ATTR(hwfifo_watermark_max, 0444,
st_lps22df_sysfs_get_hwfifo_watermark_max, NULL, 0);
static IIO_DEVICE_ATTR(hwfifo_flush, 0200, NULL,
st_lps22df_sysfs_flush_fifo, 0);
static struct attribute *st_lps22df_press_attributes[] = {
&iio_dev_attr_sampling_frequency_available.dev_attr.attr,
&iio_dev_attr_hwfifo_watermark.dev_attr.attr,
&iio_dev_attr_hwfifo_watermark_max.dev_attr.attr,
&iio_dev_attr_hwfifo_flush.dev_attr.attr,
NULL,
};
static struct attribute *st_lps22df_temp_attributes[] = {
&iio_dev_attr_sampling_frequency_available.dev_attr.attr,
NULL,
};
static const struct attribute_group st_lps22df_press_attribute_group = {
.attrs = st_lps22df_press_attributes,
};
static const struct attribute_group st_lps22df_temp_attribute_group = {
.attrs = st_lps22df_temp_attributes,
};
static const struct iio_info st_lps22df_press_info = {
.attrs = &st_lps22df_press_attribute_group,
.read_raw = st_lps22df_read_raw,
.write_raw = st_lps22df_write_raw,
};
static const struct iio_info st_lps22df_temp_info = {
.attrs = &st_lps22df_temp_attribute_group,
.read_raw = st_lps22df_read_raw,
.write_raw = st_lps22df_write_raw,
};
int st_lps22df_common_probe(struct device *dev, int irq, const char *name,
const struct st_lps22df_transfer_function *tf_ops)
{
struct st_lps22df_sensor *sensor;
struct st_lps22df_hw *hw;
struct iio_dev *iio_dev;
int err, i;
hw = devm_kzalloc(dev, sizeof(*hw), GFP_KERNEL);
if (!hw)
return -ENOMEM;
dev_set_drvdata(dev, (void *)hw);
hw->dev = dev;
hw->tf = tf_ops;
hw->irq = irq;
/* set initial watermark */
hw->watermark = 1;
mutex_init(&hw->lock);
mutex_init(&hw->fifo_lock);
err = st_lps22df_check_whoami(hw);
if (err < 0)
return err;
for (i = 0; i < ST_LPS22DF_SENSORS_NUMB; i++) {
iio_dev = devm_iio_device_alloc(dev, sizeof(*sensor));
if (!iio_dev)
return -ENOMEM;
hw->iio_devs[i] = iio_dev;
sensor = iio_priv(iio_dev);
sensor->hw = hw;
sensor->type = i;
sensor->odr = 1;
switch (i) {
case ST_LPS22DF_PRESS:
sensor->gain = ST_LPS22DF_PRESS_FS_AVL_GAIN;
scnprintf(sensor->name, sizeof(sensor->name),
"%s_press", name);
iio_dev->channels = st_lps22df_press_channels;
iio_dev->num_channels =
ARRAY_SIZE(st_lps22df_press_channels);
iio_dev->info = &st_lps22df_press_info;
break;
case ST_LPS22DF_TEMP:
sensor->gain = ST_LPS22DF_TEMP_FS_AVL_GAIN;
scnprintf(sensor->name, sizeof(sensor->name),
"%s_temp", name);
iio_dev->channels = st_lps22df_temp_channels;
iio_dev->num_channels =
ARRAY_SIZE(st_lps22df_temp_channels);
iio_dev->info = &st_lps22df_temp_info;
break;
default:
return -EINVAL;
}
iio_dev->name = sensor->name;
iio_dev->modes = INDIO_DIRECT_MODE;
}
err = st_lps22df_init_sensors(hw);
if (err < 0)
return err;
if (irq > 0) {
err = st_lps22df_allocate_buffers(hw);
if (err < 0)
return err;
}
for (i = 0; i < ST_LPS22DF_SENSORS_NUMB; i++) {
err = devm_iio_device_register(dev, hw->iio_devs[i]);
if (err)
return err;
}
return 0;
}
EXPORT_SYMBOL(st_lps22df_common_probe);
MODULE_DESCRIPTION("STMicroelectronics lps22df driver");
MODULE_AUTHOR("Mario Tesi <mario.tesi@st.com>");
MODULE_LICENSE("GPL v2");

View File

@ -0,0 +1,103 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* STMicroelectronics lps22df i2c driver
*
* Copyright 2021 STMicroelectronics Inc.
*
* Mario Tesi <mario.tesi@st.com>
*/
#include <linux/i2c.h>
#include "st_lps22df.h"
static int st_lps22df_i2c_read(struct device *dev, u8 addr, int len, u8 *data)
{
struct st_lps22df_hw *hw = dev_get_drvdata(dev);
struct i2c_client *client = to_i2c_client(dev);
struct i2c_msg msg[2];
int ret;
if (len >= ST_LPS22DF_RX_MAX_LENGTH)
return -ENOMEM;
msg[0].addr = client->addr;
msg[0].flags = client->flags;
msg[0].len = 1;
msg[0].buf = &addr;
msg[1].addr = client->addr;
msg[1].flags = client->flags | I2C_M_RD;
msg[1].len = len;
msg[1].buf = hw->tb.rx_buf;
ret = i2c_transfer(client->adapter, msg, 2);
if (ret < 0)
return ret;
memcpy(data, hw->tb.rx_buf, len * sizeof(u8));
return 0;
}
static int st_lps22df_i2c_write(struct device *dev, u8 addr, int len, u8 *data)
{
struct st_lps22df_hw *hw = dev_get_drvdata(dev);
struct i2c_client *client = to_i2c_client(dev);
struct i2c_msg msg;
int ret;
if (len >= ST_LPS22DF_TX_MAX_LENGTH)
return -ENOMEM;
hw->tb.tx_buf[0] = addr;
memcpy(&hw->tb.tx_buf[1], data, len);
msg.addr = client->addr;
msg.flags = client->flags;
msg.len = len + 1;
msg.buf = hw->tb.tx_buf;
ret = i2c_transfer(client->adapter, &msg, 1);
return ret < 0 ? ret : 0;
}
static const struct st_lps22df_transfer_function st_lps22df_tf_i2c = {
.write = st_lps22df_i2c_write,
.read = st_lps22df_i2c_read,
};
static int st_lps22df_i2c_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
return st_lps22df_common_probe(&client->dev, client->irq, client->name,
&st_lps22df_tf_i2c);
}
static const struct i2c_device_id st_lps22df_ids[] = {
{ "lps22df" },
{}
};
MODULE_DEVICE_TABLE(i2c, st_lps22df_ids);
static const struct of_device_id st_lps22df_id_table[] = {
{ .compatible = "st,lps22df" },
{},
};
MODULE_DEVICE_TABLE(of, st_lps22df_id_table);
static struct i2c_driver st_lps22df_i2c_driver = {
.driver = {
.owner = THIS_MODULE,
.name = "st_lps22df_i2c",
.of_match_table = of_match_ptr(st_lps22df_id_table),
},
.probe = st_lps22df_i2c_probe,
.id_table = st_lps22df_ids,
};
module_i2c_driver(st_lps22df_i2c_driver);
MODULE_DESCRIPTION("STMicroelectronics lps22df i2c driver");
MODULE_AUTHOR("Mario Tesi <mario.tesi@st.com>");
MODULE_LICENSE("GPL v2");

View File

@ -0,0 +1,100 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* STMicroelectronics lps22df spi driver
*
* Copyright 2021 STMicroelectronics Inc.
*
* Mario Tesi <mario.tesi@st.com>
*/
#include <linux/spi/spi.h>
#include "st_lps22df.h"
#define ST_SENSORS_SPI_READ 0x80
static int st_lps22df_spi_read(struct device *dev, u8 addr, int len, u8 *data)
{
struct spi_device *spi = to_spi_device(dev);
struct st_lps22df_hw *hw = spi_get_drvdata(spi);
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,
}
};
int err;
if (len >= ST_LPS22DF_RX_MAX_LENGTH)
return -ENOMEM;
hw->tb.tx_buf[0] = addr | ST_SENSORS_SPI_READ;
err = spi_sync_transfer(spi, xfers, ARRAY_SIZE(xfers));
if (err)
return err;
memcpy(data, hw->tb.rx_buf, len);
return err;
}
static int st_lps22df_spi_write(struct device *dev, u8 addr, int len, u8 *data)
{
struct st_lps22df_hw *hw;
struct spi_device *spi;
if (len >= ST_LPS22DF_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_lps22df_transfer_function st_lps22df_tf_spi = {
.write = st_lps22df_spi_write,
.read = st_lps22df_spi_read,
};
static int st_lps22df_spi_probe(struct spi_device *spi)
{
return st_lps22df_common_probe(&spi->dev, spi->irq, spi->modalias,
&st_lps22df_tf_spi);
}
static const struct spi_device_id st_lps22df_ids[] = {
{ "lps22df" },
{}
};
MODULE_DEVICE_TABLE(spi, st_lps22df_ids);
static const struct of_device_id st_lps22df_id_table[] = {
{ .compatible = "st,lps22df" },
{},
};
MODULE_DEVICE_TABLE(of, st_lps22df_id_table);
static struct spi_driver st_lps22df_spi_driver = {
.driver = {
.owner = THIS_MODULE,
.name = "st_lps22df_spi",
.of_match_table = of_match_ptr(st_lps22df_id_table),
},
.probe = st_lps22df_spi_probe,
.id_table = st_lps22df_ids,
};
module_spi_driver(st_lps22df_spi_driver);
MODULE_DESCRIPTION("STMicroelectronics lps22df spi driver");
MODULE_AUTHOR("Mario Tesi <mario.tesi@st.com>");
MODULE_LICENSE("GPL v2");

View File

@ -0,0 +1,3 @@
CONFIG_ST_LPS22DF_IIO=m
CONFIG_ST_LPS22DF_I2C_IIO=m
CONFIG_ST_LPS22DF_SPI_IIO=m