[PATCH v2 3/3] iio: adc: ti-ads112c14: add continuous mode support
"David Lechner (TI)" <[email protected]> Fri, 31 Jul 2026 18:48:12 -0500
| Newsgroups | org.kernel.vger.linux-iio,org.kernel.vger.linux-kernel |
|---|---|
| Message-ID | <20260731-iio-adc-ti-ads112c14-continuous-mode-v2-3-eb13da38e8fc@baylibre.com> |
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) <[email protected]> --- drivers/iio/adc/ti-ads112c14.c | 146 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 144 insertions(+), 2 deletions(-) diff --git a/drivers/iio/adc/ti-ads112c14.c b/drivers/iio/adc/ti-ads112c14.c index c6d83298c312..5147d10785fb 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,6 +259,7 @@ 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; @@ -280,11 +283,32 @@ 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 (indio_dev->trig && iio_trigger_using_own(indio_dev)) + iio_trigger_poll(data->drdy_trig); + else + complete(&data->drdy_completion); return IRQ_HANDLED; } +static int ads112c14_trigger_set_state(struct iio_trigger *trig, bool state) +{ + struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig); + struct ads112c14_data *data = iio_priv(indio_dev); + + if (state) + enable_irq(data->drdy_irq); + else + disable_irq(data->drdy_irq); + + return 0; +} + +static const struct iio_trigger_ops ads112c14_trigger_ops = { + .set_trigger_state = ads112c14_trigger_set_state, + .validate_device = iio_trigger_validate_own_device, +}; + static bool ads112c14_writeable_reg(struct device *dev, unsigned int reg) { switch (reg) { @@ -962,10 +986,31 @@ static irqreturn_t ads112c14_trigger_handler(int irq, void *private) struct iio_poll_func *pf = private; struct iio_dev *indio_dev = pf->indio_dev; struct ads112c14_data *data = iio_priv(indio_dev); + unsigned int scan_mask_len = iio_get_masklength(indio_dev); u32 offset = 0; u32 i; int ret; + if (iio_trigger_using_own(indio_dev)) { + i = find_first_bit(indio_dev->active_scan_mask, scan_mask_len); + if (i >= scan_mask_len) + goto out; + + ret = ads112c14_scan_read(data, (u8 *)&data->scan[0]); + if (ret) { + const struct iio_chan_spec *chan = &indio_dev->channels[i]; + + 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]; @@ -997,6 +1042,89 @@ static const struct iio_info ads112c14_info = { .read_label = ads112c14_read_label, }; +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 bool ads112c14_validate_scan_mask(struct iio_dev *indio_dev, + const unsigned long *mask) +{ + if (!ads112c14_using_drdy_trigger(indio_dev)) + return true; + + return iio_validate_scan_mask_onehot(indio_dev, mask); +} + +static int ads112c14_buffer_postenable(struct iio_dev *indio_dev) +{ + unsigned int scan_mask_len = iio_get_masklength(indio_dev); + struct ads112c14_data *data = iio_priv(indio_dev); + const struct iio_chan_spec *chan; + unsigned int i; + int ret; + + if (!ads112c14_using_drdy_trigger(indio_dev)) + return 0; + + i = find_first_bit(indio_dev->active_scan_mask, scan_mask_len); + if (i >= scan_mask_len) + return -EINVAL; + + chan = &indio_dev->channels[i]; + + guard(mutex)(&data->lock); + + ret = ads112c14_prepare_channel(data, chan); + if (ret) + return ret; + + ret = regmap_assign_bits(data->regmap, ADS112C14_REG_DEVICE_CFG, + ADS112C14_DEVICE_CFG_CONV_MODE, + ADS112C14_DEVICE_CFG_CONV_MODE_CONTINUOUS); + if (ret) + return ret; + + ret = regmap_write(data->regmap, ADS112C14_REG_CONVERSION_CTRL, + ADS112C14_CONVERSION_CTRL_START); + if (ret) { + regmap_assign_bits(data->regmap, ADS112C14_REG_DEVICE_CFG, + 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); + + ret = regmap_write(data->regmap, ADS112C14_REG_CONVERSION_CTRL, + ADS112C14_CONVERSION_CTRL_STOP); + if (ret) + return ret; + + return regmap_assign_bits(data->regmap, ADS112C14_REG_DEVICE_CFG, + 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, + .validate_scan_mask = ads112c14_validate_scan_mask, +}; + static int ads112c14_populate_idac_mag(u32 current_nA, u8 *idac_mag) { u32 current_uA = current_nA / (NANO / MICRO); @@ -1486,6 +1614,19 @@ static int ads112c14_probe(struct i2c_client *client) IRQF_NO_AUTOEN, dev_name(dev), indio_dev); if (ret) return ret; + + 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; } ads112c14_populate_tables(data); @@ -1496,7 +1637,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