* [PATCH v3 00/10] iio: adc: ti-ads112c14: add filter support
@ 2026-09-10 21:12 David Lechner (TI)
2026-09-10 21:13 ` [PATCH v3 01/10] iio: adc: ti-ads112c14: add DRDY interrupt support David Lechner (TI)
` (10 more replies)
0 siblings, 11 replies; 15+ messages in thread
From: David Lechner (TI) @ 2026-09-10 21:12 UTC (permalink / raw)
To: Jonathan Cameron, Nuno Sá, Andy Shevchenko
Cc: Chris Hall, Patrick Edwards, Kurt Borja, linux-iio, linux-kernel,
David Lechner (TI)
I was hoping to avoid this, but until [0] lands, sashiko doesn't know
how to handle patch series with dependencies. So I have combined my
three outstanding ti-ads112c14 series into a single one. It's really
too much all at once, but this seems the best way to speed up the review
process at the moment.
[0]: https://github.com/sashiko-dev/sashiko/pull/389
So we now have:
* patches 1-3: "iio: adc: ti-ads112c14: continuous mode support", last
reviewed as v5 [1] (identical to what was sent with [2] but review
was done on the standalone series)
* patches 4-5: "iio: adc: ti-ads112c14: add burnout current support",
last posted/reviewed as part of this bigger series [2].
* patches 6-10: "iio: adc: ti-ads112c14: add filter support", last
posted as v2 [2]
Apart from the changes listed below, the patches are the same as in
those postings.
[1]: https://patch.msgid.link/20260831-iio-adc-ti-ads112c14-continuous-mode-v5-0-76f80a04b94f@baylibre.com
[2]: https://patch.msgid.link/20260904-iio-adc-ti-ads112c14-filter-support-v2-0-f86592360658@baylibre.com
The rest of this cover letter describes the filter support portion.
TI ADS112C14 has several features related to filtering that are all
interconnected. And to make things more interesting, not of the register
fields map directly to IIO attributes. So this is one of those cases
where we need to bend the rules a bit and just document it (we've
already discussed this a bit in the previous series for this driver and
came to this conclusion).
Here is the high-level overview:
We are adding sampling_frequency, oversampling_ratio, filter_type, and
a (new to IIO) settlingtime attribute.
Since register fields have different meanings depending on filter type
we have a quirky rule that if the filter type is sinc4 or sinc4+sinc1,
then you need to set the oversampling ratio first in order to see the
expected available values for the sampling frequency. For sinc4+sinc1+pf1
it is the other way around, you have to set sampling frequency first
in order to see the expected available oversampling ratios.
In other drivers, we've opted to store the requested values for dependant
attributes like this and pick the closest available one when actually
starting sampling. I opted not to do that here as there is not much
overlap between settings. And as we will see below, there are other
reasons for being picky about sampling frequency.
We also discussed in another series about a proposed settlingtime
attribute. The conclusion was that it should be the total settling time
delay (in seconds) before a chip takes the first sample (after any
settings have changed). In this chip there is a DELAY field in a
register that programs some extra delay in addition to an always present
fixed delay. So the way the attribute will work for this chip is that
the settlingtime_available attribute will list the range including a
minimum value. This happens to be the always present fixed delay. So the
difference between that and the current value of the settlingtime
attribute will be programed as the DELAY value. It also seems that the
fixed latency period includes the conversion time. We've just glossed
over that for now and not subtracted that from the settlingtime
attribute.
Now, here is where things really get interesting/complicated. There are
even more settings that affect the settling time. We defined the
settling time as just a delay before the first sample. However, there
are a couple of things that trigger the "first" sample. On this chip,
the first sample only counts in continuous sampling mode. So only works
as described when using the DRDY trigger in this driver. When using
a generic trigger, e.g. a hrtimer trigger, single-shot sampling mode
is used, so every sample is a "first" sample and has the settling delay
added. This is mostly a non-issue other than it could throw people off
that they cannot set the hrtimer frequency close to the sampling
frequency attribute and actually get that sampling rate.
And there a few other idiosyncrasies we haven't accounted for. To keep
things simple, we've implemented settlingtime as tDELAY + tLATENCY
(datasheet values). But this actually include the conversion time as
well. Also, tLATENCY is longer if you are coming out of standby mode
(this doesn't matter at this point since we didn't implement power
management, but we wouldn't want to change it and break userspace
later).
Then there is also input chopping where the positive and negative input
channels are swapped in the mux in the ADC on each sample. In these
cases, every conversion requires the settling time because the mux is
switched after every conversion. And the actual first sample has
additional delay (presumably does two conversions). So the first sample
takes tGC_LATENCY = 2 × (tDELAY + tLATENCY) – 12 tMOD and every sample
after that takes tGC_DATA = tDELAY + tLATENCY – 12 tMOD. For this one,
I have valued simplicity over accuracy in the implementation, so it is
the same where settlingtime = tDELAY + tLATENCY and not worried about
the 12 tMOD difference. I think it makes sense to keep settlingtime
as a single tDELAY + tLATENCY in this case since the when the mux
changes after each sample, the next sample is now the first sample after
settings have changed.
This sort of breaks the definition of sampling frequency though since
in IIO, the 1 / sampling frequency is the time between each sample
being sent over the bus. I don't really want to change how sampling
frequency is implemented here though because the current values match
the datasheet which can be used to infer information like where the
notches in the filter are. The actual observed sampling rate will be
1 / tGC_DATA.
Having written all of this out now though, I'm tempted to go back and
change the settlingtime attribute implementation to be more accurate.
In any case, tDELAY will always be easy to infer because it is the
current value minus the minimum value (from the _available attribute).
Then when using input chopping one could get a reasonably accurate
sample period by taking 1 / sampling_frequency + settlingtime.
We will follow this up later with a documentation patch that explains
all of this too.
Signed-off-by: David Lechner (TI) <dlechner@baylibre.com>
---
Changes in v3:
- Patch 1 (DRDY interrupt): only require the "drdy" interrupt when it is
actually listed in interrupt-names, falling back to polling mode
otherwise instead of failing probe (the binding allows interrupt-names
to list only "fault").
- Patch 3 (continuous mode): allocate and register the DRDY trigger
before requesting the IRQ, so devres teardown disables the interrupt
before freeing the trigger (avoids a possible use-after-free that
sashiko flagged).
- Patch 6 (external clock): use FIELD_PREP() with named values for the
CLK_SEL bit instead of regmap_set_bits(). Validate that the external
clock's rate is non-zero.
- Patch 7 (filter support): fix div_u64_rem() pointer-sign warnings in
the new ODR table code by using local u32 variables for the
remainder. Use unsigned int instead of u32 for the new loop indices,
declared locally in each loop.
- Patch 9 (settlingtime attribute): widen the intermediate delay
calculation to u64 so a large requested settling time saturates
instead of wrapping around. Rewrap the poll-timeout call.
- Patch 10 (settlingtime ABI): expand the commit message to explain why
the total settling time isn't simply the difference between the first
and subsequent sample times.
- Link to v2: https://patch.msgid.link/20260904-iio-adc-ti-ads112c14-filter-support-v2-0-f86592360658@baylibre.com
Changes in v2:
- Combined the continuous mode support and burnout current support
series into this series (see above).
- Rebased on the current iio/testing.
- Bumped KernelVersion to 7.4 in the settlingtime ABI documentation so
that it matches the rest of the new ABI in this series.
- Patches 1-3 (continuous mode): no changes since v5 [1].
- FWIW, I didn't think sashiko's comments on the IRQ were realistic.
This could never be used with a level interrupt. And we haven't
typically tried to handle spurious interrupts in the past either.
Everything it suggested could only happen with broken hardware or
excessive noise (which I suppose counts as broken hardware).
- Patches 4-5 (burnout), changes since v2 [2]:
- Pass an integer rather than a boolean to FIELD_PREP() for the global
chop enable bit.
- Drop the blank line after looking up the measurement.
- Return the conversion error in preference to the error from turning
the burnout current back off.
- Use a local variable for the "burn-out-current-nanoamp" property
name.
- Compare against ADS112C14_DEVICE_CFG_BOCS_DISABLED explicitly
instead of testing for non-zero.
- Fix KernelVersion in the ABI docs.
- Patches 6-10 (filter support), changes since v1 [3]:
- Move the driver code that was accidentally squashed into the
"iio: ABI: add sinc4+sinc1+pf1 filter_type" patch back to the
"iio: adc: ti-ads112c14: add filter support" patch where it belongs.
- Fixed typos in the external clock patch commit message.
- Added a note to the settlingtime ABI documentation that the settling
time can apply to more than just the first conversion.
- Link to v1: https://patch.msgid.link/20260807-iio-adc-ti-ads112c14-filter-support-v1-0-4d3ba00caf18@baylibre.com
---
David Lechner (TI) (10):
iio: adc: ti-ads112c14: add DRDY interrupt support
iio: adc: ti-ads112c14: create data read helper functions
iio: adc: ti-ads112c14: add continuous mode support
iio: adc: ti-ads112c14: add burnout current support
iio: ABI: add sysfs attribute for _burnoutraw
iio: adc: ti-ads112c14: support external clock
iio: adc: ti-ads112c14: add filter support
iio: ABI: add sinc4+sinc1+pf1 filter_type
iio: adc: ti-ads112c14: add settlingtime attribute
iio: ABI: add settlingtime attributes
Documentation/ABI/testing/sysfs-bus-iio | 27 +
Documentation/ABI/testing/sysfs-bus-iio-adc | 9 +
drivers/iio/adc/ti-ads112c14.c | 1298 +++++++++++++++++++++++++--
3 files changed, 1275 insertions(+), 59 deletions(-)
---
base-commit: e7c1d459e542bc4a9c57f558e8ca1b14df7a7eef
change-id: 20260807-iio-adc-ti-ads112c14-filter-support-8a56850f590f
Best regards,
--
David Lechner (TI) <dlechner@baylibre.com>
^ permalink raw reply [flat|nested] 15+ messages in thread
* [PATCH v3 01/10] iio: adc: ti-ads112c14: add DRDY interrupt support
2026-09-10 21:12 [PATCH v3 00/10] iio: adc: ti-ads112c14: add filter support David Lechner (TI)
@ 2026-09-10 21:13 ` David Lechner (TI)
2026-09-10 21:13 ` [PATCH v3 02/10] iio: adc: ti-ads112c14: create data read helper functions David Lechner (TI)
` (9 subsequent siblings)
10 siblings, 0 replies; 15+ messages in thread
From: David Lechner (TI) @ 2026-09-10 21:13 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 handling for the DRDY interrupt to wait for data ready events rather
than polling (only when it is wired up).
Signed-off-by: David Lechner (TI) <dlechner@baylibre.com>
---
Small note: the hard-coded 100 ms timeout will be replaced in a future
series with a dynamic value, so I didn't bother with a macro or comments
to explain why the value was chosen.
And passing indio_dev instead of data to irq is intentional as it will
be used in the next patch.
---
drivers/iio/adc/ti-ads112c14.c | 100 ++++++++++++++++++++++++++++++++++++-----
1 file changed, 90 insertions(+), 10 deletions(-)
diff --git a/drivers/iio/adc/ti-ads112c14.c b/drivers/iio/adc/ti-ads112c14.c
index 60eab4852ba2..9058ad34bc06 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/completion.h>
#include <linux/crc8.h>
#include <linux/delay.h>
#include <linux/dev_printk.h>
@@ -19,6 +20,7 @@
#include <linux/iio/iio.h>
#include <linux/iio/trigger_consumer.h>
#include <linux/iio/triggered_buffer.h>
+#include <linux/interrupt.h>
#include <linux/math64.h>
#include <linux/minmax.h>
#include <linux/module.h>
@@ -117,9 +119,15 @@
#define ADS112C14_GPIO_CFG_GPIO2_CFG GENMASK(5, 4)
#define ADS112C14_GPIO_CFG_GPIO1_CFG GENMASK(3, 2)
#define ADS112C14_GPIO_CFG_GPIO0_CFG GENMASK(1, 0)
+#define ADS112C14_GPIO_CFG_GPIO_CFG_DISABLED 0
+#define ADS112C14_GPIO_CFG_GPIO_CFG_INPUT 1
+#define ADS112C14_GPIO_CFG_GPIO_CFG_OUTPUT_PUSH_PULL 2
+#define ADS112C14_GPIO_CFG_GPIO_CFG_OUTPUT_OPEN_DRAIN 3
#define ADS112C14_REG_GPIO_DATA_OUTPUT 0x0C
#define ADS112C14_GPIO_DATA_OUTPUT_GPIO3_SRC BIT(7)
+#define ADS112C14_GPIO_DATA_OUTPUT_GPIO3_SRC_DAT_OUT 0
+#define ADS112C14_GPIO_DATA_OUTPUT_GPIO3_SRC_DRDY 1
#define ADS112C14_GPIO_DATA_OUTPUT_GPIO2_SRC BIT(6)
#define ADS112C14_GPIO_DATA_OUTPUT_GPIO3_DAT_OUT BIT(3)
#define ADS112C14_GPIO_DATA_OUTPUT_GPIO2_DAT_OUT BIT(2)
@@ -251,6 +259,8 @@ struct ads112c14_data {
struct regmap *regmap;
/* Synchronizes access to register value fields. */
struct mutex lock;
+ int drdy_irq;
+ struct completion drdy_completion;
bool i2c_crc_enabled;
u32 avdd_uV;
u32 ext_ref_uV;
@@ -265,6 +275,16 @@ struct ads112c14_data {
ARRAY_SIZE(ads112c14_sys_mon_channels));
};
+static irqreturn_t ads112c14_drdy_irq_handler(int irq, void *private)
+{
+ struct iio_dev *indio_dev = private;
+ struct ads112c14_data *data = iio_priv(indio_dev);
+
+ complete(&data->drdy_completion);
+
+ return IRQ_HANDLED;
+}
+
static bool ads112c14_writeable_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
@@ -581,12 +601,45 @@ static int ads112c14_prepare_sys_mon_channel(struct ads112c14_data *data,
return 0;
}
+static int ads112c14_wait_for_conversion_irq(struct ads112c14_data *data)
+{
+ unsigned long remaining;
+ int ret;
+
+ reinit_completion(&data->drdy_completion);
+
+ ret = regmap_write(data->regmap, ADS112C14_REG_CONVERSION_CTRL,
+ ADS112C14_CONVERSION_CTRL_START);
+ if (ret)
+ return ret;
+
+ remaining = wait_for_completion_timeout(&data->drdy_completion,
+ msecs_to_jiffies(100));
+
+ return remaining ? 0 : -ETIMEDOUT;
+}
+
+static int ads112c14_wait_for_conversion_poll(struct ads112c14_data *data)
+{
+ u32 reg_val;
+ int ret;
+
+ ret = regmap_write(data->regmap, ADS112C14_REG_CONVERSION_CTRL,
+ ADS112C14_CONVERSION_CTRL_START);
+ if (ret)
+ return ret;
+
+ return regmap_read_poll_timeout(data->regmap,
+ ADS112C14_REG_STATUS_MSB, reg_val,
+ FIELD_GET(ADS112C14_STATUS_MSB_DRDY, reg_val),
+ 1 * USEC_PER_MSEC, 100 * USEC_PER_MSEC);
+}
+
static int ads112c14_single_conversion(struct ads112c14_data *data,
const struct iio_chan_spec *chan,
u8 *buf, bool for_scan)
{
struct i2c_client *client = to_i2c_client(regmap_get_device(data->regmap));
- u32 reg_val;
int ret;
guard(mutex)(&data->lock);
@@ -601,15 +654,10 @@ static int ads112c14_single_conversion(struct ads112c14_data *data,
return ret;
}
- ret = regmap_write(data->regmap, ADS112C14_REG_CONVERSION_CTRL,
- ADS112C14_CONVERSION_CTRL_START);
- if (ret)
- return ret;
-
- ret = regmap_read_poll_timeout(data->regmap,
- ADS112C14_REG_STATUS_MSB, reg_val,
- FIELD_GET(ADS112C14_STATUS_MSB_DRDY, reg_val),
- 1 * USEC_PER_MSEC, 100 * USEC_PER_MSEC);
+ if (data->drdy_irq)
+ ret = ads112c14_wait_for_conversion_irq(data);
+ else
+ ret = ads112c14_wait_for_conversion_poll(data);
if (ret)
return ret;
@@ -1391,6 +1439,38 @@ static int ads112c14_probe(struct i2c_client *client)
if (ret)
return ret;
+ if (fwnode_property_match_string(dev_fwnode(dev), "interrupt-names", "drdy") >= 0) {
+ data->drdy_irq = fwnode_irq_get_byname(dev_fwnode(dev), "drdy");
+ if (data->drdy_irq < 0)
+ return dev_err_probe(dev, data->drdy_irq,
+ "failed to get drdy interrupt\n");
+
+ /*
+ * REVISIT: would probably need to implement a pin controller in
+ * order to support open drain option here.
+ */
+ ret = regmap_update_bits(data->regmap, ADS112C14_REG_GPIO_CFG,
+ ADS112C14_GPIO_CFG_GPIO3_CFG,
+ FIELD_PREP(ADS112C14_GPIO_CFG_GPIO3_CFG,
+ ADS112C14_GPIO_CFG_GPIO_CFG_OUTPUT_PUSH_PULL));
+ if (ret)
+ return ret;
+
+ ret = regmap_update_bits(data->regmap, ADS112C14_REG_GPIO_DATA_OUTPUT,
+ ADS112C14_GPIO_DATA_OUTPUT_GPIO3_SRC,
+ FIELD_PREP(ADS112C14_GPIO_DATA_OUTPUT_GPIO3_SRC,
+ ADS112C14_GPIO_DATA_OUTPUT_GPIO3_SRC_DRDY));
+ if (ret)
+ return ret;
+
+ init_completion(&data->drdy_completion);
+
+ ret = devm_request_irq(dev, data->drdy_irq, ads112c14_drdy_irq_handler,
+ 0, dev_name(dev), indio_dev);
+ if (ret)
+ return ret;
+ }
+
ads112c14_populate_tables(data);
indio_dev->name = info->name;
--
2.43.0
^ permalink raw reply [flat|nested] 15+ messages in thread
* [PATCH v3 02/10] iio: adc: ti-ads112c14: create data read helper functions
2026-09-10 21:12 [PATCH v3 00/10] iio: adc: ti-ads112c14: add filter support David Lechner (TI)
2026-09-10 21:13 ` [PATCH v3 01/10] iio: adc: ti-ads112c14: add DRDY interrupt support David Lechner (TI)
@ 2026-09-10 21:13 ` David Lechner (TI)
2026-09-10 21:13 ` [PATCH v3 03/10] iio: adc: ti-ads112c14: add continuous mode support David Lechner (TI)
` (8 subsequent siblings)
10 siblings, 0 replies; 15+ messages in thread
From: David Lechner (TI) @ 2026-09-10 21:13 UTC (permalink / raw)
To: Jonathan Cameron, Nuno Sá, Andy Shevchenko
Cc: Chris Hall, Patrick Edwards, Kurt Borja, linux-iio, linux-kernel,
David Lechner (TI)
Refactor a few bits of code into helper functions. These will be reused
when continuous mode support is added in a later patch.
Signed-off-by: David Lechner (TI) <dlechner@baylibre.com>
---
drivers/iio/adc/ti-ads112c14.c | 51 +++++++++++++++++++++++++-----------------
1 file changed, 31 insertions(+), 20 deletions(-)
diff --git a/drivers/iio/adc/ti-ads112c14.c b/drivers/iio/adc/ti-ads112c14.c
index 9058ad34bc06..b2940b5d3c99 100644
--- a/drivers/iio/adc/ti-ads112c14.c
+++ b/drivers/iio/adc/ti-ads112c14.c
@@ -601,6 +601,32 @@ static int ads112c14_prepare_sys_mon_channel(struct ads112c14_data *data,
return 0;
}
+static int ads112c14_prepare_channel(struct ads112c14_data *data,
+ const struct iio_chan_spec *chan)
+{
+ if (chan->channel < ADS112C14_SYS_MON_CHANNEL_BASE)
+ return ads112c14_prepare_measurement_channel(data, chan);
+
+ return ads112c14_prepare_sys_mon_channel(data, chan);
+}
+
+static int ads112c14_scan_read(struct ads112c14_data *data, u8 *buf)
+{
+ struct i2c_client *client = to_i2c_client(regmap_get_device(data->regmap));
+ int ret;
+ u8 len;
+
+ len = BITS_TO_BYTES(data->chip_info->resolution_bits);
+ if (data->i2c_crc_enabled)
+ len += 1;
+
+ ret = i2c_smbus_read_i2c_block_data(client, ADS112C14_CMD_RDATA, len, buf);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
static int ads112c14_wait_for_conversion_irq(struct ads112c14_data *data)
{
unsigned long remaining;
@@ -644,15 +670,9 @@ static int ads112c14_single_conversion(struct ads112c14_data *data,
guard(mutex)(&data->lock);
- if (chan->channel < ADS112C14_SYS_MON_CHANNEL_BASE) {
- ret = ads112c14_prepare_measurement_channel(data, chan);
- if (ret)
- return ret;
- } else {
- ret = ads112c14_prepare_sys_mon_channel(data, chan);
- if (ret)
- return ret;
- }
+ ret = ads112c14_prepare_channel(data, chan);
+ if (ret)
+ return ret;
if (data->drdy_irq)
ret = ads112c14_wait_for_conversion_irq(data);
@@ -667,17 +687,8 @@ static int ads112c14_single_conversion(struct ads112c14_data *data,
* with CRC errors, but rather leave it to userspace to decide what to
* do.
*/
- 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;
- }
+ if (for_scan)
+ return ads112c14_scan_read(data, buf);
return ads112c14_i2c_read_bytes(client, ADS112C14_CMD_RDATA, buf,
BITS_TO_BYTES(data->chip_info->resolution_bits),
--
2.43.0
^ permalink raw reply [flat|nested] 15+ messages in thread
* [PATCH v3 03/10] iio: adc: ti-ads112c14: add continuous mode support
2026-09-10 21:12 [PATCH v3 00/10] iio: adc: ti-ads112c14: add filter support David Lechner (TI)
2026-09-10 21:13 ` [PATCH v3 01/10] iio: adc: ti-ads112c14: add DRDY interrupt support David Lechner (TI)
2026-09-10 21:13 ` [PATCH v3 02/10] iio: adc: ti-ads112c14: create data read helper functions David Lechner (TI)
@ 2026-09-10 21:13 ` David Lechner (TI)
2026-09-10 21:13 ` [PATCH v3 04/10] iio: adc: ti-ads112c14: add burnout current support David Lechner (TI)
` (7 subsequent siblings)
10 siblings, 0 replies; 15+ messages in thread
From: David Lechner (TI) @ 2026-09-10 21:13 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 support for continuous mode in the TI ADS112C14 ADC driver. In this
mode the ADC itself is starting each conversion, so we add a trigger
based on the DRDY interrupt to read each sample. This mode is also
limited in that only one channel can be enabled at a time since the
chip does not have a sequencer or simultaneous sampling capability.
Continuous mode will only be used when this new trigger is the current
trigger.
Signed-off-by: David Lechner (TI) <dlechner@baylibre.com>
---
drivers/iio/adc/ti-ads112c14.c | 153 ++++++++++++++++++++++++++++++++++++++++-
1 file changed, 151 insertions(+), 2 deletions(-)
diff --git a/drivers/iio/adc/ti-ads112c14.c b/drivers/iio/adc/ti-ads112c14.c
index b2940b5d3c99..09807ea4fb18 100644
--- a/drivers/iio/adc/ti-ads112c14.c
+++ b/drivers/iio/adc/ti-ads112c14.c
@@ -9,6 +9,7 @@
*/
#include <linux/bitfield.h>
+#include <linux/bitmap.h>
#include <linux/cleanup.h>
#include <linux/completion.h>
#include <linux/crc8.h>
@@ -18,6 +19,7 @@
#include <linux/i2c.h>
#include <linux/iio/buffer.h>
#include <linux/iio/iio.h>
+#include <linux/iio/trigger.h>
#include <linux/iio/trigger_consumer.h>
#include <linux/iio/triggered_buffer.h>
#include <linux/interrupt.h>
@@ -257,10 +259,12 @@ struct ads112c14_measurement {
struct ads112c14_data {
const struct ads112c14_chip_info *chip_info;
struct regmap *regmap;
+ struct iio_trigger *drdy_trig;
/* Synchronizes access to register value fields. */
struct mutex lock;
int drdy_irq;
struct completion drdy_completion;
+ bool continuous_mode;
bool i2c_crc_enabled;
u32 avdd_uV;
u32 ext_ref_uV;
@@ -280,11 +284,18 @@ static irqreturn_t ads112c14_drdy_irq_handler(int irq, void *private)
struct iio_dev *indio_dev = private;
struct ads112c14_data *data = iio_priv(indio_dev);
- complete(&data->drdy_completion);
+ if (READ_ONCE(data->continuous_mode))
+ iio_trigger_poll(data->drdy_trig);
+ else
+ complete(&data->drdy_completion);
return IRQ_HANDLED;
}
+static const struct iio_trigger_ops ads112c14_trigger_ops = {
+ .validate_device = iio_trigger_validate_own_device,
+};
+
static bool ads112c14_writeable_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
@@ -695,6 +706,13 @@ static int ads112c14_single_conversion(struct ads112c14_data *data,
data->i2c_crc_enabled);
}
+static bool ads112c14_using_drdy_trigger(struct iio_dev *indio_dev)
+{
+ struct ads112c14_data *data = iio_priv(indio_dev);
+
+ return data->drdy_trig && indio_dev->trig == data->drdy_trig;
+}
+
static int ads112c14_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int *val, int *val2, long mask)
@@ -898,6 +916,19 @@ static int ads112c14_write_raw_get_fmt(struct iio_dev *indio_dev,
}
}
+static int ads112c14_update_scan_mode(struct iio_dev *indio_dev,
+ const unsigned long *scan_mask)
+{
+ /* Only continuous mode is limited to a single channel. */
+ if (!ads112c14_using_drdy_trigger(indio_dev))
+ return 0;
+
+ if (!iio_validate_scan_mask_onehot(indio_dev, scan_mask))
+ return -EINVAL;
+
+ return 0;
+}
+
static int ads112c14_debugfs_reg_access(struct iio_dev *indio_dev,
unsigned int reg,
unsigned int writeval,
@@ -952,6 +983,19 @@ static int ads112c14_read_label(struct iio_dev *indio_dev,
return sysfs_emit(label, "%s\n", label_source);
}
+static const struct iio_chan_spec *
+ads112c14_first_active_channel(struct iio_dev *indio_dev)
+{
+ unsigned int scan_mask_len = iio_get_masklength(indio_dev);
+ unsigned int i;
+
+ i = find_first_bit(indio_dev->active_scan_mask, scan_mask_len);
+ if (i == scan_mask_len)
+ return NULL;
+
+ return &indio_dev->channels[i];
+}
+
static irqreturn_t ads112c14_trigger_handler(int irq, void *private)
{
struct iio_poll_func *pf = private;
@@ -961,6 +1005,26 @@ static irqreturn_t ads112c14_trigger_handler(int irq, void *private)
u32 i;
int ret;
+ if (ads112c14_using_drdy_trigger(indio_dev)) {
+ const struct iio_chan_spec *chan;
+
+ chan = ads112c14_first_active_channel(indio_dev);
+ if (!chan)
+ goto out;
+
+ ret = ads112c14_scan_read(data, (u8 *)&data->scan[0]);
+ 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);
+ goto out;
+ }
+
iio_for_each_active_channel(indio_dev, i) {
const struct iio_chan_spec *chan = &indio_dev->channels[i];
@@ -988,10 +1052,81 @@ 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,
+ .update_scan_mode = ads112c14_update_scan_mode,
.debugfs_reg_access = ads112c14_debugfs_reg_access,
.read_label = ads112c14_read_label,
};
+static int ads112c14_buffer_postenable(struct iio_dev *indio_dev)
+{
+ struct ads112c14_data *data = iio_priv(indio_dev);
+ const struct iio_chan_spec *chan;
+ int ret;
+
+ if (!ads112c14_using_drdy_trigger(indio_dev))
+ return 0;
+
+ chan = ads112c14_first_active_channel(indio_dev);
+ if (!chan)
+ return -EINVAL;
+
+ guard(mutex)(&data->lock);
+
+ ret = ads112c14_prepare_channel(data, chan);
+ if (ret)
+ return ret;
+
+ ret = regmap_update_bits(data->regmap, ADS112C14_REG_DEVICE_CFG,
+ ADS112C14_DEVICE_CFG_CONV_MODE,
+ FIELD_PREP(ADS112C14_DEVICE_CFG_CONV_MODE,
+ ADS112C14_DEVICE_CFG_CONV_MODE_CONTINUOUS));
+ if (ret)
+ return ret;
+
+ WRITE_ONCE(data->continuous_mode, true);
+
+ ret = regmap_write(data->regmap, ADS112C14_REG_CONVERSION_CTRL,
+ ADS112C14_CONVERSION_CTRL_START);
+ if (ret) {
+ WRITE_ONCE(data->continuous_mode, false);
+ regmap_update_bits(data->regmap, ADS112C14_REG_DEVICE_CFG,
+ ADS112C14_DEVICE_CFG_CONV_MODE,
+ FIELD_PREP(ADS112C14_DEVICE_CFG_CONV_MODE,
+ ADS112C14_DEVICE_CFG_CONV_MODE_SINGLE_SHOT));
+ return ret;
+ }
+
+ return 0;
+}
+
+static int ads112c14_buffer_predisable(struct iio_dev *indio_dev)
+{
+ struct ads112c14_data *data = iio_priv(indio_dev);
+ int ret;
+
+ if (!ads112c14_using_drdy_trigger(indio_dev))
+ return 0;
+
+ guard(mutex)(&data->lock);
+
+ WRITE_ONCE(data->continuous_mode, false);
+
+ ret = regmap_write(data->regmap, ADS112C14_REG_CONVERSION_CTRL,
+ ADS112C14_CONVERSION_CTRL_STOP);
+ if (ret)
+ return ret;
+
+ return regmap_update_bits(data->regmap, ADS112C14_REG_DEVICE_CFG,
+ ADS112C14_DEVICE_CFG_CONV_MODE,
+ FIELD_PREP(ADS112C14_DEVICE_CFG_CONV_MODE,
+ ADS112C14_DEVICE_CFG_CONV_MODE_SINGLE_SHOT));
+}
+
+static const struct iio_buffer_setup_ops ads112c14_buffer_setup_ops = {
+ .postenable = ads112c14_buffer_postenable,
+ .predisable = ads112c14_buffer_predisable,
+};
+
static int ads112c14_populate_idac_mag(u32 current_nA, u8 *idac_mag)
{
u32 current_uA = current_nA / (NANO / MICRO);
@@ -1476,6 +1611,19 @@ static int ads112c14_probe(struct i2c_client *client)
init_completion(&data->drdy_completion);
+ data->drdy_trig = devm_iio_trigger_alloc(dev, "%s-dev%d-drdy",
+ info->name,
+ iio_device_id(indio_dev));
+ if (!data->drdy_trig)
+ return -ENOMEM;
+
+ data->drdy_trig->ops = &ads112c14_trigger_ops;
+ iio_trigger_set_drvdata(data->drdy_trig, indio_dev);
+
+ ret = devm_iio_trigger_register(dev, data->drdy_trig);
+ if (ret)
+ return ret;
+
ret = devm_request_irq(dev, data->drdy_irq, ads112c14_drdy_irq_handler,
0, dev_name(dev), indio_dev);
if (ret)
@@ -1490,7 +1638,8 @@ static int ads112c14_probe(struct i2c_client *client)
ret = devm_iio_triggered_buffer_setup(dev, indio_dev,
iio_pollfunc_store_time,
- ads112c14_trigger_handler, NULL);
+ ads112c14_trigger_handler,
+ &ads112c14_buffer_setup_ops);
if (ret)
return ret;
--
2.43.0
^ permalink raw reply [flat|nested] 15+ messages in thread
* [PATCH v3 04/10] iio: adc: ti-ads112c14: add burnout current support
2026-09-10 21:12 [PATCH v3 00/10] iio: adc: ti-ads112c14: add filter support David Lechner (TI)
` (2 preceding siblings ...)
2026-09-10 21:13 ` [PATCH v3 03/10] iio: adc: ti-ads112c14: add continuous mode support David Lechner (TI)
@ 2026-09-10 21:13 ` David Lechner (TI)
2026-09-10 21:13 ` [PATCH v3 05/10] iio: ABI: add sysfs attribute for _burnoutraw David Lechner (TI)
` (6 subsequent siblings)
10 siblings, 0 replies; 15+ messages in thread
From: David Lechner (TI) @ 2026-09-10 21:13 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 | 125 ++++++++++++++++++++++++++++++++++++++---
1 file changed, 116 insertions(+), 9 deletions(-)
diff --git a/drivers/iio/adc/ti-ads112c14.c b/drivers/iio/adc/ti-ads112c14.c
index 09807ea4fb18..efea1e22d067 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];
@@ -461,7 +467,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;
@@ -515,7 +522,8 @@ 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) ? 1 : 0));
if (ret)
return ret;
@@ -613,10 +621,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);
}
@@ -674,14 +683,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;
@@ -742,7 +751,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;
@@ -1030,7 +1039,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",
@@ -1072,7 +1081,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;
@@ -1127,6 +1136,74 @@ static const struct iio_buffer_setup_ops ads112c14_buffer_setup_ops = {
.predisable = ads112c14_buffer_predisable,
};
+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.
+ */
+ 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 (ret < 0)
+ return ret;
+ if (ret2)
+ return ret2;
+
+ 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);
@@ -1172,6 +1249,7 @@ static int ads112c14_parse_channels(struct iio_dev *indio_dev,
device_for_each_named_child_node_scoped(dev, child, "channel") {
struct ads112c14_measurement *measurement = &data->measurements[i];
struct iio_chan_spec *spec = &channels[i];
+ const char *propname;
spec->indexed = 1;
spec->scan_index = i;
@@ -1295,6 +1373,35 @@ static int ads112c14_parse_channels(struct iio_dev *indio_dev,
measurement->global_chop = fwnode_property_read_bool(child,
"input-chopping");
+ propname = "burn-out-current-nanoamp";
+ if (fwnode_property_present(child, propname)) {
+ u32 burnout_nA;
+
+ ret = fwnode_property_read_u32(child, propname, &burnout_nA);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "failed to read %s property\n",
+ propname);
+
+ 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 %s value\n", propname);
+ }
+
+ if (measurement->burnout != ADS112C14_DEVICE_CFG_BOCS_DISABLED)
+ 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 [flat|nested] 15+ messages in thread
* [PATCH v3 05/10] iio: ABI: add sysfs attribute for _burnoutraw
2026-09-10 21:12 [PATCH v3 00/10] iio: adc: ti-ads112c14: add filter support David Lechner (TI)
` (3 preceding siblings ...)
2026-09-10 21:13 ` [PATCH v3 04/10] iio: adc: ti-ads112c14: add burnout current support David Lechner (TI)
@ 2026-09-10 21:13 ` David Lechner (TI)
2026-09-10 21:13 ` [PATCH v3 06/10] iio: adc: ti-ads112c14: support external clock David Lechner (TI)
` (5 subsequent siblings)
10 siblings, 0 replies; 15+ messages in thread
From: David Lechner (TI) @ 2026-09-10 21:13 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..e1309b371efb
--- /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.4
+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 [flat|nested] 15+ messages in thread
* [PATCH v3 06/10] iio: adc: ti-ads112c14: support external clock
2026-09-10 21:12 [PATCH v3 00/10] iio: adc: ti-ads112c14: add filter support David Lechner (TI)
` (4 preceding siblings ...)
2026-09-10 21:13 ` [PATCH v3 05/10] iio: ABI: add sysfs attribute for _burnoutraw David Lechner (TI)
@ 2026-09-10 21:13 ` David Lechner (TI)
2026-09-11 8:38 ` Joshua Crofts
2026-09-10 21:13 ` [PATCH v3 07/10] iio: adc: ti-ads112c14: add filter support David Lechner (TI)
` (4 subsequent siblings)
10 siblings, 1 reply; 15+ messages in thread
From: David Lechner (TI) @ 2026-09-10 21:13 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 support for an external clock source to the TI ADS112C14 ADC driver.
The unused fclk_Hz field is added in preparation for filter support.
Signed-off-by: David Lechner (TI) <dlechner@baylibre.com>
---
drivers/iio/adc/ti-ads112c14.c | 39 +++++++++++++++++++++++++++++++++++++++
1 file changed, 39 insertions(+)
diff --git a/drivers/iio/adc/ti-ads112c14.c b/drivers/iio/adc/ti-ads112c14.c
index efea1e22d067..0e4584b4675e 100644
--- a/drivers/iio/adc/ti-ads112c14.c
+++ b/drivers/iio/adc/ti-ads112c14.c
@@ -11,6 +11,7 @@
#include <linux/bitfield.h>
#include <linux/bitmap.h>
#include <linux/cleanup.h>
+#include <linux/clk.h>
#include <linux/completion.h>
#include <linux/crc8.h>
#include <linux/delay.h>
@@ -82,6 +83,8 @@
#define ADS112C14_DEVICE_CFG_BOCS_10_uA 3
#define ADS112C14_DEVICE_CFG_CLK_SEL BIT(3)
+#define ADS112C14_DEVICE_CFG_CLK_SEL_INTERNAL 0
+#define ADS112C14_DEVICE_CFG_CLK_SEL_EXTERNAL 1
#define ADS112C14_DEVICE_CFG_CONV_MODE BIT(2)
#define ADS112C14_DEVICE_CFG_CONV_MODE_CONTINUOUS 0
#define ADS112C14_DEVICE_CFG_CONV_MODE_SINGLE_SHOT 1
@@ -178,6 +181,8 @@ static const u32 ads112c14_pga_gains_x10[] = {
200, 320, 500, 640, 1000, 1280, 2000, 2560, /* 8 - 15 */
};
+#define ADS112C14_INTERNAL_CLK_Hz 4096000
+
#define ADS112C14_I2C_CRC8_POLYNOMIAL 0x07
DECLARE_CRC8_TABLE(ads112c14_crc8_table);
@@ -268,6 +273,7 @@ struct ads112c14_data {
struct iio_trigger *drdy_trig;
/* Synchronizes access to register value fields. */
struct mutex lock;
+ long fclk_Hz;
int drdy_irq;
struct completion drdy_completion;
bool continuous_mode;
@@ -1536,6 +1542,7 @@ static int ads112c14_probe(struct i2c_client *client)
const struct ads112c14_chip_info *info;
struct iio_dev *indio_dev;
struct ads112c14_data *data;
+ struct clk *clk;
bool need_avdd_ref, need_ext_ref;
u32 refp_uV = 0;
u32 refn_uV = 0;
@@ -1628,6 +1635,18 @@ static int ads112c14_probe(struct i2c_client *client)
return dev_err_probe(dev, -EINVAL,
"external reference measurements require either refp-supply or ti,refp-refn-resistor-ohms property\n");
+ clk = devm_clk_get_optional_enabled(dev, NULL);
+ if (IS_ERR(clk))
+ return dev_err_probe(dev, PTR_ERR(clk), "failed to get clk\n");
+
+ if (clk) {
+ data->fclk_Hz = clk_get_rate(clk);
+ if (!data->fclk_Hz)
+ return dev_err_probe(dev, -EINVAL, "clk rate is 0\n");
+ } else {
+ data->fclk_Hz = ADS112C14_INTERNAL_CLK_Hz;
+ }
+
/* It takes some time for the internal reference to stabilize. */
fsleep(10 * USEC_PER_MSEC);
@@ -1698,6 +1717,10 @@ static int ads112c14_probe(struct i2c_client *client)
return dev_err_probe(dev, data->drdy_irq,
"failed to get drdy interrupt\n");
+ if (clk)
+ return dev_err_probe(dev, -EINVAL,
+ "cannot use both DRDY and CLK - they share the same pin\n");
+
/*
* REVISIT: would probably need to implement a pin controller in
* order to support open drain option here.
@@ -1737,6 +1760,22 @@ static int ads112c14_probe(struct i2c_client *client)
return ret;
}
+ if (clk) {
+ ret = regmap_update_bits(data->regmap, ADS112C14_REG_GPIO_CFG,
+ ADS112C14_GPIO_CFG_GPIO3_CFG,
+ FIELD_PREP(ADS112C14_GPIO_CFG_GPIO3_CFG,
+ ADS112C14_GPIO_CFG_GPIO_CFG_INPUT));
+ if (ret)
+ return ret;
+
+ ret = regmap_update_bits(data->regmap, ADS112C14_REG_DEVICE_CFG,
+ ADS112C14_DEVICE_CFG_CLK_SEL,
+ FIELD_PREP(ADS112C14_DEVICE_CFG_CLK_SEL,
+ ADS112C14_DEVICE_CFG_CLK_SEL_EXTERNAL));
+ if (ret)
+ return ret;
+ }
+
ads112c14_populate_tables(data);
indio_dev->name = info->name;
--
2.43.0
^ permalink raw reply [flat|nested] 15+ messages in thread
* [PATCH v3 07/10] iio: adc: ti-ads112c14: add filter support
2026-09-10 21:12 [PATCH v3 00/10] iio: adc: ti-ads112c14: add filter support David Lechner (TI)
` (5 preceding siblings ...)
2026-09-10 21:13 ` [PATCH v3 06/10] iio: adc: ti-ads112c14: support external clock David Lechner (TI)
@ 2026-09-10 21:13 ` David Lechner (TI)
2026-09-10 21:13 ` [PATCH v3 08/10] iio: ABI: add sinc4+sinc1+pf1 filter_type David Lechner (TI)
` (3 subsequent siblings)
10 siblings, 0 replies; 15+ messages in thread
From: David Lechner (TI) @ 2026-09-10 21:13 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 support for filter_type, oversampling_ratio and sampling_frequency
attributes to the ti-ads112c14 driver.
On these chips, these three controls are interdependent and the
SPEED_MODE register value has a different meaning depending on the
filter type, which makes the interactions a bit complex. As such, the
expectation is that the user will set the filter type first, then
depending on the filter type, either set the oversampling ratio or the
sampling frequency and finally the other of these two.
Signed-off-by: David Lechner (TI) <dlechner@baylibre.com>
---
Note: I intend to sumbit a documentation patch later that explains the
"right way" to set these three attributes since it is a bit odd due to
the conditional interdependencies.
---
drivers/iio/adc/ti-ads112c14.c | 496 +++++++++++++++++++++++++++++++++++++++--
1 file changed, 483 insertions(+), 13 deletions(-)
diff --git a/drivers/iio/adc/ti-ads112c14.c b/drivers/iio/adc/ti-ads112c14.c
index 0e4584b4675e..ee4992b9e029 100644
--- a/drivers/iio/adc/ti-ads112c14.c
+++ b/drivers/iio/adc/ti-ads112c14.c
@@ -94,6 +94,14 @@
#define ADS112C14_DATA_RATE_CFG_DELAY GENMASK(7, 4)
#define ADS112C14_DATA_RATE_CFG_GC_EN BIT(3)
#define ADS112C14_DATA_RATE_CFG_FLTR_OSR GENMASK(2, 0)
+#define ADS112C14_DATA_RATE_CFG_FLTR_OSR_16 0
+#define ADS112C14_DATA_RATE_CFG_FLTR_OSR_32 1
+#define ADS112C14_DATA_RATE_CFG_FLTR_OSR_128 2
+#define ADS112C14_DATA_RATE_CFG_FLTR_OSR_256 3
+#define ADS112C14_DATA_RATE_CFG_FLTR_OSR_512 4
+#define ADS112C14_DATA_RATE_CFG_FLTR_OSR_1024 5
+#define ADS112C14_DATA_RATE_CFG_FLTR_OSR_25SPS 6
+#define ADS112C14_DATA_RATE_CFG_FLTR_OSR_20SPS 7
#define ADS112C14_REG_MUX_CFG 0x07
#define ADS112C14_MUX_CFG_AINP GENMASK(7, 4)
@@ -183,6 +191,43 @@ static const u32 ads112c14_pga_gains_x10[] = {
#define ADS112C14_INTERNAL_CLK_Hz 4096000
+/* Index corresponds to first 2 ADS112C14_DATA_RATE_CFG_FLTR_OSR values. */
+static const int ads112c14_sinc4_osr_available[] = {
+ 16, 32
+};
+
+/* Index corresponds to next 4 ADS112C14_DATA_RATE_CFG_FLTR_OSR values. */
+static const int ads112c14_sinc4_sinc1_osr_available[] = {
+ 128, 256, 512, 1024
+};
+
+/* Index corresponds to ADS112C14_DEVICE_CFG_SPEED_MODE value. */
+static const int ads112c14_sinc4_sinc1_pf1_20sps_osr_available[] = {
+ 1600, 12800, 25600, 51200
+};
+
+/* Index corresponds to ADS112C14_DEVICE_CFG_SPEED_MODE value. */
+static const int ads112c14_sinc4_sinc1_pf1_25sps_osr_available[] = {
+ 1280, 10240, 20480, 40960
+};
+
+/* Index corresponds to ADS112C14_DEVICE_CFG_SPEED_MODE value. */
+static const int ads112c14_fmod_div[] = {
+ 128, 16, 8, 4
+};
+
+enum ads112c14_filter_type {
+ ADS112C14_FILTER_TYPE_SINC4,
+ ADS112C14_FILTER_TYPE_SINC4_SINC1,
+ ADS112C14_FILTER_TYPE_SINC4_SINC1_PF1,
+};
+
+static const char * const ads112c14_filter_type_names[] = {
+ [ADS112C14_FILTER_TYPE_SINC4] = "sinc4",
+ [ADS112C14_FILTER_TYPE_SINC4_SINC1] = "sinc4+sinc1",
+ [ADS112C14_FILTER_TYPE_SINC4_SINC1_PF1] = "sinc4+sinc1+pf1",
+};
+
#define ADS112C14_I2C_CRC8_POLYNOMIAL 0x07
DECLARE_CRC8_TABLE(ads112c14_crc8_table);
@@ -204,6 +249,8 @@ enum {
ADS112C14_SYS_MON_CHANNEL_SHORT,
};
+static const struct iio_chan_spec_ext_info ads112c14_ext_info[];
+
static const struct iio_chan_spec ads112c14_sys_mon_channels[] = {
{
.type = IIO_TEMP,
@@ -212,7 +259,12 @@ static const struct iio_chan_spec ads112c14_sys_mon_channels[] = {
.address = 2,
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW)
| BIT(IIO_CHAN_INFO_SCALE)
- | BIT(IIO_CHAN_INFO_OFFSET),
+ | BIT(IIO_CHAN_INFO_OFFSET)
+ | BIT(IIO_CHAN_INFO_SAMP_FREQ)
+ | BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO),
+ .info_mask_separate_available = BIT(IIO_CHAN_INFO_SAMP_FREQ)
+ | BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO),
+ .ext_info = ads112c14_ext_info,
},
{
.type = IIO_VOLTAGE,
@@ -220,7 +272,12 @@ static const struct iio_chan_spec ads112c14_sys_mon_channels[] = {
.channel = ADS112C14_SYS_MON_CHANNEL_EXT_REF,
.address = 3,
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW)
- | BIT(IIO_CHAN_INFO_SCALE),
+ | BIT(IIO_CHAN_INFO_SCALE)
+ | BIT(IIO_CHAN_INFO_SAMP_FREQ)
+ | BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO),
+ .info_mask_separate_available = BIT(IIO_CHAN_INFO_SAMP_FREQ)
+ | BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO),
+ .ext_info = ads112c14_ext_info,
},
{
.type = IIO_VOLTAGE,
@@ -228,7 +285,12 @@ static const struct iio_chan_spec ads112c14_sys_mon_channels[] = {
.channel = ADS112C14_SYS_MON_CHANNEL_AVDD,
.address = 4,
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW)
- | BIT(IIO_CHAN_INFO_SCALE),
+ | BIT(IIO_CHAN_INFO_SCALE)
+ | BIT(IIO_CHAN_INFO_SAMP_FREQ)
+ | BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO),
+ .info_mask_separate_available = BIT(IIO_CHAN_INFO_SAMP_FREQ)
+ | BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO),
+ .ext_info = ads112c14_ext_info,
},
{
.type = IIO_VOLTAGE,
@@ -236,7 +298,12 @@ static const struct iio_chan_spec ads112c14_sys_mon_channels[] = {
.channel = ADS112C14_SYS_MON_CHANNEL_DVDD,
.address = 5,
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW)
- | BIT(IIO_CHAN_INFO_SCALE),
+ | BIT(IIO_CHAN_INFO_SCALE)
+ | BIT(IIO_CHAN_INFO_SAMP_FREQ)
+ | BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO),
+ .info_mask_separate_available = BIT(IIO_CHAN_INFO_SAMP_FREQ)
+ | BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO),
+ .ext_info = ads112c14_ext_info,
},
{
.type = IIO_VOLTAGE,
@@ -246,8 +313,13 @@ static const struct iio_chan_spec ads112c14_sys_mon_channels[] = {
.differential = 1,
.address = 1,
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW)
- | BIT(IIO_CHAN_INFO_SCALE),
- .info_mask_separate_available = BIT(IIO_CHAN_INFO_SCALE),
+ | BIT(IIO_CHAN_INFO_SCALE)
+ | BIT(IIO_CHAN_INFO_SAMP_FREQ)
+ | BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO),
+ .info_mask_separate_available = BIT(IIO_CHAN_INFO_SCALE)
+ | BIT(IIO_CHAN_INFO_SAMP_FREQ)
+ | BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO),
+ .ext_info = ads112c14_ext_info,
},
};
@@ -267,6 +339,11 @@ struct ads112c14_measurement {
int scale_available[ARRAY_SIZE(ads112c14_pga_gains_x10)][2];
};
+struct ads112c14_channel_state {
+ u8 speed_mode;
+ u8 filter_osr;
+};
+
struct ads112c14_data {
const struct ads112c14_chip_info *chip_info;
struct regmap *regmap;
@@ -284,9 +361,13 @@ struct ads112c14_data {
bool refn_is_gnd;
u32 ext_ref_ohms;
struct ads112c14_measurement *measurements;
+ struct ads112c14_channel_state *channel_states;
u32 num_measurements;
u8 sys_mon_chan_short_gain_val;
int sys_mon_chan_short_scale_available[ARRAY_SIZE(ads112c14_pga_gains_x10)][2];
+ int sinc4_sample_rate_available[ARRAY_SIZE(ads112c14_sinc4_osr_available)][ARRAY_SIZE(ads112c14_fmod_div)][2];
+ int sinc4_sinc1_sample_rate_available[ARRAY_SIZE(ads112c14_sinc4_sinc1_osr_available)][ARRAY_SIZE(ads112c14_fmod_div)][2];
+ int sinc4_sinc1_pf1_sample_rate_available[2][2];
IIO_DECLARE_BUFFER_WITH_TS(__be32, scan, ADS112C14_MAX_MEASUREMENT_CHANNELS +
ARRAY_SIZE(ads112c14_sys_mon_channels));
};
@@ -472,14 +553,46 @@ static const struct regmap_config ads112c14_regmap_config = {
.cache_type = REGCACHE_MAPLE,
};
+static int ads112c14_get_osr(struct ads112c14_channel_state *channel_state)
+{
+ u8 i;
+
+ switch (channel_state->filter_osr) {
+ case ADS112C14_DATA_RATE_CFG_FLTR_OSR_16...ADS112C14_DATA_RATE_CFG_FLTR_OSR_32:
+ i = channel_state->filter_osr;
+ return ads112c14_sinc4_osr_available[i];
+ case ADS112C14_DATA_RATE_CFG_FLTR_OSR_128...ADS112C14_DATA_RATE_CFG_FLTR_OSR_1024:
+ i = channel_state->filter_osr - ADS112C14_DATA_RATE_CFG_FLTR_OSR_128;
+ return ads112c14_sinc4_sinc1_osr_available[i];
+ case ADS112C14_DATA_RATE_CFG_FLTR_OSR_25SPS:
+ i = channel_state->speed_mode;
+ return ads112c14_sinc4_sinc1_pf1_25sps_osr_available[i];
+ case ADS112C14_DATA_RATE_CFG_FLTR_OSR_20SPS:
+ i = channel_state->speed_mode;
+ return ads112c14_sinc4_sinc1_pf1_20sps_osr_available[i];
+ default:
+ return -EINVAL;
+ }
+}
+
static int ads112c14_prepare_measurement_channel(struct ads112c14_data *data,
const struct iio_chan_spec *chan,
bool en_burnout)
{
struct ads112c14_measurement *measurement = &data->measurements[chan->scan_index];
+ struct ads112c14_channel_state *channel_state;
u32 refp_buf_en, refn_buf_en, ref_val, ref_sel;
int ret;
+ channel_state = &data->channel_states[chan->scan_index];
+
+ ret = regmap_update_bits(data->regmap, ADS112C14_REG_DEVICE_CFG,
+ ADS112C14_DEVICE_CFG_SPEED_MODE,
+ FIELD_PREP(ADS112C14_DEVICE_CFG_SPEED_MODE,
+ channel_state->speed_mode));
+ if (ret)
+ return ret;
+
ret = regmap_update_bits(data->regmap, ADS112C14_REG_MUX_CFG,
ADS112C14_MUX_CFG_AINP | ADS112C14_MUX_CFG_AINN,
FIELD_PREP(ADS112C14_MUX_CFG_AINP, chan->channel) |
@@ -526,10 +639,13 @@ static int ads112c14_prepare_measurement_channel(struct ads112c14_data *data,
return ret;
ret = regmap_update_bits(data->regmap, ADS112C14_REG_DATA_RATE_CFG,
- ADS112C14_DATA_RATE_CFG_GC_EN,
+ ADS112C14_DATA_RATE_CFG_GC_EN |
+ ADS112C14_DATA_RATE_CFG_FLTR_OSR,
FIELD_PREP(ADS112C14_DATA_RATE_CFG_GC_EN,
(measurement->global_chop &&
- !en_burnout) ? 1 : 0));
+ !en_burnout) ? 1 : 0) |
+ FIELD_PREP(ADS112C14_DATA_RATE_CFG_FLTR_OSR,
+ channel_state->filter_osr));
if (ret)
return ret;
@@ -572,9 +688,12 @@ static int ads112c14_prepare_measurement_channel(struct ads112c14_data *data,
static int ads112c14_prepare_sys_mon_channel(struct ads112c14_data *data,
const struct iio_chan_spec *chan)
{
+ struct ads112c14_channel_state *channel_state;
u32 gain_val;
int ret;
+ channel_state = &data->channel_states[chan->scan_index];
+
/*
* NB: IDAC registers are left as-is in case they are generating current
* needed for the external reference measurement.
@@ -601,6 +720,22 @@ static int ads112c14_prepare_sys_mon_channel(struct ads112c14_data *data,
if (ret)
return ret;
+ ret = regmap_update_bits(data->regmap, ADS112C14_REG_DEVICE_CFG,
+ ADS112C14_DEVICE_CFG_SPEED_MODE,
+ FIELD_PREP(ADS112C14_DEVICE_CFG_SPEED_MODE,
+ channel_state->speed_mode));
+ if (ret)
+ return ret;
+
+ ret = regmap_update_bits(data->regmap, ADS112C14_REG_DATA_RATE_CFG,
+ ADS112C14_DATA_RATE_CFG_GC_EN |
+ ADS112C14_DATA_RATE_CFG_FLTR_OSR,
+ FIELD_PREP(ADS112C14_DATA_RATE_CFG_GC_EN, 0) |
+ FIELD_PREP(ADS112C14_DATA_RATE_CFG_FLTR_OSR,
+ channel_state->filter_osr));
+ if (ret)
+ return ret;
+
/*
* REVISIT: if we implement regulator support for the REFOUT pin, we
* might need to make this voltage match what is required by that. In
@@ -835,6 +970,45 @@ static int ads112c14_read_raw(struct iio_dev *indio_dev,
*/
*val = div_s64((s64)(25 * 405 - 119500) * BIT(fsr_bits), vref_uV);
return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SAMP_FREQ: {
+ struct ads112c14_channel_state *channel_state;
+ const int (*available)[2];
+ u8 i, j;
+
+ guard(mutex)(&data->lock);
+
+ channel_state = &data->channel_states[chan->scan_index];
+
+ switch (channel_state->filter_osr) {
+ case ADS112C14_DATA_RATE_CFG_FLTR_OSR_16...ADS112C14_DATA_RATE_CFG_FLTR_OSR_32:
+ j = channel_state->filter_osr;
+ available = data->sinc4_sample_rate_available[j];
+ i = channel_state->speed_mode;
+ break;
+ case ADS112C14_DATA_RATE_CFG_FLTR_OSR_128...ADS112C14_DATA_RATE_CFG_FLTR_OSR_1024:
+ j = channel_state->filter_osr - ADS112C14_DATA_RATE_CFG_FLTR_OSR_128;
+ available = data->sinc4_sinc1_sample_rate_available[j];
+ i = channel_state->speed_mode;
+ break;
+ case ADS112C14_DATA_RATE_CFG_FLTR_OSR_25SPS:
+ case ADS112C14_DATA_RATE_CFG_FLTR_OSR_20SPS:
+ available = data->sinc4_sinc1_pf1_sample_rate_available;
+ i = channel_state->filter_osr - ADS112C14_DATA_RATE_CFG_FLTR_OSR_25SPS;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ *val = available[i][0];
+ *val2 = available[i][1];
+ return IIO_VAL_INT_PLUS_MICRO;
+ }
+ case IIO_CHAN_INFO_OVERSAMPLING_RATIO: {
+ guard(mutex)(&data->lock);
+
+ *val = ads112c14_get_osr(&data->channel_states[chan->scan_index]);
+ return IIO_VAL_INT;
+ }
default:
return -EINVAL;
}
@@ -845,6 +1019,9 @@ static int ads112c14_read_avail(struct iio_dev *indio_dev,
int *type, int *length, long mask)
{
struct ads112c14_data *data = iio_priv(indio_dev);
+ struct ads112c14_channel_state *channel_state;
+
+ channel_state = &data->channel_states[chan->scan_index];
switch (mask) {
case IIO_CHAN_INFO_SCALE:
@@ -870,6 +1047,58 @@ static int ads112c14_read_avail(struct iio_dev *indio_dev,
}
return -EINVAL;
+
+ case IIO_CHAN_INFO_SAMP_FREQ: {
+ guard(mutex)(&data->lock);
+
+ switch (channel_state->filter_osr) {
+ case ADS112C14_DATA_RATE_CFG_FLTR_OSR_16...ADS112C14_DATA_RATE_CFG_FLTR_OSR_32:
+ *vals = (const int *)data->sinc4_sample_rate_available[channel_state->filter_osr];
+ *length = 2 * ARRAY_SIZE(data->sinc4_sample_rate_available[0]);
+ *type = IIO_VAL_INT_PLUS_MICRO;
+ return IIO_AVAIL_LIST;
+ case ADS112C14_DATA_RATE_CFG_FLTR_OSR_128...ADS112C14_DATA_RATE_CFG_FLTR_OSR_1024:
+ *vals = (const int *)data->sinc4_sinc1_sample_rate_available[channel_state->filter_osr - ADS112C14_DATA_RATE_CFG_FLTR_OSR_128];
+ *length = 2 * ARRAY_SIZE(data->sinc4_sinc1_sample_rate_available[0]);
+ *type = IIO_VAL_INT_PLUS_MICRO;
+ return IIO_AVAIL_LIST;
+ case ADS112C14_DATA_RATE_CFG_FLTR_OSR_25SPS...ADS112C14_DATA_RATE_CFG_FLTR_OSR_20SPS:
+ *vals = (const int *)data->sinc4_sinc1_pf1_sample_rate_available;
+ *length = 2 * ARRAY_SIZE(data->sinc4_sinc1_pf1_sample_rate_available);
+ *type = IIO_VAL_INT_PLUS_MICRO;
+ return IIO_AVAIL_LIST;
+ default:
+ return -EINVAL;
+ }
+ }
+ case IIO_CHAN_INFO_OVERSAMPLING_RATIO: {
+ guard(mutex)(&data->lock);
+
+ switch (channel_state->filter_osr) {
+ case ADS112C14_DATA_RATE_CFG_FLTR_OSR_16...ADS112C14_DATA_RATE_CFG_FLTR_OSR_32:
+ *vals = ads112c14_sinc4_osr_available;
+ *length = ARRAY_SIZE(ads112c14_sinc4_osr_available);
+ *type = IIO_VAL_INT;
+ return IIO_AVAIL_LIST;
+ case ADS112C14_DATA_RATE_CFG_FLTR_OSR_128...ADS112C14_DATA_RATE_CFG_FLTR_OSR_1024:
+ *vals = ads112c14_sinc4_sinc1_osr_available;
+ *length = ARRAY_SIZE(ads112c14_sinc4_sinc1_osr_available);
+ *type = IIO_VAL_INT;
+ return IIO_AVAIL_LIST;
+ case ADS112C14_DATA_RATE_CFG_FLTR_OSR_25SPS:
+ *vals = ads112c14_sinc4_sinc1_pf1_25sps_osr_available;
+ *length = ARRAY_SIZE(ads112c14_sinc4_sinc1_pf1_25sps_osr_available);
+ *type = IIO_VAL_INT;
+ return IIO_AVAIL_LIST;
+ case ADS112C14_DATA_RATE_CFG_FLTR_OSR_20SPS:
+ *vals = ads112c14_sinc4_sinc1_pf1_20sps_osr_available;
+ *length = ARRAY_SIZE(ads112c14_sinc4_sinc1_pf1_20sps_osr_available);
+ *type = IIO_VAL_INT;
+ return IIO_AVAIL_LIST;
+ default:
+ return -EINVAL;
+ }
+ }
default:
return -EINVAL;
}
@@ -914,6 +1143,99 @@ static int ads112c14_write_raw(struct iio_dev *indio_dev,
return -EINVAL;
}
+ case IIO_CHAN_INFO_SAMP_FREQ: {
+ struct ads112c14_channel_state *channel_state;
+ const int (*available)[2];
+
+ guard(mutex)(&data->lock);
+
+ channel_state = &data->channel_states[chan->scan_index];
+
+ switch (channel_state->filter_osr) {
+ case ADS112C14_DATA_RATE_CFG_FLTR_OSR_16...ADS112C14_DATA_RATE_CFG_FLTR_OSR_1024:
+ if (channel_state->filter_osr < ADS112C14_DATA_RATE_CFG_FLTR_OSR_128) {
+ u8 idx = channel_state->filter_osr;
+
+ available = data->sinc4_sample_rate_available[idx];
+ } else {
+ u8 idx = channel_state->filter_osr - ADS112C14_DATA_RATE_CFG_FLTR_OSR_128;
+
+ available = data->sinc4_sinc1_sample_rate_available[idx];
+ }
+
+ for (unsigned int i = 0; i < ARRAY_SIZE(ads112c14_fmod_div); i++) {
+ if (val == available[i][0] && val2 == available[i][1]) {
+ channel_state->speed_mode = i;
+ return 0;
+ }
+ }
+
+ return -EINVAL;
+ case ADS112C14_DATA_RATE_CFG_FLTR_OSR_25SPS:
+ case ADS112C14_DATA_RATE_CFG_FLTR_OSR_20SPS: {
+ available = data->sinc4_sinc1_pf1_sample_rate_available;
+
+ for (unsigned int i = 0; i < ARRAY_SIZE(data->sinc4_sinc1_pf1_sample_rate_available); i++) {
+ if (val == available[i][0] && val2 == available[i][1]) {
+ channel_state->filter_osr = i + ADS112C14_DATA_RATE_CFG_FLTR_OSR_25SPS;
+ return 0;
+ }
+ }
+
+ return -EINVAL;
+ }
+ default:
+ return -EINVAL;
+ }
+ }
+ case IIO_CHAN_INFO_OVERSAMPLING_RATIO: {
+ struct ads112c14_channel_state *channel_state;
+
+ guard(mutex)(&data->lock);
+
+ channel_state = &data->channel_states[chan->scan_index];
+
+ switch (channel_state->filter_osr) {
+ case ADS112C14_DATA_RATE_CFG_FLTR_OSR_16...ADS112C14_DATA_RATE_CFG_FLTR_OSR_32:
+ for (unsigned int i = 0; i < ARRAY_SIZE(ads112c14_sinc4_osr_available); i++) {
+ if (val == ads112c14_sinc4_osr_available[i]) {
+ channel_state->filter_osr = i;
+ return 0;
+ }
+ }
+
+ return -EINVAL;
+ case ADS112C14_DATA_RATE_CFG_FLTR_OSR_128...ADS112C14_DATA_RATE_CFG_FLTR_OSR_1024:
+ for (unsigned int i = 0; i < ARRAY_SIZE(ads112c14_sinc4_sinc1_osr_available); i++) {
+ if (val == ads112c14_sinc4_sinc1_osr_available[i]) {
+ channel_state->filter_osr = i + ADS112C14_DATA_RATE_CFG_FLTR_OSR_128;
+ return 0;
+ }
+ }
+
+ return -EINVAL;
+ case ADS112C14_DATA_RATE_CFG_FLTR_OSR_25SPS:
+ for (unsigned int i = 0; i < ARRAY_SIZE(ads112c14_sinc4_sinc1_pf1_25sps_osr_available); i++) {
+ if (val == ads112c14_sinc4_sinc1_pf1_25sps_osr_available[i]) {
+ channel_state->speed_mode = i;
+ return 0;
+ }
+ }
+
+ return -EINVAL;
+ case ADS112C14_DATA_RATE_CFG_FLTR_OSR_20SPS:
+ for (unsigned int i = 0; i < ARRAY_SIZE(ads112c14_sinc4_sinc1_pf1_20sps_osr_available); i++) {
+ if (val == ads112c14_sinc4_sinc1_pf1_20sps_osr_available[i]) {
+ channel_state->speed_mode = i;
+ return 0;
+ }
+ }
+
+ return -EINVAL;
+ default:
+ return -EINVAL;
+ }
+ }
default:
return -EINVAL;
}
@@ -1202,11 +1524,98 @@ static ssize_t ads112c14_read_burnout_raw(struct iio_dev *indio_dev,
return sysfs_emit(buf, "%d\n", val);
}
+static int ads112c14_get_filter_type_from_state(struct ads112c14_channel_state *channel_state)
+{
+ switch (channel_state->filter_osr) {
+ case ADS112C14_DATA_RATE_CFG_FLTR_OSR_16...ADS112C14_DATA_RATE_CFG_FLTR_OSR_32:
+ return ADS112C14_FILTER_TYPE_SINC4;
+ case ADS112C14_DATA_RATE_CFG_FLTR_OSR_128...ADS112C14_DATA_RATE_CFG_FLTR_OSR_1024:
+ return ADS112C14_FILTER_TYPE_SINC4_SINC1;
+ case ADS112C14_DATA_RATE_CFG_FLTR_OSR_25SPS...ADS112C14_DATA_RATE_CFG_FLTR_OSR_20SPS:
+ return ADS112C14_FILTER_TYPE_SINC4_SINC1_PF1;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int ads112c14_set_filter_type(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ unsigned int val)
+{
+ struct ads112c14_data *data = iio_priv(indio_dev);
+ struct ads112c14_channel_state *channel_state;
+ int ret;
+
+ IIO_DEV_ACQUIRE_DIRECT_MODE(indio_dev, claim);
+ if (IIO_DEV_ACQUIRE_FAILED(claim))
+ return -EBUSY;
+
+ guard(mutex)(&data->lock);
+
+ channel_state = &data->channel_states[chan->scan_index];
+
+ ret = ads112c14_get_filter_type_from_state(channel_state);
+ if (ret < 0)
+ return ret;
+
+ /*
+ * channel_state->filter_osr affects multiple attributes, so don't modify
+ * it if the filter type is already set to the requested value.
+ */
+ if (ret == val)
+ return 0;
+
+ /* Otherwise, pick an arbitrary default for each type. */
+ switch (val) {
+ case ADS112C14_FILTER_TYPE_SINC4:
+ channel_state->filter_osr = ADS112C14_DATA_RATE_CFG_FLTR_OSR_16;
+ break;
+ case ADS112C14_FILTER_TYPE_SINC4_SINC1:
+ channel_state->filter_osr = ADS112C14_DATA_RATE_CFG_FLTR_OSR_128;
+ break;
+ case ADS112C14_FILTER_TYPE_SINC4_SINC1_PF1:
+ channel_state->filter_osr = ADS112C14_DATA_RATE_CFG_FLTR_OSR_25SPS;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int ads112c14_get_filter_type(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan)
+{
+ struct ads112c14_data *data = iio_priv(indio_dev);
+ struct ads112c14_channel_state *channel_state;
+
+ guard(mutex)(&data->lock);
+
+ channel_state = &data->channel_states[chan->scan_index];
+
+ return ads112c14_get_filter_type_from_state(channel_state);
+}
+
+static const struct iio_enum ads112c14_filter_type_enum = {
+ .items = ads112c14_filter_type_names,
+ .num_items = ARRAY_SIZE(ads112c14_filter_type_names),
+ .set = ads112c14_set_filter_type,
+ .get = ads112c14_get_filter_type,
+};
+
+static const struct iio_chan_spec_ext_info ads112c14_ext_info[] = {
+ IIO_ENUM("filter_type", IIO_SEPARATE, &ads112c14_filter_type_enum),
+ IIO_ENUM_AVAILABLE("filter_type", IIO_SEPARATE, &ads112c14_filter_type_enum),
+ { }
+};
+
static const struct iio_chan_spec_ext_info ads112c14_ext_info_burnout[] = {
{
.name = "burnoutraw",
.read = ads112c14_read_burnout_raw,
},
+ IIO_ENUM("filter_type", IIO_SEPARATE, &ads112c14_filter_type_enum),
+ IIO_ENUM_AVAILABLE("filter_type", IIO_SEPARATE, &ads112c14_filter_type_enum),
{ }
};
@@ -1232,7 +1641,7 @@ static int ads112c14_parse_channels(struct iio_dev *indio_dev,
struct ads112c14_data *data = iio_priv(indio_dev);
struct device *dev = indio_dev->dev.parent;
struct iio_chan_spec *channels;
- u32 num_child_nodes, i, pair[2];
+ u32 num_child_nodes, num_data_chans, i, pair[2];
int ret;
*need_avdd_ref = false;
@@ -1245,8 +1654,15 @@ static int ads112c14_parse_channels(struct iio_dev *indio_dev,
if (!data->measurements)
return -ENOMEM;
- channels = devm_kcalloc(dev, num_child_nodes +
- ARRAY_SIZE(ads112c14_sys_mon_channels) + 1,
+ num_data_chans = num_child_nodes + ARRAY_SIZE(ads112c14_sys_mon_channels);
+
+ data->channel_states = devm_kcalloc(dev, num_data_chans,
+ sizeof(*data->channel_states),
+ GFP_KERNEL);
+ if (!data->channel_states)
+ return -ENOMEM;
+
+ channels = devm_kcalloc(dev, num_data_chans + 1,
sizeof(*channels), GFP_KERNEL);
if (!channels)
return -ENOMEM;
@@ -1259,7 +1675,9 @@ static int ads112c14_parse_channels(struct iio_dev *indio_dev,
spec->indexed = 1;
spec->scan_index = i;
+ spec->ext_info = ads112c14_ext_info;
measurement->gain_val = 1;
+ data->channel_states[i].filter_osr = ADS112C14_DATA_RATE_CFG_FLTR_OSR_16;
if (fwnode_property_present(child, "label")) {
ret = fwnode_property_read_string(child, "label", &measurement->label);
@@ -1424,8 +1842,13 @@ static int ads112c14_parse_channels(struct iio_dev *indio_dev,
if (measurement->vref_source == ADS112C14_VREF_SOURCE_EXTERNAL)
*need_ext_ref = true;
- spec->info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE);
- spec->info_mask_separate_available = BIT(IIO_CHAN_INFO_SCALE);
+ spec->info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_SCALE) |
+ BIT(IIO_CHAN_INFO_SAMP_FREQ) |
+ BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO);
+ spec->info_mask_separate_available = BIT(IIO_CHAN_INFO_SCALE) |
+ BIT(IIO_CHAN_INFO_SAMP_FREQ) |
+ BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO);
/*
* If reference source is resistor rather than voltage supply,
@@ -1459,6 +1882,10 @@ static int ads112c14_parse_channels(struct iio_dev *indio_dev,
for (u32 j = 0; j < ARRAY_SIZE(ads112c14_sys_mon_channels); j++) {
struct iio_chan_spec *spec = &channels[i];
+ struct ads112c14_channel_state *channel_state;
+
+ channel_state = &data->channel_states[i];
+ channel_state->filter_osr = ADS112C14_DATA_RATE_CFG_FLTR_OSR_16;
/* Update the template that was already copied with dynamic values. */
spec->scan_index = i;
@@ -1497,6 +1924,48 @@ static void ads112c14_populate_scale_available(int (*scale_avail)[2],
}
}
+static void ads112c14_populate_odr_tables(struct ads112c14_data *data)
+{
+ int *available;
+ u32 osr, fmod_Hz;
+ u64 odr_uHz;
+ u32 rem;
+
+ for (unsigned int i = 0; i < ARRAY_SIZE(ads112c14_sinc4_osr_available); i++) {
+ osr = ads112c14_sinc4_osr_available[i];
+
+ for (unsigned int j = 0; j < ARRAY_SIZE(ads112c14_fmod_div); j++) {
+ fmod_Hz = data->fclk_Hz / ads112c14_fmod_div[j];
+ odr_uHz = div_u64((u64)fmod_Hz * MICRO, osr);
+ available = data->sinc4_sample_rate_available[i][j];
+ available[0] = div_u64_rem(odr_uHz, MICRO, &rem);
+ available[1] = rem;
+ }
+ }
+
+ for (unsigned int i = 0; i < ARRAY_SIZE(ads112c14_sinc4_sinc1_osr_available); i++) {
+ osr = ads112c14_sinc4_sinc1_osr_available[i];
+
+ for (unsigned int j = 0; j < ARRAY_SIZE(ads112c14_fmod_div); j++) {
+ fmod_Hz = data->fclk_Hz / ads112c14_fmod_div[j];
+ odr_uHz = div_u64((u64)fmod_Hz * MICRO, osr);
+ available = data->sinc4_sinc1_sample_rate_available[i][j];
+ available[0] = div_u64_rem(odr_uHz, MICRO, &rem);
+ available[1] = rem;
+ }
+ }
+
+ odr_uHz = div_u64((u64)25 * data->fclk_Hz * MICRO, ADS112C14_INTERNAL_CLK_Hz);
+ available = data->sinc4_sinc1_pf1_sample_rate_available[0];
+ available[0] = div_u64_rem(odr_uHz, MICRO, &rem);
+ available[1] = rem;
+
+ odr_uHz = div_u64((u64)20 * data->fclk_Hz * MICRO, ADS112C14_INTERNAL_CLK_Hz);
+ available = data->sinc4_sinc1_pf1_sample_rate_available[1];
+ available[0] = div_u64_rem(odr_uHz, MICRO, &rem);
+ available[1] = rem;
+}
+
static void ads112c14_populate_tables(struct ads112c14_data *data)
{
u32 full_scale, fsr_bits;
@@ -1534,6 +2003,7 @@ static void ads112c14_populate_tables(struct ads112c14_data *data)
ads112c14_populate_scale_available(data->sys_mon_chan_short_scale_available,
full_scale, fsr_bits);
+ ads112c14_populate_odr_tables(data);
}
static int ads112c14_probe(struct i2c_client *client)
--
2.43.0
^ permalink raw reply [flat|nested] 15+ messages in thread
* [PATCH v3 08/10] iio: ABI: add sinc4+sinc1+pf1 filter_type
2026-09-10 21:12 [PATCH v3 00/10] iio: adc: ti-ads112c14: add filter support David Lechner (TI)
` (6 preceding siblings ...)
2026-09-10 21:13 ` [PATCH v3 07/10] iio: adc: ti-ads112c14: add filter support David Lechner (TI)
@ 2026-09-10 21:13 ` David Lechner (TI)
2026-09-10 21:13 ` [PATCH v3 09/10] iio: adc: ti-ads112c14: add settlingtime attribute David Lechner (TI)
` (2 subsequent siblings)
10 siblings, 0 replies; 15+ messages in thread
From: David Lechner (TI) @ 2026-09-10 21:13 UTC (permalink / raw)
To: Jonathan Cameron, Nuno Sá, Andy Shevchenko
Cc: Chris Hall, Patrick Edwards, Kurt Borja, linux-iio, linux-kernel,
David Lechner (TI)
Document "sinc4+sinc1+pf1" as a valid value for the filter_type
attributes (used by the ti-ads112c14 driver).
Signed-off-by: David Lechner (TI) <dlechner@baylibre.com>
---
Documentation/ABI/testing/sysfs-bus-iio | 1 +
1 file changed, 1 insertion(+)
diff --git a/Documentation/ABI/testing/sysfs-bus-iio b/Documentation/ABI/testing/sysfs-bus-iio
index a4f5595722ad..a8e1cb8e7490 100644
--- a/Documentation/ABI/testing/sysfs-bus-iio
+++ b/Documentation/ABI/testing/sysfs-bus-iio
@@ -2380,6 +2380,7 @@ Description:
* "sinc4+lp" - Sinc4 + Low Pass Filter.
* "sinc4+sinc1" - Sinc4 + averaging by 8. Low 1st conversion
time.
+ * "sinc4+sinc1+pf1" - Sinc4 + Sinc1 + device specific Post Filter 1.
* "sinc4+rej60" - Sinc4 + 60Hz rejection.
* "sinc5" - The digital sinc5 filter. Excellent noise
performance
--
2.43.0
^ permalink raw reply [flat|nested] 15+ messages in thread
* [PATCH v3 09/10] iio: adc: ti-ads112c14: add settlingtime attribute
2026-09-10 21:12 [PATCH v3 00/10] iio: adc: ti-ads112c14: add filter support David Lechner (TI)
` (7 preceding siblings ...)
2026-09-10 21:13 ` [PATCH v3 08/10] iio: ABI: add sinc4+sinc1+pf1 filter_type David Lechner (TI)
@ 2026-09-10 21:13 ` David Lechner (TI)
2026-09-13 20:11 ` Jonathan Cameron
2026-09-10 21:13 ` [PATCH v3 10/10] iio: ABI: add settlingtime attributes David Lechner (TI)
2026-09-13 20:17 ` [PATCH v3 00/10] iio: adc: ti-ads112c14: add filter support Jonathan Cameron
10 siblings, 1 reply; 15+ messages in thread
From: David Lechner (TI) @ 2026-09-10 21:13 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 per-channel sysfs attributes for settlingtime and
settlingtime_available. These allow adjusting the total settling time
for each channel. The value consists of a fixed t_latency time (based
on the selected filter_type, oversampling_ratio and sampling_frequency)
plus a user-configurable t_delay that determines the value to write to
the DELAY field in the registers.
The allowable values are non-linear integer multiples, so the step size
is just the smallest step size. Writing the attribute will match the
closest matching value for the DELAY field with a time equal to or
greater than the requested settling time (unless the requested time is
larger than the maximum allowable settling time).
Signed-off-by: David Lechner (TI) <dlechner@baylibre.com>
---
drivers/iio/adc/ti-ads112c14.c | 336 ++++++++++++++++++++++++++++++++++++++++-
1 file changed, 330 insertions(+), 6 deletions(-)
diff --git a/drivers/iio/adc/ti-ads112c14.c b/drivers/iio/adc/ti-ads112c14.c
index ee4992b9e029..ec598f82cf7f 100644
--- a/drivers/iio/adc/ti-ads112c14.c
+++ b/drivers/iio/adc/ti-ads112c14.c
@@ -216,6 +216,24 @@ static const int ads112c14_fmod_div[] = {
128, 16, 8, 4
};
+#define ADS112C14_DELAY_MAX FIELD_MAX(ADS112C14_DATA_RATE_CFG_DELAY)
+
+/* Table 7-6 latency in t_MOD for OSR [16, 32, 128, 256, 512, 1024]. */
+static const int ads112c14_sinc_latency_tmod[][ARRAY_SIZE(ads112c14_fmod_div)] = {
+ { 80, 88, 88, 104 },
+ { 144, 152, 152, 168 },
+ { 240, 248, 248, 264 },
+ { 368, 376, 376, 392 },
+ { 624, 632, 632, 648 },
+ { 1136, 1144, 1144, 1160 },
+};
+
+/* Table 7-7 latency in t_MOD for output data rates [25SPS, 20SPS]. */
+static const int ads112c14_fir_latency_tmod[][ARRAY_SIZE(ads112c14_fmod_div)] = {
+ { 1416, 10384, 20624, 41120 },
+ { 1736, 12944, 25744, 51360 },
+};
+
enum ads112c14_filter_type {
ADS112C14_FILTER_TYPE_SINC4,
ADS112C14_FILTER_TYPE_SINC4_SINC1,
@@ -342,6 +360,7 @@ struct ads112c14_measurement {
struct ads112c14_channel_state {
u8 speed_mode;
u8 filter_osr;
+ u8 delay;
};
struct ads112c14_data {
@@ -368,6 +387,8 @@ struct ads112c14_data {
int sinc4_sample_rate_available[ARRAY_SIZE(ads112c14_sinc4_osr_available)][ARRAY_SIZE(ads112c14_fmod_div)][2];
int sinc4_sinc1_sample_rate_available[ARRAY_SIZE(ads112c14_sinc4_sinc1_osr_available)][ARRAY_SIZE(ads112c14_fmod_div)][2];
int sinc4_sinc1_pf1_sample_rate_available[2][2];
+ int sinc_settling_time_range_available[ARRAY_SIZE(ads112c14_sinc_latency_tmod)][ARRAY_SIZE(ads112c14_fmod_div)][3][2];
+ int fir_settling_time_range_available[ARRAY_SIZE(ads112c14_fir_latency_tmod)][ARRAY_SIZE(ads112c14_fmod_div)][3][2];
IIO_DECLARE_BUFFER_WITH_TS(__be32, scan, ADS112C14_MAX_MEASUREMENT_CHANNELS +
ARRAY_SIZE(ads112c14_sys_mon_channels));
};
@@ -575,6 +596,210 @@ static int ads112c14_get_osr(struct ads112c14_channel_state *channel_state)
}
}
+static int ads112c14_get_fmod_Hz(struct ads112c14_data *data,
+ struct ads112c14_channel_state *channel_state)
+{
+ return data->fclk_Hz / ads112c14_fmod_div[channel_state->speed_mode];
+}
+
+static int ads112c14_delay_to_tmod(u8 delay)
+{
+ if (!delay)
+ return 0;
+
+ return BIT(delay - 1);
+}
+
+static int ads112c14_get_latency_tmod(struct ads112c14_channel_state *channel_state)
+{
+ u8 speed_mode = channel_state->speed_mode;
+ u8 filter_osr = channel_state->filter_osr;
+
+ if (speed_mode >= ARRAY_SIZE(ads112c14_fmod_div))
+ return -EINVAL;
+
+ if (filter_osr <= ADS112C14_DATA_RATE_CFG_FLTR_OSR_1024)
+ return ads112c14_sinc_latency_tmod[filter_osr][speed_mode];
+
+ if (filter_osr == ADS112C14_DATA_RATE_CFG_FLTR_OSR_25SPS)
+ return ads112c14_fir_latency_tmod[0][speed_mode];
+
+ if (filter_osr == ADS112C14_DATA_RATE_CFG_FLTR_OSR_20SPS)
+ return ads112c14_fir_latency_tmod[1][speed_mode];
+
+ return -EINVAL;
+}
+
+static int ads112c14_get_settling_time_us(struct ads112c14_data *data,
+ struct ads112c14_channel_state *channel_state,
+ u8 delay, u32 *settling_time_us)
+{
+ int fmod_Hz, latency_tmod;
+ u64 total_tmod;
+
+ fmod_Hz = ads112c14_get_fmod_Hz(data, channel_state);
+ if (fmod_Hz <= 0)
+ return -EINVAL;
+
+ latency_tmod = ads112c14_get_latency_tmod(channel_state);
+ if (latency_tmod < 0)
+ return latency_tmod;
+
+ total_tmod = latency_tmod + ads112c14_delay_to_tmod(delay);
+ *settling_time_us = div64_u64(total_tmod * USEC_PER_SEC, fmod_Hz);
+
+ return 0;
+}
+
+static int ads112c14_find_delay_for_settling_time_us(struct ads112c14_data *data,
+ struct ads112c14_channel_state *channel_state,
+ s64 settling_time_us, u8 *delay)
+{
+ u64 delay_us, delay_tmod_needed;
+ u32 fixed_latency_us;
+ int ret, fmod_Hz;
+ u8 i;
+
+ ret = ads112c14_get_settling_time_us(data, channel_state, 0, &fixed_latency_us);
+ if (ret)
+ return ret;
+
+ if (settling_time_us <= fixed_latency_us) {
+ *delay = 0;
+ return 0;
+ }
+
+ fmod_Hz = ads112c14_get_fmod_Hz(data, channel_state);
+ if (fmod_Hz <= 0)
+ return -EINVAL;
+
+ delay_us = settling_time_us - fixed_latency_us;
+ delay_tmod_needed = DIV_ROUND_UP_ULL(delay_us * fmod_Hz, USEC_PER_SEC);
+
+ for (i = 1; i < ADS112C14_DELAY_MAX; i++) {
+ if (ads112c14_delay_to_tmod(i) >= delay_tmod_needed)
+ break;
+ }
+
+ *delay = i;
+
+ return 0;
+}
+
+static ssize_t ads112c14_read_settling_time(struct iio_dev *indio_dev,
+ uintptr_t private,
+ const struct iio_chan_spec *chan,
+ char *buf)
+{
+ struct ads112c14_data *data = iio_priv(indio_dev);
+ struct ads112c14_channel_state *channel_state;
+ u32 settling_time_us;
+ int vals[2];
+ int ret;
+
+ guard(mutex)(&data->lock);
+
+ channel_state = &data->channel_states[chan->scan_index];
+
+ ret = ads112c14_get_settling_time_us(data, channel_state,
+ channel_state->delay,
+ &settling_time_us);
+ if (ret)
+ return ret;
+
+ iio_val_s64_decompose(settling_time_us, &vals[0], &vals[1]);
+
+ return iio_format_value(buf, IIO_VAL_DECIMAL64_MICRO, ARRAY_SIZE(vals), vals);
+}
+
+static ssize_t ads112c14_write_settling_time(struct iio_dev *indio_dev,
+ uintptr_t private,
+ const struct iio_chan_spec *chan,
+ const char *buf, size_t len)
+{
+ struct ads112c14_data *data = iio_priv(indio_dev);
+ struct ads112c14_channel_state *channel_state;
+ s64 settling_time_us;
+ int integer;
+ int fract;
+ u8 delay;
+ int ret;
+
+ ret = iio_str_to_fixpoint(buf, 100000, &integer, &fract);
+ if (ret)
+ return ret;
+
+ settling_time_us = integer * MICRO + fract;
+ if (settling_time_us < 0)
+ return -EINVAL;
+
+ IIO_DEV_ACQUIRE_DIRECT_MODE(indio_dev, claim);
+ if (IIO_DEV_ACQUIRE_FAILED(claim))
+ return -EBUSY;
+
+ guard(mutex)(&data->lock);
+
+ channel_state = &data->channel_states[chan->scan_index];
+
+ ret = ads112c14_find_delay_for_settling_time_us(data, channel_state,
+ settling_time_us, &delay);
+ if (ret)
+ return ret;
+
+ channel_state->delay = delay;
+
+ return len;
+}
+
+static ssize_t ads112c14_read_settling_time_available(struct iio_dev *indio_dev,
+ uintptr_t private,
+ const struct iio_chan_spec *chan,
+ char *buf)
+{
+ struct ads112c14_data *data = iio_priv(indio_dev);
+ struct ads112c14_channel_state *channel_state;
+ u8 filter_osr, speed_mode;
+ const int (*range)[2];
+ size_t len = 0;
+ int i;
+
+ guard(mutex)(&data->lock);
+
+ channel_state = &data->channel_states[chan->scan_index];
+
+ filter_osr = channel_state->filter_osr;
+ speed_mode = channel_state->speed_mode;
+
+ if (speed_mode >= ARRAY_SIZE(ads112c14_fmod_div))
+ return -EINVAL;
+
+ if (filter_osr <= ADS112C14_DATA_RATE_CFG_FLTR_OSR_1024)
+ range = data->sinc_settling_time_range_available[filter_osr][speed_mode];
+ else if (filter_osr == ADS112C14_DATA_RATE_CFG_FLTR_OSR_25SPS)
+ range = data->fir_settling_time_range_available[0][speed_mode];
+ else if (filter_osr == ADS112C14_DATA_RATE_CFG_FLTR_OSR_20SPS)
+ range = data->fir_settling_time_range_available[1][speed_mode];
+ else
+ return -EINVAL;
+
+ len += sysfs_emit_at(buf, len, "[");
+ for (i = 0; i < 3; i++) {
+ s64 range_val;
+ s32 int_val, rem;
+
+ range_val = iio_val_s64_compose(range[i][0], range[i][1]);
+ int_val = div_s64_rem(range_val, MICRO, &rem);
+
+ if (i)
+ len += sysfs_emit_at(buf, len, " ");
+
+ len += sysfs_emit_at(buf, len, "%d.%06d", int_val, rem);
+ }
+ len += sysfs_emit_at(buf, len, "]\n");
+
+ return len;
+}
+
static int ads112c14_prepare_measurement_channel(struct ads112c14_data *data,
const struct iio_chan_spec *chan,
bool en_burnout)
@@ -639,8 +864,11 @@ static int ads112c14_prepare_measurement_channel(struct ads112c14_data *data,
return ret;
ret = regmap_update_bits(data->regmap, ADS112C14_REG_DATA_RATE_CFG,
+ ADS112C14_DATA_RATE_CFG_DELAY |
ADS112C14_DATA_RATE_CFG_GC_EN |
ADS112C14_DATA_RATE_CFG_FLTR_OSR,
+ FIELD_PREP(ADS112C14_DATA_RATE_CFG_DELAY,
+ channel_state->delay) |
FIELD_PREP(ADS112C14_DATA_RATE_CFG_GC_EN,
(measurement->global_chop &&
!en_burnout) ? 1 : 0) |
@@ -728,8 +956,11 @@ static int ads112c14_prepare_sys_mon_channel(struct ads112c14_data *data,
return ret;
ret = regmap_update_bits(data->regmap, ADS112C14_REG_DATA_RATE_CFG,
+ ADS112C14_DATA_RATE_CFG_DELAY |
ADS112C14_DATA_RATE_CFG_GC_EN |
ADS112C14_DATA_RATE_CFG_FLTR_OSR,
+ FIELD_PREP(ADS112C14_DATA_RATE_CFG_DELAY,
+ channel_state->delay) |
FIELD_PREP(ADS112C14_DATA_RATE_CFG_GC_EN, 0) |
FIELD_PREP(ADS112C14_DATA_RATE_CFG_FLTR_OSR,
channel_state->filter_osr));
@@ -788,7 +1019,8 @@ static int ads112c14_scan_read(struct ads112c14_data *data, u8 *buf)
return 0;
}
-static int ads112c14_wait_for_conversion_irq(struct ads112c14_data *data)
+static int ads112c14_wait_for_conversion_irq(struct ads112c14_data *data,
+ u32 settle_time_us)
{
unsigned long remaining;
int ret;
@@ -800,13 +1032,16 @@ static int ads112c14_wait_for_conversion_irq(struct ads112c14_data *data)
if (ret)
return ret;
+ /* Give it 1ms more than calculated settling time. */
remaining = wait_for_completion_timeout(&data->drdy_completion,
- msecs_to_jiffies(100));
+ usecs_to_jiffies(settle_time_us +
+ 1 * USEC_PER_MSEC));
return remaining ? 0 : -ETIMEDOUT;
}
-static int ads112c14_wait_for_conversion_poll(struct ads112c14_data *data)
+static int ads112c14_wait_for_conversion_poll(struct ads112c14_data *data,
+ u32 settle_time_us)
{
u32 reg_val;
int ret;
@@ -816,10 +1051,12 @@ static int ads112c14_wait_for_conversion_poll(struct ads112c14_data *data)
if (ret)
return ret;
+ /* Give it 1ms more than calculated settling time. */
return regmap_read_poll_timeout(data->regmap,
ADS112C14_REG_STATUS_MSB, reg_val,
FIELD_GET(ADS112C14_STATUS_MSB_DRDY, reg_val),
- 1 * USEC_PER_MSEC, 100 * USEC_PER_MSEC);
+ 1 * USEC_PER_MSEC,
+ settle_time_us + 1 * USEC_PER_MSEC);
}
static int ads112c14_single_conversion(struct ads112c14_data *data,
@@ -827,6 +1064,8 @@ static int ads112c14_single_conversion(struct ads112c14_data *data,
u8 *buf, bool en_burnout, bool for_scan)
{
struct i2c_client *client = to_i2c_client(regmap_get_device(data->regmap));
+ struct ads112c14_channel_state *channel_state;
+ u32 settle_time_us;
int ret;
guard(mutex)(&data->lock);
@@ -835,10 +1074,26 @@ static int ads112c14_single_conversion(struct ads112c14_data *data,
if (ret)
return ret;
+ channel_state = &data->channel_states[chan->scan_index];
+ ret = ads112c14_get_settling_time_us(data, channel_state,
+ channel_state->delay,
+ &settle_time_us);
+ if (ret)
+ return ret;
+
+ ret = regmap_test_bits(data->regmap, ADS112C14_REG_DATA_RATE_CFG,
+ ADS112C14_DATA_RATE_CFG_GC_EN);
+ if (ret < 0)
+ return ret;
+
+ /* Input chopping doubles the settling time. */
+ if (ret)
+ settle_time_us *= 2;
+
if (data->drdy_irq)
- ret = ads112c14_wait_for_conversion_irq(data);
+ ret = ads112c14_wait_for_conversion_irq(data, settle_time_us);
else
- ret = ads112c14_wait_for_conversion_poll(data);
+ ret = ads112c14_wait_for_conversion_poll(data, settle_time_us);
if (ret)
return ret;
@@ -1604,6 +1859,17 @@ static const struct iio_enum ads112c14_filter_type_enum = {
};
static const struct iio_chan_spec_ext_info ads112c14_ext_info[] = {
+ {
+ .name = "settlingtime",
+ .read = ads112c14_read_settling_time,
+ .write = ads112c14_write_settling_time,
+ .shared = IIO_SEPARATE,
+ },
+ {
+ .name = "settlingtime_available",
+ .read = ads112c14_read_settling_time_available,
+ .shared = IIO_SEPARATE,
+ },
IIO_ENUM("filter_type", IIO_SEPARATE, &ads112c14_filter_type_enum),
IIO_ENUM_AVAILABLE("filter_type", IIO_SEPARATE, &ads112c14_filter_type_enum),
{ }
@@ -1614,6 +1880,17 @@ static const struct iio_chan_spec_ext_info ads112c14_ext_info_burnout[] = {
.name = "burnoutraw",
.read = ads112c14_read_burnout_raw,
},
+ {
+ .name = "settlingtime",
+ .read = ads112c14_read_settling_time,
+ .write = ads112c14_write_settling_time,
+ .shared = IIO_SEPARATE,
+ },
+ {
+ .name = "settlingtime_available",
+ .read = ads112c14_read_settling_time_available,
+ .shared = IIO_SEPARATE,
+ },
IIO_ENUM("filter_type", IIO_SEPARATE, &ads112c14_filter_type_enum),
IIO_ENUM_AVAILABLE("filter_type", IIO_SEPARATE, &ads112c14_filter_type_enum),
{ }
@@ -1966,6 +2243,52 @@ static void ads112c14_populate_odr_tables(struct ads112c14_data *data)
available[1] = rem;
}
+static void ads112c14_populate_settling_range_tables(struct ads112c14_data *data)
+{
+ s32 (*avail)[2];
+ u32 i, j;
+
+ for (i = 0; i < ARRAY_SIZE(ads112c14_sinc_latency_tmod); i++) {
+ for (j = 0; j < ARRAY_SIZE(ads112c14_fmod_div); j++) {
+ u64 fmod_Hz = data->fclk_Hz / ads112c14_fmod_div[j];
+ u64 start_tmod = ads112c14_sinc_latency_tmod[i][j];
+ u64 step_tmod = ads112c14_delay_to_tmod(1);
+ u64 stop_tmod = start_tmod + ads112c14_delay_to_tmod(ADS112C14_DELAY_MAX);
+ s64 start_us, step_us, stop_us;
+
+ start_us = DIV_ROUND_CLOSEST_ULL(start_tmod * USEC_PER_SEC, fmod_Hz);
+ step_us = DIV_ROUND_CLOSEST_ULL(step_tmod * USEC_PER_SEC, fmod_Hz);
+ stop_us = DIV_ROUND_CLOSEST_ULL(stop_tmod * USEC_PER_SEC, fmod_Hz);
+
+ avail = data->sinc_settling_time_range_available[i][j];
+
+ iio_val_s64_decompose(start_us, &avail[0][0], &avail[0][1]);
+ iio_val_s64_decompose(step_us, &avail[1][0], &avail[1][1]);
+ iio_val_s64_decompose(stop_us, &avail[2][0], &avail[2][1]);
+ }
+ }
+
+ for (i = 0; i < ARRAY_SIZE(ads112c14_fir_latency_tmod); i++) {
+ for (j = 0; j < ARRAY_SIZE(ads112c14_fmod_div); j++) {
+ u64 fmod_Hz = data->fclk_Hz / ads112c14_fmod_div[j];
+ u64 start_tmod = ads112c14_fir_latency_tmod[i][j];
+ u64 step_tmod = ads112c14_delay_to_tmod(1);
+ u64 stop_tmod = start_tmod + ads112c14_delay_to_tmod(ADS112C14_DELAY_MAX);
+ s64 start_us, step_us, stop_us;
+
+ start_us = DIV_ROUND_CLOSEST_ULL(start_tmod * USEC_PER_SEC, fmod_Hz);
+ step_us = DIV_ROUND_CLOSEST_ULL(step_tmod * USEC_PER_SEC, fmod_Hz);
+ stop_us = DIV_ROUND_CLOSEST_ULL(stop_tmod * USEC_PER_SEC, fmod_Hz);
+
+ avail = data->fir_settling_time_range_available[i][j];
+
+ iio_val_s64_decompose(start_us, &avail[0][0], &avail[0][1]);
+ iio_val_s64_decompose(step_us, &avail[1][0], &avail[1][1]);
+ iio_val_s64_decompose(stop_us, &avail[2][0], &avail[2][1]);
+ }
+ }
+}
+
static void ads112c14_populate_tables(struct ads112c14_data *data)
{
u32 full_scale, fsr_bits;
@@ -2004,6 +2327,7 @@ static void ads112c14_populate_tables(struct ads112c14_data *data)
ads112c14_populate_scale_available(data->sys_mon_chan_short_scale_available,
full_scale, fsr_bits);
ads112c14_populate_odr_tables(data);
+ ads112c14_populate_settling_range_tables(data);
}
static int ads112c14_probe(struct i2c_client *client)
--
2.43.0
^ permalink raw reply [flat|nested] 15+ messages in thread
* [PATCH v3 10/10] iio: ABI: add settlingtime attributes
2026-09-10 21:12 [PATCH v3 00/10] iio: adc: ti-ads112c14: add filter support David Lechner (TI)
` (8 preceding siblings ...)
2026-09-10 21:13 ` [PATCH v3 09/10] iio: adc: ti-ads112c14: add settlingtime attribute David Lechner (TI)
@ 2026-09-10 21:13 ` David Lechner (TI)
2026-09-13 20:17 ` [PATCH v3 00/10] iio: adc: ti-ads112c14: add filter support Jonathan Cameron
10 siblings, 0 replies; 15+ messages in thread
From: David Lechner (TI) @ 2026-09-10 21:13 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 new generic attributes for input settling time introduced in the
ti-ads122c14 driver.
Usually, in IIO the output data rate is 1 / sampling_frequency. There
are known devices (generally ones with filtering and oversampling) where
the first conversion takes longer than 1 / sampling_frequency to allow
the input to settle in order to have a valid conversion result. Then,
any subsequent conversions in a continuous sampling mode would be done
at the regular interval defined by 1 / sampling_frequency.
This attribute is intended to allow controlling that settling time
duration.
The total settling time is used rather than the difference between the
time needed for the first sample and subsequent samples so that it can
be read back directly via settlingtime_available without also reading
sampling_frequency to compute a delta. The available range's minimum
value represents the fixed, always-present portion of the delay (which
typically already includes the time for a normal conversion), so
requesting that minimum is equivalent to no additional settling delay.
A device that has input chopping enabled would see this settling time on
every conversion since the device setting (i.e. the input mux) changes
on every conversion.
Signed-off-by: David Lechner (TI) <dlechner@baylibre.com>
---
Documentation/ABI/testing/sysfs-bus-iio | 26 ++++++++++++++++++++++++++
1 file changed, 26 insertions(+)
diff --git a/Documentation/ABI/testing/sysfs-bus-iio b/Documentation/ABI/testing/sysfs-bus-iio
index a8e1cb8e7490..045589058655 100644
--- a/Documentation/ABI/testing/sysfs-bus-iio
+++ b/Documentation/ABI/testing/sysfs-bus-iio
@@ -2402,6 +2402,32 @@ Description:
Specifies which filter type apply to the channel. The possible
values are given by the filter_type_available attribute.
+What: /sys/bus/iio/devices/iio:deviceX/in_resistanceY_settlingtime
+What: /sys/bus/iio/devices/iio:deviceX/in_tempY_settlingtime
+What: /sys/bus/iio/devices/iio:deviceX/in_voltageY_settlingtime
+What: /sys/bus/iio/devices/iio:deviceX/in_voltageY-voltageZ_settlingtime
+KernelVersion: 7.4
+Contact: linux-iio@vger.kernel.org
+Description:
+ Set/read total settling time in seconds for a device to settle
+ after starting the first conversion after any settings have
+ changed. The data for the conversion is outputted at the end of
+ this time.
+
+ Depending on the device and how it is used, this settling time
+ may apply to more than just the first conversion, e.g. to every
+ conversion in a single-shot conversion mode or when a device
+ setting changes after each conversion (input chopping).
+
+What: /sys/bus/iio/devices/iio:deviceX/in_resistanceY_settlingtime_available
+What: /sys/bus/iio/devices/iio:deviceX/in_tempY_settlingtime_available
+What: /sys/bus/iio/devices/iio:deviceX/in_voltageY_settlingtime_available
+What: /sys/bus/iio/devices/iio:deviceX/in_voltageY-voltageZ_settlingtime_available
+KernelVersion: 7.4
+Contact: linux-iio@vger.kernel.org
+Description:
+ Available settlingtime values in IIO range or list format.
+
What: /sys/.../events/in_proximity_thresh_either_runningperiod
KernelVersion: 6.6
Contact: linux-iio@vger.kernel.org
--
2.43.0
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH v3 06/10] iio: adc: ti-ads112c14: support external clock
2026-09-10 21:13 ` [PATCH v3 06/10] iio: adc: ti-ads112c14: support external clock David Lechner (TI)
@ 2026-09-11 8:38 ` Joshua Crofts
2026-09-11 9:16 ` Andy Shevchenko
0 siblings, 1 reply; 15+ messages in thread
From: Joshua Crofts @ 2026-09-11 8: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, 10 Sep 2026 16:13:05 -0500
"David Lechner (TI)" <dlechner@baylibre.com> wrote:
> Add support for an external clock source to the TI ADS112C14 ADC driver.
> The unused fclk_Hz field is added in preparation for filter support.
>
> Signed-off-by: David Lechner (TI) <dlechner@baylibre.com>
> ---
> drivers/iio/adc/ti-ads112c14.c | 39 +++++++++++++++++++++++++++++++++++++++
> 1 file changed, 39 insertions(+)
>
> diff --git a/drivers/iio/adc/ti-ads112c14.c b/drivers/iio/adc/ti-ads112c14.c
> index efea1e22d067..0e4584b4675e 100644
> --- a/drivers/iio/adc/ti-ads112c14.c
> +++ b/drivers/iio/adc/ti-ads112c14.c
> @@ -11,6 +11,7 @@
> #include <linux/bitfield.h>
> #include <linux/bitmap.h>
> #include <linux/cleanup.h>
> +#include <linux/clk.h>
> #include <linux/completion.h>
> #include <linux/crc8.h>
> #include <linux/delay.h>
> @@ -82,6 +83,8 @@
> #define ADS112C14_DEVICE_CFG_BOCS_10_uA 3
>
> #define ADS112C14_DEVICE_CFG_CLK_SEL BIT(3)
> +#define ADS112C14_DEVICE_CFG_CLK_SEL_INTERNAL 0
> +#define ADS112C14_DEVICE_CFG_CLK_SEL_EXTERNAL 1
> #define ADS112C14_DEVICE_CFG_CONV_MODE BIT(2)
> #define ADS112C14_DEVICE_CFG_CONV_MODE_CONTINUOUS 0
> #define ADS112C14_DEVICE_CFG_CONV_MODE_SINGLE_SHOT 1
> @@ -178,6 +181,8 @@ static const u32 ads112c14_pga_gains_x10[] = {
> 200, 320, 500, 640, 1000, 1280, 2000, 2560, /* 8 - 15 */
> };
>
> +#define ADS112C14_INTERNAL_CLK_Hz 4096000
4096 * KILO?
Otherwise LGTM.
Reviewed-by: Joshua Crofts <joshua.crofts1@gmail.com>
--
Kind regards,
Joshua Crofts
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH v3 06/10] iio: adc: ti-ads112c14: support external clock
2026-09-11 8:38 ` Joshua Crofts
@ 2026-09-11 9:16 ` Andy Shevchenko
0 siblings, 0 replies; 15+ messages in thread
From: Andy Shevchenko @ 2026-09-11 9:16 UTC (permalink / raw)
To: Joshua Crofts
Cc: David Lechner (TI), Jonathan Cameron, Nuno Sá,
Andy Shevchenko, Chris Hall, Patrick Edwards, Kurt Borja,
linux-iio, linux-kernel
On Fri, Sep 11, 2026 at 10:38:27AM +0200, Joshua Crofts wrote:
> On Thu, 10 Sep 2026 16:13:05 -0500
> "David Lechner (TI)" <dlechner@baylibre.com> wrote:
...
> > +#define ADS112C14_INTERNAL_CLK_Hz 4096000
>
> 4096 * KILO?
>
> Otherwise LGTM.
>
> Reviewed-by: Joshua Crofts <joshua.crofts1@gmail.com>
HZ_PER_KHZ rather than KILO.
--
With Best Regards,
Andy Shevchenko
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH v3 09/10] iio: adc: ti-ads112c14: add settlingtime attribute
2026-09-10 21:13 ` [PATCH v3 09/10] iio: adc: ti-ads112c14: add settlingtime attribute David Lechner (TI)
@ 2026-09-13 20:11 ` Jonathan Cameron
0 siblings, 0 replies; 15+ messages in thread
From: Jonathan Cameron @ 2026-09-13 20:11 UTC (permalink / raw)
To: David Lechner (TI)
Cc: Nuno Sá,
Andy Shevchenko, Chris Hall, Patrick Edwards, Kurt Borja,
linux-iio, linux-kernel
On Thu, 10 Sep 2026 16:13:08 -0500
"David Lechner (TI)" <dlechner@baylibre.com> wrote:
> Add per-channel sysfs attributes for settlingtime and
> settlingtime_available. These allow adjusting the total settling time
> for each channel. The value consists of a fixed t_latency time (based
> on the selected filter_type, oversampling_ratio and sampling_frequency)
> plus a user-configurable t_delay that determines the value to write to
> the DELAY field in the registers.
>
> The allowable values are non-linear integer multiples, so the step size
> is just the smallest step size. Writing the attribute will match the
> closest matching value for the DELAY field with a time equal to or
> greater than the requested settling time (unless the requested time is
> larger than the maximum allowable settling time).
>
> Signed-off-by: David Lechner (TI) <dlechner@baylibre.com>
Hi David,
I flagged a couple more sashiko comments in v2 that are still there.
I was expecting a reply to that v2 thread to reassure that they
weren't relevant.
Anyhow I'm not going to hold the series up on those (at least to
get it into a branch 0-day will hammer) but please do take a look
and shout if these needs more work. This is mostly based on you
having updated for one of them which almost certainly means you
looked at the others as well.
Jonathan
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH v3 00/10] iio: adc: ti-ads112c14: add filter support
2026-09-10 21:12 [PATCH v3 00/10] iio: adc: ti-ads112c14: add filter support David Lechner (TI)
` (9 preceding siblings ...)
2026-09-10 21:13 ` [PATCH v3 10/10] iio: ABI: add settlingtime attributes David Lechner (TI)
@ 2026-09-13 20:17 ` Jonathan Cameron
10 siblings, 0 replies; 15+ messages in thread
From: Jonathan Cameron @ 2026-09-13 20:17 UTC (permalink / raw)
To: David Lechner (TI)
Cc: Nuno Sá,
Andy Shevchenko, Chris Hall, Patrick Edwards, Kurt Borja,
linux-iio, linux-kernel
On Thu, 10 Sep 2026 16:12:59 -0500
"David Lechner (TI)" <dlechner@baylibre.com> wrote:
> I was hoping to avoid this, but until [0] lands, sashiko doesn't know
> how to handle patch series with dependencies. So I have combined my
> three outstanding ti-ads112c14 series into a single one. It's really
> too much all at once, but this seems the best way to speed up the review
> process at the moment.
>
> [0]: https://github.com/sashiko-dev/sashiko/pull/389
>
Whole lot applied to the testing branch of iio.git with:
diff --git a/drivers/iio/adc/ti-ads112c14.c b/drivers/iio/adc/ti-ads112c14.c
index 0e4584b4675e..3730e5539310 100644
--- a/drivers/iio/adc/ti-ads112c14.c
+++ b/drivers/iio/adc/ti-ads112c14.c
@@ -181,7 +181,7 @@ static const u32 ads112c14_pga_gains_x10[] = {
200, 320, 500, 640, 1000, 1280, 2000, 2560, /* 8 - 15 */
};
-#define ADS112C14_INTERNAL_CLK_Hz 4096000
+#define ADS112C14_INTERNAL_CLK_Hz (4096 * HZ_PER_KHZ)
#define ADS112C14_I2C_CRC8_POLYNOMIAL 0x07
DECLARE_CRC8_TABLE(ads112c14_crc8_table);
for the thing Joshua and Andy both chipped in on.
Rest looked good to me.
Jonathan
^ permalink raw reply [flat|nested] 15+ messages in thread
end of thread, other threads:[~2026-09-13 20:17 UTC | newest]
Thread overview: 15+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-09-10 21:12 [PATCH v3 00/10] iio: adc: ti-ads112c14: add filter support David Lechner (TI)
2026-09-10 21:13 ` [PATCH v3 01/10] iio: adc: ti-ads112c14: add DRDY interrupt support David Lechner (TI)
2026-09-10 21:13 ` [PATCH v3 02/10] iio: adc: ti-ads112c14: create data read helper functions David Lechner (TI)
2026-09-10 21:13 ` [PATCH v3 03/10] iio: adc: ti-ads112c14: add continuous mode support David Lechner (TI)
2026-09-10 21:13 ` [PATCH v3 04/10] iio: adc: ti-ads112c14: add burnout current support David Lechner (TI)
2026-09-10 21:13 ` [PATCH v3 05/10] iio: ABI: add sysfs attribute for _burnoutraw David Lechner (TI)
2026-09-10 21:13 ` [PATCH v3 06/10] iio: adc: ti-ads112c14: support external clock David Lechner (TI)
2026-09-11 8:38 ` Joshua Crofts
2026-09-11 9:16 ` Andy Shevchenko
2026-09-10 21:13 ` [PATCH v3 07/10] iio: adc: ti-ads112c14: add filter support David Lechner (TI)
2026-09-10 21:13 ` [PATCH v3 08/10] iio: ABI: add sinc4+sinc1+pf1 filter_type David Lechner (TI)
2026-09-10 21:13 ` [PATCH v3 09/10] iio: adc: ti-ads112c14: add settlingtime attribute David Lechner (TI)
2026-09-13 20:11 ` Jonathan Cameron
2026-09-10 21:13 ` [PATCH v3 10/10] iio: ABI: add settlingtime attributes David Lechner (TI)
2026-09-13 20:17 ` [PATCH v3 00/10] iio: adc: ti-ads112c14: add filter support Jonathan Cameron
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®