[PATCH 1/2] iio: lsm6dsx: Support temperature channel

From: Linus Walleij
Date: Fri Aug 11 2023 - 05:08:38 EST


The LSM6DSX (possibly other ST IMUs as well) has a temperature
channel that can be read out. Modify the lsm6dsx core to
accomodate temperature readout: make headspace for three
members in the channels and odr_table, support offset and make
the available milli_hz frequency resolution optional.

Signed-off-by: Linus Walleij <linus.walleij@xxxxxxxxxx>
---
drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h | 24 ++++++++-
drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c | 4 ++
drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c | 67 ++++++++++++++++++++++++--
3 files changed, 89 insertions(+), 6 deletions(-)

diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h
index c19237717e81..4d013889c287 100644
--- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h
+++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h
@@ -118,6 +118,23 @@ enum st_lsm6dsx_hw_id {
.ext_info = st_lsm6dsx_ext_info, \
}

+#define ST_LSM6DSX_TEMP(chan_type, addr, scan_idx) \
+{ \
+ .type = chan_type, \
+ .address = addr, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
+ BIT(IIO_CHAN_INFO_SCALE) | \
+ BIT(IIO_CHAN_INFO_OFFSET), \
+ .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), \
+ .scan_index = scan_idx, \
+ .scan_type = { \
+ .sign = 's', \
+ .realbits = 16, \
+ .storagebits = 16, \
+ .endianness = IIO_LE, \
+ }, \
+}
+
struct st_lsm6dsx_reg {
u8 addr;
u8 mask;
@@ -320,7 +337,7 @@ struct st_lsm6dsx_settings {
struct {
const struct iio_chan_spec *chan;
int len;
- } channels[2];
+ } channels[3];
struct {
struct st_lsm6dsx_reg irq1;
struct st_lsm6dsx_reg irq2;
@@ -332,7 +349,7 @@ struct st_lsm6dsx_settings {
struct st_lsm6dsx_reg od;
} irq_config;
struct st_lsm6dsx_reg drdy_mask;
- struct st_lsm6dsx_odr_table_entry odr_table[2];
+ struct st_lsm6dsx_odr_table_entry odr_table[3];
struct st_lsm6dsx_samples_to_discard samples_to_discard[2];
struct st_lsm6dsx_fs_table_entry fs_table[2];
struct st_lsm6dsx_reg decimator[ST_LSM6DSX_MAX_ID];
@@ -346,6 +363,7 @@ struct st_lsm6dsx_settings {
enum st_lsm6dsx_sensor_id {
ST_LSM6DSX_ID_GYRO,
ST_LSM6DSX_ID_ACC,
+ ST_LSM6DSX_ID_TEMP,
ST_LSM6DSX_ID_EXT0,
ST_LSM6DSX_ID_EXT1,
ST_LSM6DSX_ID_EXT2,
@@ -364,6 +382,7 @@ enum st_lsm6dsx_fifo_mode {
* @hw: Pointer to instance of struct st_lsm6dsx_hw.
* @gain: Configured sensor sensitivity.
* @odr: Output data rate of the sensor [Hz].
+ * @offset: Constant offset of the sensor
* @samples_to_discard: Number of samples to discard for filters settling time.
* @watermark: Sensor watermark level.
* @decimator: Sensor decimation factor.
@@ -378,6 +397,7 @@ struct st_lsm6dsx_sensor {

u32 gain;
u32 odr;
+ u32 offset;

u16 samples_to_discard;
u16 watermark;
diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c
index 066fe561c5e8..c588451e2ddf 100644
--- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c
+++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c
@@ -61,6 +61,7 @@ struct st_lsm6dsx_decimator_entry {
enum st_lsm6dsx_fifo_tag {
ST_LSM6DSX_GYRO_TAG = 0x01,
ST_LSM6DSX_ACC_TAG = 0x02,
+ ST_LSM6DSX_TEMP_TAG = 0x03,
ST_LSM6DSX_TS_TAG = 0x04,
ST_LSM6DSX_EXT0_TAG = 0x0f,
ST_LSM6DSX_EXT1_TAG = 0x10,
@@ -532,6 +533,9 @@ st_lsm6dsx_push_tagged_data(struct st_lsm6dsx_hw *hw, u8 tag,
case ST_LSM6DSX_ACC_TAG:
iio_dev = hw->iio_devs[ST_LSM6DSX_ID_ACC];
break;
+ case ST_LSM6DSX_TEMP_TAG:
+ iio_dev = hw->iio_devs[ST_LSM6DSX_ID_TEMP];
+ break;
case ST_LSM6DSX_EXT0_TAG:
if (hw->enable_mask & BIT(ST_LSM6DSX_ID_EXT0))
iio_dev = hw->iio_devs[ST_LSM6DSX_ID_EXT0];
diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c
index 6a18b363cf73..c743c4871ad6 100644
--- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c
+++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c
@@ -100,6 +100,11 @@ static const struct iio_chan_spec st_lsm6ds0_gyro_channels[] = {
IIO_CHAN_SOFT_TIMESTAMP(3),
};

+static const struct iio_chan_spec st_lsm6dsx_temp_channels[] = {
+ ST_LSM6DSX_TEMP(IIO_TEMP, 0x20, 0),
+ IIO_CHAN_SOFT_TIMESTAMP(1),
+};
+
static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = {
{
.reset = {
@@ -835,6 +840,10 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = {
.chan = st_lsm6dsx_gyro_channels,
.len = ARRAY_SIZE(st_lsm6dsx_gyro_channels),
},
+ [ST_LSM6DSX_ID_TEMP] = {
+ .chan = st_lsm6dsx_temp_channels,
+ .len = ARRAY_SIZE(st_lsm6dsx_temp_channels),
+ },
},
.drdy_mask = {
.addr = 0x13,
@@ -869,6 +878,15 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = {
.odr_avl[6] = { 833000, 0x07 },
.odr_len = 7,
},
+ [ST_LSM6DSX_ID_TEMP] = {
+ .reg = {
+ .addr = 0x0A,
+ .mask = GENMASK(5, 4),
+ },
+ .odr_avl[0] = { 26000, 0x02 },
+ .odr_avl[1] = { 52000, 0x03 },
+ .odr_len = 2,
+ },
},
.fs_table = {
[ST_LSM6DSX_ID_ACC] = {
@@ -937,6 +955,10 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = {
.addr = 0x09,
.mask = GENMASK(7, 4),
},
+ [ST_LSM6DSX_ID_TEMP] = {
+ .addr = 0x0A,
+ .mask = GENMASK(5, 4),
+ },
},
.fifo_ops = {
.update_fifo = st_lsm6dsx_update_fifo,
@@ -1661,6 +1683,7 @@ st_lsm6dsx_set_odr(struct st_lsm6dsx_sensor *sensor, u32 req_odr)
switch (sensor->id) {
case ST_LSM6DSX_ID_GYRO:
break;
+ case ST_LSM6DSX_ID_TEMP:
case ST_LSM6DSX_ID_EXT0:
case ST_LSM6DSX_ID_EXT1:
case ST_LSM6DSX_ID_EXT2:
@@ -1800,6 +1823,10 @@ static int st_lsm6dsx_read_raw(struct iio_dev *iio_dev,
*val2 = sensor->gain;
ret = IIO_VAL_INT_PLUS_NANO;
break;
+ case IIO_CHAN_INFO_OFFSET:
+ *val = sensor->offset;
+ ret = IIO_VAL_INT;
+ break;
default:
ret = -EINVAL;
break;
@@ -2016,9 +2043,11 @@ st_lsm6dsx_sysfs_sampling_frequency_avail(struct device *dev,

odr_table = &sensor->hw->settings->odr_table[sensor->id];
for (i = 0; i < odr_table->odr_len; i++)
- len += scnprintf(buf + len, PAGE_SIZE - len, "%d.%03d ",
- odr_table->odr_avl[i].milli_hz / 1000,
- odr_table->odr_avl[i].milli_hz % 1000);
+ if (odr_table->odr_avl[i].milli_hz) {
+ len += scnprintf(buf + len, PAGE_SIZE - len, "%d.%03d ",
+ odr_table->odr_avl[i].milli_hz / 1000,
+ odr_table->odr_avl[i].milli_hz % 1000);
+ }
buf[len - 1] = '\n';

return len;
@@ -2106,6 +2135,22 @@ static const struct iio_info st_lsm6dsx_gyro_info = {
.write_raw_get_fmt = st_lsm6dsx_write_raw_get_fmt,
};

+static struct attribute *st_lsm6dsx_temp_attributes[] = {
+ &iio_dev_attr_sampling_frequency_available.dev_attr.attr,
+ NULL,
+};
+
+static const struct attribute_group st_lsm6dsx_temp_attribute_group = {
+ .attrs = st_lsm6dsx_temp_attributes,
+};
+
+static const struct iio_info st_lsm6dsx_temp_info = {
+ .attrs = &st_lsm6dsx_temp_attribute_group,
+ .read_raw = st_lsm6dsx_read_raw,
+ .write_raw = st_lsm6dsx_write_raw,
+ .hwfifo_set_watermark = st_lsm6dsx_set_watermark,
+};
+
static int st_lsm6dsx_get_drdy_pin(struct st_lsm6dsx_hw *hw, int *drdy_pin)
{
struct device *dev = hw->dev;
@@ -2379,7 +2424,16 @@ static struct iio_dev *st_lsm6dsx_alloc_iiodev(struct st_lsm6dsx_hw *hw,
sensor->id = id;
sensor->hw = hw;
sensor->odr = hw->settings->odr_table[id].odr_avl[0].milli_hz;
- sensor->gain = hw->settings->fs_table[id].fs_avl[0].gain;
+ if (id == ST_LSM6DSX_ID_TEMP) {
+ /*
+ * The temperature sensor has a fixed scale and offset such
+ * that: temp_C = (raw / 256) + 25
+ */
+ sensor->gain = 3906;
+ sensor->offset = 25;
+ } else {
+ sensor->gain = hw->settings->fs_table[id].fs_avl[0].gain;
+ }
sensor->watermark = 1;

switch (id) {
@@ -2393,6 +2447,11 @@ static struct iio_dev *st_lsm6dsx_alloc_iiodev(struct st_lsm6dsx_hw *hw,
scnprintf(sensor->name, sizeof(sensor->name), "%s_gyro",
name);
break;
+ case ST_LSM6DSX_ID_TEMP:
+ iio_dev->info = &st_lsm6dsx_temp_info;
+ scnprintf(sensor->name, sizeof(sensor->name), "%s_temp",
+ name);
+ break;
default:
return NULL;
}

--
2.34.1