* [PATCH v2 0/2] iio: adc: ti-ads112c14: add burnout current support
@ 2026-08-27 22:27 David Lechner (TI)
2026-08-27 22:27 ` [PATCH v2 1/2] " David Lechner (TI)
2026-08-27 22:27 ` [PATCH v2 2/2] iio: ABI: add sysfs attribute for _burnoutraw David Lechner (TI)
0 siblings, 2 replies; 5+ messages in thread
From: David Lechner (TI) @ 2026-08-27 22:27 UTC (permalink / raw)
To: Jonathan Cameron, Nuno Sá, Andy Shevchenko
Cc: Chris Hall, Patrick Edwards, Kurt Borja, linux-iio, linux-kernel,
David Lechner (TI)
The TI ADS112C14 ADC has a feature to enable a "burnout" current when
taking a measurement. This can be used to detect open/short circuit
conditions of a connected sensor. However, enabling this affects the
accuracy of the reading. So we add a custom "burnoutraw" attribute for
this. This works like the standard raw attribute to do a direct read
but does so with the burnout current enabled.
If this sounds familiar, it is because we discussed this on a similar
chip recently [1]. And this is why I put the ABI docs in a common ADC
file rather than one specific to this chip.
[1]: https://lore.kernel.org/linux-iio/20260622110223.7e854dde@jic23-huawei/
Signed-off-by: David Lechner (TI) <dlechner@baylibre.com>
---
Changes in v2:
- Rebased on "[PATCH v4 0/3] iio: adc: ti-ads112c14: continuous mode support"
- Link to v1: https://patch.msgid.link/20260724-iio-adc-ti-ads112c14-burnout-v1-0-f0204cecbe3a@baylibre.com
---
David Lechner (TI) (2):
iio: adc: ti-ads112c14: add burnout current support
iio: ABI: add sysfs attribute for _burnoutraw
Documentation/ABI/testing/sysfs-bus-iio-adc | 9 ++
drivers/iio/adc/ti-ads112c14.c | 126 ++++++++++++++++++++++++++--
2 files changed, 126 insertions(+), 9 deletions(-)
---
base-commit: 22359083a9e74d538ce383d5c0ee30cc20182187
change-id: 20260724-iio-adc-ti-ads112c14-burnout-184304669165
prerequisite-change-id: 20260724-iio-adc-ti-ads112c14-continuous-mode-dd1580754609:v3
prerequisite-patch-id: 0be96266a72458a63f7da7ca876d87cdf5769a8b
prerequisite-patch-id: 32ba48fd5eacaee60d50a0272557859cc9ea83ca
prerequisite-patch-id: c1eb09689bd5f259ea0110cbbfb3fa76689bdb04
Best regards,
--
David Lechner (TI) <dlechner@baylibre.com>
^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCH v2 1/2] iio: adc: ti-ads112c14: add burnout current support
2026-08-27 22:27 [PATCH v2 0/2] iio: adc: ti-ads112c14: add burnout current support David Lechner (TI)
@ 2026-08-27 22:27 ` David Lechner (TI)
2026-08-28 7:38 ` Andy Shevchenko
2026-08-27 22:27 ` [PATCH v2 2/2] iio: ABI: add sysfs attribute for _burnoutraw David Lechner (TI)
1 sibling, 1 reply; 5+ messages in thread
From: David Lechner (TI) @ 2026-08-27 22:27 UTC (permalink / raw)
To: Jonathan Cameron, Nuno Sá, Andy Shevchenko
Cc: Chris Hall, Patrick Edwards, Kurt Borja, linux-iio, linux-kernel,
David Lechner (TI)
Add a custom attribute via ext_info when a channel has a burnout current
specified in the devicetree. This adds an in_{voltageY,resistanceY,
voltageY-voltageX}_burnoutraw sysfs attribute for the channel that
performs a single conversion (same as _raw attribute) except that it
enables the burnout current. The chip also has a restriction that input
chopping cannot be enabled when burnout current is enabled, so we also
disable input chopping when burnout current is active.
Signed-off-by: David Lechner (TI) <dlechner@baylibre.com>
---
drivers/iio/adc/ti-ads112c14.c | 126 ++++++++++++++++++++++++++++++++++++++---
1 file changed, 117 insertions(+), 9 deletions(-)
diff --git a/drivers/iio/adc/ti-ads112c14.c b/drivers/iio/adc/ti-ads112c14.c
index 878764deffc5..58c815f5faeb 100644
--- a/drivers/iio/adc/ti-ads112c14.c
+++ b/drivers/iio/adc/ti-ads112c14.c
@@ -76,6 +76,11 @@
#define ADS112C14_DEVICE_CFG_PWDN BIT(7)
#define ADS112C14_DEVICE_CFG_STBY_MODE BIT(6)
#define ADS112C14_DEVICE_CFG_BOCS GENMASK(5, 4)
+#define ADS112C14_DEVICE_CFG_BOCS_DISABLED 0
+#define ADS112C14_DEVICE_CFG_BOCS_200_nA 1
+#define ADS112C14_DEVICE_CFG_BOCS_1_uA 2
+#define ADS112C14_DEVICE_CFG_BOCS_10_uA 3
+
#define ADS112C14_DEVICE_CFG_CLK_SEL BIT(3)
#define ADS112C14_DEVICE_CFG_CONV_MODE BIT(2)
#define ADS112C14_DEVICE_CFG_CONV_MODE_CONTINUOUS 0
@@ -251,6 +256,7 @@ struct ads112c14_measurement {
u8 idac2_mux;
u8 iadc_count;
u8 gain_val;
+ u8 burnout;
bool global_chop;
bool bipolar;
int scale_available[ARRAY_SIZE(ads112c14_pga_gains_x10)][2];
@@ -460,7 +466,8 @@ static const struct regmap_config ads112c14_regmap_config = {
};
static int ads112c14_prepare_measurement_channel(struct ads112c14_data *data,
- const struct iio_chan_spec *chan)
+ const struct iio_chan_spec *chan,
+ bool en_burnout)
{
struct ads112c14_measurement *measurement = &data->measurements[chan->scan_index];
u32 refp_buf_en, refn_buf_en, ref_val, ref_sel;
@@ -514,7 +521,7 @@ static int ads112c14_prepare_measurement_channel(struct ads112c14_data *data,
ret = regmap_update_bits(data->regmap, ADS112C14_REG_DATA_RATE_CFG,
ADS112C14_DATA_RATE_CFG_GC_EN,
FIELD_PREP(ADS112C14_DATA_RATE_CFG_GC_EN,
- measurement->global_chop));
+ measurement->global_chop && !en_burnout));
if (ret)
return ret;
@@ -612,10 +619,11 @@ static int ads112c14_prepare_sys_mon_channel(struct ads112c14_data *data,
}
static int ads112c14_prepare_channel(struct ads112c14_data *data,
- const struct iio_chan_spec *chan)
+ const struct iio_chan_spec *chan,
+ bool en_burnout)
{
if (chan->channel < ADS112C14_SYS_MON_CHANNEL_BASE)
- return ads112c14_prepare_measurement_channel(data, chan);
+ return ads112c14_prepare_measurement_channel(data, chan, en_burnout);
return ads112c14_prepare_sys_mon_channel(data, chan);
}
@@ -673,14 +681,14 @@ static int ads112c14_wait_for_conversion_poll(struct ads112c14_data *data)
static int ads112c14_single_conversion(struct ads112c14_data *data,
const struct iio_chan_spec *chan,
- u8 *buf, bool for_scan)
+ u8 *buf, bool en_burnout, bool for_scan)
{
struct i2c_client *client = to_i2c_client(regmap_get_device(data->regmap));
int ret;
guard(mutex)(&data->lock);
- ret = ads112c14_prepare_channel(data, chan);
+ ret = ads112c14_prepare_channel(data, chan, en_burnout);
if (ret)
return ret;
@@ -734,7 +742,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, false);
+ ret = ads112c14_single_conversion(data, chan, buf, false, false);
if (ret)
return ret;
@@ -1009,7 +1017,7 @@ static irqreturn_t ads112c14_trigger_handler(int irq, void *private)
ret = ads112c14_single_conversion(data, chan,
(u8 *)&data->scan[offset++],
- true);
+ false, true);
if (ret) {
dev_err_once(indio_dev->dev.parent,
"failed to read channel %d: %pe; additional errors will be suppressed\n",
@@ -1066,7 +1074,7 @@ static int ads112c14_buffer_postenable(struct iio_dev *indio_dev)
guard(mutex)(&data->lock);
- ret = ads112c14_prepare_channel(data, chan);
+ ret = ads112c14_prepare_channel(data, chan, false);
if (ret)
return ret;
@@ -1117,6 +1125,78 @@ static const struct iio_buffer_setup_ops ads112c14_buffer_setup_ops = {
.validate_scan_mask = ads112c14_validate_scan_mask,
};
+static ssize_t ads112c14_read_burnout_raw(struct iio_dev *indio_dev,
+ uintptr_t private,
+ struct iio_chan_spec const *chan,
+ char *buf)
+{
+ struct ads112c14_data *data = iio_priv(indio_dev);
+ struct ads112c14_measurement *measurement;
+ int ret, ret2, val;
+ u8 raw_buf[3];
+
+ if (chan->channel >= ADS112C14_SYS_MON_CHANNEL_BASE)
+ return -EINVAL;
+
+ measurement = &data->measurements[chan->scan_index];
+
+ if (!measurement->burnout)
+ return -EINVAL;
+
+ IIO_DEV_ACQUIRE_DIRECT_MODE(indio_dev, claim);
+ if (IIO_DEV_ACQUIRE_FAILED(claim))
+ return -EBUSY;
+
+ ret = regmap_update_bits(data->regmap, ADS112C14_REG_DEVICE_CFG,
+ ADS112C14_DEVICE_CFG_BOCS,
+ FIELD_PREP(ADS112C14_DEVICE_CFG_BOCS,
+ measurement->burnout));
+ if (ret)
+ return ret;
+
+ ret = ads112c14_single_conversion(data, chan, raw_buf, true, false);
+
+ /*
+ * Important to always turn off burnout current even if the conversion
+ * fails so that it does not affect subsequent measurements. This error
+ * also takes precedence over the conversion error since the device may
+ * be left in a bad state.
+ */
+ ret2 = regmap_update_bits(data->regmap, ADS112C14_REG_DEVICE_CFG,
+ ADS112C14_DEVICE_CFG_BOCS,
+ FIELD_PREP(ADS112C14_DEVICE_CFG_BOCS,
+ ADS112C14_DEVICE_CFG_BOCS_DISABLED));
+ if (ret2)
+ return ret2;
+
+ if (ret < 0)
+ return ret;
+
+ switch (data->chip_info->resolution_bits) {
+ case 16:
+ val = get_unaligned_be16(raw_buf);
+ break;
+ case 24:
+ val = get_unaligned_be24(raw_buf);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (measurement->bipolar)
+ val = sign_extend32(val, data->chip_info->resolution_bits - 1);
+
+ return sysfs_emit(buf, "%d\n", val);
+}
+
+static const struct iio_chan_spec_ext_info ads112c14_ext_info_burnout[] = {
+ {
+ .name = "burnoutraw",
+ .read = ads112c14_read_burnout_raw,
+ },
+ { }
+};
+
static int ads112c14_populate_idac_mag(u32 current_nA, u8 *idac_mag)
{
u32 current_uA = current_nA / (NANO / MICRO);
@@ -1285,6 +1365,34 @@ static int ads112c14_parse_channels(struct iio_dev *indio_dev,
measurement->global_chop = fwnode_property_read_bool(child,
"input-chopping");
+ if (fwnode_property_present(child, "burn-out-current-nanoamp")) {
+ u32 burnout_nA;
+
+ ret = fwnode_property_read_u32(child, "burn-out-current-nanoamp",
+ &burnout_nA);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "failed to read burn-out-current-nanoamp property\n");
+
+ switch (burnout_nA) {
+ case 200:
+ measurement->burnout = ADS112C14_DEVICE_CFG_BOCS_200_nA;
+ break;
+ case 1000:
+ measurement->burnout = ADS112C14_DEVICE_CFG_BOCS_1_uA;
+ break;
+ case 10000:
+ measurement->burnout = ADS112C14_DEVICE_CFG_BOCS_10_uA;
+ break;
+ default:
+ return dev_err_probe(dev, -EINVAL,
+ "invalid burn-out-current-nanoamp value\n");
+ }
+
+ if (measurement->burnout)
+ spec->ext_info = ads112c14_ext_info_burnout;
+ }
+
if (fwnode_property_present(child, "reference-sources")) {
ret = fwnode_property_match_property_string(child,
"reference-sources", ads112c14_vref_source_names,
--
2.43.0
^ permalink raw reply related [flat|nested] 5+ messages in thread
* [PATCH v2 2/2] iio: ABI: add sysfs attribute for _burnoutraw
2026-08-27 22:27 [PATCH v2 0/2] iio: adc: ti-ads112c14: add burnout current support David Lechner (TI)
2026-08-27 22:27 ` [PATCH v2 1/2] " David Lechner (TI)
@ 2026-08-27 22:27 ` David Lechner (TI)
2026-08-28 7:39 ` Andy Shevchenko
1 sibling, 1 reply; 5+ messages in thread
From: David Lechner (TI) @ 2026-08-27 22:27 UTC (permalink / raw)
To: Jonathan Cameron, Nuno Sá, Andy Shevchenko
Cc: Chris Hall, Patrick Edwards, Kurt Borja, linux-iio, linux-kernel,
David Lechner (TI)
Add a new _burnoutraw attribute to the IIO ADC ABI. This is likely only
applicable to ADCs (but is seen on multiple chips and vendors) so it
gets its own file instead of being added to the main IIO ABI file.
Signed-off-by: David Lechner (TI) <dlechner@baylibre.com>
---
Documentation/ABI/testing/sysfs-bus-iio-adc | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/Documentation/ABI/testing/sysfs-bus-iio-adc b/Documentation/ABI/testing/sysfs-bus-iio-adc
new file mode 100644
index 000000000000..d45f0a4eb18e
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-bus-iio-adc
@@ -0,0 +1,9 @@
+What: /sys/bus/iio/devices/iio:deviceX/in_resistanceY_burnoutraw
+What: /sys/bus/iio/devices/iio:deviceX/in_voltageY_burnoutraw
+What: /sys/bus/iio/devices/iio:deviceX/in_voltageY-voltageZ_burnoutraw
+KernelVersion: 7.3
+Contact: linux-iio@vger.kernel.org
+Description:
+ Raw value from channel Y read using a single conversion with
+ the channel burnout current enabled. This is typically used
+ for diagnostic purposes to detect an open or shorted input.
--
2.43.0
^ permalink raw reply related [flat|nested] 5+ messages in thread
* Re: [PATCH v2 1/2] iio: adc: ti-ads112c14: add burnout current support
2026-08-27 22:27 ` [PATCH v2 1/2] " David Lechner (TI)
@ 2026-08-28 7:38 ` Andy Shevchenko
0 siblings, 0 replies; 5+ messages in thread
From: Andy Shevchenko @ 2026-08-28 7:38 UTC (permalink / raw)
To: David Lechner (TI)
Cc: Jonathan Cameron, Nuno Sá, Andy Shevchenko, Chris Hall,
Patrick Edwards, Kurt Borja, linux-iio, linux-kernel
On Thu, Aug 27, 2026 at 05:27:02PM -0500, David Lechner (TI) wrote:
> Add a custom attribute via ext_info when a channel has a burnout current
> specified in the devicetree. This adds an in_{voltageY,resistanceY,
> voltageY-voltageX}_burnoutraw sysfs attribute for the channel that
> performs a single conversion (same as _raw attribute) except that it
> enables the burnout current. The chip also has a restriction that input
> chopping cannot be enabled when burnout current is enabled, so we also
> disable input chopping when burnout current is active.
...
> ret = regmap_update_bits(data->regmap, ADS112C14_REG_DATA_RATE_CFG,
> ADS112C14_DATA_RATE_CFG_GC_EN,
> FIELD_PREP(ADS112C14_DATA_RATE_CFG_GC_EN,
> - measurement->global_chop));
> + measurement->global_chop && !en_burnout));
Strictly speaking the argument for FIELD_PREP should be integer and not boolean.
> if (ret)
> return ret;
...
> +static ssize_t ads112c14_read_burnout_raw(struct iio_dev *indio_dev,
> + uintptr_t private,
> + struct iio_chan_spec const *chan,
> + char *buf)
> +{
> + struct ads112c14_data *data = iio_priv(indio_dev);
> + struct ads112c14_measurement *measurement;
> + int ret, ret2, val;
> + u8 raw_buf[3];
> +
> + if (chan->channel >= ADS112C14_SYS_MON_CHANNEL_BASE)
> + return -EINVAL;
> +
> + measurement = &data->measurements[chan->scan_index];
> +
Unneeded blank line.
> + if (!measurement->burnout)
> + return -EINVAL;
> +
> + IIO_DEV_ACQUIRE_DIRECT_MODE(indio_dev, claim);
> + if (IIO_DEV_ACQUIRE_FAILED(claim))
> + return -EBUSY;
> +
> + ret = regmap_update_bits(data->regmap, ADS112C14_REG_DEVICE_CFG,
> + ADS112C14_DEVICE_CFG_BOCS,
> + FIELD_PREP(ADS112C14_DEVICE_CFG_BOCS,
> + measurement->burnout));
> + if (ret)
> + return ret;
> +
> + ret = ads112c14_single_conversion(data, chan, raw_buf, true, false);
> +
> + /*
> + * Important to always turn off burnout current even if the conversion
> + * fails so that it does not affect subsequent measurements. This error
> + * also takes precedence over the conversion error since the device may
> + * be left in a bad state.
> + */
> + ret2 = regmap_update_bits(data->regmap, ADS112C14_REG_DEVICE_CFG,
> + ADS112C14_DEVICE_CFG_BOCS,
> + FIELD_PREP(ADS112C14_DEVICE_CFG_BOCS,
> + ADS112C14_DEVICE_CFG_BOCS_DISABLED));
> + if (ret2)
> + return ret2;
What we will get of sharing this error code instead of 'ret' in case of single
conversion failure? I think there is no recovery mechanism involved, right?
> + if (ret < 0)
> + return ret;
> + switch (data->chip_info->resolution_bits) {
> + case 16:
> + val = get_unaligned_be16(raw_buf);
> + break;
> + case 24:
> + val = get_unaligned_be24(raw_buf);
> + break;
> + default:
> + return -EINVAL;
> + }
> +
> + if (measurement->bipolar)
> + val = sign_extend32(val, data->chip_info->resolution_bits - 1);
> +
> + return sysfs_emit(buf, "%d\n", val);
> +}
And this is after all a user space interaction, so whatever error code is
returned, user space will know it. I assume you are telling that the restoring
the "burnout current" setting is important, but how will user space distinguish
that case from the single conversion failure?
...
With
const char *propname;
...
propname = "burn-out-current-nanoamp";
> + if (fwnode_property_present(child, "burn-out-current-nanoamp")) {
> + u32 burnout_nA;
> +
> + ret = fwnode_property_read_u32(child, "burn-out-current-nanoamp",
> + &burnout_nA);
> + if (ret)
> + return dev_err_probe(dev, ret,
> + "failed to read burn-out-current-nanoamp property\n");
This can be shortened as well as the other message below.
> + switch (burnout_nA) {
> + case 200:
> + measurement->burnout = ADS112C14_DEVICE_CFG_BOCS_200_nA;
> + break;
> + case 1000:
> + measurement->burnout = ADS112C14_DEVICE_CFG_BOCS_1_uA;
> + break;
> + case 10000:
> + measurement->burnout = ADS112C14_DEVICE_CFG_BOCS_10_uA;
> + break;
> + default:
> + return dev_err_probe(dev, -EINVAL,
I would use different error code, EINVAL is abused and overloaded a lot in the
kernel, and basically errors like ENODEV and EINVAL are synonyms to "*an* error"
happened. Unfortunately, reading the errno*.h I haven't found anything better.
> + "invalid burn-out-current-nanoamp value\n");
> + }
> +
> + if (measurement->burnout)
> + spec->ext_info = ads112c14_ext_info_burnout;
> + }
--
With Best Regards,
Andy Shevchenko
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [PATCH v2 2/2] iio: ABI: add sysfs attribute for _burnoutraw
2026-08-27 22:27 ` [PATCH v2 2/2] iio: ABI: add sysfs attribute for _burnoutraw David Lechner (TI)
@ 2026-08-28 7:39 ` Andy Shevchenko
0 siblings, 0 replies; 5+ messages in thread
From: Andy Shevchenko @ 2026-08-28 7:39 UTC (permalink / raw)
To: David Lechner (TI)
Cc: Jonathan Cameron, Nuno Sá, Andy Shevchenko, Chris Hall,
Patrick Edwards, Kurt Borja, linux-iio, linux-kernel
On Thu, Aug 27, 2026 at 05:27:03PM -0500, David Lechner (TI) wrote:
> Add a new _burnoutraw attribute to the IIO ADC ABI. This is likely only
> applicable to ADCs (but is seen on multiple chips and vendors) so it
> gets its own file instead of being added to the main IIO ABI file.
...
> +What: /sys/bus/iio/devices/iio:deviceX/in_resistanceY_burnoutraw
> +What: /sys/bus/iio/devices/iio:deviceX/in_voltageY_burnoutraw
> +What: /sys/bus/iio/devices/iio:deviceX/in_voltageY-voltageZ_burnoutraw
> +KernelVersion: 7.3
Can't be possible. See https://hansen.beer/~dave/phb/.
> +Contact: linux-iio@vger.kernel.org
--
With Best Regards,
Andy Shevchenko
^ permalink raw reply [flat|nested] 5+ messages in thread
end of thread, other threads:[~2026-08-28 7:39 UTC | newest]
Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-27 22:27 [PATCH v2 0/2] iio: adc: ti-ads112c14: add burnout current support David Lechner (TI)
2026-08-27 22:27 ` [PATCH v2 1/2] " David Lechner (TI)
2026-08-28 7:38 ` Andy Shevchenko
2026-08-27 22:27 ` [PATCH v2 2/2] iio: ABI: add sysfs attribute for _burnoutraw David Lechner (TI)
2026-08-28 7:39 ` Andy Shevchenko
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.