[PATCH v3 5/9] iio: adc: ad4062: Add IIO Trigger support

Jorge Marques posted 9 patches 1 week, 5 days ago
There is a newer version of this series
[PATCH v3 5/9] iio: adc: ad4062: Add IIO Trigger support
Posted by Jorge Marques 1 week, 5 days ago
Adds support for IIO Trigger. Optionally, gp1 is assigned as Data Ready
signal, if not present, fallback to an I3C IBI with the same role.
The software trigger is allocated by the device, but must be attached by
the user before enabling the buffer. The purpose is to not impede
removing the driver due to the increased reference count when
iio_trigger_set_immutable() or iio_trigger_get() is used.

Signed-off-by: Jorge Marques <jorge.marques@analog.com>
---
 drivers/iio/adc/Kconfig  |   2 +
 drivers/iio/adc/ad4062.c | 188 +++++++++++++++++++++++++++++++++++++++++++----
 2 files changed, 175 insertions(+), 15 deletions(-)

diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
index e506dbe83f488..ddb7820f0bdcc 100644
--- a/drivers/iio/adc/Kconfig
+++ b/drivers/iio/adc/Kconfig
@@ -74,6 +74,8 @@ config AD4062
 	tristate "Analog Devices AD4062 Driver"
 	depends on I3C
 	select REGMAP_I3C
+	select IIO_BUFFER
+	select IIO_TRIGGERED_BUFFER
 	help
 	  Say yes here to build support for Analog Devices AD4062 I3C analog
 	  to digital converters (ADC).
diff --git a/drivers/iio/adc/ad4062.c b/drivers/iio/adc/ad4062.c
index 54f7f69e40879..080dc80fd1621 100644
--- a/drivers/iio/adc/ad4062.c
+++ b/drivers/iio/adc/ad4062.c
@@ -9,11 +9,16 @@
 #include <linux/bitops.h>
 #include <linux/completion.h>
 #include <linux/delay.h>
+#include <linux/devm-helpers.h>
 #include <linux/err.h>
 #include <linux/i3c/device.h>
 #include <linux/i3c/master.h>
+#include <linux/iio/buffer.h>
 #include <linux/iio/iio.h>
 #include <linux/iio/sysfs.h>
+#include <linux/iio/trigger.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
 #include <linux/interrupt.h>
 #include <linux/jiffies.h>
 #include <linux/math.h>
@@ -60,6 +65,7 @@
 #define     AD4062_REG_DEVICE_STATUS_DEVICE_RESET	BIT(6)
 #define AD4062_REG_IBI_STATUS				0x48
 #define AD4062_REG_CONV_READ_LSB			0x50
+#define AD4062_REG_CONV_READ				0x53
 #define AD4062_REG_CONV_TRIGGER				0x59
 #define AD4062_REG_CONV_AUTO				0x61
 #define AD4062_MAX_REG					AD4062_REG_CONV_AUTO
@@ -137,6 +143,7 @@ struct ad4062_state {
 	const struct ad4062_chip_info *chip;
 	const struct ad4062_bus_ops *ops;
 	enum ad4062_operation_mode mode;
+	struct work_struct trig_conv;
 	struct completion completion;
 	struct iio_trigger *trigger;
 	struct iio_dev *indio_dev;
@@ -144,6 +151,7 @@ struct ad4062_state {
 	struct regmap *regmap;
 	int vref_uV;
 	unsigned int samp_freqs[ARRAY_SIZE(ad4062_conversion_freqs)];
+	bool gpo_irq[2];
 	union {
 		__be32 be32;
 		__be16 be16;
@@ -411,7 +419,10 @@ static irqreturn_t ad4062_irq_handler_drdy(int irq, void *private)
 	struct iio_dev *indio_dev = private;
 	struct ad4062_state *st = iio_priv(indio_dev);
 
-	complete(&st->completion);
+	if (iio_buffer_enabled(indio_dev) && iio_trigger_using_own(indio_dev))
+		iio_trigger_poll(st->trigger);
+	else
+		complete(&st->completion);
 
 	return IRQ_HANDLED;
 }
@@ -421,7 +432,57 @@ static void ad4062_ibi_handler(struct i3c_device *i3cdev,
 {
 	struct ad4062_state *st = i3cdev_get_drvdata(i3cdev);
 
-	complete(&st->completion);
+	if (iio_buffer_enabled(st->indio_dev))
+		iio_trigger_poll_nested(st->trigger);
+	else
+		complete(&st->completion);
+}
+
+static void ad4062_trigger_work(struct work_struct *work)
+{
+	struct ad4062_state *st =
+		container_of(work, struct ad4062_state, trig_conv);
+	int ret;
+
+	/*
+	 * Read current conversion, if at reg CONV_READ, stop bit triggers
+	 * next sample and does not need writing the address.
+	 */
+	struct i3c_priv_xfer t[2] = {
+		{
+			.data.in = &st->buf.be32,
+			.len = sizeof(st->buf.be32),
+			.rnw = true,
+		},
+		{
+			.data.out = &st->reg_addr_conv,
+			.len = sizeof(st->reg_addr_conv),
+			.rnw = false,
+		},
+	};
+
+	ret = i3c_device_do_priv_xfers(st->i3cdev, &t[0], 1);
+	if (ret)
+		return;
+
+	iio_push_to_buffers_with_timestamp(st->indio_dev, &st->buf.be32,
+					   iio_get_time_ns(st->indio_dev));
+	if (st->gpo_irq[1])
+		return;
+
+	i3c_device_do_priv_xfers(st->i3cdev, &t[1], 1);
+}
+
+static irqreturn_t ad4062_poll_handler(int irq, void *p)
+{
+	struct iio_poll_func *pf = p;
+	struct iio_dev *indio_dev = pf->indio_dev;
+	struct ad4062_state *st = iio_priv(indio_dev);
+
+	iio_trigger_notify_done(indio_dev->trig);
+	schedule_work(&st->trig_conv);
+
+	return IRQ_HANDLED;
 }
 
 static void ad4062_remove_ibi(void *data)
@@ -466,16 +527,48 @@ static int ad4062_request_irq(struct iio_dev *indio_dev)
 	if (ret == -EPROBE_DEFER) {
 		return ret;
 	} else if (ret < 0) {
+		st->gpo_irq[1] = false;
+		st->reg_addr_conv = AD4062_REG_CONV_TRIGGER;
 		return regmap_update_bits(st->regmap, AD4062_REG_ADC_IBI_EN,
 					  AD4062_REG_ADC_IBI_EN_CONV_TRIGGER,
 					  AD4062_REG_ADC_IBI_EN_CONV_TRIGGER);
 	}
+	st->gpo_irq[1] = true;
+	st->reg_addr_conv = AD4062_REG_CONV_READ;
 	return devm_request_threaded_irq(dev, ret,
 					 ad4062_irq_handler_drdy,
 					 NULL, IRQF_ONESHOT, indio_dev->name,
 					 indio_dev);
 }
 
+static const struct iio_trigger_ops ad4062_trigger_ops = {
+	.validate_device = &iio_trigger_validate_own_device,
+};
+
+static int ad4062_request_trigger(struct iio_dev *indio_dev)
+{
+	struct ad4062_state *st = iio_priv(indio_dev);
+	struct device *dev = &st->i3cdev->dev;
+	int ret;
+
+	st->trigger = devm_iio_trigger_alloc(dev, "%s-dev%d",
+					     indio_dev->name,
+					     iio_device_id(indio_dev));
+	if (!st->trigger)
+		return -ENOMEM;
+
+	st->trigger->ops = &ad4062_trigger_ops;
+	iio_trigger_set_drvdata(st->trigger, indio_dev);
+
+	ret = devm_iio_trigger_register(dev, st->trigger);
+	if (ret)
+		return ret;
+
+	indio_dev->trig = iio_trigger_get(st->trigger);
+
+	return 0;
+}
+
 static const int ad4062_oversampling_avail[] = {
 	1, 2, 4, 8, 16, 32, 64, 128,		/*  0 -  7 */
 	256, 512, 1024, 2048, 4096,		/*  8 - 12 */
@@ -572,15 +665,17 @@ static int ad4062_read_chan_raw(struct ad4062_state *st, int *val)
 {
 	int ret;
 	struct i3c_device *i3cdev = st->i3cdev;
-	struct i3c_priv_xfer t0 = {
-		.data.out = &st->reg_addr_conv,
-		.len = sizeof(st->reg_addr_conv),
-		.rnw = false,
-	};
-	struct i3c_priv_xfer t1 = {
-		.data.in = &st->buf.be32,
-		.len = sizeof(st->buf.be32),
-		.rnw = true,
+	struct i3c_priv_xfer t[] = {
+		{
+			.data.out = &st->reg_addr_conv,
+			.len = sizeof(st->reg_addr_conv),
+			.rnw = false,
+		},
+		{
+			.data.in = &st->buf.be32,
+			.len = sizeof(st->buf.be32),
+			.rnw = true,
+		}
 	};
 
 	ACQUIRE(pm_runtime_active_try_enabled, pm)(&st->i3cdev->dev);
@@ -593,8 +688,8 @@ static int ad4062_read_chan_raw(struct ad4062_state *st, int *val)
 		return ret;
 
 	reinit_completion(&st->completion);
-	/* Change address pointer to trigger conversion */
-	ret = i3c_device_do_priv_xfers(i3cdev, &t0, 1);
+	/* Change address pointer (and read if CONV_READ) to trigger conversion. */
+	ret = i3c_device_do_priv_xfers(i3cdev, t, st->gpo_irq[1] ? 2 : 1);
 	if (ret)
 		return ret;
 	/*
@@ -606,7 +701,7 @@ static int ad4062_read_chan_raw(struct ad4062_state *st, int *val)
 	if (!ret)
 		return -ETIMEDOUT;
 
-	ret = i3c_device_do_priv_xfers(i3cdev, &t1, 1);
+	ret = i3c_device_do_priv_xfers(i3cdev, &t[1], 1);
 	if (ret)
 		return ret;
 	*val = get_unaligned_be32(st->buf.bytes);
@@ -687,6 +782,55 @@ static int ad4062_write_raw(struct iio_dev *indio_dev,
 	return ret;
 }
 
+static int ad4062_triggered_buffer_postenable(struct iio_dev *indio_dev)
+{
+	struct ad4062_state *st = iio_priv(indio_dev);
+	int ret;
+
+	ACQUIRE(pm_runtime_active_try_enabled, pm)(&st->i3cdev->dev);
+	ret = ACQUIRE_ERR(pm_runtime_active_try_enabled, &pm);
+	if (ret)
+		return ret;
+
+	ret = ad4062_set_operation_mode(st, st->mode);
+	if (ret)
+		return ret;
+
+	/* CONV_READ requires read to trigger first sample. */
+	struct i3c_priv_xfer t[2] = {
+		{
+			.data.out = &st->reg_addr_conv,
+			.len = sizeof(st->reg_addr_conv),
+			.rnw = false,
+		},
+		{
+			.data.in = &st->buf.be32,
+			.len = sizeof(st->buf.be32),
+			.rnw = true,
+		}
+	};
+
+	ret = i3c_device_do_priv_xfers(st->i3cdev, t, st->gpo_irq[1] ? 2 : 1);
+	if (ret)
+		return ret;
+
+	pm_runtime_get_noresume(&st->i3cdev->dev);
+	return 0;
+}
+
+static int ad4062_triggered_buffer_predisable(struct iio_dev *indio_dev)
+{
+	struct ad4062_state *st = iio_priv(indio_dev);
+
+	pm_runtime_put_autosuspend(&st->i3cdev->dev);
+	return 0;
+}
+
+static const struct iio_buffer_setup_ops ad4062_triggered_buffer_setup_ops = {
+	.postenable = &ad4062_triggered_buffer_postenable,
+	.predisable = &ad4062_triggered_buffer_predisable,
+};
+
 static int ad4062_debugfs_reg_access(struct iio_dev *indio_dev, unsigned int reg,
 				     unsigned int writeval, unsigned int *readval)
 {
@@ -798,7 +942,6 @@ static int ad4062_probe(struct i3c_device *i3cdev)
 	st->sampling_frequency = 0;
 	st->oversamp_ratio = 0;
 	st->indio_dev = indio_dev;
-	st->reg_addr_conv = AD4062_REG_CONV_TRIGGER;
 
 	indio_dev->modes = INDIO_DIRECT_MODE;
 	indio_dev->num_channels = 1;
@@ -822,6 +965,17 @@ static int ad4062_probe(struct i3c_device *i3cdev)
 	if (ret)
 		return ret;
 
+	ret = ad4062_request_trigger(indio_dev);
+	if (ret)
+		return ret;
+
+	ret = devm_iio_triggered_buffer_setup(&i3cdev->dev, indio_dev,
+					      iio_pollfunc_store_time,
+					      ad4062_poll_handler,
+					      &ad4062_triggered_buffer_setup_ops);
+	if (ret)
+		return ret;
+
 	pm_runtime_set_active(dev);
 	ret = devm_pm_runtime_enable(dev);
 	if (ret)
@@ -834,6 +988,10 @@ static int ad4062_probe(struct i3c_device *i3cdev)
 	if (ret)
 		return dev_err_probe(dev, ret, "Failed to request i3c ibi\n");
 
+	ret = devm_work_autocancel(dev, &st->trig_conv, ad4062_trigger_work);
+	if (ret)
+		return ret;
+
 	return devm_iio_device_register(dev, indio_dev);
 }
 

-- 
2.51.1
Re: [PATCH v3 5/9] iio: adc: ad4062: Add IIO Trigger support
Posted by Jonathan Cameron 1 week, 4 days ago
On Fri, 5 Dec 2025 16:12:06 +0100
Jorge Marques <jorge.marques@analog.com> wrote:

> Adds support for IIO Trigger. Optionally, gp1 is assigned as Data Ready
> signal, if not present, fallback to an I3C IBI with the same role.
> The software trigger is allocated by the device, but must be attached by
> the user before enabling the buffer. The purpose is to not impede
> removing the driver due to the increased reference count when
> iio_trigger_set_immutable() or iio_trigger_get() is used.
> 
> Signed-off-by: Jorge Marques <jorge.marques@analog.com>

+CC Rafael; I'd like input on the ACQUIRE + take extra reference pattern
and whether Rafael thinks it is a good idea!

> ---
>  drivers/iio/adc/Kconfig  |   2 +
>  drivers/iio/adc/ad4062.c | 188 +++++++++++++++++++++++++++++++++++++++++++----
>  2 files changed, 175 insertions(+), 15 deletions(-)
> 
> diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
> index e506dbe83f488..ddb7820f0bdcc 100644
> --- a/drivers/iio/adc/Kconfig
> +++ b/drivers/iio/adc/Kconfig
> @@ -74,6 +74,8 @@ config AD4062
>  	tristate "Analog Devices AD4062 Driver"
>  	depends on I3C
>  	select REGMAP_I3C
> +	select IIO_BUFFER
> +	select IIO_TRIGGERED_BUFFER
>  	help
>  	  Say yes here to build support for Analog Devices AD4062 I3C analog
>  	  to digital converters (ADC).
> diff --git a/drivers/iio/adc/ad4062.c b/drivers/iio/adc/ad4062.c
> index 54f7f69e40879..080dc80fd1621 100644
> --- a/drivers/iio/adc/ad4062.c
> +++ b/drivers/iio/adc/ad4062.c

> +static void ad4062_trigger_work(struct work_struct *work)
> +{
> +	struct ad4062_state *st =
> +		container_of(work, struct ad4062_state, trig_conv);
> +	int ret;
> +
> +	/*
> +	 * Read current conversion, if at reg CONV_READ, stop bit triggers
> +	 * next sample and does not need writing the address.
> +	 */
> +	struct i3c_priv_xfer t[2] = {
> +		{
> +			.data.in = &st->buf.be32,
> +			.len = sizeof(st->buf.be32),
> +			.rnw = true,
> +		},
> +		{
> +			.data.out = &st->reg_addr_conv,
> +			.len = sizeof(st->reg_addr_conv),
> +			.rnw = false,
> +		},
> +	};
> +
> +	ret = i3c_device_do_priv_xfers(st->i3cdev, &t[0], 1);
> +	if (ret)
> +		return;
> +
> +	iio_push_to_buffers_with_timestamp(st->indio_dev, &st->buf.be32,
> +					   iio_get_time_ns(st->indio_dev));

Use push_to_buffers_with_ts() (this function is deprecated)
which would have had the helpful result here of pointing out the buffer
isn't big enough for the timestamp.  So this will write the timestamp
over later fields in the st structure.

Given that this sometimes fits in a be16 I wonder if it is worth
storing those in a be16 element of the kfifo. That will halve it's size
if the timestamp isn't enabled which would be a nice thing to have.
Storing in a be32 isn't an ABI issue, it's just a bit unusual
so if I'm missing some reason it makes more sense then fair enough.

> +	if (st->gpo_irq[1])
> +		return;
> +
> +	i3c_device_do_priv_xfers(st->i3cdev, &t[1], 1);
> +}

...

> @@ -572,15 +665,17 @@ static int ad4062_read_chan_raw(struct ad4062_state *st, int *val)
>  {
>  	int ret;
>  	struct i3c_device *i3cdev = st->i3cdev;
> -	struct i3c_priv_xfer t0 = {
> -		.data.out = &st->reg_addr_conv,
> -		.len = sizeof(st->reg_addr_conv),
> -		.rnw = false,
> -	};
> -	struct i3c_priv_xfer t1 = {
> -		.data.in = &st->buf.be32,
> -		.len = sizeof(st->buf.be32),
> -		.rnw = true,
> +	struct i3c_priv_xfer t[] = {

Do this in the earlier patch, not here.

> +		{
> +			.data.out = &st->reg_addr_conv,
> +			.len = sizeof(st->reg_addr_conv),
> +			.rnw = false,
> +		},
> +		{
> +			.data.in = &st->buf.be32,
> +			.len = sizeof(st->buf.be32),
> +			.rnw = true,
> +		}
>  	};

> @@ -687,6 +782,55 @@ static int ad4062_write_raw(struct iio_dev *indio_dev,
>  	return ret;
>  }
>  
> +static int ad4062_triggered_buffer_postenable(struct iio_dev *indio_dev)
> +{
> +	struct ad4062_state *st = iio_priv(indio_dev);
> +	int ret;
> +
> +	ACQUIRE(pm_runtime_active_try_enabled, pm)(&st->i3cdev->dev);
> +	ret = ACQUIRE_ERR(pm_runtime_active_try_enabled, &pm);

This may also be affected by Rafael's patch set to provide some helpers
to make this more readable.


> +	if (ret)
> +		return ret;
> +
> +	ret = ad4062_set_operation_mode(st, st->mode);
> +	if (ret)
> +		return ret;
> +
> +	/* CONV_READ requires read to trigger first sample. */
> +	struct i3c_priv_xfer t[2] = {
> +		{
> +			.data.out = &st->reg_addr_conv,
> +			.len = sizeof(st->reg_addr_conv),
> +			.rnw = false,
> +		},
> +		{
> +			.data.in = &st->buf.be32,
> +			.len = sizeof(st->buf.be32),
> +			.rnw = true,
> +		}
> +	};
> +
> +	ret = i3c_device_do_priv_xfers(st->i3cdev, t, st->gpo_irq[1] ? 2 : 1);
> +	if (ret)
> +		return ret;
> +
> +	pm_runtime_get_noresume(&st->i3cdev->dev);
As per my late reply I'm not keen on the double increment as a complex way
to steal the ACQUIRED() reference. Might be better to just factor the stuff
where you currently have acquired a reference out into a helper and use
the traditional runtime pm calls in this outer function.
 
> +	return 0;
Re: [PATCH v3 5/9] iio: adc: ad4062: Add IIO Trigger support
Posted by Jorge Marques 1 week, 2 days ago
On Sat, Dec 06, 2025 at 05:45:03PM +0000, Jonathan Cameron wrote:
> On Fri, 5 Dec 2025 16:12:06 +0100
> Jorge Marques <jorge.marques@analog.com> wrote:
> 
Hi Jonathan,
> > Adds support for IIO Trigger. Optionally, gp1 is assigned as Data Ready
> > signal, if not present, fallback to an I3C IBI with the same role.
> > The software trigger is allocated by the device, but must be attached by
> > the user before enabling the buffer. The purpose is to not impede
> > removing the driver due to the increased reference count when
> > iio_trigger_set_immutable() or iio_trigger_get() is used.
> > 
> > Signed-off-by: Jorge Marques <jorge.marques@analog.com>
> 
> +CC Rafael; I'd like input on the ACQUIRE + take extra reference pattern
> and whether Rafael thinks it is a good idea!
> 
> > ---
> >  drivers/iio/adc/Kconfig  |   2 +
> >  drivers/iio/adc/ad4062.c | 188 +++++++++++++++++++++++++++++++++++++++++++----
> >  2 files changed, 175 insertions(+), 15 deletions(-)
> > 
> > diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
> > index e506dbe83f488..ddb7820f0bdcc 100644
> > --- a/drivers/iio/adc/Kconfig
> > +++ b/drivers/iio/adc/Kconfig
> > @@ -74,6 +74,8 @@ config AD4062
> >  	tristate "Analog Devices AD4062 Driver"
> >  	depends on I3C
> >  	select REGMAP_I3C
> > +	select IIO_BUFFER
> > +	select IIO_TRIGGERED_BUFFER
> >  	help
> >  	  Say yes here to build support for Analog Devices AD4062 I3C analog
> >  	  to digital converters (ADC).
> > diff --git a/drivers/iio/adc/ad4062.c b/drivers/iio/adc/ad4062.c
> > index 54f7f69e40879..080dc80fd1621 100644
> > --- a/drivers/iio/adc/ad4062.c
> > +++ b/drivers/iio/adc/ad4062.c
> 
> > +static void ad4062_trigger_work(struct work_struct *work)
> > +{
> > +	struct ad4062_state *st =
> > +		container_of(work, struct ad4062_state, trig_conv);
> > +	int ret;
> > +
> > +	/*
> > +	 * Read current conversion, if at reg CONV_READ, stop bit triggers
> > +	 * next sample and does not need writing the address.
> > +	 */
> > +	struct i3c_priv_xfer t[2] = {
> > +		{
> > +			.data.in = &st->buf.be32,
> > +			.len = sizeof(st->buf.be32),
> > +			.rnw = true,
> > +		},
> > +		{
> > +			.data.out = &st->reg_addr_conv,
> > +			.len = sizeof(st->reg_addr_conv),
> > +			.rnw = false,
> > +		},
> > +	};
> > +
> > +	ret = i3c_device_do_priv_xfers(st->i3cdev, &t[0], 1);
> > +	if (ret)
> > +		return;
> > +
> > +	iio_push_to_buffers_with_timestamp(st->indio_dev, &st->buf.be32,
> > +					   iio_get_time_ns(st->indio_dev));
> 
> Use push_to_buffers_with_ts() (this function is deprecated)
> which would have had the helpful result here of pointing out the buffer
> isn't big enough for the timestamp.  So this will write the timestamp
> over later fields in the st structure.
> 
> Given that this sometimes fits in a be16 I wonder if it is worth
> storing those in a be16 element of the kfifo. That will halve it's size
> if the timestamp isn't enabled which would be a nice thing to have.
> Storing in a be32 isn't an ABI issue, it's just a bit unusual
> so if I'm missing some reason it makes more sense then fair enough.
> 
Per last e-mail, due to ad4062 burst avg, it will be kept as 32-bits.
	const bool is_32b = st->chip->prod_id == 0x7C;
	const size_t _sizeof = is_32b ? sizeof(st->buf.be32) : sizeof(st->buf.be16);
	//...
	iio_push_to_buffers_with_ts(st->indio_dev, &st->buf.be32, _sizeof,
				    iio_get_time_ns(st->indio_dev));
> > +	if (st->gpo_irq[1])
> > +		return;
> > +
> > +	i3c_device_do_priv_xfers(st->i3cdev, &t[1], 1);
> > +}
> 
> ...
> 
> > +		{
> > +			.data.out = &st->reg_addr_conv,
> > +			.len = sizeof(st->reg_addr_conv),
> > +			.rnw = false,
> > +		},
> > +		{
> > +			.data.in = &st->buf.be32,
> > +			.len = sizeof(st->buf.be32),
> > +			.rnw = true,
> > +		}
> >  	};
> 
> > @@ -687,6 +782,55 @@ static int ad4062_write_raw(struct iio_dev *indio_dev,
> >  	return ret;
> >  }
> >  
> > +static int ad4062_triggered_buffer_postenable(struct iio_dev *indio_dev)
> > +{
> > +	struct ad4062_state *st = iio_priv(indio_dev);
> > +	int ret;
> > +
> > +	ACQUIRE(pm_runtime_active_try_enabled, pm)(&st->i3cdev->dev);
> > +	ret = ACQUIRE_ERR(pm_runtime_active_try_enabled, &pm);
> 
> This may also be affected by Rafael's patch set to provide some helpers
> to make this more readable.
> 
> 
> > +	if (ret)
> > +		return ret;
> > +
> > +	ret = ad4062_set_operation_mode(st, st->mode);
> > +	if (ret)
> > +		return ret;
> > +
> > +	/* CONV_READ requires read to trigger first sample. */
> > +	struct i3c_priv_xfer t[2] = {
> > +		{
> > +			.data.out = &st->reg_addr_conv,
> > +			.len = sizeof(st->reg_addr_conv),
> > +			.rnw = false,
> > +		},
> > +		{
> > +			.data.in = &st->buf.be32,
> > +			.len = sizeof(st->buf.be32),
> > +			.rnw = true,
> > +		}
> > +	};
> > +
> > +	ret = i3c_device_do_priv_xfers(st->i3cdev, t, st->gpo_irq[1] ? 2 : 1);
> > +	if (ret)
> > +		return ret;
> > +
> > +	pm_runtime_get_noresume(&st->i3cdev->dev);
> As per my late reply I'm not keen on the double increment as a complex way
> to steal the ACQUIRED() reference. Might be better to just factor the stuff
> where you currently have acquired a reference out into a helper and use
> the traditional runtime pm calls in this outer function.
>  
I will use a helper pm_ad4062_monitor_mode_enable() and
pm_ad4062_triggered_buffer_postenable().

> > +	return 0;
> 
> 
Best regards,
Jorge