summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul Geurts <paul.geurts@prodrive-technologies.com>2026-07-06 09:48:03 +0200
committerJonathan Cameron <jonathan.cameron@oss.qualcomm.com>2026-08-07 23:49:34 +0100
commitbcb721c1bcb02ab225b3937bf131a0bc6fc1fecd (patch)
tree31c8dfe03c54c7fc801d708a452c925d9ae134d3
parenta41000ba3a230bed1e422f283486ff8f77fe0d30 (diff)
downloadlinux-bcb721c1bcb02ab225b3937bf131a0bc6fc1fecd.tar.gz
linux-bcb721c1bcb02ab225b3937bf131a0bc6fc1fecd.zip
iio: ti-ads7138: Disable STATS_EN bit while reading conversion results
There is a data race in reading the STATS registers, resulting in wrong data being read. When the data in the RECENT register switches between 0x24F0 and 0x2500, occasionally value 0x2400 or 0x25F0 is read. This happens when the value is updated in between reading MSB and LSB. The data sheet says: "Until a new conversion result is available, previous values can be read from the statistics registers. Before reading the statistics registers, set STATS_EN to 0 to prevent any updates to this register block." As the STATS_EN is currently not cleared, the values of the stats registers might change mid read, giving faulty values. Disable the STATS_EN bit before reading one of the statistics registers to make sure the device does not update the register mid read. This is applicable to registers MAX_CHn_xSB, MIN_CHn_xSB and RECENT_CHn_xSB. This means reading one of the statistics registers resets the MAX and MIN registers. This is unfortunate, but necessary to get correct data from the device. Signed-off-by: Paul Geurts <paul.geurts@prodrive-technologies.com> Fixes: 024b08fee342 ("iio: adc: Add driver for ADS7128 / ADS7138") Reviewed-by: David Lechner <dlechner@baylibre.com> Cc: <Stable@vger.kernel.org> Signed-off-by: Jonathan Cameron <jonathan.cameron@oss.qualcomm.com>
-rw-r--r--drivers/iio/adc/ti-ads7138.c42
1 files changed, 33 insertions, 9 deletions
diff --git a/drivers/iio/adc/ti-ads7138.c b/drivers/iio/adc/ti-ads7138.c
index af87f5f19a0f..da82a6947b2b 100644
--- a/drivers/iio/adc/ti-ads7138.c
+++ b/drivers/iio/adc/ti-ads7138.c
@@ -227,6 +227,26 @@ static int ads7138_osr_to_bits(int osr)
return -EINVAL;
}
+static int ads7138_read_statistics(const struct i2c_client *client, u8 reg,
+ u8 *out_values, u8 length)
+{
+ int ret;
+
+ /* Disable statistics update so the value is not updated mid read */
+ ret = ads7138_i2c_clear_bit(client, ADS7138_REG_GENERAL_CFG,
+ ADS7138_GENERAL_CFG_STATS_EN);
+ if (ret)
+ return ret;
+
+ ret = ads7138_i2c_read_block(client, reg, out_values, length);
+ if (ret)
+ return ret;
+
+ /* Enable statistics update after read */
+ return ads7138_i2c_set_bit(client, ADS7138_REG_GENERAL_CFG,
+ ADS7138_GENERAL_CFG_STATS_EN);
+}
+
static int ads7138_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan, int *val,
int *val2, long mask)
@@ -236,28 +256,32 @@ static int ads7138_read_raw(struct iio_dev *indio_dev,
u8 values[2];
switch (mask) {
+ /*
+ * Reading the statistics registers reinitializes them. This is
+ * unfortunate but necessary to prevent data races.
+ */
case IIO_CHAN_INFO_RAW:
- ret = ads7138_i2c_read_block(data->client,
- ADS7138_REG_RECENT_LSB_CH(chan->channel),
- values, ARRAY_SIZE(values));
+ ret = ads7138_read_statistics(data->client,
+ ADS7138_REG_RECENT_LSB_CH(chan->channel),
+ values, ARRAY_SIZE(values));
if (ret)
return ret;
*val = get_unaligned_le16(values);
return IIO_VAL_INT;
case IIO_CHAN_INFO_PEAK:
- ret = ads7138_i2c_read_block(data->client,
- ADS7138_REG_MAX_LSB_CH(chan->channel),
- values, ARRAY_SIZE(values));
+ ret = ads7138_read_statistics(data->client,
+ ADS7138_REG_MAX_LSB_CH(chan->channel),
+ values, ARRAY_SIZE(values));
if (ret)
return ret;
*val = get_unaligned_le16(values);
return IIO_VAL_INT;
case IIO_CHAN_INFO_TROUGH:
- ret = ads7138_i2c_read_block(data->client,
- ADS7138_REG_MIN_LSB_CH(chan->channel),
- values, ARRAY_SIZE(values));
+ ret = ads7138_read_statistics(data->client,
+ ADS7138_REG_MIN_LSB_CH(chan->channel),
+ values, ARRAY_SIZE(values));
if (ret)
return ret;