[PATCH v2 3/3] iio: adc: ltc2497: add 2x conversion speed mode
Andrei Stancovici <[email protected]>
| Newsgroups | org.kernel.vger.linux-iio,org.kernel.vger.linux-devicetree,org.kernel.vger.linux-kernel |
|---|---|
| Message-ID | <[email protected]> |
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 <[email protected]> --- Changes in v2: - use a DMA-safe buffer for LTC2499 two-byte commands in the SPD path - record conversion timing state before interruptible waits - replace generic includes with the precise kernel headers that own the used symbols - use unsigned iterators where appropriate - simplify ARRAY_SIZE() completion checks - clarify conversion-time rounding rules and timing assumptions drivers/iio/adc/ltc2497-core.c | 176 +++++++++++++++++++++++++++++++-- drivers/iio/adc/ltc2497.c | 28 +++--- drivers/iio/adc/ltc2497.h | 34 ++++++- 3 files changed, 219 insertions(+), 19 deletions(-) diff --git a/drivers/iio/adc/ltc2497-core.c b/drivers/iio/adc/ltc2497-core.c index ed84ae67255d..26b0d7e012f8 100644 --- a/drivers/iio/adc/ltc2497-core.c +++ b/drivers/iio/adc/ltc2497-core.c @@ -7,6 +7,8 @@ */ #include <linux/delay.h> +#include <linux/device/devres.h> +#include <linux/gfp.h> #include <linux/iio/iio.h> #include <linux/iio/driver.h> #include <linux/math64.h> @@ -21,24 +23,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 */ @@ -50,9 +85,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; @@ -62,7 +106,17 @@ 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. + * Record its start time and duration before sleeping, so that if + * msleep_interruptible() is interrupted the conversion state is + * already consistent: the next retry then waits only the time + * remaining from the real start instead of from a stale + * time_prev, which would let it reprogram/read too early. + */ + ddata->time_prev = ktime_get(); + ddata->conv_time_prev = conv_time_ms; + if (msleep_interruptible(conv_time_ms)) return -ERESTARTSYS; } @@ -71,6 +125,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; } @@ -137,6 +193,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; + unsigned 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; } @@ -209,6 +340,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) @@ -235,6 +368,33 @@ int ltc2497core_probe(struct device *dev, struct iio_dev *indio_dev) else indio_dev->num_channels = ARRAY_SIZE(ltc2497core_channel) - 1; + /* + * 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; + + channels = devm_kmemdup(dev, ltc2497core_channel, + sizeof(ltc2497core_channel), GFP_KERNEL); + if (!channels) + return -ENOMEM; + + for (unsigned int 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; @@ -259,6 +419,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 57a5406977ed..08ceec79e011 100644 --- a/drivers/iio/adc/ltc2497.c +++ b/drivers/iio/adc/ltc2497.c @@ -85,28 +85,33 @@ 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 = 1 for a temperature read, - * EN2 alone (IM = 0) to (re)select an external input. FA = FB = 0 keeps - * the power-on simultaneous 50/60Hz rejection, whose worst-case - * conversion time the driver's wait already covers. + * 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. + * FA = FB = 0 keeps the power-on simultaneous 50/60Hz rejection, whose + * worst-case conversion time the driver's wait already covers. * * The two bytes are assembled in the DMA-safe st->data buffer rather than * on the stack, so the pointer handed to i2c_master_send() stays valid on * adapters that DMA the transfer (e.g. with CONFIG_VMAP_STACK). */ - if (ddata->chip_info->has_temp) { + if (ddata->chip_info->has_temp || ddata->chip_info->has_speed_mode) { if (address == LTC2497_TEMP_ADDR) { st->data.d8[0] = LTC2497_ENABLE | LTC2497_CONFIG_DEFAULT; st->data.d8[1] = LTC2499_EN2 | LTC2499_IM; } else { st->data.d8[0] = LTC2497_ENABLE | address; st->data.d8[1] = LTC2499_EN2; + if (ddata->sped_2x) + st->data.d8[1] |= LTC2499_SPD; } ret = i2c_master_send(st->client, (char *)st->data.d8, 2); @@ -172,6 +177,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..25313dbeb56f 100644 --- a/drivers/iio/adc/ltc2497.h +++ b/drivers/iio/adc/ltc2497.h @@ -2,7 +2,29 @@ #define LTC2497_ENABLE 0xA0 #define LTC2497_CONFIG_DEFAULT LTC2497_ENABLE -#define LTC2497_CONVERSION_TIME_MS 150ULL + +/* + * Conversion-time bounds used to gate reads. Each value is the datasheet + * t_CONV maximum, rounded UP to the next whole millisecond. Rounding is + * always towards +inf (a ceiling), never to nearest: the number is only used + * as a *minimum* wait - the argument to msleep_interruptible() and the + * threshold compared against ktime_ms_delta() - so it must never fall below + * the true worst case, or a read can be issued before the result is ready and + * return -EIO. Both of those APIs operate in whole milliseconds (msleep also + * rounds up to the next jiffy, typically 1-10 ms), so storing sub-millisecond + * precision would not change the actual wait; the whole-ms ceiling is exact + * for this purpose. + * + * 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 /* ceil(t_CONV_1 simult. max 149.9) */ +#define LTC2499_CONV_TIME_2X_MS 76ULL /* ceil(t_CONV_2 simult. max 75.1) */ /* * Sentinel passed as `address` to result_and_measure() to request a @@ -14,11 +36,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 +52,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