* [PATCH 1/3] iio: adc: ti-ads112c14: add debugfs register access
2026-07-15 0:19 [PATCH 0/3] iio: adc: ti-ads112c14: buffered read support David Lechner (TI)
@ 2026-07-15 0:19 ` David Lechner (TI)
2026-07-20 0:24 ` Jonathan Cameron
2026-07-15 0:19 ` [PATCH 2/3] iio: adc: ti-ads112c14: add support for I2C CRC8 David Lechner (TI)
2026-07-15 0:19 ` [PATCH 3/3] iio: adc: ti-ads112c14: add support for buffered read David Lechner (TI)
2 siblings, 1 reply; 13+ messages in thread
From: David Lechner (TI) @ 2026-07-15 0:19 UTC (permalink / raw)
To: Jonathan Cameron, Nuno Sá, Andy Shevchenko
Cc: Chris Hall, Patrick Edwards, Kurt Borja, Nguyen Minh Tien,
linux-iio, linux-kernel, David Lechner (TI)
Add debugfs register access to the ads112c14 driver. This is a complex
chip and being able to poke registers is useful for debugging and
diagnostic/calibration purposes.
Signed-off-by: David Lechner (TI) <dlechner@baylibre.com>
---
drivers/iio/adc/ti-ads112c14.c | 14 ++++++++++++++
1 file changed, 14 insertions(+)
diff --git a/drivers/iio/adc/ti-ads112c14.c b/drivers/iio/adc/ti-ads112c14.c
index fef3a9c37ced..28824c81908e 100644
--- a/drivers/iio/adc/ti-ads112c14.c
+++ b/drivers/iio/adc/ti-ads112c14.c
@@ -690,6 +690,19 @@ static int ads112c14_write_raw_get_fmt(struct iio_dev *indio_dev,
}
}
+static int ads112c14_debugfs_reg_access(struct iio_dev *indio_dev,
+ unsigned int reg,
+ unsigned int writeval,
+ unsigned int *readval)
+{
+ struct ads112c14_data *data = iio_priv(indio_dev);
+
+ if (readval)
+ return regmap_read(data->regmap, reg, readval);
+
+ return regmap_write(data->regmap, reg, writeval);
+}
+
static int ads112c14_read_label(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan, char *label)
{
@@ -736,6 +749,7 @@ static const struct iio_info ads112c14_info = {
.read_avail = ads112c14_read_avail,
.write_raw = ads112c14_write_raw,
.write_raw_get_fmt = ads112c14_write_raw_get_fmt,
+ .debugfs_reg_access = ads112c14_debugfs_reg_access,
.read_label = ads112c14_read_label,
};
--
2.43.0
^ permalink raw reply related [flat|nested] 13+ messages in thread* Re: [PATCH 1/3] iio: adc: ti-ads112c14: add debugfs register access
2026-07-15 0:19 ` [PATCH 1/3] iio: adc: ti-ads112c14: add debugfs register access David Lechner (TI)
@ 2026-07-20 0:24 ` Jonathan Cameron
2026-07-22 22:52 ` David Lechner
0 siblings, 1 reply; 13+ messages in thread
From: Jonathan Cameron @ 2026-07-20 0:24 UTC (permalink / raw)
To: David Lechner (TI)
Cc: Nuno Sá, Andy Shevchenko, Chris Hall, Patrick Edwards,
Kurt Borja, Nguyen Minh Tien, linux-iio, linux-kernel
On Tue, 14 Jul 2026 19:19:30 -0500
"David Lechner (TI)" <dlechner@baylibre.com> wrote:
> Add debugfs register access to the ads112c14 driver. This is a complex
> chip and being able to poke registers is useful for debugging and
> diagnostic/calibration purposes.
>
> Signed-off-by: David Lechner (TI) <dlechner@baylibre.com>
Given this one is trivial, applied.
Jonathan
> ---
> drivers/iio/adc/ti-ads112c14.c | 14 ++++++++++++++
> 1 file changed, 14 insertions(+)
>
> diff --git a/drivers/iio/adc/ti-ads112c14.c b/drivers/iio/adc/ti-ads112c14.c
> index fef3a9c37ced..28824c81908e 100644
> --- a/drivers/iio/adc/ti-ads112c14.c
> +++ b/drivers/iio/adc/ti-ads112c14.c
> @@ -690,6 +690,19 @@ static int ads112c14_write_raw_get_fmt(struct iio_dev *indio_dev,
> }
> }
>
> +static int ads112c14_debugfs_reg_access(struct iio_dev *indio_dev,
> + unsigned int reg,
> + unsigned int writeval,
> + unsigned int *readval)
> +{
> + struct ads112c14_data *data = iio_priv(indio_dev);
> +
> + if (readval)
> + return regmap_read(data->regmap, reg, readval);
> +
> + return regmap_write(data->regmap, reg, writeval);
> +}
> +
> static int ads112c14_read_label(struct iio_dev *indio_dev,
> struct iio_chan_spec const *chan, char *label)
> {
> @@ -736,6 +749,7 @@ static const struct iio_info ads112c14_info = {
> .read_avail = ads112c14_read_avail,
> .write_raw = ads112c14_write_raw,
> .write_raw_get_fmt = ads112c14_write_raw_get_fmt,
> + .debugfs_reg_access = ads112c14_debugfs_reg_access,
> .read_label = ads112c14_read_label,
> };
>
>
^ permalink raw reply [flat|nested] 13+ messages in thread* Re: [PATCH 1/3] iio: adc: ti-ads112c14: add debugfs register access
2026-07-20 0:24 ` Jonathan Cameron
@ 2026-07-22 22:52 ` David Lechner
2026-07-23 23:18 ` Jonathan Cameron
0 siblings, 1 reply; 13+ messages in thread
From: David Lechner @ 2026-07-22 22:52 UTC (permalink / raw)
To: Jonathan Cameron
Cc: Nuno Sá, Andy Shevchenko, Chris Hall, Patrick Edwards,
Kurt Borja, Nguyen Minh Tien, linux-iio, linux-kernel
On 7/19/26 7:24 PM, Jonathan Cameron wrote:
> On Tue, 14 Jul 2026 19:19:30 -0500
> "David Lechner (TI)" <dlechner@baylibre.com> wrote:
>
>> Add debugfs register access to the ads112c14 driver. This is a complex
>> chip and being able to poke registers is useful for debugging and
>> diagnostic/calibration purposes.
>>
>> Signed-off-by: David Lechner (TI) <dlechner@baylibre.com>
> Given this one is trivial, applied.
Did this get dropped or just not pushed out yet?
>
> Jonathan
>
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH 1/3] iio: adc: ti-ads112c14: add debugfs register access
2026-07-22 22:52 ` David Lechner
@ 2026-07-23 23:18 ` Jonathan Cameron
0 siblings, 0 replies; 13+ messages in thread
From: Jonathan Cameron @ 2026-07-23 23:18 UTC (permalink / raw)
To: David Lechner
Cc: Nuno Sá, Andy Shevchenko, Chris Hall, Patrick Edwards,
Kurt Borja, Nguyen Minh Tien, linux-iio, linux-kernel
On Wed, 22 Jul 2026 17:52:41 -0500
David Lechner <dlechner@baylibre.com> wrote:
> On 7/19/26 7:24 PM, Jonathan Cameron wrote:
> > On Tue, 14 Jul 2026 19:19:30 -0500
> > "David Lechner (TI)" <dlechner@baylibre.com> wrote:
> >
> >> Add debugfs register access to the ads112c14 driver. This is a complex
> >> chip and being able to poke registers is useful for debugging and
> >> diagnostic/calibration purposes.
> >>
> >> Signed-off-by: David Lechner (TI) <dlechner@baylibre.com>
> > Given this one is trivial, applied.
>
> Did this get dropped or just not pushed out yet?
>
I forgot to push. Done now.
J
> >
> > Jonathan
> >
>
^ permalink raw reply [flat|nested] 13+ messages in thread
* [PATCH 2/3] iio: adc: ti-ads112c14: add support for I2C CRC8
2026-07-15 0:19 [PATCH 0/3] iio: adc: ti-ads112c14: buffered read support David Lechner (TI)
2026-07-15 0:19 ` [PATCH 1/3] iio: adc: ti-ads112c14: add debugfs register access David Lechner (TI)
@ 2026-07-15 0:19 ` David Lechner (TI)
2026-07-20 0:35 ` Jonathan Cameron
2026-07-15 0:19 ` [PATCH 3/3] iio: adc: ti-ads112c14: add support for buffered read David Lechner (TI)
2 siblings, 1 reply; 13+ messages in thread
From: David Lechner (TI) @ 2026-07-15 0:19 UTC (permalink / raw)
To: Jonathan Cameron, Nuno Sá, Andy Shevchenko
Cc: Chris Hall, Patrick Edwards, Kurt Borja, Nguyen Minh Tien,
linux-iio, linux-kernel, David Lechner (TI)
Add support for I2C CRC8 to the TI ADS112C14 ADC driver. This verifies
data integrity of all I2C transactions with the device.
For now, it is always enabled, but it could be made optional in the
future if needed (e.g. for higher-speed data acquisition).
Signed-off-by: David Lechner (TI) <dlechner@baylibre.com>
---
drivers/iio/adc/Kconfig | 1 -
drivers/iio/adc/ti-ads112c14.c | 145 +++++++++++++++++++++++++++++++++++++++--
2 files changed, 140 insertions(+), 6 deletions(-)
diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
index 642c42b1fae7..4ed6f01386bf 100644
--- a/drivers/iio/adc/Kconfig
+++ b/drivers/iio/adc/Kconfig
@@ -1802,7 +1802,6 @@ config TI_ADS112C14
tristate "Texas Instruments ADS112C14/ADS122C14"
depends on I2C
select REGMAP
- select REGMAP_I2C
help
If you say yes here you get support for Texas Instruments ADS112C14,
ADS122C14 ADC chips.
diff --git a/drivers/iio/adc/ti-ads112c14.c b/drivers/iio/adc/ti-ads112c14.c
index 28824c81908e..99ccacd6d56f 100644
--- a/drivers/iio/adc/ti-ads112c14.c
+++ b/drivers/iio/adc/ti-ads112c14.c
@@ -10,6 +10,7 @@
#include <linux/bitfield.h>
#include <linux/cleanup.h>
+#include <linux/crc8.h>
#include <linux/delay.h>
#include <linux/dev_printk.h>
#include <linux/device/devres.h>
@@ -156,6 +157,9 @@ static const u32 ads112c14_pga_gains_x10[] = {
200, 320, 500, 640, 1000, 1280, 2000, 2560, /* 8 - 15 */
};
+#define ADS112C14_I2C_CRC8_POLYNOMIAL 0x07
+DECLARE_CRC8_TABLE(ads112c14_crc8_table);
+
struct ads112c14_chip_info {
const char *name;
u8 device_id;
@@ -241,6 +245,7 @@ struct ads112c14_data {
struct regmap *regmap;
/* Synchronizes access to register value fields. */
struct mutex lock;
+ bool i2c_crc_enabled;
u32 avdd_uV;
u32 ext_ref_uV;
bool refp_is_avdd;
@@ -289,6 +294,120 @@ static const struct reg_default ads112c14_reg_defaults[] = {
{ ADS112C14_REG_IDAC_MUX_CFG, FIELD_PREP_CONST(ADS112C14_IDAC_MUX_CFG_I2MUX, 1) },
};
+/**
+ * ads112c14_i2c_read_bytes() - Read bytes from the device over I2C
+ * @client: I2C client for the device
+ * @cmd: Command to send to the device before reading
+ * @buf: Buffer to store the read bytes
+ * @len: Number of bytes to read
+ * @use_crc: Whether to use CRC8 for data integrity check
+ *
+ * If I2C_CRC is enabled, @use_crc may be set to true to perform a CRC8 check
+ * on the received data.
+ */
+static int ads112c14_i2c_read_bytes(struct i2c_client *client, u8 cmd,
+ u8 *buf, u8 len, bool use_crc)
+{
+ u8 rx_buf[4]; /* Up to 3 data bytes + 1 CRC byte. */
+ u8 rx_len;
+ int ret;
+
+ rx_len = len + (use_crc ? 1 : 0);
+
+ if (rx_len > sizeof(rx_buf))
+ return -EINVAL;
+
+ ret = i2c_smbus_read_i2c_block_data(client, cmd, rx_len, rx_buf);
+ if (ret < 0)
+ return ret;
+
+ if (use_crc) {
+ u8 crc = crc8(ads112c14_crc8_table, rx_buf, len, CRC8_INIT_VALUE);
+
+ if (crc != rx_buf[len])
+ return -EBADMSG;
+ }
+
+ memcpy(buf, rx_buf, len);
+
+ return 0;
+}
+
+/**
+ * ads112c14_regmap_bus_read() - Read a register from the device
+ * @context: Pointer to the device context
+ * @reg_buf: Register address to read
+ * @reg_size: Size of the register address (should be 1)
+ * @val_buf: Buffer to store the read value
+ * @val_size: Size of the value to read
+ *
+ * Custom regmap read function that also does CRC check when enabled.
+ */
+static int ads112c14_regmap_bus_read(void *context, const void *reg_buf,
+ size_t reg_size, void *val_buf,
+ size_t val_size)
+{
+ struct ads112c14_data *data = context;
+ struct device *dev = regmap_get_device(data->regmap);
+ struct i2c_client *client = to_i2c_client(dev);
+ const u8 *cmd = reg_buf;
+
+ if (reg_size != 1)
+ return -EINVAL;
+
+ return ads112c14_i2c_read_bytes(client, cmd[0], val_buf, val_size,
+ data->i2c_crc_enabled);
+}
+
+/**
+ * ads112c14_regmap_bus_write() - Write a register to the device
+ * @context: Pointer to the device context
+ * @data_buf: Buffer containing the register address and value to write
+ * @count: Number of bytes to write
+ *
+ * Custom regmap write function that also does readback with CRC check of
+ * nonvolatile registers when CRC is enabled.
+ */
+static int ads112c14_regmap_bus_write(void *context, const void *data_buf,
+ size_t count)
+{
+ struct ads112c14_data *data = context;
+ struct device *dev = regmap_get_device(data->regmap);
+ struct i2c_client *client = to_i2c_client(dev);
+ const u8 *tx = data_buf;
+ u8 reg, readback;
+ int ret;
+
+ if (count != 2)
+ return -EINVAL;
+
+ ret = i2c_smbus_write_byte_data(client, tx[0], tx[1]);
+ if (ret)
+ return ret;
+
+ reg = tx[0] & ~ADS112C14_CMD_WREG;
+
+ if (!data->i2c_crc_enabled || ads112c14_volatile_reg(dev, reg))
+ return 0;
+
+ ret = ads112c14_i2c_read_bytes(client, reg | ADS112C14_CMD_RREG,
+ &readback, sizeof(readback), true);
+ if (ret)
+ return ret;
+
+ if (readback != tx[1])
+ return -EIO;
+
+ return 0;
+}
+
+static const struct regmap_bus ads112c14_regmap_bus = {
+ .read = ads112c14_regmap_bus_read,
+ .write = ads112c14_regmap_bus_write,
+ .reg_format_endian_default = REGMAP_ENDIAN_BIG,
+ .val_format_endian_default = REGMAP_ENDIAN_BIG,
+};
+
static const struct regmap_config ads112c14_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
@@ -486,9 +605,9 @@ static int ads112c14_single_conversion(struct ads112c14_data *data,
if (ret)
return ret;
- return i2c_smbus_read_i2c_block_data(client, ADS112C14_CMD_RDATA,
- BITS_TO_BYTES(data->chip_info->resolution_bits),
- buf);
+ return ads112c14_i2c_read_bytes(client, ADS112C14_CMD_RDATA, buf,
+ BITS_TO_BYTES(data->chip_info->resolution_bits),
+ data->i2c_crc_enabled);
}
static int ads112c14_read_raw(struct iio_dev *indio_dev,
@@ -1117,7 +1236,8 @@ static int ads112c14_probe(struct i2c_client *client)
/* It takes some time for the internal reference to stabilize. */
fsleep(10 * USEC_PER_MSEC);
- data->regmap = devm_regmap_init_i2c(client, &ads112c14_regmap_config);
+ data->regmap = devm_regmap_init(dev, &ads112c14_regmap_bus, data,
+ &ads112c14_regmap_config);
if (IS_ERR(data->regmap))
return dev_err_probe(dev, PTR_ERR(data->regmap),
"failed to init regmap\n");
@@ -1154,6 +1274,14 @@ static int ads112c14_probe(struct i2c_client *client)
if (ret)
return ret;
+ ret = regmap_update_bits(data->regmap, ADS112C14_REG_DIGITAL_CFG,
+ ADS112C14_DIGITAL_CFG_I2C_CRC_EN,
+ ADS112C14_DIGITAL_CFG_I2C_CRC_EN);
+ if (ret)
+ return ret;
+
+ data->i2c_crc_enabled = true;
+
ret = regmap_read(data->regmap, ADS112C14_REG_DEVICE_ID, ®_val);
if (ret)
return ret;
@@ -1205,6 +1333,13 @@ static const struct i2c_device_id ads112c14_id[] = {
};
MODULE_DEVICE_TABLE(i2c, ads112c14_id);
+static int ads112c14_i2c_add_driver(struct i2c_driver *driver)
+{
+ crc8_populate_msb(ads112c14_crc8_table, ADS112C14_I2C_CRC8_POLYNOMIAL);
+
+ return i2c_add_driver(driver);
+}
+
static struct i2c_driver ads112c14_driver = {
.driver = {
.name = "ads112c14",
@@ -1213,7 +1348,7 @@ static struct i2c_driver ads112c14_driver = {
.probe = ads112c14_probe,
.id_table = ads112c14_id,
};
-module_i2c_driver(ads112c14_driver);
+module_driver(ads112c14_driver, ads112c14_i2c_add_driver, i2c_del_driver);
MODULE_AUTHOR("David Lechner (TI) <dlechner@baylibre.com>");
MODULE_DESCRIPTION("TI ADS112C14 I2C ADC driver");
--
2.43.0
^ permalink raw reply related [flat|nested] 13+ messages in thread* Re: [PATCH 2/3] iio: adc: ti-ads112c14: add support for I2C CRC8
2026-07-15 0:19 ` [PATCH 2/3] iio: adc: ti-ads112c14: add support for I2C CRC8 David Lechner (TI)
@ 2026-07-20 0:35 ` Jonathan Cameron
0 siblings, 0 replies; 13+ messages in thread
From: Jonathan Cameron @ 2026-07-20 0:35 UTC (permalink / raw)
To: David Lechner (TI)
Cc: Nuno Sá, Andy Shevchenko, Chris Hall, Patrick Edwards,
Kurt Borja, Nguyen Minh Tien, linux-iio, linux-kernel
On Tue, 14 Jul 2026 19:19:31 -0500
"David Lechner (TI)" <dlechner@baylibre.com> wrote:
> Add support for I2C CRC8 to the TI ADS112C14 ADC driver. This verifies
> data integrity of all I2C transactions with the device.
>
> For now, it is always enabled, but it could be made optional in the
> future if needed (e.g. for higher-speed data acquisition).
A few bits of this need a rebase after the tweaks I made when
applying the earlier series. Otherwise looks good to me.
Jonathan
^ permalink raw reply [flat|nested] 13+ messages in thread
* [PATCH 3/3] iio: adc: ti-ads112c14: add support for buffered read
2026-07-15 0:19 [PATCH 0/3] iio: adc: ti-ads112c14: buffered read support David Lechner (TI)
2026-07-15 0:19 ` [PATCH 1/3] iio: adc: ti-ads112c14: add debugfs register access David Lechner (TI)
2026-07-15 0:19 ` [PATCH 2/3] iio: adc: ti-ads112c14: add support for I2C CRC8 David Lechner (TI)
@ 2026-07-15 0:19 ` David Lechner (TI)
2026-07-20 0:55 ` Jonathan Cameron
2 siblings, 1 reply; 13+ messages in thread
From: David Lechner (TI) @ 2026-07-15 0:19 UTC (permalink / raw)
To: Jonathan Cameron, Nuno Sá, Andy Shevchenko
Cc: Chris Hall, Patrick Edwards, Kurt Borja, Nguyen Minh Tien,
linux-iio, linux-kernel, David Lechner (TI)
Add support for buffered reads using a triggered buffer.
The device has a continuous conversion mode, but that can only be used
with one channel at a time since there is nothing like a sequencer to
support that in hardware. Instead, we use single-shot reads like we do
for direct reads to be able to read multiple channels.
Support for continuous conversion mode could be added in the future if
needed via a 2nd buffer.
Signed-off-by: David Lechner (TI) <dlechner@baylibre.com>
---
drivers/iio/adc/ti-ads112c14.c | 103 +++++++++++++++++++++++++++++++++++++++--
1 file changed, 99 insertions(+), 4 deletions(-)
diff --git a/drivers/iio/adc/ti-ads112c14.c b/drivers/iio/adc/ti-ads112c14.c
index 99ccacd6d56f..1404ca31324b 100644
--- a/drivers/iio/adc/ti-ads112c14.c
+++ b/drivers/iio/adc/ti-ads112c14.c
@@ -15,7 +15,10 @@
#include <linux/dev_printk.h>
#include <linux/device/devres.h>
#include <linux/i2c.h>
+#include <linux/iio/buffer.h>
#include <linux/iio/iio.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
#include <linux/math64.h>
#include <linux/minmax.h>
#include <linux/module.h>
@@ -29,6 +32,9 @@
#include <linux/unaligned.h>
#include <linux/units.h>
+/* Arbitrary limit since channels are dynamic. */
+#define ADS112C14_MAX_MEASUREMENT_CHANNELS 16
+
/* Datasheet t_d(RST) - time to wait after reset before next I2C use. */
#define ADS112C14_DELAY_RESET_US 500
@@ -255,6 +261,8 @@ struct ads112c14_data {
u32 num_measurements;
u8 sys_mon_chan_short_gain_val;
int sys_mon_chan_short_scale_available[ARRAY_SIZE(ads112c14_pga_gains_x10)][2];
+ IIO_DECLARE_BUFFER_WITH_TS(__be32, scan, ADS112C14_MAX_MEASUREMENT_CHANNELS +
+ ARRAY_SIZE(ads112c14_sys_mon_channels));
};
static bool ads112c14_writeable_reg(struct device *dev, unsigned int reg)
@@ -575,7 +583,7 @@ static int ads112c14_prepare_sys_mon_channel(struct ads112c14_data *data,
static int ads112c14_single_conversion(struct ads112c14_data *data,
const struct iio_chan_spec *chan,
- u8 *buf)
+ u8 *buf, bool for_scan)
{
struct i2c_client *client = to_i2c_client(regmap_get_device(data->regmap));
u32 reg_val;
@@ -605,6 +613,22 @@ static int ads112c14_single_conversion(struct ads112c14_data *data,
if (ret)
return ret;
+ /*
+ * When doing buffered read, we don't check the CRC, but rather pass it
+ * along with the raw data.
+ */
+ if (for_scan) {
+ u8 len = BITS_TO_BYTES(data->chip_info->resolution_bits) +
+ (data->i2c_crc_enabled ? 1 : 0);
+
+ ret = i2c_smbus_read_i2c_block_data(client, ADS112C14_CMD_RDATA,
+ len, buf);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+ }
+
return ads112c14_i2c_read_bytes(client, ADS112C14_CMD_RDATA, buf,
BITS_TO_BYTES(data->chip_info->resolution_bits),
data->i2c_crc_enabled);
@@ -639,7 +663,7 @@ static int ads112c14_read_raw(struct iio_dev *indio_dev,
if (IIO_DEV_ACQUIRE_FAILED(claim))
return -EBUSY;
- ret = ads112c14_single_conversion(data, chan, buf);
+ ret = ads112c14_single_conversion(data, chan, buf, false);
if (ret < 0)
return ret;
@@ -765,6 +789,10 @@ static int ads112c14_write_raw(struct iio_dev *indio_dev,
const int (*scale_avail)[2];
u8 *gain_val;
+ IIO_DEV_ACQUIRE_DIRECT_MODE(indio_dev, claim);
+ if (IIO_DEV_ACQUIRE_FAILED(claim))
+ return -EBUSY;
+
switch (mask) {
case IIO_CHAN_INFO_SCALE: {
guard(mutex)(&data->lock);
@@ -863,6 +891,37 @@ static int ads112c14_read_label(struct iio_dev *indio_dev,
return sysfs_emit(label, "%s\n", label_source);
}
+static irqreturn_t ads112c14_trigger_handler(int irq, void *private)
+{
+ struct iio_poll_func *pf = private;
+ struct iio_dev *indio_dev = pf->indio_dev;
+ struct ads112c14_data *data = iio_priv(indio_dev);
+ u32 offset = 0;
+ u32 i;
+ int ret;
+
+ iio_for_each_active_channel(indio_dev, i) {
+ const struct iio_chan_spec *chan = &indio_dev->channels[i];
+
+ ret = ads112c14_single_conversion(data, chan,
+ (u8 *)&data->scan[offset++],
+ true);
+ if (ret) {
+ dev_err_once(indio_dev->dev.parent,
+ "failed to read channel %d: %pe; additional errors will be suppressed\n",
+ chan->channel, ERR_PTR(ret));
+ goto out;
+ }
+ }
+
+ iio_push_to_buffers_with_ts(indio_dev, data->scan,
+ sizeof(data->scan), pf->timestamp);
+out:
+ iio_trigger_notify_done(indio_dev->trig);
+
+ return IRQ_HANDLED;
+}
+
static const struct iio_info ads112c14_info = {
.read_raw = ads112c14_read_raw,
.read_avail = ads112c14_read_avail,
@@ -908,7 +967,7 @@ static int ads112c14_parse_channels(struct iio_dev *indio_dev,
return -ENOMEM;
channels = devm_kcalloc(dev, num_child_nodes +
- ARRAY_SIZE(ads112c14_sys_mon_channels),
+ ARRAY_SIZE(ads112c14_sys_mon_channels) + 1,
sizeof(*channels), GFP_KERNEL);
if (!channels)
return -ENOMEM;
@@ -1069,14 +1128,44 @@ static int ads112c14_parse_channels(struct iio_dev *indio_dev,
if (spec->type == IIO_RESISTANCE)
spec->differential = 0;
+ spec->scan_type = (struct iio_scan_type){
+ .format = measurement->bipolar ?
+ IIO_SCAN_FORMAT_SIGNED_INT :
+ IIO_SCAN_FORMAT_UNSIGNED_INT,
+ .realbits = data->chip_info->resolution_bits,
+ .storagebits = 32,
+ .shift = 32 - data->chip_info->resolution_bits,
+ .endianness = IIO_BE,
+ };
+
i++;
}
data->num_measurements = i;
+ if (data->num_measurements >= ADS112C14_MAX_MEASUREMENT_CHANNELS)
+ return dev_err_probe(dev, -EINVAL,
+ "too many measurement channels defined\n");
+
memcpy(channels + i, ads112c14_sys_mon_channels, sizeof(ads112c14_sys_mon_channels));
+ for (u32 j = 0; j < ARRAY_SIZE(ads112c14_sys_mon_channels); j++) {
+ struct iio_chan_spec *spec = &channels[i + j];
+
+ spec->scan_index = i + j;
+ spec->scan_type = (struct iio_scan_type){
+ .format = IIO_SCAN_FORMAT_SIGNED_INT,
+ .realbits = data->chip_info->resolution_bits,
+ .storagebits = 32,
+ .shift = 32 - data->chip_info->resolution_bits,
+ .endianness = IIO_BE,
+ };
+ }
+
indio_dev->channels = channels;
- indio_dev->num_channels = i + ARRAY_SIZE(ads112c14_sys_mon_channels);
+ indio_dev->num_channels = i + ARRAY_SIZE(ads112c14_sys_mon_channels) + 1;
+
+ i = indio_dev->num_channels - 1;
+ channels[i] = IIO_CHAN_SOFT_TIMESTAMP(i);
return 0;
}
@@ -1304,6 +1393,12 @@ static int ads112c14_probe(struct i2c_client *client)
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->info = &ads112c14_info;
+ ret = devm_iio_triggered_buffer_setup(dev, indio_dev,
+ iio_pollfunc_store_time,
+ ads112c14_trigger_handler, NULL);
+ if (ret)
+ return ret;
+
return devm_iio_device_register(dev, indio_dev);
}
--
2.43.0
^ permalink raw reply related [flat|nested] 13+ messages in thread* Re: [PATCH 3/3] iio: adc: ti-ads112c14: add support for buffered read
2026-07-15 0:19 ` [PATCH 3/3] iio: adc: ti-ads112c14: add support for buffered read David Lechner (TI)
@ 2026-07-20 0:55 ` Jonathan Cameron
2026-07-21 23:23 ` David Lechner
0 siblings, 1 reply; 13+ messages in thread
From: Jonathan Cameron @ 2026-07-20 0:55 UTC (permalink / raw)
To: David Lechner (TI)
Cc: Nuno Sá, Andy Shevchenko, Chris Hall, Patrick Edwards,
Kurt Borja, Nguyen Minh Tien, linux-iio, linux-kernel
On Tue, 14 Jul 2026 19:19:32 -0500
"David Lechner (TI)" <dlechner@baylibre.com> wrote:
> Add support for buffered reads using a triggered buffer.
>
> The device has a continuous conversion mode, but that can only be used
> with one channel at a time since there is nothing like a sequencer to
> support that in hardware. Instead, we use single-shot reads like we do
> for direct reads to be able to read multiple channels.
>
> Support for continuous conversion mode could be added in the future if
> needed via a 2nd buffer.
How about enabling that if only one channel is requested? I vaguely recall
us doing that for another driver (though I might be dreaming :) I did
see your comment in the cover letter about it affecting timing and that
making life complex. Fine to leave considering this for another day
but maybe don't suggest a particular solution here.
Talk a little in here about why you parse the crc on to userspace.
I think that makes sense but good to have a record of your reasoning.
We aren't telling userspace it is there afterall, it's just in some
left over space!
>
> Signed-off-by: David Lechner (TI) <dlechner@baylibre.com>
> ---
> drivers/iio/adc/ti-ads112c14.c | 103 +++++++++++++++++++++++++++++++++++++++--
> 1 file changed, 99 insertions(+), 4 deletions(-)
>
> diff --git a/drivers/iio/adc/ti-ads112c14.c b/drivers/iio/adc/ti-ads112c14.c
> index 99ccacd6d56f..1404ca31324b 100644
> --- a/drivers/iio/adc/ti-ads112c14.c
> +++ b/drivers/iio/adc/ti-ads112c14.c
> @@ -15,7 +15,10 @@
> #include <linux/dev_printk.h>
> #include <linux/device/devres.h>
> #include <linux/i2c.h>
> +#include <linux/iio/buffer.h>
> #include <linux/iio/iio.h>
> +#include <linux/iio/trigger_consumer.h>
> +#include <linux/iio/triggered_buffer.h>
> #include <linux/math64.h>
> #include <linux/minmax.h>
> #include <linux/module.h>
> @@ -29,6 +32,9 @@
> #include <linux/unaligned.h>
> #include <linux/units.h>
>
> +/* Arbitrary limit since channels are dynamic. */
> +#define ADS112C14_MAX_MEASUREMENT_CHANNELS 16
> +
> /* Datasheet t_d(RST) - time to wait after reset before next I2C use. */
> #define ADS112C14_DELAY_RESET_US 500
>
> @@ -255,6 +261,8 @@ struct ads112c14_data {
> u32 num_measurements;
> u8 sys_mon_chan_short_gain_val;
> int sys_mon_chan_short_scale_available[ARRAY_SIZE(ads112c14_pga_gains_x10)][2];
> + IIO_DECLARE_BUFFER_WITH_TS(__be32, scan, ADS112C14_MAX_MEASUREMENT_CHANNELS +
> + ARRAY_SIZE(ads112c14_sys_mon_channels));
> };
>
> static bool ads112c14_writeable_reg(struct device *dev, unsigned int reg)
> @@ -575,7 +583,7 @@ static int ads112c14_prepare_sys_mon_channel(struct ads112c14_data *data,
>
> static int ads112c14_single_conversion(struct ads112c14_data *data,
> const struct iio_chan_spec *chan,
> - u8 *buf)
> + u8 *buf, bool for_scan)
> {
> struct i2c_client *client = to_i2c_client(regmap_get_device(data->regmap));
> u32 reg_val;
> @@ -605,6 +613,22 @@ static int ads112c14_single_conversion(struct ads112c14_data *data,
> if (ret)
> return ret;
>
> + /*
> + * When doing buffered read, we don't check the CRC, but rather pass it
> + * along with the raw data.
Perhaps say why.
> + */
> + if (for_scan) {
> + u8 len = BITS_TO_BYTES(data->chip_info->resolution_bits) +
> + (data->i2c_crc_enabled ? 1 : 0);
> +
> + ret = i2c_smbus_read_i2c_block_data(client, ADS112C14_CMD_RDATA,
> + len, buf);
> + if (ret < 0)
> + return ret;
> +
> + return 0;
> + }
> +
> @@ -908,7 +967,7 @@ static int ads112c14_parse_channels(struct iio_dev *indio_dev,
> return -ENOMEM;
>
> channels = devm_kcalloc(dev, num_child_nodes +
> - ARRAY_SIZE(ads112c14_sys_mon_channels),
> + ARRAY_SIZE(ads112c14_sys_mon_channels) + 1,
> sizeof(*channels), GFP_KERNEL);
> if (!channels)
> return -ENOMEM;
> @@ -1069,14 +1128,44 @@ static int ads112c14_parse_channels(struct iio_dev *indio_dev,
> if (spec->type == IIO_RESISTANCE)
> spec->differential = 0;
>
> + spec->scan_type = (struct iio_scan_type){
> + .format = measurement->bipolar ?
> + IIO_SCAN_FORMAT_SIGNED_INT :
> + IIO_SCAN_FORMAT_UNSIGNED_INT,
> + .realbits = data->chip_info->resolution_bits,
> + .storagebits = 32,
> + .shift = 32 - data->chip_info->resolution_bits,
> + .endianness = IIO_BE,
> + };
> +
> i++;
> }
>
> data->num_measurements = i;
> + if (data->num_measurements >= ADS112C14_MAX_MEASUREMENT_CHANNELS)
> + return dev_err_probe(dev, -EINVAL,
> + "too many measurement channels defined\n");
> +
> memcpy(channels + i, ads112c14_sys_mon_channels, sizeof(ads112c14_sys_mon_channels));
>
> + for (u32 j = 0; j < ARRAY_SIZE(ads112c14_sys_mon_channels); j++) {
> + struct iio_chan_spec *spec = &channels[i + j];
> +
Add a comment to say this is updating elements of the template.
> + spec->scan_index = i + j;
> + spec->scan_type = (struct iio_scan_type){
> + .format = IIO_SCAN_FORMAT_SIGNED_INT,
> + .realbits = data->chip_info->resolution_bits,
> + .storagebits = 32,
> + .shift = 32 - data->chip_info->resolution_bits,
> + .endianness = IIO_BE,
> + };
> + }
> +
> indio_dev->channels = channels;
> - indio_dev->num_channels = i + ARRAY_SIZE(ads112c14_sys_mon_channels);
> + indio_dev->num_channels = i + ARRAY_SIZE(ads112c14_sys_mon_channels) + 1;
> +
> + i = indio_dev->num_channels - 1;
If this is different from doing
i += ARRAY_SIZE(ads112c14_sys_mon_channels); and using that above and here
(with a increment after to account for this) then add a comment on why.
Otherwise I'd prefer that form as it puts it in the same scheme as the channel
increments above.
> + channels[i] = IIO_CHAN_SOFT_TIMESTAMP(i);
>
> return 0;
> }
^ permalink raw reply [flat|nested] 13+ messages in thread* Re: [PATCH 3/3] iio: adc: ti-ads112c14: add support for buffered read
2026-07-20 0:55 ` Jonathan Cameron
@ 2026-07-21 23:23 ` David Lechner
2026-07-23 23:25 ` Jonathan Cameron
0 siblings, 1 reply; 13+ messages in thread
From: David Lechner @ 2026-07-21 23:23 UTC (permalink / raw)
To: Jonathan Cameron
Cc: Nuno Sá, Andy Shevchenko, Chris Hall, Patrick Edwards,
Kurt Borja, Nguyen Minh Tien, linux-iio, linux-kernel
On 7/19/26 7:55 PM, Jonathan Cameron wrote:
> On Tue, 14 Jul 2026 19:19:32 -0500
> "David Lechner (TI)" <dlechner@baylibre.com> wrote:
>
>> Add support for buffered reads using a triggered buffer.
>>
>> The device has a continuous conversion mode, but that can only be used
>> with one channel at a time since there is nothing like a sequencer to
>> support that in hardware. Instead, we use single-shot reads like we do
>> for direct reads to be able to read multiple channels.
>>
>> Support for continuous conversion mode could be added in the future if
>> needed via a 2nd buffer.
>
> How about enabling that if only one channel is requested? I vaguely recall
> us doing that for another driver (though I might be dreaming :) I did
> see your comment in the cover letter about it affecting timing and that
> making life complex. Fine to leave considering this for another day
> but maybe don't suggest a particular solution here.
>
Actually, my latest thought it to do it by trigger type. This chip has
a DRDY interrupt that can be used as the trigger for the continuous
conversion mode but we need some software trigger to the single-shot
mode. It think this would take care of the timing issues as well.
The logic would be that if the self trigger (DRDY) is selected in
the trigger/current_trigger attribute, then buffered read will use
continuous mode and fail if more than one channel is enabled. If
another (software) trigger is enabled, then use single-shot mode
and allow multiple channels.
The timing issue is that (or will be since I haven't sent the relevant
patches yet) there is a settling delay before the first sample. For
single-shot mode, every sample is considered the first sample (because
it is "single"), so the chip applies this settling time on every sample.
So it might be good enough to just document that as a quirk of software
triggers for this particular chip since they would be using single-shot
mode?
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH 3/3] iio: adc: ti-ads112c14: add support for buffered read
2026-07-21 23:23 ` David Lechner
@ 2026-07-23 23:25 ` Jonathan Cameron
2026-07-24 14:47 ` David Lechner
0 siblings, 1 reply; 13+ messages in thread
From: Jonathan Cameron @ 2026-07-23 23:25 UTC (permalink / raw)
To: David Lechner
Cc: Nuno Sá, Andy Shevchenko, Chris Hall, Patrick Edwards,
Kurt Borja, Nguyen Minh Tien, linux-iio, linux-kernel
On Tue, 21 Jul 2026 18:23:58 -0500
David Lechner <dlechner@baylibre.com> wrote:
> On 7/19/26 7:55 PM, Jonathan Cameron wrote:
> > On Tue, 14 Jul 2026 19:19:32 -0500
> > "David Lechner (TI)" <dlechner@baylibre.com> wrote:
> >
> >> Add support for buffered reads using a triggered buffer.
> >>
> >> The device has a continuous conversion mode, but that can only be used
> >> with one channel at a time since there is nothing like a sequencer to
> >> support that in hardware. Instead, we use single-shot reads like we do
> >> for direct reads to be able to read multiple channels.
> >>
> >> Support for continuous conversion mode could be added in the future if
> >> needed via a 2nd buffer.
> >
> > How about enabling that if only one channel is requested? I vaguely recall
> > us doing that for another driver (though I might be dreaming :) I did
> > see your comment in the cover letter about it affecting timing and that
> > making life complex. Fine to leave considering this for another day
> > but maybe don't suggest a particular solution here.
> >
> Actually, my latest thought it to do it by trigger type. This chip has
> a DRDY interrupt that can be used as the trigger for the continuous
> conversion mode but we need some software trigger to the single-shot
> mode. It think this would take care of the timing issues as well.
>
> The logic would be that if the self trigger (DRDY) is selected in
> the trigger/current_trigger attribute, then buffered read will use
> continuous mode and fail if more than one channel is enabled. If
> another (software) trigger is enabled, then use single-shot mode
> and allow multiple channels.
Hmm. Bit unintuitive but can't really be helped.
>
> The timing issue is that (or will be since I haven't sent the relevant
> patches yet) there is a settling delay before the first sample. For
> single-shot mode, every sample is considered the first sample (because
> it is "single"), so the chip applies this settling time on every sample.
> So it might be good enough to just document that as a quirk of software
> triggers for this particular chip since they would be using single-shot
> mode?
>
This is where it gets messy if settling time is reported. I guess acceptable
given it doesn't make that much sense for an external trigger anyway.
In theory if the settling time has to happen each single shot, we could
just merge it into sampling frequency (as no longer depends on whether
channel changes or not) but then we have to make that dependent on whether
it is the devices own trigger or not. Messy. I guess one to paper over
as a quirk / corner case and document as you say.
>
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH 3/3] iio: adc: ti-ads112c14: add support for buffered read
2026-07-23 23:25 ` Jonathan Cameron
@ 2026-07-24 14:47 ` David Lechner
2026-07-24 21:34 ` Jonathan Cameron
0 siblings, 1 reply; 13+ messages in thread
From: David Lechner @ 2026-07-24 14:47 UTC (permalink / raw)
To: Jonathan Cameron
Cc: Nuno Sá, Andy Shevchenko, Chris Hall, Patrick Edwards,
Kurt Borja, Nguyen Minh Tien, linux-iio, linux-kernel
On 7/23/26 6:25 PM, Jonathan Cameron wrote:
> On Tue, 21 Jul 2026 18:23:58 -0500
> David Lechner <dlechner@baylibre.com> wrote:
>
>> On 7/19/26 7:55 PM, Jonathan Cameron wrote:
>>> On Tue, 14 Jul 2026 19:19:32 -0500
>>> "David Lechner (TI)" <dlechner@baylibre.com> wrote:
>>>
>>>> Add support for buffered reads using a triggered buffer.
>>>>
>>>> The device has a continuous conversion mode, but that can only be used
>>>> with one channel at a time since there is nothing like a sequencer to
>>>> support that in hardware. Instead, we use single-shot reads like we do
>>>> for direct reads to be able to read multiple channels.
>>>>
>>>> Support for continuous conversion mode could be added in the future if
>>>> needed via a 2nd buffer.
>>>
>>> How about enabling that if only one channel is requested? I vaguely recall
>>> us doing that for another driver (though I might be dreaming :) I did
>>> see your comment in the cover letter about it affecting timing and that
>>> making life complex. Fine to leave considering this for another day
>>> but maybe don't suggest a particular solution here.
>>>
>> Actually, my latest thought it to do it by trigger type. This chip has
>> a DRDY interrupt that can be used as the trigger for the continuous
>> conversion mode but we need some software trigger to the single-shot
>> mode. It think this would take care of the timing issues as well.
>>
>> The logic would be that if the self trigger (DRDY) is selected in
>> the trigger/current_trigger attribute, then buffered read will use
>> continuous mode and fail if more than one channel is enabled. If
>> another (software) trigger is enabled, then use single-shot mode
>> and allow multiple channels.
>
> Hmm. Bit unintuitive but can't really be helped.
>
>>
>> The timing issue is that (or will be since I haven't sent the relevant
>> patches yet) there is a settling delay before the first sample. For
>> single-shot mode, every sample is considered the first sample (because
>> it is "single"), so the chip applies this settling time on every sample.
>> So it might be good enough to just document that as a quirk of software
>> triggers for this particular chip since they would be using single-shot
>> mode?
>>
> This is where it gets messy if settling time is reported. I guess acceptable
> given it doesn't make that much sense for an external trigger anyway.
> In theory if the settling time has to happen each single shot, we could
> just merge it into sampling frequency (as no longer depends on whether
> channel changes or not) but then we have to make that dependent on whether
> it is the devices own trigger or not. Messy. I guess one to paper over
> as a quirk / corner case and document as you say.
Yes, this is the direction I am leaning as well. The input chopping feature
that we have been talking about has a similar issue. In addition to doubling
the sample period, it also includes the settling time (2x as well) minus a
few clock cycles. Even messier.
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH 3/3] iio: adc: ti-ads112c14: add support for buffered read
2026-07-24 14:47 ` David Lechner
@ 2026-07-24 21:34 ` Jonathan Cameron
0 siblings, 0 replies; 13+ messages in thread
From: Jonathan Cameron @ 2026-07-24 21:34 UTC (permalink / raw)
To: David Lechner
Cc: Nuno Sá, Andy Shevchenko, Chris Hall, Patrick Edwards,
Kurt Borja, Nguyen Minh Tien, linux-iio, linux-kernel
On Fri, 24 Jul 2026 09:47:34 -0500
David Lechner <dlechner@baylibre.com> wrote:
> On 7/23/26 6:25 PM, Jonathan Cameron wrote:
> > On Tue, 21 Jul 2026 18:23:58 -0500
> > David Lechner <dlechner@baylibre.com> wrote:
> >
> >> On 7/19/26 7:55 PM, Jonathan Cameron wrote:
> >>> On Tue, 14 Jul 2026 19:19:32 -0500
> >>> "David Lechner (TI)" <dlechner@baylibre.com> wrote:
> >>>
> >>>> Add support for buffered reads using a triggered buffer.
> >>>>
> >>>> The device has a continuous conversion mode, but that can only be used
> >>>> with one channel at a time since there is nothing like a sequencer to
> >>>> support that in hardware. Instead, we use single-shot reads like we do
> >>>> for direct reads to be able to read multiple channels.
> >>>>
> >>>> Support for continuous conversion mode could be added in the future if
> >>>> needed via a 2nd buffer.
> >>>
> >>> How about enabling that if only one channel is requested? I vaguely recall
> >>> us doing that for another driver (though I might be dreaming :) I did
> >>> see your comment in the cover letter about it affecting timing and that
> >>> making life complex. Fine to leave considering this for another day
> >>> but maybe don't suggest a particular solution here.
> >>>
> >> Actually, my latest thought it to do it by trigger type. This chip has
> >> a DRDY interrupt that can be used as the trigger for the continuous
> >> conversion mode but we need some software trigger to the single-shot
> >> mode. It think this would take care of the timing issues as well.
> >>
> >> The logic would be that if the self trigger (DRDY) is selected in
> >> the trigger/current_trigger attribute, then buffered read will use
> >> continuous mode and fail if more than one channel is enabled. If
> >> another (software) trigger is enabled, then use single-shot mode
> >> and allow multiple channels.
> >
> > Hmm. Bit unintuitive but can't really be helped.
> >
> >>
> >> The timing issue is that (or will be since I haven't sent the relevant
> >> patches yet) there is a settling delay before the first sample. For
> >> single-shot mode, every sample is considered the first sample (because
> >> it is "single"), so the chip applies this settling time on every sample.
> >> So it might be good enough to just document that as a quirk of software
> >> triggers for this particular chip since they would be using single-shot
> >> mode?
> >>
> > This is where it gets messy if settling time is reported. I guess acceptable
> > given it doesn't make that much sense for an external trigger anyway.
> > In theory if the settling time has to happen each single shot, we could
> > just merge it into sampling frequency (as no longer depends on whether
> > channel changes or not) but then we have to make that dependent on whether
> > it is the devices own trigger or not. Messy. I guess one to paper over
> > as a quirk / corner case and document as you say.
> Yes, this is the direction I am leaning as well. The input chopping feature
> that we have been talking about has a similar issue. In addition to doubling
> the sample period, it also includes the settling time (2x as well) minus a
> few clock cycles. Even messier.
>
For that one I think we need at least a readonly sysfs attribute to indicate
chopping is going on. Then we tweak the settling time docs to include
a reference to chopping.
Jonathan
>
^ permalink raw reply [flat|nested] 13+ messages in thread