* [PATCH 0/3] iio: adc: add LTC2499 features support
@ 2026-08-05 9:41 Andrei Stancovici
2026-08-05 9:41 ` [PATCH 1/3] dt-bindings: iio: adc: lltc,ltc2497: add LTC2499 to title Andrei Stancovici
` (2 more replies)
0 siblings, 3 replies; 8+ messages in thread
From: Andrei Stancovici @ 2026-08-05 9:41 UTC (permalink / raw)
To: Nuno Sá, Michael Hennerich, Jonathan Cameron, David Lechner,
Andy Shevchenko, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Liam Beguin, linux, linux-iio, devicetree, linux-kernel
Cc: Andrei Stancovici
This series adds LTC2499 support on top of the existing LTC2497 driver.
It includes:
- a small binding title correction for LTC2499,
- the LTC2499 internal temperature channel,
- the LTC2499 2x conversion speed mode exposed via sampling_frequency.
Validation performed on Raspberry Pi 4 hardware with an DC1012A-A board:
- temperature raw/scale/offset read correctly,
- voltage channels remain sane after temperature reads,
- sampling_frequency 1x/2x switching works,
- pyadi-iio LTC2499 validation passed.
Andrei Stancovici (3):
dt-bindings: iio: adc: lltc,ltc2497: add LTC2499 to title
iio: adc: ltc2497: add LTC2499 internal temperature channel
iio: adc: ltc2497: add 2x conversion speed mode
.../bindings/iio/adc/lltc,ltc2497.yaml | 2 +-
drivers/iio/adc/ltc2497-core.c | 221 +++++++++++++++++-
drivers/iio/adc/ltc2497.c | 37 +++
drivers/iio/adc/ltc2497.h | 36 ++-
4 files changed, 284 insertions(+), 12 deletions(-)
--
2.43.0
^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH 1/3] dt-bindings: iio: adc: lltc,ltc2497: add LTC2499 to title
2026-08-05 9:41 [PATCH 0/3] iio: adc: add LTC2499 features support Andrei Stancovici
@ 2026-08-05 9:41 ` Andrei Stancovici
2026-08-05 9:41 ` [PATCH 2/3] iio: adc: ltc2497: add LTC2499 internal temperature channel Andrei Stancovici
2026-08-05 9:41 ` [PATCH 3/3] iio: adc: ltc2497: add 2x conversion speed mode Andrei Stancovici
2 siblings, 0 replies; 8+ messages in thread
From: Andrei Stancovici @ 2026-08-05 9:41 UTC (permalink / raw)
To: Nuno Sá, Michael Hennerich, Jonathan Cameron, David Lechner,
Andy Shevchenko, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Liam Beguin, linux, linux-iio, devicetree, linux-kernel
Cc: Andrei Stancovici
The LTC2499 is already listed in the compatible enum and covered by the
description, but was missing from the binding title. Add it so the title
matches the set of devices the schema documents. No schema or property
change is needed.
Signed-off-by: Andrei Stancovici <andrei.stancovici@analog.com>
---
Documentation/devicetree/bindings/iio/adc/lltc,ltc2497.yaml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Documentation/devicetree/bindings/iio/adc/lltc,ltc2497.yaml b/Documentation/devicetree/bindings/iio/adc/lltc,ltc2497.yaml
index c884b6e03767..8aa1ba0a8728 100644
--- a/Documentation/devicetree/bindings/iio/adc/lltc,ltc2497.yaml
+++ b/Documentation/devicetree/bindings/iio/adc/lltc,ltc2497.yaml
@@ -4,7 +4,7 @@
$id: http://devicetree.org/schemas/iio/adc/lltc,ltc2497.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
-title: Linear Technology / Analog Devices LTC2497 and LTC2309 ADC
+title: Linear Technology / Analog Devices LTC2497, LTC2499 and LTC2309 ADC
maintainers:
- Michael Hennerich <michael.hennerich@analog.com>
--
2.43.0
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH 2/3] iio: adc: ltc2497: add LTC2499 internal temperature channel
2026-08-05 9:41 [PATCH 0/3] iio: adc: add LTC2499 features support Andrei Stancovici
2026-08-05 9:41 ` [PATCH 1/3] dt-bindings: iio: adc: lltc,ltc2497: add LTC2499 to title Andrei Stancovici
@ 2026-08-05 9:41 ` Andrei Stancovici
2026-08-05 9:57 ` sashiko-bot
2026-08-05 10:00 ` Andy Shevchenko
2026-08-05 9:41 ` [PATCH 3/3] iio: adc: ltc2497: add 2x conversion speed mode Andrei Stancovici
2 siblings, 2 replies; 8+ messages in thread
From: Andrei Stancovici @ 2026-08-05 9:41 UTC (permalink / raw)
To: Nuno Sá, Michael Hennerich, Jonathan Cameron, David Lechner,
Andy Shevchenko, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Liam Beguin, linux, linux-iio, devicetree, linux-kernel
Cc: Andrei Stancovici
The LTC2499 has an internal PTAT (proportional to absolute temperature)
sensor that is activated by a second I2C configuration byte (EN2 | IM).
Expose it as an IIO_TEMP channel providing raw, scale and offset so the
standard IIO formula
T[m°C] = (raw + offset) * scale
reconstructs the temperature.
The PTAT sensor yields the absolute temperature as
T(K) = DATAOUT24 * Vref / 1570 (Vref in volts)
The raw value exported here is sign-extended and normalised to
2^(resolution + 1) == 2^25, i.e. raw = 2 * DATAOUT24, so on the IIO
milli-degree-Celsius convention
scale[m°C/LSB] = Vref_uV / 3140000
offset = -273150 * 3140000 / Vref_uV
The scale and offset are derived from the reference voltage returned by
regulator_get_voltage(); its error is propagated as before, so a board
that fails to describe vref-supply gets a clear read error instead of a
silently wrong temperature. No board-specific reference value is assumed
in the driver.
The single temperature channel is appended as the last entry of the
shared channel array and excluded via num_channels for parts without an
internal sensor, so the existing LTC2497 channel layout and device name
are unchanged.
The LTC2499 latches its converter configuration from the second command
byte and only re-evaluates it when that byte has EN2 set. EN2 | IM
selects the internal temperature sensor. Because a single-byte command,
or a second byte with EN2 = 0, means "keep previous", a one-byte channel
select cannot pull the device back out of temperature mode: after a
temperature read every subsequent voltage read would keep returning the
PTAT result instead of the selected input. Temperature support is
therefore only correct if the voltage path also emits a second command
byte that re-selects an external input.
Send two-byte commands for all conversions on parts that have the sensor
(has_temp):
temperature: EN2 | IM
voltage: EN2 (IM = 0 -> external input)
The LTC2497 and LTC2496, which lack the second-byte mechanism, keep using
the original single-byte channel select and are unchanged.
Signed-off-by: Andrei Stancovici <andrei.stancovici@analog.com>
---
drivers/iio/adc/ltc2497-core.c | 54 +++++++++++++++++++++++++++++++---
drivers/iio/adc/ltc2497.c | 31 +++++++++++++++++++
drivers/iio/adc/ltc2497.h | 12 ++++++++
3 files changed, 93 insertions(+), 4 deletions(-)
diff --git a/drivers/iio/adc/ltc2497-core.c b/drivers/iio/adc/ltc2497-core.c
index 2dc5c7044269..4ca7271ca007 100644
--- a/drivers/iio/adc/ltc2497-core.c
+++ b/drivers/iio/adc/ltc2497-core.c
@@ -9,6 +9,7 @@
#include <linux/delay.h>
#include <linux/iio/iio.h>
#include <linux/iio/driver.h>
+#include <linux/math64.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/regulator/consumer.h>
@@ -95,10 +96,43 @@ static int ltc2497core_read_raw(struct iio_dev *indio_dev,
if (ret < 0)
return ret;
- *val = ret / 1000;
- *val2 = ddata->chip_info->resolution + 1;
-
- return IIO_VAL_FRACTIONAL_LOG2;
+ switch (chan->type) {
+ case IIO_TEMP:
+ /*
+ * raw is normalised to 2^(resolution + 1), i.e.
+ * raw = 2 * DATAOUT24, so the PTAT scale (datasheet
+ * Vref / 1570 per kelvin) doubles its denominator and,
+ * in m°C, becomes Vref_uV / 3140000.
+ */
+ *val = ret;
+ *val2 = 3140000;
+ return IIO_VAL_FRACTIONAL;
+ case IIO_VOLTAGE:
+ *val = ret / 1000;
+ *val2 = ddata->chip_info->resolution + 1;
+ return IIO_VAL_FRACTIONAL_LOG2;
+ default:
+ return -EINVAL;
+ }
+
+ case IIO_CHAN_INFO_OFFSET:
+ switch (chan->type) {
+ case IIO_TEMP:
+ ret = regulator_get_voltage(ddata->ref);
+ if (ret < 0)
+ return ret;
+ /*
+ * 0 °C == 273.15 K must map to raw + offset such that
+ * (raw + offset) * scale == 0 m°C, i.e.
+ * offset = -273150 / scale
+ * = -273150 * 3140000 / Vref_uV
+ * Computed in 64-bit to avoid overflow.
+ */
+ *val = div_s64(-273150LL * 3140000, ret);
+ return IIO_VAL_INT;
+ default:
+ return -EINVAL;
+ }
default:
return -EINVAL;
@@ -126,6 +160,14 @@ static int ltc2497core_read_raw(struct iio_dev *indio_dev,
.differential = 1, \
}
+#define LTC2497_TEMP_CHANNEL { \
+ .type = IIO_TEMP, \
+ .address = LTC2497_TEMP_ADDR, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
+ BIT(IIO_CHAN_INFO_SCALE) | \
+ BIT(IIO_CHAN_INFO_OFFSET), \
+}
+
static const struct iio_chan_spec ltc2497core_channel[] = {
LTC2497_CHAN(0, LTC2497_SGL, "CH0"),
LTC2497_CHAN(1, LTC2497_SGL, "CH1"),
@@ -159,6 +201,7 @@ static const struct iio_chan_spec ltc2497core_channel[] = {
LTC2497_CHAN_DIFF(5, LTC2497_DIFF | LTC2497_SIGN),
LTC2497_CHAN_DIFF(6, LTC2497_DIFF | LTC2497_SIGN),
LTC2497_CHAN_DIFF(7, LTC2497_DIFF | LTC2497_SIGN),
+ LTC2497_TEMP_CHANNEL,
};
static const struct iio_info ltc2497core_info = {
@@ -184,6 +227,9 @@ int ltc2497core_probe(struct device *dev, struct iio_dev *indio_dev)
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->channels = ltc2497core_channel;
indio_dev->num_channels = ARRAY_SIZE(ltc2497core_channel);
+ /* Only the ltc2499 has a temperature channel; it is the last entry. */
+ if (!ddata->chip_info->has_temp)
+ indio_dev->num_channels--;
ret = ddata->result_and_measure(ddata, LTC2497_CONFIG_DEFAULT, NULL);
if (ret < 0)
diff --git a/drivers/iio/adc/ltc2497.c b/drivers/iio/adc/ltc2497.c
index c1668b5a351e..2ff53b292f74 100644
--- a/drivers/iio/adc/ltc2497.c
+++ b/drivers/iio/adc/ltc2497.c
@@ -84,6 +84,36 @@ static int ltc2497_result_and_measure(struct ltc2497core_driverdata *ddata,
return 0;
}
+ /*
+ * Parts with the internal PTAT sensor (LTC2499) latch their converter
+ * configuration via a second command byte and only re-evaluate it when
+ * that byte has EN2 set; a single byte, or a second byte with EN2 = 0,
+ * means "keep previous". A one-byte channel select therefore cannot pull
+ * the device back out of temperature mode, so a voltage read after a
+ * temperature read would keep returning the PTAT result. Always drive the
+ * second byte with EN2 set on these parts: IM | temperature-rejection for
+ * a temperature read, EN2 alone (IM = 0) to (re)select an external input.
+ */
+ if (ddata->chip_info->has_temp) {
+ u8 cmd[2];
+
+ if (address == LTC2497_TEMP_ADDR) {
+ cmd[0] = LTC2497_ENABLE | LTC2497_CONFIG_DEFAULT;
+ cmd[1] = LTC2499_EN2 | LTC2499_IM;
+ } else {
+ cmd[0] = LTC2497_ENABLE | address;
+ cmd[1] = LTC2499_EN2;
+ }
+
+ ret = i2c_master_send(st->client, cmd, sizeof(cmd));
+ if (ret < 0) {
+ dev_err(&st->client->dev, "i2c transfer failed: %pe\n",
+ ERR_PTR(ret));
+ return ret;
+ }
+ return 0;
+ }
+
ret = i2c_smbus_write_byte(st->client,
LTC2497_ENABLE | address);
if (ret)
@@ -137,6 +167,7 @@ static const struct ltc2497_chip_info ltc2497_info[] = {
[TYPE_LTC2499] = {
.resolution = 24,
.name = "ltc2499",
+ .has_temp = true,
},
};
diff --git a/drivers/iio/adc/ltc2497.h b/drivers/iio/adc/ltc2497.h
index 64e81c95a3dd..1da2cfec3b6a 100644
--- a/drivers/iio/adc/ltc2497.h
+++ b/drivers/iio/adc/ltc2497.h
@@ -4,9 +4,21 @@
#define LTC2497_CONFIG_DEFAULT LTC2497_ENABLE
#define LTC2497_CONVERSION_TIME_MS 150ULL
+/*
+ * Sentinel passed as `address` to result_and_measure() to request a
+ * temperature conversion instead of a voltage channel. Valid channel
+ * addresses fit in 5 bits (0x00–0x1F), so 0xFF is unambiguous.
+ */
+#define LTC2497_TEMP_ADDR 0xFF
+
+/* Second config-byte bits (LTC2499 / LTC2493 only) */
+#define LTC2499_EN2 BIT(7) /* enable second config byte */
+#define LTC2499_IM BIT(6) /* 1 = measure internal temp sensor */
+
struct ltc2497_chip_info {
u32 resolution;
const char *name;
+ bool has_temp;
};
struct ltc2497core_driverdata {
--
2.43.0
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH 3/3] iio: adc: ltc2497: add 2x conversion speed mode
2026-08-05 9:41 [PATCH 0/3] iio: adc: add LTC2499 features support Andrei Stancovici
2026-08-05 9:41 ` [PATCH 1/3] dt-bindings: iio: adc: lltc,ltc2497: add LTC2499 to title Andrei Stancovici
2026-08-05 9:41 ` [PATCH 2/3] iio: adc: ltc2497: add LTC2499 internal temperature channel Andrei Stancovici
@ 2026-08-05 9:41 ` Andrei Stancovici
2026-08-05 9:56 ` sashiko-bot
2026-08-05 10:08 ` Andy Shevchenko
2 siblings, 2 replies; 8+ messages in thread
From: Andrei Stancovici @ 2026-08-05 9:41 UTC (permalink / raw)
To: Nuno Sá, Michael Hennerich, Jonathan Cameron, David Lechner,
Andy Shevchenko, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Liam Beguin, linux, linux-iio, devicetree, linux-kernel
Cc: Andrei Stancovici
The LTC2499 supports a 2x output rate (SPD bit in the second
configuration byte). In 2x mode the offset auto-calibration is
disabled, roughly doubling the conversion rate (~13.6Hz vs ~6.8Hz in
simultaneous 50/60Hz rejection) while leaving linearity and full-scale
errors unchanged (datasheet). During a temperature measurement the part
always converts at 1x regardless of SPD.
Expose the rate through the standard sampling_frequency /
sampling_frequency_available ABI on the voltage channels only: SPD is
ignored for temperature conversions, so the temperature channel
deliberately carries no SAMP_FREQ attribute. A new has_speed_mode
capability flag gates the feature (LTC2499); the two-byte command path
is now taken for has_temp || has_speed_mode, since both features need the
second config byte.
The conversion-time wait becomes mode dependent: 150ms at 1x, 76ms at 2x
(datasheet t_CONV max, simultaneous rejection, rounded up). The wait is
keyed on the conversion currently in flight, whose duration is fixed by
the mode that was active when it started - not by the newly selected
mode. This matters on a 1x->2x switch: a 1x conversion may still be
running when the first 2x read arrives, and reprogramming the device
before it finishes would be NACKed with -EIO. Timing is centralized in
ltc2497core_conv_time_ms() so a future FA/FB rejection-mode selection
can extend it into a [rejection][speed] lookup without touching callers.
LTC2496/LTC2497 (no speed mode) keep the single-byte path and the
unchanged 150ms wait.
Validated on a live LTC2499: 20 reads take ~3.1s at 1x and ~1.6s at 2x
(~0.5x, no -EIO), voltage and temperature readings stay sane in both
modes, and the temperature/voltage interleave (sticky-PTAT) regression
still passes at 1x and 2x.
Signed-off-by: Andrei Stancovici <andrei.stancovici@analog.com>
---
drivers/iio/adc/ltc2497-core.c | 169 +++++++++++++++++++++++++++++++--
drivers/iio/adc/ltc2497.c | 24 +++--
drivers/iio/adc/ltc2497.h | 24 ++++-
3 files changed, 200 insertions(+), 17 deletions(-)
diff --git a/drivers/iio/adc/ltc2497-core.c b/drivers/iio/adc/ltc2497-core.c
index 4ca7271ca007..68b53a983e05 100644
--- a/drivers/iio/adc/ltc2497-core.c
+++ b/drivers/iio/adc/ltc2497-core.c
@@ -7,12 +7,14 @@
*/
#include <linux/delay.h>
+#include <linux/device.h>
#include <linux/iio/iio.h>
#include <linux/iio/driver.h>
#include <linux/math64.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
#include "ltc2497.h"
@@ -20,24 +22,57 @@
#define LTC2497_DIFF 0
#define LTC2497_SIGN BIT(3)
-static int ltc2497core_wait_conv(struct ltc2497core_driverdata *ddata)
+/*
+ * Output-rate modes, indexed by ltc2497core_driverdata.sped_2x
+ * (0 = 1x, the power-on default; 1 = 2x, LTC2499 only). The advertised
+ * sampling_frequency and the conversion-time budget are two views of the same
+ * mode, so they are kept in lock-step here and can never drift apart. Only the
+ * two simultaneous 50/60Hz rejection rates are reachable today; adding FA/FB
+ * rejection selection later turns this into a [rejection][speed] lookup without
+ * changing any caller.
+ */
+static const int ltc2497core_samp_freq_avail[] = {
+ 6, 800000, /* 1x: ~6.8 Hz (1 / t_CONV_1 typ 146.9ms) */
+ 13, 600000, /* 2x: ~13.6 Hz (1 / t_CONV_2 typ 73.6ms) */
+};
+
+static const unsigned int ltc2497core_conv_time_ms_tbl[] = {
+ LTC2497_CONV_TIME_1X_MS, /* 1x */
+ LTC2499_CONV_TIME_2X_MS, /* 2x */
+};
+
+static unsigned int ltc2497core_conv_time_ms(struct ltc2497core_driverdata *ddata,
+ u8 address)
+{
+ /*
+ * SPD is ignored by the part during a temperature measurement: it
+ * always converts at 1x, so budget the 1x time regardless of the
+ * selected voltage-channel mode.
+ */
+ if (address == LTC2497_TEMP_ADDR)
+ return ltc2497core_conv_time_ms_tbl[0];
+
+ return ltc2497core_conv_time_ms_tbl[ddata->sped_2x];
+}
+
+static int ltc2497core_wait_conv(struct ltc2497core_driverdata *ddata,
+ unsigned int conv_time_ms)
{
s64 time_elapsed;
time_elapsed = ktime_ms_delta(ktime_get(), ddata->time_prev);
- if (time_elapsed < LTC2497_CONVERSION_TIME_MS) {
+ if (time_elapsed < conv_time_ms) {
/* delay if conversion time not passed
* since last read or write
*/
- if (msleep_interruptible(
- LTC2497_CONVERSION_TIME_MS - time_elapsed))
+ if (msleep_interruptible(conv_time_ms - time_elapsed))
return -ERESTARTSYS;
return 0;
}
- if (time_elapsed - LTC2497_CONVERSION_TIME_MS <= 0) {
+ if (time_elapsed - conv_time_ms <= 0) {
/* We're in automatic mode -
* so the last reading is still not outdated
*/
@@ -49,9 +84,18 @@ static int ltc2497core_wait_conv(struct ltc2497core_driverdata *ddata)
static int ltc2497core_read(struct ltc2497core_driverdata *ddata, u8 address, int *val)
{
+ unsigned int conv_time_ms = ltc2497core_conv_time_ms(ddata, address);
int ret;
- ret = ltc2497core_wait_conv(ddata);
+ /*
+ * Wait for the conversion currently in flight, whose duration was fixed
+ * by the mode active when it was started (ddata->conv_time_prev). This
+ * can be longer than the freshly selected mode's time - e.g. a 1x
+ * conversion is still running when the first 2x read arrives after a
+ * sampling_frequency change - and reprogramming the device before it
+ * finishes would be NACKed (-EIO).
+ */
+ ret = ltc2497core_wait_conv(ddata, ddata->conv_time_prev);
if (ret < 0)
return ret;
@@ -61,7 +105,9 @@ static int ltc2497core_read(struct ltc2497core_driverdata *ddata, u8 address, in
return ret;
ddata->addr_prev = address;
- if (msleep_interruptible(LTC2497_CONVERSION_TIME_MS))
+ /* The reprogram above starts a conversion in the new mode. */
+ ddata->conv_time_prev = conv_time_ms;
+ if (msleep_interruptible(conv_time_ms))
return -ERESTARTSYS;
}
@@ -70,6 +116,8 @@ static int ltc2497core_read(struct ltc2497core_driverdata *ddata, u8 address, in
return ret;
ddata->time_prev = ktime_get();
+ /* The read above auto-starts the next conversion in the current mode. */
+ ddata->conv_time_prev = conv_time_ms;
return ret;
}
@@ -134,6 +182,81 @@ static int ltc2497core_read_raw(struct iio_dev *indio_dev,
return -EINVAL;
}
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ /*
+ * Only advertised on the voltage channels of parts with a speed
+ * mode; the sampling frequency is a property of the selected 1x/2x
+ * mode, not of an individual conversion.
+ */
+ mutex_lock(&ddata->lock);
+ *val = ltc2497core_samp_freq_avail[ddata->sped_2x * 2];
+ *val2 = ltc2497core_samp_freq_avail[ddata->sped_2x * 2 + 1];
+ mutex_unlock(&ddata->lock);
+
+ return IIO_VAL_INT_PLUS_MICRO;
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static int ltc2497core_read_avail(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ const int **vals, int *type, int *length,
+ long mask)
+{
+ switch (mask) {
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ *vals = ltc2497core_samp_freq_avail;
+ *type = IIO_VAL_INT_PLUS_MICRO;
+ *length = ARRAY_SIZE(ltc2497core_samp_freq_avail);
+ return IIO_AVAIL_LIST;
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static int ltc2497core_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val, int val2, long mask)
+{
+ struct ltc2497core_driverdata *ddata = iio_priv(indio_dev);
+ bool sped_2x;
+ int i;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ /* Match the (val, val2) pair against the advertised rates. */
+ for (i = 0; i < ARRAY_SIZE(ltc2497core_samp_freq_avail); i += 2) {
+ if (val == ltc2497core_samp_freq_avail[i] &&
+ val2 == ltc2497core_samp_freq_avail[i + 1])
+ break;
+ }
+ if (i >= ARRAY_SIZE(ltc2497core_samp_freq_avail))
+ return -EINVAL;
+
+ sped_2x = i / 2;
+
+ mutex_lock(&ddata->lock);
+ ddata->sped_2x = sped_2x;
+ /*
+ * The new speed only takes effect once the second command byte
+ * is reprogrammed, so force the next read to reprogram rather
+ * than reuse the value already latched for this address.
+ * LTC2497_CONFIG_DEFAULT is not a valid channel/temperature
+ * address, so it is a safe re-arm sentinel (as used at probe).
+ *
+ * A conversion started under the old speed may still be in
+ * flight; its own duration (conv_time_prev), not the new mode's,
+ * still gates the next reprogram, so the timing state is left
+ * untouched here.
+ */
+ ddata->addr_prev = LTC2497_CONFIG_DEFAULT;
+ mutex_unlock(&ddata->lock);
+
+ return 0;
+
default:
return -EINVAL;
}
@@ -206,6 +329,8 @@ static const struct iio_chan_spec ltc2497core_channel[] = {
static const struct iio_info ltc2497core_info = {
.read_raw = ltc2497core_read_raw,
+ .read_avail = ltc2497core_read_avail,
+ .write_raw = ltc2497core_write_raw,
};
int ltc2497core_probe(struct device *dev, struct iio_dev *indio_dev)
@@ -231,6 +356,34 @@ int ltc2497core_probe(struct device *dev, struct iio_dev *indio_dev)
if (!ddata->chip_info->has_temp)
indio_dev->num_channels--;
+ /*
+ * Parts with a speed mode expose in_voltage_sampling_frequency /
+ * _available on the voltage channels only. SPD is ignored during a
+ * temperature measurement, so the temperature channel deliberately
+ * carries no SAMP_FREQ attribute. Patch a private copy of the shared
+ * channel array so parts without a speed mode stay untouched.
+ */
+ if (ddata->chip_info->has_speed_mode) {
+ struct iio_chan_spec *channels;
+ unsigned int i;
+
+ channels = devm_kmemdup(dev, ltc2497core_channel,
+ sizeof(ltc2497core_channel), GFP_KERNEL);
+ if (!channels)
+ return -ENOMEM;
+
+ for (i = 0; i < indio_dev->num_channels; i++) {
+ if (channels[i].type != IIO_VOLTAGE)
+ continue;
+ channels[i].info_mask_shared_by_type |=
+ BIT(IIO_CHAN_INFO_SAMP_FREQ);
+ channels[i].info_mask_shared_by_type_available |=
+ BIT(IIO_CHAN_INFO_SAMP_FREQ);
+ }
+
+ indio_dev->channels = channels;
+ }
+
ret = ddata->result_and_measure(ddata, LTC2497_CONFIG_DEFAULT, NULL);
if (ret < 0)
return ret;
@@ -255,6 +408,8 @@ int ltc2497core_probe(struct device *dev, struct iio_dev *indio_dev)
ddata->addr_prev = LTC2497_CONFIG_DEFAULT;
ddata->time_prev = ktime_get();
+ /* Power-on default mode is 1x; a conversion is already in flight. */
+ ddata->conv_time_prev = LTC2497_CONV_TIME_1X_MS;
mutex_init(&ddata->lock);
diff --git a/drivers/iio/adc/ltc2497.c b/drivers/iio/adc/ltc2497.c
index 2ff53b292f74..3fc31d418cf4 100644
--- a/drivers/iio/adc/ltc2497.c
+++ b/drivers/iio/adc/ltc2497.c
@@ -85,16 +85,19 @@ static int ltc2497_result_and_measure(struct ltc2497core_driverdata *ddata,
}
/*
- * Parts with the internal PTAT sensor (LTC2499) latch their converter
- * configuration via a second command byte and only re-evaluate it when
- * that byte has EN2 set; a single byte, or a second byte with EN2 = 0,
- * means "keep previous". A one-byte channel select therefore cannot pull
- * the device back out of temperature mode, so a voltage read after a
- * temperature read would keep returning the PTAT result. Always drive the
- * second byte with EN2 set on these parts: IM | temperature-rejection for
- * a temperature read, EN2 alone (IM = 0) to (re)select an external input.
+ * Parts with a second config byte (LTC2499: internal PTAT sensor and/or
+ * the 2x speed mode) latch their converter configuration from that byte
+ * and only re-evaluate it when EN2 is set; a single byte, or a second
+ * byte with EN2 = 0, means "keep previous". A one-byte channel select
+ * therefore cannot pull the device back out of temperature mode, so a
+ * voltage read after a temperature read would keep returning the PTAT
+ * result. Always drive the second byte with EN2 set on these parts:
+ * - temperature read: IM = 1 (SPD is ignored by the part in
+ * temperature mode and is left 0 here);
+ * - voltage read: IM = 0 (external input), plus SPD when 2x is
+ * selected.
*/
- if (ddata->chip_info->has_temp) {
+ if (ddata->chip_info->has_temp || ddata->chip_info->has_speed_mode) {
u8 cmd[2];
if (address == LTC2497_TEMP_ADDR) {
@@ -103,6 +106,8 @@ static int ltc2497_result_and_measure(struct ltc2497core_driverdata *ddata,
} else {
cmd[0] = LTC2497_ENABLE | address;
cmd[1] = LTC2499_EN2;
+ if (ddata->sped_2x)
+ cmd[1] |= LTC2499_SPD;
}
ret = i2c_master_send(st->client, cmd, sizeof(cmd));
@@ -168,6 +173,7 @@ static const struct ltc2497_chip_info ltc2497_info[] = {
.resolution = 24,
.name = "ltc2499",
.has_temp = true,
+ .has_speed_mode = true,
},
};
diff --git a/drivers/iio/adc/ltc2497.h b/drivers/iio/adc/ltc2497.h
index 1da2cfec3b6a..5707ee3ff8a5 100644
--- a/drivers/iio/adc/ltc2497.h
+++ b/drivers/iio/adc/ltc2497.h
@@ -2,7 +2,19 @@
#define LTC2497_ENABLE 0xA0
#define LTC2497_CONFIG_DEFAULT LTC2497_ENABLE
-#define LTC2497_CONVERSION_TIME_MS 150ULL
+
+/*
+ * Worst-case conversion times (datasheet t_CONV max). The driver only ever
+ * programs simultaneous 50/60Hz rejection (FA/FB selection is not implemented),
+ * so only those two rates are listed. The 1x value also covers the
+ * LTC2496/LTC2497, which have no speed mode.
+ *
+ * The 2x mode (LTC2499_SPD, LTC2499 only) disables the offset auto-calibration
+ * to roughly double the output rate; adding the 2x wait time is what makes the
+ * SPD control actually faster.
+ */
+#define LTC2497_CONV_TIME_1X_MS 150ULL /* t_CONV_1 simult. max 149.9 */
+#define LTC2499_CONV_TIME_2X_MS 76ULL /* t_CONV_2 simult. max 75.1 */
/*
* Sentinel passed as `address` to result_and_measure() to request a
@@ -14,11 +26,13 @@
/* Second config-byte bits (LTC2499 / LTC2493 only) */
#define LTC2499_EN2 BIT(7) /* enable second config byte */
#define LTC2499_IM BIT(6) /* 1 = measure internal temp sensor */
+#define LTC2499_SPD BIT(3) /* 1 = 2x output rate (offset cal off) */
struct ltc2497_chip_info {
u32 resolution;
const char *name;
bool has_temp;
+ bool has_speed_mode; /* SPD bit in the 2nd config byte (LTC2499/LTC2493) */
};
struct ltc2497core_driverdata {
@@ -28,6 +42,14 @@ struct ltc2497core_driverdata {
struct mutex lock;
const struct ltc2497_chip_info *chip_info;
u8 addr_prev;
+ bool sped_2x; /* SPD: false = 1x (default), true = 2x */
+ /*
+ * Conversion time (ms) of the conversion currently in flight. It is
+ * fixed by the mode active when that conversion was started, which
+ * differs from the newly selected mode for the first read after a
+ * sampling_frequency change.
+ */
+ unsigned int conv_time_prev;
int (*result_and_measure)(struct ltc2497core_driverdata *ddata,
u8 address, int *val);
};
--
2.43.0
^ permalink raw reply related [flat|nested] 8+ messages in thread
* Re: [PATCH 3/3] iio: adc: ltc2497: add 2x conversion speed mode
2026-08-05 9:41 ` [PATCH 3/3] iio: adc: ltc2497: add 2x conversion speed mode Andrei Stancovici
@ 2026-08-05 9:56 ` sashiko-bot
2026-08-05 10:08 ` Andy Shevchenko
1 sibling, 0 replies; 8+ messages in thread
From: sashiko-bot @ 2026-08-05 9:56 UTC (permalink / raw)
To: Andrei Stancovici; +Cc: conor+dt, robh, devicetree
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [High] I2C transfer buffer `cmd` is allocated on the stack, which strictly violates DMA mapping rules. If the underlying I2C host controller relies on DMA and attempts to map the transfer buffer, calling `dma_map_single()` on a stack address will fail or cause a kernel panic on architectures with `CONFIG_VMAP_STACK` enabled (as the stack may not be physically contiguous). The driver author correctly used an `__aligned(IIO_DMA_MINALIGN)` heap buffer for `st->data.d8` / `st->data.d32` to safely handle DMA, making this stack allocation an oversight.
- [Medium] Missing update of `time_prev` before returning `-ERESTARTSYS` on an interrupted wait leads to state corruption and spurious I2C NACKs (`-EIO`). When switching modes or channels, the driver issues a dummy write to start a new conversion and then calls `msleep_interruptible()`. If this sleep is interrupted by a signal, the driver returns early without updating `ddata->time_prev` to reflect the newly started conversion. On the next read retry, `ltc2497core_wait_conv()` calculates the elapsed time against this stale `time_prev`. If switching from a faster mode (e.g., 76ms) to a slower mode (150ms), the wait logic may falsely conclude the 150ms delay is satisfied because the time elapsed since the *old* `time_prev` exceeds the remaining wait time. It then directly executes a premature I2C read before the ADC has finished converting, resulting in a hardware NACK.
--
--- Patch [3]: [PATCH 3/3] iio: adc: ltc2497: add 2x conversion speed mode ---
commit 3de76fde39bdb91c8ae6ce607b01c574bc7d76f1
Author: Andrei Stancovici <andrei.stancovici@analog.com>
iio: adc: ltc2497: add 2x conversion speed mode
This commit introduces a 2x output rate mode for the LTC2499 ADC by exposing
a new sampling_frequency attribute on voltage channels. It updates the
conversion wait times and the two-byte command path to accommodate the speed
mode configuration.
> diff --git a/drivers/iio/adc/ltc2497-core.c b/drivers/iio/adc/ltc2497-core.c
> index 4ca7271ca0071..68b53a983e053 100644
> --- a/drivers/iio/adc/ltc2497-core.c
> +++ b/drivers/iio/adc/ltc2497-core.c
[ ... ]
> @@ -61,7 +105,9 @@ static int ltc2497core_read(struct ltc2497core_driverdata *ddata, u8 address, in
> return ret;
> ddata->addr_prev = address;
>
> - if (msleep_interruptible(LTC2497_CONVERSION_TIME_MS))
> + /* The reprogram above starts a conversion in the new mode. */
> + ddata->conv_time_prev = conv_time_ms;
> + if (msleep_interruptible(conv_time_ms))
> return -ERESTARTSYS;
[Severity: Medium]
Will this lead to state corruption and spurious I2C NACKs if the sleep is
interrupted?
When switching modes or channels in ltc2497core_read(), a dummy write starts
a new conversion and then calls msleep_interruptible(). If this sleep is
interrupted by a signal, the driver returns early without updating
ddata->time_prev to reflect the newly started conversion.
On the next read retry, ltc2497core_wait_conv() calculates the elapsed time
against the stale time_prev. If switching from a faster mode (e.g., 76ms) to
a slower mode (150ms), the wait logic may falsely conclude the 150ms delay is
satisfied because the time elapsed since the old time_prev exceeds the
remaining wait time. It then directly executes a premature I2C read before
the ADC has finished converting, resulting in a hardware NACK (-EIO).
> }
[ ... ]
> diff --git a/drivers/iio/adc/ltc2497.c b/drivers/iio/adc/ltc2497.c
> index 4e0f5c7671f41..6bb016f495e6e 100644
> --- a/drivers/iio/adc/ltc2497.c
> +++ b/drivers/iio/adc/ltc2497.c
[ ... ]
> @@ -104,6 +107,8 @@ static int ltc2497_result_and_measure(struct ltc2497core_driverdata *ddata,
> } else {
> cmd[0] = LTC2497_ENABLE | address;
> cmd[1] = LTC2499_EN2;
> + if (ddata->sped_2x)
> + cmd[1] |= LTC2499_SPD;
> }
>
> ret = i2c_master_send(st->client, cmd, sizeof(cmd));
[Severity: High]
Is it safe to pass a stack-allocated buffer to i2c_master_send()?
If the underlying I2C host controller relies on DMA and attempts to map the
transfer buffer, calling dma_map_single() on a stack address will fail or
cause a kernel panic on architectures with CONFIG_VMAP_STACK enabled.
Should we use an __aligned(IIO_DMA_MINALIGN) heap buffer for this transfer
instead, similar to how st->data is handled?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260805094135.11762-1-andrei.stancovici@analog.com?part=3
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH 2/3] iio: adc: ltc2497: add LTC2499 internal temperature channel
2026-08-05 9:41 ` [PATCH 2/3] iio: adc: ltc2497: add LTC2499 internal temperature channel Andrei Stancovici
@ 2026-08-05 9:57 ` sashiko-bot
2026-08-05 10:00 ` Andy Shevchenko
1 sibling, 0 replies; 8+ messages in thread
From: sashiko-bot @ 2026-08-05 9:57 UTC (permalink / raw)
To: Andrei Stancovici; +Cc: robh, conor+dt, devicetree
Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
- [High] Potential division by zero kernel panic if regulator_get_voltage() returns 0 when reading the temperature offset.
- [High] Stack-allocated buffer passed to i2c_master_send() is not DMA-safe.
- [High] The 2-byte command inadvertently configures 50Hz-only rejection, breaking 60Hz rejection and causing a hardcoded 150ms conversion delay to time out.
--
--- Patch [2]: [PATCH 2/3] iio: adc: ltc2497: add LTC2499 internal temperature channel ---
commit 20bcf3fffe757278d79cfcae060ca4eaa317443e
Author: Andrei Stancovici <andrei.stancovici@analog.com>
iio: adc: ltc2497: add LTC2499 internal temperature channel
This commit adds support for the LTC2499's internal PTAT temperature sensor.
It exposes the sensor as an IIO_TEMP channel, utilizing a second I2C
configuration byte to enable temperature conversions and calculate the exact
temperature using the reference voltage.
> diff --git a/drivers/iio/adc/ltc2497-core.c b/drivers/iio/adc/ltc2497-core.c
> index 2dc5c70442694..4ca7271ca0071 100644
> --- a/drivers/iio/adc/ltc2497-core.c
> +++ b/drivers/iio/adc/ltc2497-core.c
[ ... ]
> @@ -95,10 +96,43 @@ static int ltc2497core_read_raw(struct iio_dev *indio_dev,
[ ... ]
> + case IIO_CHAN_INFO_OFFSET:
> + switch (chan->type) {
> + case IIO_TEMP:
> + ret = regulator_get_voltage(ddata->ref);
> + if (ret < 0)
> + return ret;
> + /*
> + * 0 °C == 273.15 K must map to raw + offset such that
> + * (raw + offset) * scale == 0 m°C, i.e.
> + * offset = -273150 / scale
> + * = -273150 * 3140000 / Vref_uV
> + * Computed in 64-bit to avoid overflow.
> + */
> + *val = div_s64(-273150LL * 3140000, ret);
[Severity: High]
If regulator_get_voltage() returns exactly 0 (for instance, via a dummy
regulator or misconfigured devicetree), will this trigger a divide-by-zero
panic?
The check above handles negative error codes but appears to miss a
check for zero before the division.
> + return IIO_VAL_INT;
> + default:
> + return -EINVAL;
> + }
>
> default:
> return -EINVAL;
[ ... ]
> diff --git a/drivers/iio/adc/ltc2497.c b/drivers/iio/adc/ltc2497.c
> index eb9d521e86e54..4e0f5c7671f41 100644
> --- a/drivers/iio/adc/ltc2497.c
> +++ b/drivers/iio/adc/ltc2497.c
[ ... ]
> @@ -85,6 +85,36 @@ static int ltc2497_result_and_measure(struct ltc2497core_driverdata *ddata,
[ ... ]
> + if (ddata->chip_info->has_temp) {
> + u8 cmd[2];
[Severity: High]
Is this stack-allocated buffer safe to use with i2c_master_send()?
If the underlying I2C adapter driver uses DMA for transfers, it might attempt
to map this stack memory. With CONFIG_VMAP_STACK enabled, stack memory is
allocated in vmalloc space and is not DMA-capable, which could lead to
memory corruption or mapping errors.
Should this use a DMA-safe buffer instead?
> +
> + if (address == LTC2497_TEMP_ADDR) {
> + cmd[0] = LTC2497_ENABLE | LTC2497_CONFIG_DEFAULT;
> + cmd[1] = LTC2499_EN2 | LTC2499_IM;
> + } else {
> + cmd[0] = LTC2497_ENABLE | address;
> + cmd[1] = LTC2499_EN2;
> + }
[Severity: High]
By omitting bits 5 (FA) and 4 (FB) in the second command byte, these
bits default to 0. According to the LTC2499 datasheet, does this inadvertently
configure the part for 50Hz-only noise rejection?
If so, 50Hz-only mode increases the maximum conversion time to 160.3ms.
Since the driver hardcodes the wait time to 150ms via
LTC2497_CONVERSION_TIME_MS, could this result in the I2C read transaction
being NACKed by the busy ADC, causing persistent read failures?
Would explicitly setting the FA bit to maintain the 149.9ms simultaneous
50/60Hz rejection mode prevent this timeout?
> +
> + ret = i2c_master_send(st->client, cmd, sizeof(cmd));
> + if (ret < 0) {
> + dev_err(&st->client->dev, "i2c transfer failed: %pe\n",
> + ERR_PTR(ret));
> + return ret;
> + }
> + return 0;
> + }
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260805094135.11762-1-andrei.stancovici@analog.com?part=2
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH 2/3] iio: adc: ltc2497: add LTC2499 internal temperature channel
2026-08-05 9:41 ` [PATCH 2/3] iio: adc: ltc2497: add LTC2499 internal temperature channel Andrei Stancovici
2026-08-05 9:57 ` sashiko-bot
@ 2026-08-05 10:00 ` Andy Shevchenko
1 sibling, 0 replies; 8+ messages in thread
From: Andy Shevchenko @ 2026-08-05 10:00 UTC (permalink / raw)
To: Andrei Stancovici
Cc: Nuno Sá, Michael Hennerich, Jonathan Cameron, David Lechner,
Andy Shevchenko, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Liam Beguin, linux, linux-iio, devicetree, linux-kernel
On Wed, Aug 5, 2026 at 12:44 PM Andrei Stancovici
<andrei.stancovici@analog.com> wrote:
>
> The LTC2499 has an internal PTAT (proportional to absolute temperature)
> sensor that is activated by a second I2C configuration byte (EN2 | IM).
> Expose it as an IIO_TEMP channel providing raw, scale and offset so the
> standard IIO formula
>
> T[m°C] = (raw + offset) * scale
>
> reconstructs the temperature.
>
> The PTAT sensor yields the absolute temperature as
>
> T(K) = DATAOUT24 * Vref / 1570 (Vref in volts)
>
> The raw value exported here is sign-extended and normalised to
> 2^(resolution + 1) == 2^25, i.e. raw = 2 * DATAOUT24, so on the IIO
> milli-degree-Celsius convention
>
> scale[m°C/LSB] = Vref_uV / 3140000
> offset = -273150 * 3140000 / Vref_uV
>
> The scale and offset are derived from the reference voltage returned by
> regulator_get_voltage(); its error is propagated as before, so a board
> that fails to describe vref-supply gets a clear read error instead of a
> silently wrong temperature. No board-specific reference value is assumed
> in the driver.
>
> The single temperature channel is appended as the last entry of the
> shared channel array and excluded via num_channels for parts without an
> internal sensor, so the existing LTC2497 channel layout and device name
> are unchanged.
>
> The LTC2499 latches its converter configuration from the second command
> byte and only re-evaluates it when that byte has EN2 set. EN2 | IM
> selects the internal temperature sensor. Because a single-byte command,
> or a second byte with EN2 = 0, means "keep previous", a one-byte channel
> select cannot pull the device back out of temperature mode: after a
> temperature read every subsequent voltage read would keep returning the
> PTAT result instead of the selected input. Temperature support is
> therefore only correct if the voltage path also emits a second command
> byte that re-selects an external input.
>
> Send two-byte commands for all conversions on parts that have the sensor
> (has_temp):
>
> temperature: EN2 | IM
> voltage: EN2 (IM = 0 -> external input)
>
> The LTC2497 and LTC2496, which lack the second-byte mechanism, keep using
> the original single-byte channel select and are unchanged.
...
> + case IIO_VOLTAGE:
> + *val = ret / 1000;
We have SI unit multipliers
> + *val2 = ddata->chip_info->resolution + 1;
> + return IIO_VAL_FRACTIONAL_LOG2;
...
> + case IIO_CHAN_INFO_OFFSET:
> + switch (chan->type) {
> + case IIO_TEMP:
> + ret = regulator_get_voltage(ddata->ref);
> + if (ret < 0)
> + return ret;
> + /*
> + * 0 °C == 273.15 K must map to raw + offset such that
> + * (raw + offset) * scale == 0 m°C, i.e.
> + * offset = -273150 / scale
> + * = -273150 * 3140000 / Vref_uV
> + * Computed in 64-bit to avoid overflow.
> + */
> + *val = div_s64(-273150LL * 3140000, ret);
We have ABSOLUTE_ZERO_MILLICELSIUS, use it.
> + return IIO_VAL_INT;
...
> indio_dev->num_channels = ARRAY_SIZE(ltc2497core_channel);
> + /* Only the ltc2499 has a temperature channel; it is the last entry. */
> + if (!ddata->chip_info->has_temp)
> + indio_dev->num_channels--;
I would do it explicitly:
if (...)
num_channels = ARRAY_SIZE(...);
else
num_channels = ARRAY_SIZE(...) - 1;
--
With Best Regards,
Andy Shevchenko
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH 3/3] iio: adc: ltc2497: add 2x conversion speed mode
2026-08-05 9:41 ` [PATCH 3/3] iio: adc: ltc2497: add 2x conversion speed mode Andrei Stancovici
2026-08-05 9:56 ` sashiko-bot
@ 2026-08-05 10:08 ` Andy Shevchenko
1 sibling, 0 replies; 8+ messages in thread
From: Andy Shevchenko @ 2026-08-05 10:08 UTC (permalink / raw)
To: Andrei Stancovici
Cc: Nuno Sá, Michael Hennerich, Jonathan Cameron, David Lechner,
Andy Shevchenko, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Liam Beguin, linux, linux-iio, devicetree, linux-kernel
On Wed, Aug 5, 2026 at 12:44 PM Andrei Stancovici
<andrei.stancovici@analog.com> wrote:
>
> The LTC2499 supports a 2x output rate (SPD bit in the second
> configuration byte). In 2x mode the offset auto-calibration is
> disabled, roughly doubling the conversion rate (~13.6Hz vs ~6.8Hz in
> simultaneous 50/60Hz rejection) while leaving linearity and full-scale
> errors unchanged (datasheet). During a temperature measurement the part
> always converts at 1x regardless of SPD.
>
> Expose the rate through the standard sampling_frequency /
> sampling_frequency_available ABI on the voltage channels only: SPD is
> ignored for temperature conversions, so the temperature channel
> deliberately carries no SAMP_FREQ attribute. A new has_speed_mode
> capability flag gates the feature (LTC2499); the two-byte command path
> is now taken for has_temp || has_speed_mode, since both features need the
> second config byte.
>
> The conversion-time wait becomes mode dependent: 150ms at 1x, 76ms at 2x
> (datasheet t_CONV max, simultaneous rejection, rounded up). The wait is
> keyed on the conversion currently in flight, whose duration is fixed by
> the mode that was active when it started - not by the newly selected
> mode. This matters on a 1x->2x switch: a 1x conversion may still be
> running when the first 2x read arrives, and reprogramming the device
> before it finishes would be NACKed with -EIO. Timing is centralized in
> ltc2497core_conv_time_ms() so a future FA/FB rejection-mode selection
> can extend it into a [rejection][speed] lookup without touching callers.
>
> LTC2496/LTC2497 (no speed mode) keep the single-byte path and the
> unchanged 150ms wait.
>
> Validated on a live LTC2499: 20 reads take ~3.1s at 1x and ~1.6s at 2x
> (~0.5x, no -EIO), voltage and temperature readings stay sane in both
> modes, and the temperature/voltage interleave (sticky-PTAT) regression
> still passes at 1x and 2x.
...
> #include <linux/delay.h>
> +#include <linux/device.h>
Why? On a brief look I haven't noticed the use of this header.
> #include <linux/iio/iio.h>
> #include <linux/iio/driver.h>
> #include <linux/math64.h>
> #include <linux/module.h>
> #include <linux/mutex.h>
> #include <linux/regulator/consumer.h>
> +#include <linux/slab.h>
Neither is this.
Perhaps you wanted the device/devres.h?
...
> +static int ltc2497core_write_raw(struct iio_dev *indio_dev,
> + struct iio_chan_spec const *chan,
> + int val, int val2, long mask)
> +{
> + struct ltc2497core_driverdata *ddata = iio_priv(indio_dev);
> + bool sped_2x;
> + int i;
Why signed?
> + switch (mask) {
> + case IIO_CHAN_INFO_SAMP_FREQ:
> + /* Match the (val, val2) pair against the advertised rates. */
> + for (i = 0; i < ARRAY_SIZE(ltc2497core_samp_freq_avail); i += 2) {
> + if (val == ltc2497core_samp_freq_avail[i] &&
> + val2 == ltc2497core_samp_freq_avail[i + 1])
> + break;
> + }
> + if (i >= ARRAY_SIZE(ltc2497core_samp_freq_avail))
'==' should suffice.
> + return -EINVAL;
> +
> + sped_2x = i / 2;
> +
> + mutex_lock(&ddata->lock);
> + ddata->sped_2x = sped_2x;
> + /*
> + * The new speed only takes effect once the second command byte
> + * is reprogrammed, so force the next read to reprogram rather
> + * than reuse the value already latched for this address.
> + * LTC2497_CONFIG_DEFAULT is not a valid channel/temperature
> + * address, so it is a safe re-arm sentinel (as used at probe).
> + *
> + * A conversion started under the old speed may still be in
> + * flight; its own duration (conv_time_prev), not the new mode's,
> + * still gates the next reprogram, so the timing state is left
> + * untouched here.
> + */
> + ddata->addr_prev = LTC2497_CONFIG_DEFAULT;
> + mutex_unlock(&ddata->lock);
> +
> + return 0;
...
> + if (ddata->chip_info->has_speed_mode) {
> + struct iio_chan_spec *channels;
> + unsigned int i;
> +
> + channels = devm_kmemdup(dev, ltc2497core_channel,
> + sizeof(ltc2497core_channel), GFP_KERNEL);
> + if (!channels)
> + return -ENOMEM;
> + for (i = 0; i < indio_dev->num_channels; i++) {
Iterrator is not used outside, so can be
for (unsigned int i...)
> + if (channels[i].type != IIO_VOLTAGE)
> + continue;
> + channels[i].info_mask_shared_by_type |=
> + BIT(IIO_CHAN_INFO_SAMP_FREQ);
> + channels[i].info_mask_shared_by_type_available |=
> + BIT(IIO_CHAN_INFO_SAMP_FREQ);
> + }
> +
> + indio_dev->channels = channels;
> + }
...
> +#define LTC2497_CONV_TIME_1X_MS 150ULL /* t_CONV_1 simult. max 149.9 */
> +#define LTC2499_CONV_TIME_2X_MS 76ULL /* t_CONV_2 simult. max 75.1 */
I didn't get the rounding rules. Is it just a ceiling for all cases?
Can we use more precise (10x) values instead?
--
With Best Regards,
Andy Shevchenko
^ permalink raw reply [flat|nested] 8+ messages in thread
end of thread, other threads:[~2026-08-05 10:09 UTC | newest]
Thread overview: 8+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-05 9:41 [PATCH 0/3] iio: adc: add LTC2499 features support Andrei Stancovici
2026-08-05 9:41 ` [PATCH 1/3] dt-bindings: iio: adc: lltc,ltc2497: add LTC2499 to title Andrei Stancovici
2026-08-05 9:41 ` [PATCH 2/3] iio: adc: ltc2497: add LTC2499 internal temperature channel Andrei Stancovici
2026-08-05 9:57 ` sashiko-bot
2026-08-05 10:00 ` Andy Shevchenko
2026-08-05 9:41 ` [PATCH 3/3] iio: adc: ltc2497: add 2x conversion speed mode Andrei Stancovici
2026-08-05 9:56 ` sashiko-bot
2026-08-05 10:08 ` Andy Shevchenko
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox