From a276e09d44c608ffb4439c7d0512c7dac574c4a2 Mon Sep 17 00:00:00 2001 From: mario tesi Date: Mon, 22 Nov 2021 12:14:51 +0100 Subject: [PATCH] drivers:iio:stm:pressure: Add support to ST MEMS LPS33HW pressure sensor Added support to LPS33HW ST MEMS pressure and temperature sensor List of supported features: - Configurable ODRs [Power_Off ... 75] 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 Change-Id: Iae0b2ad72d7242e237450a74e9d8d25133a5ccc0 --- drivers/iio/stm/pressure/Kconfig | 19 + drivers/iio/stm/pressure/Makefile | 6 + drivers/iio/stm/pressure/st_lps33hw.h | 88 ++++ drivers/iio/stm/pressure/st_lps33hw_buffer.c | 277 +++++++++++ drivers/iio/stm/pressure/st_lps33hw_core.c | 484 +++++++++++++++++++ drivers/iio/stm/pressure/st_lps33hw_i2c.c | 87 ++++ drivers/iio/stm/pressure/st_lps33hw_spi.c | 97 ++++ stm_iio_configs/lps33hw_defconfig | 3 + 8 files changed, 1061 insertions(+) create mode 100644 drivers/iio/stm/pressure/st_lps33hw.h create mode 100644 drivers/iio/stm/pressure/st_lps33hw_buffer.c create mode 100644 drivers/iio/stm/pressure/st_lps33hw_core.c create mode 100644 drivers/iio/stm/pressure/st_lps33hw_i2c.c create mode 100644 drivers/iio/stm/pressure/st_lps33hw_spi.c create mode 100644 stm_iio_configs/lps33hw_defconfig diff --git a/drivers/iio/stm/pressure/Kconfig b/drivers/iio/stm/pressure/Kconfig index 914fc08c558e..e4e3193242a6 100644 --- a/drivers/iio/stm/pressure/Kconfig +++ b/drivers/iio/stm/pressure/Kconfig @@ -66,4 +66,23 @@ config ST_LPS22HB_SPI_IIO tristate depends on ST_LPS22HB_IIO +config ST_LPS33HW_IIO + tristate "STMicroelectronics LPS33HW sensor" + depends on (I2C || SPI_MASTER) && SYSFS + select IIO_BUFFER + select IIO_KFIFO_BUF + select ST_LPS33HW_I2C_IIO if (I2C) + select ST_LPS33HW_SPI_IIO if (SPI) + help + This driver supports LPS33HW pressure sensors. This driver can be + built as a module. The module will be called st-lps33hw. + +config ST_LPS33HW_I2C_IIO + tristate + depends on ST_LPS33HW_IIO + +config ST_LPS33HW_SPI_IIO + tristate + depends on ST_LPS33HW_IIO + endmenu diff --git a/drivers/iio/stm/pressure/Makefile b/drivers/iio/stm/pressure/Makefile index ad2bc6f8c978..e546baf86ed6 100644 --- a/drivers/iio/stm/pressure/Makefile +++ b/drivers/iio/stm/pressure/Makefile @@ -21,3 +21,9 @@ obj-$(CONFIG_ST_LPS22HB_I2C_IIO) += st_lps22hb_i2c.o obj-$(CONFIG_ST_LPS22HB_SPI_IIO) += st_lps22hb_spi.o st_lps22hb-y += st_lps22hb_core.o st_lps22hb_buffer.o + +obj-$(CONFIG_ST_LPS33HW_IIO) += st_lps33hw.o +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 diff --git a/drivers/iio/stm/pressure/st_lps33hw.h b/drivers/iio/stm/pressure/st_lps33hw.h new file mode 100644 index 000000000000..d4b19d229b69 --- /dev/null +++ b/drivers/iio/stm/pressure/st_lps33hw.h @@ -0,0 +1,88 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * STMicroelectronics lps33hw driver + * + * Copyright 2017 STMicroelectronics Inc. + * + * Lorenzo Bianconi + */ + +#ifndef __ST_LPS33HW_H +#define __ST_LPS33HW_H + +#include +#include +#include +#include + +#define ST_LPS33HW_MAX_FIFO_LENGTH 31 + +#define ST_LPS33HW_CTRL3_ADDR 0x12 + +enum st_lps33hw_sensor_type { + ST_LPS33HW_PRESS = 0, + ST_LPS33HW_TEMP, + ST_LPS33HW_SENSORS_NUMB, +}; + +enum st_lps33hw_fifo_mode { + ST_LPS33HW_BYPASS = 0x0, + ST_LPS33HW_STREAM = 0x6, +}; + +#define ST_LPS33HW_TX_MAX_LENGTH 8 +#define ST_LPS33HW_RX_MAX_LENGTH 192 + +struct st_lps33hw_transfer_buffer { + u8 rx_buf[ST_LPS33HW_RX_MAX_LENGTH]; + u8 tx_buf[ST_LPS33HW_TX_MAX_LENGTH] ____cacheline_aligned; +}; + +struct st_lps33hw_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_lps33hw_hw { + struct device *dev; + int irq; + + struct mutex fifo_lock; + struct mutex lock; + u8 watermark; + + struct iio_dev *iio_devs[ST_LPS33HW_SENSORS_NUMB]; + u8 enable_mask; + u8 odr; + + s64 delta_ts; + s64 ts_irq; + s64 ts; + + const struct st_lps33hw_transfer_function *tf; + struct st_lps33hw_transfer_buffer tb; +}; + +struct st_lps33hw_sensor { + struct st_lps33hw_hw *hw; + enum st_lps33hw_sensor_type type; + char name[32]; + + u32 gain; + u8 odr; +}; + +int st_lps33hw_common_probe(struct device *dev, int irq, const char *name, + const struct st_lps33hw_transfer_function *tf_ops); +int st_lps33hw_write_with_mask(struct st_lps33hw_hw *hw, u8 addr, u8 mask, + u8 data); +int st_lps33hw_allocate_buffers(struct st_lps33hw_hw *hw); +int st_lps33hw_set_enable(struct st_lps33hw_sensor *sensor, bool enable); +ssize_t st_lps33hw_sysfs_set_hwfifo_watermark(struct device * dev, + struct device_attribute * attr, + const char *buf, size_t count); +ssize_t st_lps33hw_sysfs_flush_fifo(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size); + +#endif /* __ST_LPS33HW_H */ diff --git a/drivers/iio/stm/pressure/st_lps33hw_buffer.c b/drivers/iio/stm/pressure/st_lps33hw_buffer.c new file mode 100644 index 000000000000..53b97539979b --- /dev/null +++ b/drivers/iio/stm/pressure/st_lps33hw_buffer.c @@ -0,0 +1,277 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * STMicroelectronics lps33hw buffer driver + * + * Copyright 2017 STMicroelectronics Inc. + * + * Lorenzo Bianconi + */ + +#include +#include +#include +#include +#include + +#include "st_lps33hw.h" + +#define ST_LPS33HW_FIFO_CTRL_ADDR 0x14 +#define ST_LPS33HW_FIFO_THS_MASK 0x1f +#define ST_LPS33HW_FIFO_MODE_MASK 0xe0 +#define ST_LPS33HW_INT_FTH_MASK 0x10 + +#define ST_LPS33HW_FIFO_SRC_ADDR 0x26 +#define ST_LPS33HW_FIFO_SRC_DIFF_MASK 0x1f + +#define ST_LPS33HW_PRESS_OUT_XL_ADDR 0x28 + +#define ST_LPS33HW_PRESS_SAMPLE_LEN 3 +#define ST_LPS33HW_TEMP_SAMPLE_LEN 2 +#define ST_LPS33HW_FIFO_SAMPLE_LEN (ST_LPS33HW_PRESS_SAMPLE_LEN + \ + ST_LPS33HW_TEMP_SAMPLE_LEN) + +static inline s64 st_lps33hw_get_time_ns(struct iio_dev *iio_dev) +{ + return iio_get_time_ns(iio_dev); +} + +#define ST_LPS33HW_EWMA_LEVEL 96 +#define ST_LPS33HW_EWMA_DIV 128 +static inline s64 st_lps33hw_ewma(s64 old, s64 new, int weight) +{ + s64 diff, incr; + + diff = new - old; + incr = div_s64((ST_LPS33HW_EWMA_DIV - weight) * diff, + ST_LPS33HW_EWMA_DIV); + + return old + incr; +} + +static int st_lps33hw_set_fifo_mode(struct st_lps33hw_hw *hw, + enum st_lps33hw_fifo_mode mode) +{ + switch (mode) { + case ST_LPS33HW_BYPASS: + case ST_LPS33HW_STREAM: + break; + default: + return -EINVAL; + } + return st_lps33hw_write_with_mask(hw, ST_LPS33HW_FIFO_CTRL_ADDR, + ST_LPS33HW_FIFO_MODE_MASK, mode); +} + +static int st_lps33hw_update_fifo_watermark(struct st_lps33hw_hw *hw, u8 val) +{ + int err; + + err = st_lps33hw_write_with_mask(hw, ST_LPS33HW_FIFO_CTRL_ADDR, + ST_LPS33HW_FIFO_THS_MASK, val); + if (err < 0) + return err; + + hw->watermark = val; + + return 0; +} + +ssize_t st_lps33hw_sysfs_set_hwfifo_watermark(struct device * dev, + struct device_attribute * attr, + const char *buf, size_t count) +{ + struct st_lps33hw_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_LPS33HW_MAX_FIFO_LENGTH) + return -EINVAL; + + err = st_lps33hw_update_fifo_watermark(sensor->hw, watermark); + + return err < 0 ? err : count; +} + +static int st_lps33hw_read_fifo(struct st_lps33hw_hw *hw) +{ + u8 iio_buff[ALIGN(sizeof(u32) + sizeof(s64), sizeof(s64))]; + u8 status, buff[ST_LPS33HW_RX_MAX_LENGTH]; + int err, i, read_len; + + err = hw->tf->read(hw->dev, ST_LPS33HW_FIFO_SRC_ADDR, + sizeof(status), &status); + if (err < 0) + return err; + + read_len = (status & ST_LPS33HW_FIFO_SRC_DIFF_MASK) * + ST_LPS33HW_FIFO_SAMPLE_LEN; + if (!read_len) + return 0; + + err = hw->tf->read(hw->dev, ST_LPS33HW_PRESS_OUT_XL_ADDR, + read_len, buff); + if (err < 0) + return err; + + for (i = 0; i < read_len; i += ST_LPS33HW_FIFO_SAMPLE_LEN) { + memcpy(iio_buff, buff + i, ST_LPS33HW_PRESS_SAMPLE_LEN); + iio_push_to_buffers_with_timestamp( + hw->iio_devs[ST_LPS33HW_PRESS], + iio_buff, hw->ts); + /* temp sample */ + memcpy(iio_buff, buff + i + ST_LPS33HW_PRESS_SAMPLE_LEN, + ST_LPS33HW_TEMP_SAMPLE_LEN); + iio_push_to_buffers_with_timestamp( + hw->iio_devs[ST_LPS33HW_TEMP], + iio_buff, hw->ts); + hw->ts += hw->delta_ts; + } + + return read_len; +} + +ssize_t st_lps33hw_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_lps33hw_sensor *sensor = iio_priv(indio_dev); + struct st_lps33hw_hw *hw = sensor->hw; + u64 type, event; + int len; + + mutex_lock(&indio_dev->mlock); + if (!iio_buffer_enabled(indio_dev)) { + mutex_unlock(&indio_dev->mlock); + return -EINVAL; + } + + mutex_lock(&hw->fifo_lock); + len = st_lps33hw_read_fifo(hw); + mutex_unlock(&hw->fifo_lock); + + type = len > 0 ? IIO_EV_DIR_FIFO_DATA : IIO_EV_DIR_FIFO_EMPTY; + if (sensor->type == ST_LPS33HW_PRESS) + event = IIO_UNMOD_EVENT_CODE(IIO_PRESSURE, -1, + IIO_EV_TYPE_FIFO_FLUSH, type); + else + event = IIO_UNMOD_EVENT_CODE(IIO_TEMP, -1, + IIO_EV_TYPE_FIFO_FLUSH, type); + iio_push_event(indio_dev, event, st_lps33hw_get_time_ns(indio_dev)); + mutex_unlock(&indio_dev->mlock); + + return size; +} + + +static irqreturn_t st_lps33hw_irq_handler(int irq, void *private) +{ + struct st_lps33hw_hw *hw = private; + s64 delta_ts, ts; + + ts = st_lps33hw_get_time_ns(hw->iio_devs[ST_LPS33HW_PRESS]); + delta_ts = div_s64((ts - hw->ts_irq), hw->watermark); + if (hw->odr >= 50) + hw->delta_ts = st_lps33hw_ewma(hw->delta_ts, delta_ts, + ST_LPS33HW_EWMA_LEVEL); + else + hw->delta_ts = delta_ts; + hw->ts_irq = ts; + + return IRQ_WAKE_THREAD; +} + +static irqreturn_t st_lps33hw_irq_thread(int irq, void *private) +{ + struct st_lps33hw_hw *hw = private; + + mutex_lock(&hw->fifo_lock); + st_lps33hw_read_fifo(hw); + mutex_unlock(&hw->fifo_lock); + + return IRQ_HANDLED; +} + +static int st_lps33hw_buffer_preenable(struct iio_dev *indio_dev) +{ + struct st_lps33hw_sensor *sensor = iio_priv(indio_dev); + struct st_lps33hw_hw *hw = sensor->hw; + int err; + + err = st_lps33hw_set_fifo_mode(sensor->hw, ST_LPS33HW_STREAM); + if (err < 0) + return err; + + err = st_lps33hw_update_fifo_watermark(hw, hw->watermark); + if (err < 0) + return err; + + err = st_lps33hw_write_with_mask(sensor->hw, ST_LPS33HW_CTRL3_ADDR, + ST_LPS33HW_INT_FTH_MASK, true); + if (err < 0) + return err; + + err = st_lps33hw_set_enable(sensor, true); + if (err < 0) + return err; + + hw->delta_ts = div_s64(1000000000UL, hw->odr); + hw->ts = st_lps33hw_get_time_ns(indio_dev); + hw->ts_irq = hw->ts; + + return 0; +} + +static int st_lps33hw_buffer_postdisable(struct iio_dev *indio_dev) +{ + struct st_lps33hw_sensor *sensor = iio_priv(indio_dev); + int err; + + err = st_lps33hw_set_fifo_mode(sensor->hw, ST_LPS33HW_BYPASS); + if (err < 0) + return err; + + err = st_lps33hw_write_with_mask(sensor->hw, ST_LPS33HW_CTRL3_ADDR, + ST_LPS33HW_INT_FTH_MASK, false); + if (err < 0) + return err; + + return st_lps33hw_set_enable(sensor, false); +} + +static const struct iio_buffer_setup_ops st_lps33hw_buffer_ops = { + .preenable = st_lps33hw_buffer_preenable, + .postdisable = st_lps33hw_buffer_postdisable, +}; + +int st_lps33hw_allocate_buffers(struct st_lps33hw_hw *hw) +{ + struct iio_buffer *buffer; + int err, i; + + err = devm_request_threaded_irq(hw->dev, hw->irq, + st_lps33hw_irq_handler, + st_lps33hw_irq_thread, + IRQF_TRIGGER_HIGH | IRQF_ONESHOT, + "lps33hw", hw); + if (err) + return err; + + for (i = 0; i < ST_LPS33HW_SENSORS_NUMB; i++) { + 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_lps33hw_buffer_ops; + } + return 0; +} + +MODULE_DESCRIPTION("STMicroelectronics lps33hw buffer driver"); +MODULE_AUTHOR("Lorenzo Bianconi "); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/stm/pressure/st_lps33hw_core.c b/drivers/iio/stm/pressure/st_lps33hw_core.c new file mode 100644 index 000000000000..5c6911d02114 --- /dev/null +++ b/drivers/iio/stm/pressure/st_lps33hw_core.c @@ -0,0 +1,484 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * STMicroelectronics lps33hw driver + * + * Copyright 2017 STMicroelectronics Inc. + * + * Lorenzo Bianconi + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "st_lps33hw.h" + +#define ST_LPS33HW_WHO_AM_I_ADDR 0x0f +#define ST_LPS33HW_WHO_AM_I_DEF 0xb1 + +#define ST_LPS33HW_CTRL1_ADDR 0x10 +#define ST_LPS33HW_BDU_MASK 0x02 +#define ST_LPS33HW_CTRL2_ADDR 0x11 +#define ST_LPS33HW_SOFT_RESET_MASK 0x04 +#define ST_LPS33HW_FIFO_ENABLE_MASK 0x40 + +#define ST_LPS33HW_LIR_ADDR 0x0b +#define ST_LPS33HW_LIR_MASK 0x04 + +#define ST_LPS33HW_PRESS_FS_AVL_GAIN (1000000000UL / 4096UL) +#define ST_LPS33HW_TEMP_FS_AVL_GAIN (1000000000UL / 100UL) + +#define ST_LPS33HW_ODR_LIST_NUM 6 +struct st_lps33hw_odr_table_t { + u8 addr; + u8 mask; + u8 odr_avl[ST_LPS33HW_ODR_LIST_NUM]; +}; + +const static struct st_lps33hw_odr_table_t st_lps33hw_odr_table = { + .addr = 0x10, + .mask = 0x70, + .odr_avl = { 0, 1, 10, 25, 50, 75 }, +}; + +const struct iio_event_spec st_lps33hw_fifo_flush_event = { + .type = IIO_EV_TYPE_FIFO_FLUSH, + .dir = IIO_EV_DIR_EITHER, +}; + +static const struct iio_chan_spec st_lps33hw_press_channels[] = { + { + .type = IIO_PRESSURE, + .address = 0x28, + .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_lps33hw_fifo_flush_event, + .num_event_specs = 1, + }, + IIO_CHAN_SOFT_TIMESTAMP(1) +}; + +static const struct iio_chan_spec st_lps33hw_temp_channels[] = { + { + .type = IIO_TEMP, + .address = 0x2b, + .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, + }, + }, + { + .type = IIO_TEMP, + .scan_index = -1, + .indexed = -1, + .event_spec = &st_lps33hw_fifo_flush_event, + .num_event_specs = 1, + }, + IIO_CHAN_SOFT_TIMESTAMP(1) +}; + +int st_lps33hw_write_with_mask(struct st_lps33hw_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_lps33hw_check_whoami(struct st_lps33hw_hw *hw) +{ + int err; + u8 data; + + err = hw->tf->read(hw->dev, ST_LPS33HW_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_LPS33HW_WHO_AM_I_DEF) { + dev_err(hw->dev, "Who-Am-I value not valid.\n"); + return -ENODEV; + } + return 0; +} + +static int st_lps33hw_get_odr(struct st_lps33hw_sensor *sensor, u8 odr) +{ + int i; + + for (i = 0; i < ST_LPS33HW_ODR_LIST_NUM; i++) { + if (st_lps33hw_odr_table.odr_avl[i] == odr) + break; + } + return i == ST_LPS33HW_ODR_LIST_NUM ? -EINVAL : i; +} + +int st_lps33hw_set_enable(struct st_lps33hw_sensor *sensor, bool enable) +{ + struct st_lps33hw_hw *hw = sensor->hw; + u32 max_odr = enable ? sensor->odr : 0; + int i; + + for (i = 0; i < ST_LPS33HW_SENSORS_NUMB; i++) { + if (sensor->type == i) + continue; + + if (hw->enable_mask & BIT(i)) { + struct st_lps33hw_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_lps33hw_get_odr(sensor, max_odr); + if (ret < 0) + return ret; + + err = st_lps33hw_write_with_mask(hw, st_lps33hw_odr_table.addr, + st_lps33hw_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_lps33hw_init_sensors(struct st_lps33hw_hw *hw) +{ + int err; + + /* soft reset the device on power on. */ + err = st_lps33hw_write_with_mask(hw, ST_LPS33HW_CTRL2_ADDR, + ST_LPS33HW_SOFT_RESET_MASK, 1); + if (err < 0) + return err; + + msleep(200); + + /* enable latched interrupt mode */ + err = st_lps33hw_write_with_mask(hw, ST_LPS33HW_LIR_ADDR, + ST_LPS33HW_LIR_MASK, 1); + if (err < 0) + return err; + + /* enable FIFO */ + err = st_lps33hw_write_with_mask(hw, ST_LPS33HW_CTRL2_ADDR, + ST_LPS33HW_FIFO_ENABLE_MASK, 1); + if (err < 0) + return err; + + /* enable BDU */ + return st_lps33hw_write_with_mask(hw, ST_LPS33HW_CTRL1_ADDR, + ST_LPS33HW_BDU_MASK, 1); +} + +static ssize_t +st_lps33hw_get_sampling_frequency_avail(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int i, len = 0; + + for (i = 1; i < ST_LPS33HW_ODR_LIST_NUM; i++) { + len += scnprintf(buf + len, PAGE_SIZE - len, "%d ", + st_lps33hw_odr_table.odr_avl[i]); + } + buf[len - 1] = '\n'; + + return len; +} + +static ssize_t +st_lps33hw_sysfs_get_hwfifo_watermark(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct st_lps33hw_sensor *sensor = iio_priv(dev_get_drvdata(dev)); + + return sprintf(buf, "%d\n", sensor->hw->watermark); +} + +static ssize_t +st_lps33hw_sysfs_get_hwfifo_watermark_min(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + return sprintf(buf, "%d\n", 1); +} + +static ssize_t +st_lps33hw_sysfs_get_hwfifo_watermark_max(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + return sprintf(buf, "%d\n", ST_LPS33HW_MAX_FIFO_LENGTH); +} + +static int st_lps33hw_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *ch, + int *val, int *val2, long mask) +{ + struct st_lps33hw_sensor *sensor = iio_priv(indio_dev); + struct st_lps33hw_hw *hw = sensor->hw; + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: { + u8 len = ch->scan_type.realbits / 8; + u8 data[4] = {}; + + mutex_lock(&indio_dev->mlock); + if (iio_buffer_enabled(indio_dev)) { + mutex_unlock(&indio_dev->mlock); + ret = -EBUSY; + break; + } + + ret = st_lps33hw_set_enable(sensor, true); + if (ret < 0) { + mutex_unlock(&indio_dev->mlock); + ret = -EBUSY; + break; + } + + msleep(40); + ret = hw->tf->read(hw->dev, ch->address, len, data); + if (ret < 0) { + mutex_unlock(&indio_dev->mlock); + return ret; + } + + if (sensor->type == ST_LPS33HW_PRESS) + *val = (s32)get_unaligned_le32(data); + else if (sensor->type == ST_LPS33HW_TEMP) + *val = (s16)get_unaligned_le16(data); + + ret = st_lps33hw_set_enable(sensor, false); + mutex_unlock(&indio_dev->mlock); + + if (ret < 0) + return ret; + + ret = IIO_VAL_INT; + break; + } + case IIO_CHAN_INFO_SCALE: + *val = 0; + *val2 = sensor->gain; + ret = IIO_VAL_INT_PLUS_NANO; + break; + case IIO_CHAN_INFO_SAMP_FREQ: + *val = sensor->odr; + ret = IIO_VAL_INT; + break; + default: + ret = -EINVAL; + break; + } + return ret; +} + +static int st_lps33hw_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *ch, + int val, int val2, long mask) +{ + struct st_lps33hw_sensor *sensor = iio_priv(indio_dev); + int ret; + + switch (mask) { + case IIO_CHAN_INFO_SAMP_FREQ: + ret = st_lps33hw_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_lps33hw_get_sampling_frequency_avail); +static IIO_DEVICE_ATTR(hwfifo_watermark, S_IWUSR | S_IRUGO, + st_lps33hw_sysfs_get_hwfifo_watermark, + st_lps33hw_sysfs_set_hwfifo_watermark, 0); +static IIO_DEVICE_ATTR(hwfifo_watermark_min, S_IRUGO, + st_lps33hw_sysfs_get_hwfifo_watermark_min, NULL, 0); +static IIO_DEVICE_ATTR(hwfifo_watermark_max, S_IRUGO, + st_lps33hw_sysfs_get_hwfifo_watermark_max, NULL, 0); +static IIO_DEVICE_ATTR(hwfifo_flush, S_IWUSR, NULL, + st_lps33hw_sysfs_flush_fifo, 0); + +static struct attribute *st_lps33hw_press_attributes[] = { + &iio_dev_attr_sampling_frequency_available.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 struct attribute *st_lps33hw_temp_attributes[] = { + &iio_dev_attr_sampling_frequency_available.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 st_lps33hw_press_attribute_group = { + .attrs = st_lps33hw_press_attributes, +}; +static const struct attribute_group st_lps33hw_temp_attribute_group = { + .attrs = st_lps33hw_temp_attributes, +}; + +static const struct iio_info st_lps33hw_press_info = { + .attrs = &st_lps33hw_press_attribute_group, + .read_raw = st_lps33hw_read_raw, + .write_raw = st_lps33hw_write_raw, +}; + +static const struct iio_info st_lps33hw_temp_info = { + .attrs = &st_lps33hw_temp_attribute_group, + .read_raw = st_lps33hw_read_raw, + .write_raw = st_lps33hw_write_raw, +}; + +int st_lps33hw_common_probe(struct device *dev, int irq, const char *name, + const struct st_lps33hw_transfer_function *tf_ops) +{ + struct st_lps33hw_sensor *sensor; + struct st_lps33hw_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_lps33hw_check_whoami(hw); + if (err < 0) + return err; + + for (i = 0; i < ST_LPS33HW_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_LPS33HW_PRESS: + sensor->gain = ST_LPS33HW_PRESS_FS_AVL_GAIN; + scnprintf(sensor->name, sizeof(sensor->name), + "%s_press", name); + iio_dev->channels = st_lps33hw_press_channels; + iio_dev->num_channels = + ARRAY_SIZE(st_lps33hw_press_channels); + iio_dev->info = &st_lps33hw_press_info; + break; + case ST_LPS33HW_TEMP: + sensor->gain = ST_LPS33HW_TEMP_FS_AVL_GAIN; + scnprintf(sensor->name, sizeof(sensor->name), + "%s_temp", name); + iio_dev->channels = st_lps33hw_temp_channels; + iio_dev->num_channels = + ARRAY_SIZE(st_lps33hw_temp_channels); + iio_dev->info = &st_lps33hw_temp_info; + break; + default: + return -EINVAL; + }; + iio_dev->name = sensor->name; + iio_dev->modes = INDIO_DIRECT_MODE; + } + + err = st_lps33hw_init_sensors(hw); + if (err < 0) + return err; + + if (irq > 0) { + err = st_lps33hw_allocate_buffers(hw); + if (err < 0) + return err; + } + + for (i = 0; i < ST_LPS33HW_SENSORS_NUMB; i++) { + err = devm_iio_device_register(dev, hw->iio_devs[i]); + if (err) + return err; + } + return 0; +} +EXPORT_SYMBOL(st_lps33hw_common_probe); + +MODULE_DESCRIPTION("STMicroelectronics lps33hw driver"); +MODULE_AUTHOR("Lorenzo Bianconi "); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/stm/pressure/st_lps33hw_i2c.c b/drivers/iio/stm/pressure/st_lps33hw_i2c.c new file mode 100644 index 000000000000..e684a873f1f1 --- /dev/null +++ b/drivers/iio/stm/pressure/st_lps33hw_i2c.c @@ -0,0 +1,87 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * STMicroelectronics lps33hw i2c driver + * + * Copyright 2017 STMicroelectronics Inc. + * + * Lorenzo Bianconi + */ + +#include + +#include "st_lps33hw.h" + +static int st_lps33hw_i2c_read(struct device *dev, u8 addr, int len, u8 *data) +{ + struct i2c_client *client = to_i2c_client(dev); + struct i2c_msg msg[2]; + + 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 = data; + + return i2c_transfer(client->adapter, msg, 2); +} + +static int st_lps33hw_i2c_write(struct device *dev, u8 addr, int len, u8 *data) +{ + struct i2c_client *client = to_i2c_client(dev); + struct i2c_msg msg; + u8 send[len + 1]; + + send[0] = 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 st_lps33hw_transfer_function st_lps33hw_tf_i2c = { + .write = st_lps33hw_i2c_write, + .read = st_lps33hw_i2c_read, +}; + +static int st_lps33hw_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + return st_lps33hw_common_probe(&client->dev, client->irq, client->name, + &st_lps33hw_tf_i2c); +} + +static const struct i2c_device_id st_lps33hw_ids[] = { + { "lps33hw" }, + {} +}; +MODULE_DEVICE_TABLE(i2c, st_lps33hw_ids); + +static const struct of_device_id st_lps33hw_id_table[] = { + { .compatible = "st,lps33hw" }, + {}, +}; +MODULE_DEVICE_TABLE(of, st_lps33hw_id_table); + +static struct i2c_driver st_lps33hw_i2c_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "st_lps33hw_i2c", + .of_match_table = of_match_ptr(st_lps33hw_id_table), + }, + .probe = st_lps33hw_i2c_probe, + .id_table = st_lps33hw_ids, +}; +module_i2c_driver(st_lps33hw_i2c_driver); + +MODULE_DESCRIPTION("STMicroelectronics lps33hw i2c driver"); +MODULE_AUTHOR("Lorenzo Bianconi "); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/stm/pressure/st_lps33hw_spi.c b/drivers/iio/stm/pressure/st_lps33hw_spi.c new file mode 100644 index 000000000000..e3d7b6d36284 --- /dev/null +++ b/drivers/iio/stm/pressure/st_lps33hw_spi.c @@ -0,0 +1,97 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * STMicroelectronics lps33hw spi driver + * + * Copyright 2017 STMicroelectronics Inc. + * + * Lorenzo Bianconi + */ + +#include + +#include "st_lps33hw.h" + +#define ST_SENSORS_SPI_READ 0x80 + +static int st_lps33hw_spi_read(struct device *dev, u8 addr, int len, u8 *data) +{ + struct spi_device *spi = to_spi_device(dev); + struct st_lps33hw_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; + + 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 len; +} + +static int st_lps33hw_spi_write(struct device *dev, u8 addr, int len, u8 *data) +{ + struct st_lps33hw_hw *hw; + struct spi_device *spi; + + if (len >= ST_LPS33HW_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_lps33hw_transfer_function st_lps33hw_tf_spi = { + .write = st_lps33hw_spi_write, + .read = st_lps33hw_spi_read, +}; + +static int st_lps33hw_spi_probe(struct spi_device *spi) +{ + return st_lps33hw_common_probe(&spi->dev, spi->irq, spi->modalias, + &st_lps33hw_tf_spi); +} + +static const struct spi_device_id st_lps33hw_ids[] = { + { "lps33hw" }, + {} +}; +MODULE_DEVICE_TABLE(spi, st_lps33hw_ids); + +static const struct of_device_id st_lps33hw_id_table[] = { + { .compatible = "st,lps33hw" }, + {}, +}; +MODULE_DEVICE_TABLE(of, st_lps33hw_id_table); + +static struct spi_driver st_lps33hw_spi_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "st_lps33hw_spi", + .of_match_table = of_match_ptr(st_lps33hw_id_table), + }, + .probe = st_lps33hw_spi_probe, + .id_table = st_lps33hw_ids, +}; +module_spi_driver(st_lps33hw_spi_driver); + +MODULE_AUTHOR("Lorenzo Bianconi "); +MODULE_DESCRIPTION("STMicroelectronics lps33hw spi driver"); +MODULE_LICENSE("GPL v2"); diff --git a/stm_iio_configs/lps33hw_defconfig b/stm_iio_configs/lps33hw_defconfig new file mode 100644 index 000000000000..657cb64e40b4 --- /dev/null +++ b/stm_iio_configs/lps33hw_defconfig @@ -0,0 +1,3 @@ +CONFIG_ST_LPS33HW_IIO=m +CONFIG_ST_LPS33HW_I2C_IIO=m +CONFIG_ST_LPS33HW_SPI_IIO=m