drivers:iio:stm:pressure: add support to ilps22qs STMEMS Pressure sensor

Preliminary version of ilps22qs STMEMS Pressure driver to enable
reading data from pressure and temperature sensors.
Since the interrupts of the device are exclusively software, timers
have been used for the management of the data polling in continuous
mode with IIO buffer.

Available features:
 - Configure ODR for Pressure and Temperature sensor
 - Reading raw Pressure and Temperature sensor data
 - Reading IIO buffer Pressure and Temperature sensor data

Signed-off-by: Mario Tesi <mario.tesi@st.com>
Change-Id: Ifce3655b2687911b88bdbc854f6ff42778b60a40
Reviewed-on: https://gerrit.st.com/c/linuxandroidopen/stm-ldd-iio/+/284832
Tested-by: CITOOLS <MDG-smet-aci-reviews@list.st.com>
Reviewed-by: Matteo DAMENO <matteo.dameno@st.com>
This commit is contained in:
Mario Tesi 2023-01-13 14:00:08 +01:00 committed by Matteo DAMENO
parent d51f3298c0
commit 3a162cf3d9
8 changed files with 1109 additions and 0 deletions

View File

@ -0,0 +1,27 @@
* st_ilps22qs driver for pressure MEMS sensors
Required properties for all bus drivers:
- compatible: must be "st,ilps22qs"
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
Example for an spi device node:
ilps22qs-pressure@0 {
compatible = "st,ilps22qs";
reg = <0x0>;
spi-max-frequency = <1000000>;
};
Example for an i2c device node:
ilps22qs-pressure@5c {
compatible = "st,ilps22qs";
reg = <0x5c>;
};

View File

@ -85,4 +85,23 @@ config ST_LPS33HW_SPI_IIO
tristate
depends on ST_LPS33HW_IIO
config ST_ILPS22QS_IIO
tristate "STMicroelectronics ILPS22QS sensor"
depends on (I2C || SPI_MASTER) && SYSFS
select IIO_BUFFER
select IIO_KFIFO_BUF
select ST_ILPS22QS_I2C_IIO if (I2C)
select ST_ILPS22QS_SPI_IIO if (SPI)
help
This driver supports ILPS22QS sensor. This driver can be
built as a module. The module will be called st-ilps22qs.
config ST_ILPS22QS_I2C_IIO
tristate
depends on ST_ILPS22QS_IIO
config ST_ILPS22QS_SPI_IIO
tristate
depends on ST_ILPS22QS_IIO
endmenu

View File

@ -27,3 +27,9 @@ obj-$(CONFIG_ST_LPS33HW_I2C_IIO) += st_lps33hw_i2c.o
obj-$(CONFIG_ST_LPS33HW_SPI_IIO) += st_lps33hw_spi.o
st_lps33hw-y += st_lps33hw_core.o st_lps33hw_buffer.o
obj-$(CONFIG_ST_ILPS22QS_IIO) += st_ilps22qs.o
obj-$(CONFIG_ST_ILPS22QS_I2C_IIO) += st_ilps22qs_i2c.o
obj-$(CONFIG_ST_ILPS22QS_SPI_IIO) += st_ilps22qs_spi.o
st_ilps22qs-y += st_ilps22qs_core.o

View File

@ -0,0 +1,149 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* STMicroelectronics ilps22qs driver
*
* Copyright 2023 STMicroelectronics Inc.
*
* MEMS Software Solutions Team
*/
#ifndef __ST_ILPS22QS_H
#define __ST_ILPS22QS_H
#include <linux/bitfield.h>
#include <linux/iio/iio.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/of.h>
#include <linux/regmap.h>
#include <linux/types.h>
#include <linux/workqueue.h>
#define ST_ILPS22QS_DEV_NAME "ilps22qs"
#define ST_ILPS22QS_WHO_AM_I_ADDR 0x0f
#define ST_ILPS22QS_WHOAMI_VAL 0xb4
#define ST_ILPS22QS_CTRL1_ADDR 0x10
#define ST_ILPS22QS_ODR_MASK GENMASK(6, 3)
#define ST_ILPS22QS_CTRL2_ADDR 0x11
#define ST_ILPS22QS_SOFT_RESET_MASK BIT(2)
#define ST_ILPS22QS_BDU_MASK BIT(3)
#define ST_ILPS22QS_PRESS_OUT_XL_ADDR 0x28
#define ST_ILPS22QS_TEMP_OUT_L_ADDR 0x2b
#define ST_ILPS22QS_PRESS_FS_AVL_GAIN (1000000000UL / 4096UL)
#define ST_ILPS22QS_TEMP_FS_AVL_GAIN 100
#define ST_ILPS22QS_SHIFT_VAL(val, mask) (((val) << __ffs(mask)) & (mask))
#define ST_ILPS22QS_ODR_LIST_NUM 8
enum st_ilps22qs_sensor_id {
ST_ILPS22QS_PRESS = 0,
ST_ILPS22QS_TEMP,
ST_ILPS22QS_SENSORS_NUM,
};
struct st_ilps22qs_odr_t {
u8 hz;
u8 val;
};
struct st_ilps22qs_reg {
u8 addr;
u8 mask;
};
struct st_ilps22qs_odr_table_t {
u8 size;
struct st_ilps22qs_reg reg;
struct st_ilps22qs_odr_t odr_avl[ST_ILPS22QS_ODR_LIST_NUM];
};
struct st_ilps22qs_hw {
struct iio_dev *iio_devs[ST_ILPS22QS_SENSORS_NUM];
struct workqueue_struct *workqueue;
struct regulator *vddio_supply;
struct regulator *vdd_supply;
struct regmap *regmap;
struct device *dev;
struct mutex lock;
u8 enable_mask;
u8 odr;
};
struct st_ilps22qs_sensor {
enum st_ilps22qs_sensor_id id;
struct work_struct iio_work;
struct st_ilps22qs_hw *hw;
struct hrtimer hr_timer;
ktime_t ktime;
int64_t timestamp;
char name[32];
u32 gain;
u8 odr;
};
extern const struct dev_pm_ops st_ilps22qs_pm_ops;
static inline int st_ilps22qs_update_locked(struct st_ilps22qs_hw *hw,
unsigned int addr,
unsigned int mask,
unsigned int data)
{
unsigned int val = ST_ILPS22QS_SHIFT_VAL(data, mask);
int err;
mutex_lock(&hw->lock);
err = regmap_update_bits(hw->regmap, addr, mask, val);
mutex_unlock(&hw->lock);
return err;
}
static inline int st_ilps22qs_read_locked(struct st_ilps22qs_hw *hw,
unsigned int addr, void *val,
unsigned int len)
{
int err;
mutex_lock(&hw->lock);
err = regmap_bulk_read(hw->regmap, addr, val, len);
mutex_unlock(&hw->lock);
return err;
}
static inline void st_ilps22qs_flush_works(struct st_ilps22qs_hw *hw)
{
flush_workqueue(hw->workqueue);
}
static inline int st_ilps22qs_destroy_workqueue(struct st_ilps22qs_hw *hw)
{
if (hw->workqueue)
destroy_workqueue(hw->workqueue);
return 0;
}
static inline int st_ilps22qs_allocate_workqueue(struct st_ilps22qs_hw *hw)
{
if (!hw->workqueue)
hw->workqueue = create_workqueue(ST_ILPS22QS_DEV_NAME);
return !hw->workqueue ? -ENOMEM : 0;
}
static inline s64 st_ilps22qs_get_time_ns(struct st_ilps22qs_hw *hw)
{
return iio_get_time_ns(hw->iio_devs[ST_ILPS22QS_PRESS]);
}
int st_ilps22qs_probe(struct device *dev, struct regmap *regmap);
int st_ilps22qs_remove(struct device *dev);
#endif /* __ST_ILPS22QS_H */

View File

@ -0,0 +1,751 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* STMicroelectronics ilps22qs driver
*
* Copyright 2023 STMicroelectronics Inc.
*
* MEMS Software Solutions Team
*/
#include <asm/unaligned.h>
#include <linux/delay.h>
#include <linux/kernel.h>
#include <linux/iio/buffer.h>
#include <linux/iio/events.h>
#include <linux/iio/iio.h>
#include <linux/iio/kfifo_buf.h>
#include <linux/iio/sysfs.h>
#include <linux/iio/trigger_consumer.h>
#include <linux/iio/triggered_buffer.h>
#include <linux/iio/trigger.h>
#include <linux/regulator/consumer.h>
#include <linux/slab.h>
#include <linux/types.h>
#include <linux/version.h>
#include "st_ilps22qs.h"
const static struct st_ilps22qs_odr_table_t st_ilps22qs_odr_table = {
.size = ST_ILPS22QS_ODR_LIST_NUM,
.reg = {
.addr = ST_ILPS22QS_CTRL1_ADDR,
.mask = ST_ILPS22QS_ODR_MASK,
},
.odr_avl[0] = { 1, 0x01 },
.odr_avl[1] = { 4, 0x02 },
.odr_avl[2] = { 10, 0x03 },
.odr_avl[3] = { 25, 0x04 },
.odr_avl[4] = { 50, 0x05 },
.odr_avl[5] = { 75, 0x06 },
.odr_avl[6] = { 100, 0x07 },
.odr_avl[7] = { 200, 0x08 },
};
static const struct iio_chan_spec st_ilps22qs_press_channels[] = {
{
.type = IIO_PRESSURE,
.address = ST_ILPS22QS_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,
},
},
IIO_CHAN_SOFT_TIMESTAMP(1)
};
static const struct iio_chan_spec st_ilps22qs_temp_channels[] = {
{
.type = IIO_TEMP,
.address = ST_ILPS22QS_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,
},
},
IIO_CHAN_SOFT_TIMESTAMP(1)
};
static enum hrtimer_restart
st_ilps22qs_poll_function_read(struct hrtimer *timer)
{
struct st_ilps22qs_sensor *sensor;
sensor = container_of((struct hrtimer *)timer,
struct st_ilps22qs_sensor, hr_timer);
sensor->timestamp = st_ilps22qs_get_time_ns(sensor->hw);
queue_work(sensor->hw->workqueue, &sensor->iio_work);
return HRTIMER_NORESTART;
}
static void st_ilps22qs_report_temp(struct st_ilps22qs_sensor *sensor,
u8 *tmp, int64_t timestamp)
{
struct iio_dev *iio_dev = sensor->hw->iio_devs[sensor->id];
u8 iio_buf[ALIGN(2, sizeof(s64)) + sizeof(s64)];
memcpy(iio_buf, tmp, 2);
iio_push_to_buffers_with_timestamp(iio_dev, iio_buf, timestamp);
}
static void st_silps22qs_report_press(struct st_ilps22qs_sensor *sensor,
u8 *tmp, int64_t timestamp)
{
struct iio_dev *iio_dev = sensor->hw->iio_devs[sensor->id];
u8 iio_buf[ALIGN(3, sizeof(s64)) + sizeof(s64)];
memcpy(iio_buf, tmp, 3);
iio_push_to_buffers_with_timestamp(iio_dev, iio_buf, timestamp);
}
static void st_ilps22qs_poll_function_work(struct work_struct *iio_work)
{
struct st_ilps22qs_sensor *sensor;
struct st_ilps22qs_hw *hw;
ktime_t tmpkt, ktdelta;
u8 data[3];
int len;
int err;
int id;
sensor = container_of((struct work_struct *)iio_work,
struct st_ilps22qs_sensor, iio_work);
hw = sensor->hw;
id = sensor->id;
/* adjust delta time */
ktdelta = ktime_set(0,
(st_ilps22qs_get_time_ns(hw) - sensor->timestamp));
/* avoid negative value in case of high odr */
if (ktime_after(sensor->ktime, ktdelta))
tmpkt = ktime_sub(sensor->ktime, ktdelta);
else
tmpkt = sensor->ktime;
hrtimer_start(&sensor->hr_timer, tmpkt, HRTIMER_MODE_REL);
len = hw->iio_devs[id]->channels->scan_type.realbits >> 3;
switch (id) {
case ST_ILPS22QS_PRESS:
err = st_ilps22qs_read_locked(hw,
hw->iio_devs[id]->channels->address,
data, len);
if (err < 0)
return;
st_silps22qs_report_press(sensor, data, sensor->timestamp);
break;
case ST_ILPS22QS_TEMP:
err = st_ilps22qs_read_locked(hw,
hw->iio_devs[id]->channels->address,
data, len);
if (err < 0)
return;
st_ilps22qs_report_temp(sensor, data, sensor->timestamp);
break;
default:
break;
}
}
static int st_ilps22qs_check_whoami(struct st_ilps22qs_hw *hw)
{
int data;
int err;
err = regmap_read(hw->regmap, ST_ILPS22QS_WHO_AM_I_ADDR, &data);
if (err < 0) {
dev_err(hw->dev, "failed to read whoami register\n");
return err;
}
if (data != ST_ILPS22QS_WHOAMI_VAL) {
dev_err(hw->dev, "unsupported whoami [%02x]\n", data);
return -ENODEV;
}
return 0;
}
static __maybe_unused int st_ilps22qs_reg_access(struct iio_dev *iio_dev,
unsigned int reg,
unsigned int writeval,
unsigned int *readval)
{
struct st_ilps22qs_sensor *sensor = iio_priv(iio_dev);
int ret;
ret = iio_device_claim_direct_mode(iio_dev);
if (ret)
return ret;
if (readval == NULL)
ret = regmap_write(sensor->hw->regmap, reg, writeval);
else
ret = regmap_read(sensor->hw->regmap, reg, readval);
iio_device_release_direct_mode(iio_dev);
return (ret < 0) ? ret : 0;
}
static int st_ilps22qs_get_odr(struct st_ilps22qs_sensor *sensor, u8 odr)
{
int i;
for (i = 0; i < st_ilps22qs_odr_table.size; i++) {
if (st_ilps22qs_odr_table.odr_avl[i].hz >= odr)
break;
}
return i == st_ilps22qs_odr_table.size ? -EINVAL : i;
}
static int st_ilps22qs_set_odr(struct st_ilps22qs_sensor *sensor, u8 odr)
{
struct st_ilps22qs_hw *hw = sensor->hw;
u8 max_odr = odr;
int i;
for (i = 0; i < ST_ILPS22QS_SENSORS_NUM; i++) {
if (hw->enable_mask & BIT(i) && sensor->id != i) {
struct st_ilps22qs_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_ilps22qs_get_odr(sensor, max_odr);
if (ret < 0)
return ret;
err = st_ilps22qs_update_locked(hw,
st_ilps22qs_odr_table.reg.addr,
st_ilps22qs_odr_table.reg.mask,
st_ilps22qs_odr_table.odr_avl[ret].hz);
if (err < 0)
return err;
hw->odr = max_odr;
}
return 0;
}
static int st_ilps22qs_set_enable(struct st_ilps22qs_sensor *sensor,
bool enable)
{
struct st_ilps22qs_hw *hw = sensor->hw;
u8 odr = enable ? sensor->odr : 0;
int err;
err = st_ilps22qs_set_odr(sensor, odr);
if (err < 0)
return err;
if (enable) {
ktime_t ktime = ktime_set(0, 1000000000 / sensor->odr);
hrtimer_start(&sensor->hr_timer, ktime, HRTIMER_MODE_REL);
sensor->ktime = ktime;
hw->enable_mask |= BIT(sensor->id);
} else {
cancel_work_sync(&sensor->iio_work);
hrtimer_cancel(&sensor->hr_timer);
hw->enable_mask &= ~BIT(sensor->id);
}
return 0;
}
static int st_ilps22qs_init_sensors(struct st_ilps22qs_hw *hw)
{
int err;
/* soft reset the device on power on */
err = st_ilps22qs_update_locked(hw, ST_ILPS22QS_CTRL2_ADDR,
ST_ILPS22QS_SOFT_RESET_MASK, 1);
if (err < 0)
return err;
usleep_range(50, 60);
/* enable BDU */
return st_ilps22qs_update_locked(hw, ST_ILPS22QS_CTRL1_ADDR,
ST_ILPS22QS_BDU_MASK, 1);
}
static ssize_t
st_ilps22qs_get_sampling_frequency_avail(struct device *dev,
struct device_attribute *attr,
char *buf)
{
int i, len = 0;
for (i = 0; i < st_ilps22qs_odr_table.size; i++) {
len += scnprintf(buf + len, PAGE_SIZE - len, "%d ",
st_ilps22qs_odr_table.odr_avl[i].hz);
}
buf[len - 1] = '\n';
return len;
}
static int st_ilps22qs_read_raw(struct iio_dev *iio_dev,
struct iio_chan_spec const *ch,
int *val, int *val2, long mask)
{
struct st_ilps22qs_sensor *sensor = iio_priv(iio_dev);
struct st_ilps22qs_hw *hw = sensor->hw;
int ret;
switch (mask) {
case IIO_CHAN_INFO_RAW: {
u8 data[4] = {};
int delay;
ret = iio_device_claim_direct_mode(iio_dev);
if (ret)
return ret;
ret = st_ilps22qs_set_enable(sensor, true);
if (ret < 0)
goto read_error;
delay = 1000000 / sensor->odr;
usleep_range(delay, 2 * delay);
ret = regmap_bulk_read(hw->regmap, ch->address, data,
ch->scan_type.realbits >> 3);
if (ret < 0)
goto read_error;
if (sensor->id == ST_ILPS22QS_PRESS)
*val = (s32)get_unaligned_le32(data);
else if (sensor->id == ST_ILPS22QS_TEMP)
*val = (s16)get_unaligned_le16(data);
ret = st_ilps22qs_set_enable(sensor, false);
read_error:
iio_device_release_direct_mode(iio_dev);
if (ret < 0)
return ret;
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_ilps22qs_write_raw(struct iio_dev *iio_dev,
struct iio_chan_spec const *ch,
int val, int val2, long mask)
{
struct st_ilps22qs_sensor *sensor = iio_priv(iio_dev);
int ret;
ret = iio_device_claim_direct_mode(iio_dev);
if (ret)
return ret;
switch (mask) {
case IIO_CHAN_INFO_SAMP_FREQ:
ret = st_ilps22qs_get_odr(sensor, val);
if (ret < 0)
goto exit_fail;
sensor->odr = st_ilps22qs_odr_table.odr_avl[ret].hz;
break;
default:
ret = -EINVAL;
break;
}
exit_fail:
iio_device_release_direct_mode(iio_dev);
return ret < 0 ? ret : 0;
}
static IIO_DEV_ATTR_SAMP_FREQ_AVAIL(st_ilps22qs_get_sampling_frequency_avail);
static struct attribute *st_ilps22qs_press_attributes[] = {
&iio_dev_attr_sampling_frequency_available.dev_attr.attr,
NULL,
};
static const struct attribute_group st_ilps22qs_press_attribute_group = {
.attrs = st_ilps22qs_press_attributes,
};
static const struct iio_info st_ilps22qs_press_info = {
.attrs = &st_ilps22qs_press_attribute_group,
.read_raw = st_ilps22qs_read_raw,
.write_raw = st_ilps22qs_write_raw,
.debugfs_reg_access = st_ilps22qs_reg_access,
};
static struct attribute *st_ilps22qs_temp_attributes[] = {
&iio_dev_attr_sampling_frequency_available.dev_attr.attr,
NULL,
};
static const struct attribute_group st_ilps22qs_temp_attribute_group = {
.attrs = st_ilps22qs_temp_attributes,
};
static const struct iio_info st_ilps22qs_temp_info = {
.attrs = &st_ilps22qs_temp_attribute_group,
.read_raw = st_ilps22qs_read_raw,
.write_raw = st_ilps22qs_write_raw,
.debugfs_reg_access = st_ilps22qs_reg_access,
};
static int st_ilps22qs_preenable(struct iio_dev *iio_dev)
{
struct st_ilps22qs_sensor *sensor = iio_priv(iio_dev);
return st_ilps22qs_set_enable(sensor, true);
}
static int st_ilps22qs_postdisable(struct iio_dev *iio_dev)
{
struct st_ilps22qs_sensor *sensor = iio_priv(iio_dev);
return st_ilps22qs_set_enable(sensor, false);
}
static const struct iio_buffer_setup_ops st_ilps22qs_fifo_ops = {
.preenable = st_ilps22qs_preenable,
.postdisable = st_ilps22qs_postdisable,
};
static void st_ilps22qs_disable_regulator_action(void *_data)
{
struct st_ilps22qs_hw *hw = _data;
regulator_disable(hw->vddio_supply);
regulator_disable(hw->vdd_supply);
}
static int st_ilps22qs_power_enable(struct st_ilps22qs_hw *hw)
{
int err;
hw->vdd_supply = devm_regulator_get(hw->dev, "vdd");
if (IS_ERR(hw->vdd_supply)) {
if (PTR_ERR(hw->vdd_supply) != -EPROBE_DEFER)
dev_err(hw->dev, "Failed to get vdd regulator %d\n",
(int)PTR_ERR(hw->vdd_supply));
return PTR_ERR(hw->vdd_supply);
}
hw->vddio_supply = devm_regulator_get(hw->dev, "vddio");
if (IS_ERR(hw->vddio_supply)) {
if (PTR_ERR(hw->vddio_supply) != -EPROBE_DEFER)
dev_err(hw->dev, "Failed to get vddio regulator %d\n",
(int)PTR_ERR(hw->vddio_supply));
return PTR_ERR(hw->vddio_supply);
}
err = regulator_enable(hw->vdd_supply);
if (err) {
dev_err(hw->dev, "Failed to enable vdd regulator: %d\n", err);
return err;
}
err = regulator_enable(hw->vddio_supply);
if (err) {
regulator_disable(hw->vdd_supply);
return err;
}
err = devm_add_action_or_reset(hw->dev,
st_ilps22qs_disable_regulator_action,
hw);
if (err) {
dev_err(hw->dev,
"Failed to setup regulator cleanup action %d\n", err);
return err;
}
/*
* after the device is powered up, the ILPS22QS performs a 10 ms
* boot procedure to load the trimming parameters
*/
usleep_range(10000, 11000);
return 0;
}
static struct iio_dev *st_ilps22qs_alloc_iiodev(struct st_ilps22qs_hw *hw,
enum st_ilps22qs_sensor_id id)
{
struct st_ilps22qs_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;
sensor = iio_priv(iio_dev);
sensor->hw = hw;
sensor->id = id;
sensor->odr = st_ilps22qs_odr_table.odr_avl[0].hz;
switch (id) {
case ST_ILPS22QS_PRESS:
sensor->gain = ST_ILPS22QS_PRESS_FS_AVL_GAIN;
scnprintf(sensor->name, sizeof(sensor->name),
ST_ILPS22QS_DEV_NAME "_press");
iio_dev->channels = st_ilps22qs_press_channels;
iio_dev->num_channels = ARRAY_SIZE(st_ilps22qs_press_channels);
iio_dev->info = &st_ilps22qs_press_info;
break;
case ST_ILPS22QS_TEMP:
sensor->gain = ST_ILPS22QS_TEMP_FS_AVL_GAIN;
scnprintf(sensor->name, sizeof(sensor->name),
ST_ILPS22QS_DEV_NAME "_temp");
iio_dev->channels = st_ilps22qs_temp_channels;
iio_dev->num_channels = ARRAY_SIZE(st_ilps22qs_temp_channels);
iio_dev->info = &st_ilps22qs_temp_info;
break;
default:
return NULL;
}
iio_dev->name = sensor->name;
/* configure sensor hrtimer */
hrtimer_init(&sensor->hr_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
sensor->hr_timer.function = &st_ilps22qs_poll_function_read;
INIT_WORK(&sensor->iio_work, st_ilps22qs_poll_function_work);
return iio_dev;
}
int st_ilps22qs_probe(struct device *dev, struct regmap *regmap)
{
struct st_ilps22qs_hw *hw;
int err, i;
hw = devm_kzalloc(dev, sizeof(*hw), GFP_KERNEL);
if (!hw)
return -ENOMEM;
mutex_init(&hw->lock);
dev_set_drvdata(dev, (void *)hw);
hw->dev = dev;
hw->regmap = regmap;
err = st_ilps22qs_power_enable(hw);
if (err)
return err;
err = st_ilps22qs_check_whoami(hw);
if (err < 0)
return err;
err = st_ilps22qs_init_sensors(hw);
if (err < 0)
return err;
for (i = 0; i < ST_ILPS22QS_SENSORS_NUM; i++) {
#if KERNEL_VERSION(5, 13, 0) > LINUX_VERSION_CODE
struct iio_buffer *buffer;
#endif /* LINUX_VERSION_CODE */
hw->iio_devs[i] = st_ilps22qs_alloc_iiodev(hw, i);
if (!hw->iio_devs[i])
return -ENOMEM;
#if KERNEL_VERSION(5, 19, 0) <= LINUX_VERSION_CODE
err = devm_iio_kfifo_buffer_setup(hw->dev,
hw->iio_devs[i],
&st_ilps22qs_fifo_ops);
if (err)
return err;
#elif KERNEL_VERSION(5, 13, 0) <= LINUX_VERSION_CODE
err = devm_iio_kfifo_buffer_setup(hw->dev, hw->iio_devs[i],
INDIO_BUFFER_SOFTWARE,
&st_ilps22qs_fifo_ops);
if (err)
return err;
#else /* LINUX_VERSION_CODE */
buffer = devm_iio_kfifo_allocate(hw->dev);
if (!buffer)
return -ENOMEM;
iio_device_attach_buffer(hw->iio_devs[i], buffer);
hw->iio_devs[i]->modes |= INDIO_BUFFER_SOFTWARE;
hw->iio_devs[i]->setup_ops = &st_ilps22qs_fifo_ops;
#endif /* LINUX_VERSION_CODE */
err = devm_iio_device_register(hw->dev, hw->iio_devs[i]);
if (err)
return err;
}
err = st_ilps22qs_allocate_workqueue(hw);
if (err)
return err;
dev_info(dev, "device probed\n");
return 0;
}
EXPORT_SYMBOL(st_ilps22qs_probe);
int st_ilps22qs_remove(struct device *dev)
{
struct st_ilps22qs_hw *hw = dev_get_drvdata(dev);
struct st_ilps22qs_sensor *sensor;
int i;
for (i = 0; i < ST_ILPS22QS_SENSORS_NUM; i++) {
int err;
if (!hw->iio_devs[i])
continue;
sensor = iio_priv(hw->iio_devs[i]);
if (!(hw->enable_mask & BIT(sensor->id)))
continue;
err = st_ilps22qs_set_enable(sensor, false);
if (err < 0)
return err;
}
st_ilps22qs_flush_works(hw);
st_ilps22qs_destroy_workqueue(hw);
return 0;
}
EXPORT_SYMBOL(st_ilps22qs_remove);
static int __maybe_unused st_ilps22qs_suspend(struct device *dev)
{
struct st_ilps22qs_hw *hw = dev_get_drvdata(dev);
struct st_ilps22qs_sensor *sensor;
int i;
for (i = 0; i < ST_ILPS22QS_SENSORS_NUM; i++) {
int err;
if (!hw->iio_devs[i])
continue;
sensor = iio_priv(hw->iio_devs[i]);
if (!(hw->enable_mask & BIT(sensor->id)))
continue;
err = st_ilps22qs_set_odr(sensor, 0);
if (err < 0)
return err;
cancel_work_sync(&sensor->iio_work);
hrtimer_cancel(&sensor->hr_timer);
}
dev_info(dev, "Suspending device\n");
return 0;
}
static int __maybe_unused st_ilps22qs_resume(struct device *dev)
{
struct st_ilps22qs_hw *hw = dev_get_drvdata(dev);
struct st_ilps22qs_sensor *sensor;
int i;
dev_info(dev, "Resuming device\n");
for (i = 0; i < ST_ILPS22QS_SENSORS_NUM; i++) {
int err;
if (!hw->iio_devs[i])
continue;
sensor = iio_priv(hw->iio_devs[i]);
if (!(hw->enable_mask & BIT(sensor->id)))
continue;
err = st_ilps22qs_set_enable(sensor, true);
if (err < 0)
return err;
}
return 0;
}
const struct dev_pm_ops st_ilps22qs_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(st_ilps22qs_suspend, st_ilps22qs_resume)
};
EXPORT_SYMBOL(st_ilps22qs_pm_ops);
MODULE_AUTHOR("MEMS Software Solutions Team");
MODULE_DESCRIPTION("STMicroelectronics ilps22qs driver");
MODULE_LICENSE("GPL v2");

View File

@ -0,0 +1,80 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* STMicroelectronics ilps22qs i2c driver
*
* Copyright 2023 STMicroelectronics Inc.
*
* MEMS Software Solutions Team
*/
#include <linux/kernel.h>
#include <linux/i2c.h>
#include <linux/iio/iio.h>
#include <linux/of.h>
#include <linux/module.h>
#include <linux/version.h>
#include "st_ilps22qs.h"
static const struct regmap_config st_ilps22qs_i2c_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
};
static int st_ilps22qs_i2c_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct regmap *regmap;
regmap = devm_regmap_init_i2c(client, &st_ilps22qs_i2c_regmap_config);
if (IS_ERR(regmap)) {
dev_err(&client->dev,
"Failed to register i2c regmap %d\n",
(int)PTR_ERR(regmap));
return PTR_ERR(regmap);
}
return st_ilps22qs_probe(&client->dev, regmap);
}
#if KERNEL_VERSION(6, 1, 0) <= LINUX_VERSION_CODE
static void st_ilps22qs_i2c_remove(struct i2c_client *client)
{
st_ilps22qs_remove(&client->dev);
}
#else /* LINUX_VERSION_CODE */
static int st_ilps22qs_i2c_remove(struct i2c_client *client)
{
return st_ilps22qs_remove(&client->dev);
}
#endif /* LINUX_VERSION_CODE */
static const struct i2c_device_id st_ilps22qs_ids[] = {
{ ST_ILPS22QS_DEV_NAME },
{}
};
MODULE_DEVICE_TABLE(i2c, st_ilps22qs_ids);
static const struct of_device_id st_ilps22qs_id_table[] = {
{ .compatible = "st," ST_ILPS22QS_DEV_NAME },
{},
};
MODULE_DEVICE_TABLE(of, st_ilps22qs_id_table);
static struct i2c_driver st_ilps22qs_i2c_driver = {
.driver = {
.owner = THIS_MODULE,
.name = "st_" ST_ILPS22QS_DEV_NAME "_i2c",
.pm = &st_ilps22qs_pm_ops,
.of_match_table = of_match_ptr(st_ilps22qs_id_table),
},
.probe = st_ilps22qs_i2c_probe,
.remove = st_ilps22qs_i2c_remove,
.id_table = st_ilps22qs_ids,
};
module_i2c_driver(st_ilps22qs_i2c_driver);
MODULE_AUTHOR("MEMS Software Solutions Team");
MODULE_DESCRIPTION("STMicroelectronics ilps22qs i2c driver");
MODULE_LICENSE("GPL v2");

View File

@ -0,0 +1,74 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* STMicroelectronics ilps22qs spi driver
*
* Copyright 2023 STMicroelectronics Inc.
*
* MEMS Software Solutions Team
*/
#include <linux/spi/spi.h>
#include <linux/version.h>
#include "st_ilps22qs.h"
static const struct regmap_config st_ilps22qs_spi_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
};
static int st_ilps22qs_spi_probe(struct spi_device *spi)
{
struct regmap *regmap;
regmap = devm_regmap_init_spi(spi, &st_ilps22qs_spi_regmap_config);
if (IS_ERR(regmap)) {
dev_err(&spi->dev, "Failed to register spi regmap %d\n",
(int)PTR_ERR(regmap));
return PTR_ERR(regmap);
}
return st_ilps22qs_probe(&spi->dev, regmap);
}
#if KERNEL_VERSION(5, 18, 0) <= LINUX_VERSION_CODE
static void st_ilps22qs_spi_remove(struct spi_device *spi)
{
st_ilps22qs_remove(&spi->dev);
}
#else /* LINUX_VERSION_CODE */
static int st_ilps22qs_spi_remove(struct spi_device *spi)
{
return st_ilps22qs_remove(&spi->dev);
}
#endif /* LINUX_VERSION_CODE */
static const struct spi_device_id st_ilps22qs_ids[] = {
{ ST_ILPS22QS_DEV_NAME },
{}
};
MODULE_DEVICE_TABLE(spi, st_ilps22qs_ids);
static const struct of_device_id st_ilps22qs_id_table[] = {
{ .compatible = "st," ST_ILPS22QS_DEV_NAME },
{},
};
MODULE_DEVICE_TABLE(of, st_ilps22qs_id_table);
static struct spi_driver st_ilps22qs_spi_driver = {
.driver = {
.owner = THIS_MODULE,
.name = "st_" ST_ILPS22QS_DEV_NAME "_spi",
.pm = &st_ilps22qs_pm_ops,
.of_match_table = of_match_ptr(st_ilps22qs_id_table),
},
.probe = st_ilps22qs_spi_probe,
.remove = st_ilps22qs_spi_remove,
.id_table = st_ilps22qs_ids,
};
module_spi_driver(st_ilps22qs_spi_driver);
MODULE_AUTHOR("MEMS Software Solutions Team");
MODULE_DESCRIPTION("STMicroelectronics ilps22qs spi driver");
MODULE_LICENSE("GPL v2");

View File

@ -0,0 +1,3 @@
CONFIG_ST_ILPS22QS_IIO=m
CONFIG_ST_ILPS22QS_I2C_IIO=m
CONFIG_ST_ILPS22QS_SPI_IIO=m