[PATCH 1/2] iio: adc: ti-ads112c14: add DRDY interrupt support

From: David Lechner (TI)

Date: Fri Jul 24 2026 - 16:14:10 EST


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@xxxxxxxxxxxx>
---

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 | 82 ++++++++++++++++++++++++++++++++++++++----
1 file changed, 75 insertions(+), 7 deletions(-)

diff --git a/drivers/iio/adc/ti-ads112c14.c b/drivers/iio/adc/ti-ads112c14.c
index 8ad8caee0ff7..57e301c15314 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) {
@@ -601,17 +621,33 @@ static int ads112c14_single_conversion(struct ads112c14_data *data,
return ret;
}

+ if (data->drdy_irq) {
+ reinit_completion(&data->drdy_completion);
+ enable_irq(data->drdy_irq);
+ }
+
ret = regmap_write(data->regmap, ADS112C14_REG_CONVERSION_CTRL,
ADS112C14_CONVERSION_CTRL_START);
- if (ret)
+ if (ret) {
+ if (data->drdy_irq)
+ disable_irq(data->drdy_irq);
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 (ret)
- return ret;
+ if (data->drdy_irq) {
+ ret = wait_for_completion_timeout(&data->drdy_completion,
+ msecs_to_jiffies(100));
+ disable_irq(data->drdy_irq);
+ if (ret == 0)
+ return -ETIMEDOUT;
+ } else {
+ 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 (ret)
+ return ret;
+ }

/*
* When doing buffered read, we don't check the CRC, but rather pass it
@@ -1392,6 +1428,38 @@ static int ads112c14_probe(struct i2c_client *client)
if (ret)
return ret;

+ if (device_property_present(dev, "interrupts")) {
+ 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,
+ IRQF_NO_AUTOEN, dev_name(dev), indio_dev);
+ if (ret)
+ return ret;
+ }
+
ads112c14_populate_tables(data);

indio_dev->name = info->name;

--
2.43.0