[{"id":3673458,"web_url":"http://patchwork.ozlabs.org/comment/3673458/","msgid":"<e38e5b97-e90f-4613-a15e-6c3d08cd77f7@baylibre.com>","list_archive_url":null,"date":"2026-04-04T15:12:04","subject":"Re: [PATCH v6 3/4] iio: adc: ad4691: add triggered buffer support","submitter":{"id":87228,"url":"http://patchwork.ozlabs.org/api/people/87228/","name":"David Lechner","email":"dlechner@baylibre.com"},"content":"On 4/3/26 6:03 AM, Radu Sabau via B4 Relay wrote:\n> From: Radu Sabau <radu.sabau@analog.com>\n> \n> Add buffered capture support using the IIO triggered buffer framework.\n> \n> CNV Burst Mode: the GP pin identified by interrupt-names in the device\n> tree is configured as DATA_READY output. The IRQ handler stops\n> conversions and fires the IIO trigger; the trigger handler executes a\n> pre-built SPI message that reads all active channels from the AVG_IN\n> accumulator registers and then resets accumulator state and restarts\n> conversions for the next cycle.\n> \n> Manual Mode: CNV is tied to SPI CS so each transfer simultaneously\n> reads the previous result and starts the next conversion (pipelined\n> N+1 scheme). At preenable time a pre-built, optimised SPI message of\n> N+1 transfers is constructed (N channel reads plus one NOOP to drain\n> the pipeline). The trigger handler executes the message in a single\n> spi_sync() call and collects the results. An external trigger (e.g.\n> iio-trig-hrtimer) is required to drive the trigger at the desired\n> sample rate.\n> \n> Both modes share the same trigger handler and push a complete scan —\n> one u16 slot per channel at its scan_index position, followed by a\n> timestamp — to the IIO buffer via iio_push_to_buffers_with_ts().\n> \n> The CNV Burst Mode sampling frequency (PWM period) is exposed as a\n> buffer-level attribute via IIO_DEVICE_ATTR.\n> \n> Signed-off-by: Radu Sabau <radu.sabau@analog.com>\n> ---\n>  drivers/iio/adc/Kconfig  |   2 +\n>  drivers/iio/adc/ad4691.c | 592 ++++++++++++++++++++++++++++++++++++++++++++++-\n>  2 files changed, 592 insertions(+), 2 deletions(-)\n> \n> diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig\n> index 3685a03aa8dc..d498f16c0816 100644\n> --- a/drivers/iio/adc/Kconfig\n> +++ b/drivers/iio/adc/Kconfig\n> @@ -142,6 +142,8 @@ config AD4170_4\n>  config AD4691\n>  \ttristate \"Analog Devices AD4691 Family ADC Driver\"\n>  \tdepends on SPI\n> +\tselect IIO_BUFFER\n> +\tselect IIO_TRIGGERED_BUFFER\n>  \tselect REGMAP\n>  \thelp\n>  \t  Say yes here to build support for Analog Devices AD4691 Family MuxSAR\n> diff --git a/drivers/iio/adc/ad4691.c b/drivers/iio/adc/ad4691.c\n> index 43bd408c3d11..f2a7273e43b9 100644\n> --- a/drivers/iio/adc/ad4691.c\n> +++ b/drivers/iio/adc/ad4691.c\n> @@ -5,15 +5,19 @@\n>   */\n>  #include <linux/array_size.h>\n>  #include <linux/bitfield.h>\n> +#include <linux/bitmap.h>\n>  #include <linux/bitops.h>\n>  #include <linux/cleanup.h>\n>  #include <linux/delay.h>\n>  #include <linux/dev_printk.h>\n>  #include <linux/device/devres.h>\n>  #include <linux/err.h>\n> +#include <linux/interrupt.h>\n>  #include <linux/math.h>\n>  #include <linux/module.h>\n>  #include <linux/mod_devicetable.h>\n> +#include <linux/property.h>\n> +#include <linux/pwm.h>\n>  #include <linux/regmap.h>\n>  #include <linux/regulator/consumer.h>\n>  #include <linux/reset.h>\n> @@ -21,7 +25,12 @@\n>  #include <linux/units.h>\n>  #include <linux/unaligned.h>\n>  \n> +#include <linux/iio/buffer.h>\n>  #include <linux/iio/iio.h>\n> +#include <linux/iio/sysfs.h>\n> +#include <linux/iio/trigger.h>\n> +#include <linux/iio/triggered_buffer.h>\n> +#include <linux/iio/trigger_consumer.h>\n>  \n>  #define AD4691_VREF_uV_MIN\t\t\t2400000\n>  #define AD4691_VREF_uV_MAX\t\t\t5250000\n> @@ -30,6 +39,8 @@\n>  #define AD4691_VREF_3P3_uV_MAX\t\t\t3750000\n>  #define AD4691_VREF_4P096_uV_MAX\t\t4500000\n>  \n> +#define AD4691_CNV_DUTY_CYCLE_NS\t\t380\n> +\n>  #define AD4691_SPI_CONFIG_A_REG\t\t\t0x000\n>  #define AD4691_SW_RESET\t\t\t\t(BIT(7) | BIT(0))\n>  \n> @@ -37,6 +48,7 @@\n>  #define AD4691_CLAMP_STATUS1_REG\t\t0x01A\n>  #define AD4691_CLAMP_STATUS2_REG\t\t0x01B\n>  #define AD4691_DEVICE_SETUP\t\t\t0x020\n> +#define AD4691_MANUAL_MODE\t\t\tBIT(2)\n>  #define AD4691_LDO_EN\t\t\t\tBIT(4)\n>  #define AD4691_REF_CTRL\t\t\t\t0x021\n>  #define AD4691_REF_CTRL_MASK\t\t\tGENMASK(4, 2)\n> @@ -44,13 +56,18 @@\n>  #define AD4691_OSC_FREQ_REG\t\t\t0x023\n>  #define AD4691_OSC_FREQ_MASK\t\t\tGENMASK(3, 0)\n>  #define AD4691_STD_SEQ_CONFIG\t\t\t0x025\n> +#define AD4691_SEQ_ALL_CHANNELS_OFF\t\t0x00\n>  #define AD4691_SPARE_CONTROL\t\t\t0x02A\n>  \n> +#define AD4691_NOOP\t\t\t\t0x00\n> +#define AD4691_ADC_CHAN(ch)\t\t\t((0x10 + (ch)) << 3)\n> +\n>  #define AD4691_OSC_EN_REG\t\t\t0x180\n>  #define AD4691_STATE_RESET_REG\t\t\t0x181\n>  #define AD4691_STATE_RESET_ALL\t\t\t0x01\n>  #define AD4691_ADC_SETUP\t\t\t0x182\n>  #define AD4691_ADC_MODE_MASK\t\t\tGENMASK(1, 0)\n> +#define AD4691_CNV_BURST_MODE\t\t\t0x01\n>  #define AD4691_AUTONOMOUS_MODE\t\t\t0x02\n>  /*\n>   * ACC_MASK_REG covers both mask bytes via ADDR_DESCENDING SPI: writing a\n> @@ -60,6 +77,8 @@\n>  #define AD4691_ACC_DEPTH_IN(n)\t\t\t(0x186 + (n))\n>  #define AD4691_GPIO_MODE1_REG\t\t\t0x196\n>  #define AD4691_GPIO_MODE2_REG\t\t\t0x197\n> +#define AD4691_GP_MODE_MASK\t\t\tGENMASK(3, 0)\n> +#define AD4691_GP_MODE_DATA_READY\t\t0x06\n>  #define AD4691_GPIO_READ\t\t\t0x1A0\n>  #define AD4691_ACC_STATUS_FULL1_REG\t\t0x1B0\n>  #define AD4691_ACC_STATUS_FULL2_REG\t\t0x1B1\n> @@ -95,9 +114,11 @@ struct ad4691_chip_info {\n>  \t\t.type = IIO_VOLTAGE,\t\t\t\t\t\\\n>  \t\t.indexed = 1,\t\t\t\t\t\t\\\n>  \t\t.info_mask_separate = BIT(IIO_CHAN_INFO_RAW)\t\t\\\n> -\t\t\t\t    | BIT(IIO_CHAN_INFO_SAMP_FREQ),\t\\\n> +\t\t\t\t    | BIT(IIO_CHAN_INFO_SAMP_FREQ)\t\\\n> +\t\t\t\t    | BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO),\t\\\n>  \t\t.info_mask_separate_available =\t\t\t\t\\\n> -\t\t\t\t      BIT(IIO_CHAN_INFO_SAMP_FREQ),\t\\\n> +\t\t\t\t      BIT(IIO_CHAN_INFO_SAMP_FREQ)\t\\\n> +\t\t\t\t    | BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO),\t\\\n>  \t\t.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SCALE),\t\\\n>  \t\t.channel = ch,\t\t\t\t\t\t\\\n>  \t\t.scan_index = ch,\t\t\t\t\t\\\n> @@ -125,6 +146,7 @@ static const struct iio_chan_spec ad4691_channels[] = {\n>  \tAD4691_CHANNEL(13),\n>  \tAD4691_CHANNEL(14),\n>  \tAD4691_CHANNEL(15),\n> +\tIIO_CHAN_SOFT_TIMESTAMP(16),\n>  };\n>  \n>  static const struct iio_chan_spec ad4693_channels[] = {\n> @@ -136,6 +158,7 @@ static const struct iio_chan_spec ad4693_channels[] = {\n>  \tAD4691_CHANNEL(5),\n>  \tAD4691_CHANNEL(6),\n>  \tAD4691_CHANNEL(7),\n> +\tIIO_CHAN_SOFT_TIMESTAMP(8),\n>  };\n>  \n>  /*\n> @@ -162,6 +185,14 @@ static const int ad4691_osc_freqs_Hz[] = {\n>  \t[0xF] = 1250,\n>  };\n>  \n> +static const char * const ad4691_gp_names[] = { \"gp0\", \"gp1\", \"gp2\", \"gp3\" };\n> +\n> +/*\n> + * Valid ACC_DEPTH values where the effective divisor equals the count.\n> + * From Table 13: ACC_DEPTH = 2^N yields right-shift = N, divisor = 2^N.\n> + */\n> +static const int ad4691_oversampling_ratios[] = { 1, 2, 4, 8, 16, 32 };\n\nIt would be nice to add oversampling in a separate commit as that is a\nseparate feature.\n\nOversampling also affects sampling frequency. When there isn't oversampling,\nsample rate == conversion rate. However, with oversampling, sample rate ==\nconversion rate / oversampling ratio (because each sample involves #OSR\nconversions).\n\nSo more code will be required to make IIO_CHAN_INFO_SAMP_FREQ attributes\n(both read/write_raw and read_avail) adjust the values based on the current\noversampling ratio.\n\n> +\n>  static const struct ad4691_chip_info ad4691_chip_info = {\n>  \t.channels = ad4691_channels,\n>  \t.name = \"ad4691\",\n> @@ -193,16 +224,55 @@ static const struct ad4691_chip_info ad4694_chip_info = {\n>  struct ad4691_state {\n>  \tconst struct ad4691_chip_info *info;\n>  \tstruct regmap *regmap;\n> +\n> +\tstruct pwm_device *conv_trigger;\n> +\tint irq;\n> +\n> +\tbool manual_mode;\n> +\n>  \tint vref_uV;\n> +\tu8 osr[16];\n>  \tbool refbuf_en;\n>  \tbool ldo_en;\n> +\tu32 cnv_period_ns;\n>  \t/*\n>  \t * Synchronize access to members of the driver state, and ensure\n>  \t * atomicity of consecutive SPI operations.\n>  \t */\n>  \tstruct mutex lock;\n> +\t/*\n> +\t * Per-buffer-enable lifetime resources:\n> +\t * Manual Mode - a pre-built SPI message that clocks out N+1\n> +\t *\t\t transfers in one go.\n> +\t * CNV Burst Mode - a pre-built SPI message that clocks out 2*N\n> +\t *\t\t    transfers in one go.\n> +\t */\n> +\tstruct spi_message scan_msg;\n> +\tstruct spi_transfer *scan_xfers;\n> +\t__be16 *scan_tx;\n> +\t__be16 *scan_rx;\n\nWhy not embed these arrays here? Then we don't have to deal with\nalloc/free later.\n\n> +\t/* Scan buffer: one slot per channel plus timestamp */\n> +\tstruct {\n> +\t\tu16 vals[16];\n> +\t\taligned_s64 ts;\n> +\t} scan __aligned(IIO_DMA_MINALIGN);\n\nBetter would be IIO_DECLARE_BUFFER_WITH_TS() since we don't always\nuse all vals.\n\nAlso, current usage doesn't need to be DMA-safe because scan_tx\nis being used for the actual SPI xfer.\n\n>  };\n>  \n> +/*\n> + * Configure the given GP pin (0-3) as DATA_READY output.\n> + * GP0/GP1 → GPIO_MODE1_REG, GP2/GP3 → GPIO_MODE2_REG.\n> + * Even pins occupy bits [3:0], odd pins bits [7:4].\n> + */\n> +static int ad4691_gpio_setup(struct ad4691_state *st, unsigned int gp_num)\n> +{\n> +\tunsigned int shift = 4 * (gp_num % 2);\n> +\n> +\treturn regmap_update_bits(st->regmap,\n> +\t\t\t\t  AD4691_GPIO_MODE1_REG + gp_num / 2,\n> +\t\t\t\t  AD4691_GP_MODE_MASK << shift,\n> +\t\t\t\t  AD4691_GP_MODE_DATA_READY << shift);\n> +}\n> +\n>  static int ad4691_reg_read(void *context, unsigned int reg, unsigned int *val)\n>  {\n>  \tstruct spi_device *spi = context;\n> @@ -362,6 +432,24 @@ static int ad4691_set_sampling_freq(struct iio_dev *indio_dev, int freq)\n>  \treturn -EINVAL;\n>  }\n>  \n> +static int ad4691_set_oversampling_ratio(struct iio_dev *indio_dev,\n> +\t\t\t\t\t const struct iio_chan_spec *chan,\n> +\t\t\t\t\t int osr)\n> +{\n> +\tstruct ad4691_state *st = iio_priv(indio_dev);\n> +\n> +\tif (osr < 1 || osr > 32 || !is_power_of_2(osr))\n> +\t\treturn -EINVAL;\n> +\n> +\tIIO_DEV_ACQUIRE_DIRECT_MODE(indio_dev, claim);\n> +\tif (IIO_DEV_ACQUIRE_FAILED(claim))\n> +\t\treturn -EBUSY;\n> +\n> +\tst->osr[chan->scan_index] = osr;\n> +\treturn regmap_write(st->regmap,\n> +\t\t\t    AD4691_ACC_DEPTH_IN(chan->scan_index), osr);\n> +}\n> +\n>  static int ad4691_read_avail(struct iio_dev *indio_dev,\n>  \t\t\t     struct iio_chan_spec const *chan,\n>  \t\t\t     const int **vals, int *type,\n> @@ -376,6 +464,11 @@ static int ad4691_read_avail(struct iio_dev *indio_dev,\n>  \t\t*type = IIO_VAL_INT;\n>  \t\t*length = ARRAY_SIZE(ad4691_osc_freqs_Hz) - start;\n>  \t\treturn IIO_AVAIL_LIST;\n> +\tcase IIO_CHAN_INFO_OVERSAMPLING_RATIO:\n> +\t\t*vals = ad4691_oversampling_ratios;\n> +\t\t*type = IIO_VAL_INT;\n> +\t\t*length = ARRAY_SIZE(ad4691_oversampling_ratios);\n> +\t\treturn IIO_AVAIL_LIST;\n>  \tdefault:\n>  \t\treturn -EINVAL;\n>  \t}\n> @@ -406,6 +499,11 @@ static int ad4691_single_shot_read(struct iio_dev *indio_dev,\n>  \tif (ret)\n>  \t\treturn ret;\n>  \n> +\tret = regmap_write(st->regmap, AD4691_ACC_DEPTH_IN(chan->scan_index),\n> +\t\t\t   st->osr[chan->scan_index]);\n> +\tif (ret)\n> +\t\treturn ret;\n> +\n>  \tret = regmap_read(st->regmap, AD4691_OSC_FREQ_REG, &reg_val);\n>  \tif (ret)\n>  \t\treturn ret;\n> @@ -452,6 +550,9 @@ static int ad4691_read_raw(struct iio_dev *indio_dev,\n>  \t}\n>  \tcase IIO_CHAN_INFO_SAMP_FREQ:\n>  \t\treturn ad4691_get_sampling_freq(st, val);\n> +\tcase IIO_CHAN_INFO_OVERSAMPLING_RATIO:\n> +\t\t*val = st->osr[chan->scan_index];\n> +\t\treturn IIO_VAL_INT;\n>  \tcase IIO_CHAN_INFO_SCALE:\n>  \t\t*val = st->vref_uV / (MICRO / MILLI);\n>  \t\t*val2 = chan->scan_type.realbits;\n> @@ -468,6 +569,8 @@ static int ad4691_write_raw(struct iio_dev *indio_dev,\n>  \tswitch (mask) {\n>  \tcase IIO_CHAN_INFO_SAMP_FREQ:\n>  \t\treturn ad4691_set_sampling_freq(indio_dev, val);\n> +\tcase IIO_CHAN_INFO_OVERSAMPLING_RATIO:\n> +\t\treturn ad4691_set_oversampling_ratio(indio_dev, chan, val);\n>  \tdefault:\n>  \t\treturn -EINVAL;\n>  \t}\n> @@ -486,6 +589,385 @@ static int ad4691_reg_access(struct iio_dev *indio_dev, unsigned int reg,\n>  \treturn regmap_write(st->regmap, reg, writeval);\n>  }\n>  \n> +static int ad4691_set_pwm_freq(struct ad4691_state *st, int freq)\n> +{\n> +\tif (!freq)\n> +\t\treturn -EINVAL;\n> +\n> +\tst->cnv_period_ns = DIV_ROUND_UP(NSEC_PER_SEC, freq);\n> +\treturn 0;\n> +}\n> +\n> +static int ad4691_sampling_enable(struct ad4691_state *st, bool enable)\n> +{\n> +\tstruct pwm_state conv_state = {\n> +\t\t.period     = st->cnv_period_ns,\n> +\t\t.duty_cycle = AD4691_CNV_DUTY_CYCLE_NS,\n> +\t\t.polarity   = PWM_POLARITY_NORMAL,\n> +\t\t.enabled    = enable,\n> +\t};\n> +\n> +\treturn pwm_apply_might_sleep(st->conv_trigger, &conv_state);\n> +}\n> +\n> +/*\n> + * ad4691_enter_conversion_mode - Switch the chip to its buffer conversion mode.\n> + *\n> + * Configures the ADC hardware registers for the mode selected at probe\n> + * (CNV_BURST or MANUAL). Called from buffer preenable before starting\n> + * sampling. The chip is in AUTONOMOUS mode during idle (for read_raw).\n> + */\n> +static int ad4691_enter_conversion_mode(struct ad4691_state *st)\n> +{\n> +\tint ret;\n> +\n> +\tif (st->manual_mode)\n> +\t\treturn regmap_update_bits(st->regmap, AD4691_DEVICE_SETUP,\n> +\t\t\t\t\t  AD4691_MANUAL_MODE, AD4691_MANUAL_MODE);\n> +\n> +\tret = regmap_update_bits(st->regmap, AD4691_ADC_SETUP,\n> +\t\t\t\t AD4691_ADC_MODE_MASK, AD4691_CNV_BURST_MODE);\n> +\tif (ret)\n> +\t\treturn ret;\n> +\n> +\treturn regmap_write(st->regmap, AD4691_STATE_RESET_REG,\n> +\t\t\t    AD4691_STATE_RESET_ALL);\n> +}\n> +\n> +/*\n> + * ad4691_exit_conversion_mode - Return the chip to AUTONOMOUS mode.\n> + *\n> + * Called from buffer postdisable to restore the chip to the\n> + * idle state used by read_raw. Clears the sequencer and resets state.\n> + */\n> +static int ad4691_exit_conversion_mode(struct ad4691_state *st)\n> +{\n> +\tif (st->manual_mode)\n> +\t\treturn regmap_update_bits(st->regmap, AD4691_DEVICE_SETUP,\n> +\t\t\t\t\t  AD4691_MANUAL_MODE, 0);\n> +\n> +\treturn regmap_update_bits(st->regmap, AD4691_ADC_SETUP,\n> +\t\t\t\t  AD4691_ADC_MODE_MASK, AD4691_AUTONOMOUS_MODE);\n> +}\n> +\n> +static void ad4691_free_scan_bufs(struct ad4691_state *st)\n> +{\n> +\tkfree(st->scan_xfers);\n> +\tkfree(st->scan_tx);\n> +\tkfree(st->scan_rx);\n> +}\n> +\n> +static int ad4691_manual_buffer_preenable(struct iio_dev *indio_dev)\n> +{\n> +\tstruct ad4691_state *st = iio_priv(indio_dev);\n> +\tstruct device *dev = regmap_get_device(st->regmap);\n> +\tstruct spi_device *spi = to_spi_device(dev);\n> +\tunsigned int n_active = bitmap_weight(indio_dev->active_scan_mask,\n> +\t\t\t\t\t      iio_get_masklength(indio_dev));\n> +\tunsigned int n_xfers = n_active + 1;\n> +\tunsigned int k, i;\n> +\tint ret;\n> +\n> +\tst->scan_xfers = kcalloc(n_xfers, sizeof(*st->scan_xfers), GFP_KERNEL);\n\nUsually, we make st->scan_xfers a fixed array with the max number of possible\nxfers. Then we don't have to deal with alloc/free.\n\n> +\tif (!st->scan_xfers)\n> +\t\treturn -ENOMEM;\n> +\n> +\tst->scan_tx = kcalloc(n_xfers, sizeof(*st->scan_tx), GFP_KERNEL);\n> +\tif (!st->scan_tx) {\n> +\t\tkfree(st->scan_xfers);\n> +\t\treturn -ENOMEM;\n> +\t}\n> +\n> +\tst->scan_rx = kcalloc(n_xfers, sizeof(*st->scan_rx), GFP_KERNEL);\n> +\tif (!st->scan_rx) {\n> +\t\tkfree(st->scan_tx);\n> +\t\tkfree(st->scan_xfers);\n> +\t\treturn -ENOMEM;\n> +\t}\n> +\n> +\tspi_message_init(&st->scan_msg);\n> +\n> +\tk = 0;\n> +\tiio_for_each_active_channel(indio_dev, i) {\n> +\t\tst->scan_tx[k] = cpu_to_be16(AD4691_ADC_CHAN(i));\n> +\t\tst->scan_xfers[k].tx_buf = &st->scan_tx[k];\n> +\t\tst->scan_xfers[k].rx_buf = &st->scan_rx[k];\n> +\t\tst->scan_xfers[k].len = sizeof(__be16);\n> +\t\tst->scan_xfers[k].cs_change = 1;\n> +\t\tspi_message_add_tail(&st->scan_xfers[k], &st->scan_msg);\n> +\t\tk++;\n> +\t}\n> +\n> +\t/* Final NOOP transfer to retrieve last channel's result. */\n> +\tst->scan_tx[k] = cpu_to_be16(AD4691_NOOP);\n> +\tst->scan_xfers[k].tx_buf = &st->scan_tx[k];\n> +\tst->scan_xfers[k].rx_buf = &st->scan_rx[k];\n> +\tst->scan_xfers[k].len = sizeof(__be16);\n> +\tspi_message_add_tail(&st->scan_xfers[k], &st->scan_msg);\n> +\n> +\tst->scan_msg.spi = spi;\n\nThis isn't how the SPI framework is intended to be used. We should\nhave st->spi = spi in probe instead.\n\n> +\n> +\tret = spi_optimize_message(spi, &st->scan_msg);\n> +\tif (ret) {\n> +\t\tad4691_free_scan_bufs(st);\n> +\t\treturn ret;\n> +\t}\n> +\n> +\tret = ad4691_enter_conversion_mode(st);\n> +\tif (ret) {\n> +\t\tspi_unoptimize_message(&st->scan_msg);\n> +\t\tad4691_free_scan_bufs(st);\n> +\t\treturn ret;\n> +\t}\n> +\n> +\treturn 0;\n> +}\n> +\n> +static int ad4691_manual_buffer_postdisable(struct iio_dev *indio_dev)\n> +{\n> +\tstruct ad4691_state *st = iio_priv(indio_dev);\n> +\tint ret;\n> +\n> +\tret = ad4691_exit_conversion_mode(st);\n> +\tspi_unoptimize_message(&st->scan_msg);\n> +\tad4691_free_scan_bufs(st);\n> +\treturn ret;\n> +}\n> +\n> +static const struct iio_buffer_setup_ops ad4691_manual_buffer_setup_ops = {\n> +\t.preenable = &ad4691_manual_buffer_preenable,\n> +\t.postdisable = &ad4691_manual_buffer_postdisable,\n> +};\n> +\n> +static int ad4691_cnv_burst_buffer_preenable(struct iio_dev *indio_dev)\n> +{\n> +\tstruct ad4691_state *st = iio_priv(indio_dev);\n> +\tstruct device *dev = regmap_get_device(st->regmap);\n> +\tstruct spi_device *spi = to_spi_device(dev);\n> +\tunsigned int n_active = bitmap_weight(indio_dev->active_scan_mask,\n> +\t\t\t\t\t      iio_get_masklength(indio_dev));\n> +\tunsigned int bit, k, i;\n> +\tint ret;\n> +\n> +\tst->scan_xfers = kcalloc(2 * n_active, sizeof(*st->scan_xfers), GFP_KERNEL);\n> +\tif (!st->scan_xfers)\n> +\t\treturn -ENOMEM;\n> +\n> +\tst->scan_tx = kcalloc(n_active, sizeof(*st->scan_tx), GFP_KERNEL);\n> +\tif (!st->scan_tx) {\n> +\t\tkfree(st->scan_xfers);\n> +\t\treturn -ENOMEM;\n> +\t}\n> +\n> +\tst->scan_rx = kcalloc(n_active, sizeof(*st->scan_rx), GFP_KERNEL);\n> +\tif (!st->scan_rx) {\n> +\t\tkfree(st->scan_tx);\n> +\t\tkfree(st->scan_xfers);\n> +\t\treturn -ENOMEM;\n> +\t}\n> +\n> +\tspi_message_init(&st->scan_msg);\n> +\n> +\t/*\n> +\t * Each AVG_IN read needs two transfers: a 2-byte address write phase\n> +\t * followed by a 2-byte data read phase. CS toggles between channels\n> +\t * (cs_change=1 on the read phase of all but the last channel).\n> +\t */\n> +\tk = 0;\n> +\tiio_for_each_active_channel(indio_dev, i) {\n> +\t\tst->scan_tx[k] = cpu_to_be16(0x8000 | AD4691_AVG_IN(i));\n> +\t\tst->scan_xfers[2 * k].tx_buf = &st->scan_tx[k];\n> +\t\tst->scan_xfers[2 * k].len = sizeof(__be16);\n> +\t\tspi_message_add_tail(&st->scan_xfers[2 * k], &st->scan_msg);\n> +\t\tst->scan_xfers[2 * k + 1].rx_buf = &st->scan_rx[k];\n> +\t\tst->scan_xfers[2 * k + 1].len = sizeof(__be16);\n> +\t\tif (k < n_active - 1)\n> +\t\t\tst->scan_xfers[2 * k + 1].cs_change = 1;\n> +\t\tspi_message_add_tail(&st->scan_xfers[2 * k + 1], &st->scan_msg);\n> +\t\tk++;\n> +\t}\n> +\n> +\tst->scan_msg.spi = spi;\n> +\n> +\tret = spi_optimize_message(spi, &st->scan_msg);\n> +\tif (ret)\n> +\t\tgoto err_free_bufs;\n> +\n> +\tret = regmap_write(st->regmap, AD4691_STD_SEQ_CONFIG,\n> +\t\t\t   bitmap_read(indio_dev->active_scan_mask, 0,\n> +\t\t\t\t       iio_get_masklength(indio_dev)));\n> +\tif (ret)\n> +\t\tgoto err;\n> +\n> +\tret = regmap_write(st->regmap, AD4691_ACC_MASK_REG,\n> +\t\t\t   ~bitmap_read(indio_dev->active_scan_mask, 0,\n> +\t\t\t\tiio_get_masklength(indio_dev)) & GENMASK(15, 0));\n> +\tif (ret)\n> +\t\tgoto err;\n> +\n> +\tiio_for_each_active_channel(indio_dev, bit) {\n> +\t\tret = regmap_write(st->regmap, AD4691_ACC_DEPTH_IN(bit),\n> +\t\t\t\t   st->osr[bit]);\n> +\t\tif (ret)\n> +\t\t\tgoto err;\n> +\t}\n> +\n> +\tret = ad4691_enter_conversion_mode(st);\n> +\tif (ret)\n> +\t\tgoto err;\n> +\n> +\tret = ad4691_sampling_enable(st, true);\n> +\tif (ret)\n> +\t\tgoto err;\n\nDo we need to do something to exit conversion mode on error here?\n\n> +\n> +\tenable_irq(st->irq);\n> +\treturn 0;\n> +err:\n> +\tspi_unoptimize_message(&st->scan_msg);\n> +err_free_bufs:\n> +\tad4691_free_scan_bufs(st);\n> +\treturn ret;\n> +}\n> +\n> +static int ad4691_cnv_burst_buffer_postdisable(struct iio_dev *indio_dev)\n> +{\n> +\tstruct ad4691_state *st = iio_priv(indio_dev);\n> +\tint ret;\n> +\n> +\tdisable_irq(st->irq);\n> +\n> +\tret = ad4691_sampling_enable(st, false);\n> +\tif (ret)\n> +\t\treturn ret;\n> +\n> +\tret = regmap_write(st->regmap, AD4691_STD_SEQ_CONFIG,\n> +\t\t\t   AD4691_SEQ_ALL_CHANNELS_OFF);\n> +\tif (ret)\n> +\t\treturn ret;\n> +\n\nThis order of unwinding is not the exact reverse of how it was\nset up. So either the order needs to be fixed or a comment added\nexplaining why this order is needed instead.\n\n> +\tret = ad4691_exit_conversion_mode(st);\n> +\tspi_unoptimize_message(&st->scan_msg);\n> +\tad4691_free_scan_bufs(st);\n> +\treturn ret;\n> +}\n> +\n> +static const struct iio_buffer_setup_ops ad4691_cnv_burst_buffer_setup_ops = {\n> +\t.preenable = &ad4691_cnv_burst_buffer_preenable,\n> +\t.postdisable = &ad4691_cnv_burst_buffer_postdisable,\n> +};\n> +\n> +static ssize_t sampling_frequency_show(struct device *dev,\n> +\t\t\t\t       struct device_attribute *attr,\n> +\t\t\t\t       char *buf)\n> +{\n> +\tstruct iio_dev *indio_dev = dev_to_iio_dev(dev);\n> +\tstruct ad4691_state *st = iio_priv(indio_dev);\n> +\n> +\treturn sysfs_emit(buf, \"%u\\n\", (u32)(NSEC_PER_SEC / st->cnv_period_ns));\n> +}\n> +\n> +static ssize_t sampling_frequency_store(struct device *dev,\n> +\t\t\t\t\tstruct device_attribute *attr,\n> +\t\t\t\t\tconst char *buf, size_t len)\n> +{\n> +\tstruct iio_dev *indio_dev = dev_to_iio_dev(dev);\n> +\tstruct ad4691_state *st = iio_priv(indio_dev);\n> +\tint freq, ret;\n> +\n> +\tret = kstrtoint(buf, 10, &freq);\n> +\tif (ret)\n> +\t\treturn ret;\n> +\n> +\tguard(mutex)(&st->lock);\n> +\n> +\tif (iio_buffer_enabled(indio_dev))\n\nThis should be using iio_device_claim_direct(), otherwise\nit is racy.\n\n> +\t\treturn -EBUSY;\n> +\n> +\tret = ad4691_set_pwm_freq(st, freq);\n> +\tif (ret)\n> +\t\treturn ret;\n> +\n> +\treturn len;\n> +}\n> +\n> +static IIO_DEVICE_ATTR(sampling_frequency, 0644,\n> +\t\t       sampling_frequency_show,\n> +\t\t       sampling_frequency_store, 0);\n> +\n> +static const struct iio_dev_attr *ad4691_buffer_attrs[] = {\n> +\t&iio_dev_attr_sampling_frequency,\n> +\tNULL\n> +};\n> +\n> +static irqreturn_t ad4691_irq(int irq, void *private)\n> +{\n> +\tstruct iio_dev *indio_dev = private;\n> +\tstruct ad4691_state *st = iio_priv(indio_dev);\n> +\n> +\t/*\n> +\t * GPx has asserted: stop conversions before reading so the\n\nDoes this happen per-channel or only once per complete sequence?\n\n> +\t * accumulator does not continue sampling while the trigger handler\n> +\t * processes the data. Then fire the IIO trigger to push the sample\n> +\t * to the buffer.\n> +\t */\n> +\tad4691_sampling_enable(st, false);\n> +\tiio_trigger_poll(indio_dev->trig);\n> +\n> +\treturn IRQ_HANDLED;\n> +}\n> +\n> +static const struct iio_trigger_ops ad4691_trigger_ops = {\n> +\t.validate_device = iio_trigger_validate_own_device,\n> +};\n> +\n> +static int ad4691_read_scan(struct iio_dev *indio_dev, s64 timestamp)\n> +{\n> +\tstruct ad4691_state *st = iio_priv(indio_dev);\n> +\tunsigned int i, k = 0;\n> +\tint ret;\n> +\n> +\tguard(mutex)(&st->lock);\n> +\n> +\tret = spi_sync(st->scan_msg.spi, &st->scan_msg);\n> +\tif (ret)\n> +\t\treturn ret;\n> +\n> +\tif (st->manual_mode) {\n> +\t\tiio_for_each_active_channel(indio_dev, i) {\n> +\t\t\tst->scan.vals[i] = be16_to_cpu(st->scan_rx[k + 1]);\n> +\t\t\tk++;\n> +\t\t}\n> +\t} else {\n> +\t\tiio_for_each_active_channel(indio_dev, i) {\n> +\t\t\tst->scan.vals[i] = be16_to_cpu(st->scan_rx[k]);\n> +\t\t\tk++;\n> +\t\t}\n\nI suppose this is fine, but we usually try to avoid extra copiying and\nbyte swapping of bufferes like this if we can. It seems completly doable\nin both modes. Manual mode will just one extra two-byte buffer for the\nthrow-away conversion on the first read xfer (or just write to the same\nelement twice).\n\n> +\n> +\t\tret = regmap_write(st->regmap, AD4691_STATE_RESET_REG,\n> +\t\t\t\t   AD4691_STATE_RESET_ALL);\n> +\t\tif (ret)\n> +\t\t\treturn ret;\n> +\n> +\t\tret = ad4691_sampling_enable(st, true);\n> +\t\tif (ret)\n> +\t\t\treturn ret;\n> +\t}\n> +\n> +\tiio_push_to_buffers_with_ts(indio_dev, &st->scan, sizeof(st->scan),\n> +\t\t\t\t    timestamp);\n> +\treturn 0;\n> +}\n> +\n> +static irqreturn_t ad4691_trigger_handler(int irq, void *p)\n> +{\n> +\tstruct iio_poll_func *pf = p;\n> +\tstruct iio_dev *indio_dev = pf->indio_dev;\n> +\n> +\tad4691_read_scan(indio_dev, pf->timestamp);\n> +\tiio_trigger_notify_done(indio_dev->trig);\n> +\treturn IRQ_HANDLED;\n> +}\n> +\n>  static const struct iio_info ad4691_info = {\n>  \t.read_raw = &ad4691_read_raw,\n>  \t.write_raw = &ad4691_write_raw,\n> @@ -493,6 +975,18 @@ static const struct iio_info ad4691_info = {\n>  \t.debugfs_reg_access = &ad4691_reg_access,\n>  };\n>  \n> +static int ad4691_pwm_setup(struct ad4691_state *st)\n> +{\n> +\tstruct device *dev = regmap_get_device(st->regmap);\n> +\n> +\tst->conv_trigger = devm_pwm_get(dev, \"cnv\");\n> +\tif (IS_ERR(st->conv_trigger))\n> +\t\treturn dev_err_probe(dev, PTR_ERR(st->conv_trigger),\n> +\t\t\t\t     \"Failed to get cnv pwm\\n\");\n> +\n> +\treturn ad4691_set_pwm_freq(st, st->info->max_rate);\n> +}\n> +\n>  static int ad4691_regulator_setup(struct ad4691_state *st)\n>  {\n>  \tstruct device *dev = regmap_get_device(st->regmap);\n> @@ -558,6 +1052,22 @@ static int ad4691_config(struct ad4691_state *st)\n>  \tunsigned int val;\n>  \tint ret;\n>  \n> +\t/*\n> +\t * Determine buffer conversion mode from DT: if a PWM is provided it\n> +\t * drives the CNV pin (CNV_BURST_MODE); otherwise CNV is tied to CS\n> +\t * and each SPI transfer triggers a conversion (MANUAL_MODE).\n> +\t * Both modes idle in AUTONOMOUS mode so that read_raw can use the\n> +\t * internal oscillator without disturbing the hardware configuration.\n> +\t */\n> +\tif (device_property_present(dev, \"pwms\")) {\n> +\t\tst->manual_mode = false;\n> +\t\tret = ad4691_pwm_setup(st);\n> +\t\tif (ret)\n> +\t\t\treturn ret;\n> +\t} else {\n> +\t\tst->manual_mode = true;\n> +\t}\n> +\n>  \tswitch (st->vref_uV) {\n>  \tcase AD4691_VREF_uV_MIN ... AD4691_VREF_2P5_uV_MAX:\n>  \t\tref_val = AD4691_VREF_2P5;\n> @@ -613,6 +1123,78 @@ static int ad4691_config(struct ad4691_state *st)\n>  \treturn 0;\n>  }\n>  \n> +static int ad4691_setup_triggered_buffer(struct iio_dev *indio_dev,\n> +\t\t\t\t\t struct ad4691_state *st)\n> +{\n> +\tstruct device *dev = regmap_get_device(st->regmap);\n> +\tstruct iio_trigger *trig;\n> +\tunsigned int i;\n> +\tint irq, ret;\n> +\n> +\ttrig = devm_iio_trigger_alloc(dev, \"%s-dev%d\",\n> +\t\t\t\t      indio_dev->name,\n> +\t\t\t\t      iio_device_id(indio_dev));\n> +\tif (!trig)\n> +\t\treturn -ENOMEM;\n> +\n> +\ttrig->ops = &ad4691_trigger_ops;\n> +\tiio_trigger_set_drvdata(trig, st);\n> +\n> +\tret = devm_iio_trigger_register(dev, trig);\n> +\tif (ret)\n> +\t\treturn dev_err_probe(dev, ret, \"IIO trigger register failed\\n\");\n> +\n> +\tindio_dev->trig = iio_trigger_get(trig);\n> +\n> +\tif (!st->manual_mode) {\n\nI would invert the if since the other case is shorter.\n\n> +\t\t/*\n> +\t\t * The GP pin named in interrupt-names asserts at end-of-conversion.\n> +\t\t * The IRQ handler stops conversions and fires the IIO trigger so\n> +\t\t * the trigger handler can read and push the sample to the buffer.\n> +\t\t * The IRQ is kept disabled until the buffer is enabled.\n> +\t\t */\n> +\t\tirq = -ENODEV;\n> +\t\tfor (i = 0; i < ARRAY_SIZE(ad4691_gp_names); i++) {\n> +\t\t\tirq = fwnode_irq_get_byname(dev_fwnode(dev),\n> +\t\t\t\t\t\t    ad4691_gp_names[i]);\n> +\t\t\tif (irq > 0)\n> +\t\t\t\tbreak;\n> +\t\t}\n> +\t\tif (irq <= 0)\n> +\t\t\treturn dev_err_probe(dev, irq < 0 ? irq : -ENODEV,\n> +\t\t\t\t\t     \"failed to get GP interrupt\\n\");\n\nUsually we would usually just use spi->irq since it already\nhas been looked up. But I guess it is OK to do it like this.\n\n> +\n> +\t\tst->irq = irq;\n> +\n> +\t\tret = ad4691_gpio_setup(st, i);\n> +\t\tif (ret)\n> +\t\t\treturn ret;\n> +\n> +\t\t/*\n> +\t\t * IRQ is kept disabled until the buffer is enabled to prevent\n> +\t\t * spurious DATA_READY events before the SPI message is set up.\n> +\t\t */\n> +\t\tret = devm_request_threaded_irq(dev, irq, NULL,\n> +\t\t\t\t\t\t&ad4691_irq,\n> +\t\t\t\t\t\tIRQF_ONESHOT | IRQF_NO_AUTOEN,\n> +\t\t\t\t\t\tindio_dev->name, indio_dev);\n> +\t\tif (ret)\n> +\t\t\treturn ret;\n> +\n> +\t\treturn devm_iio_triggered_buffer_setup_ext(dev, indio_dev,\n> +\t\t\t\t\t\t\t   &iio_pollfunc_store_time,\n> +\t\t\t\t\t\t\t   &ad4691_trigger_handler,\n> +\t\t\t\t\t\t\t   IIO_BUFFER_DIRECTION_IN,\n> +\t\t\t\t\t\t\t   &ad4691_cnv_burst_buffer_setup_ops,\n> +\t\t\t\t\t\t\t   ad4691_buffer_attrs);\n> +\t}\n> +\n> +\treturn devm_iio_triggered_buffer_setup(dev, indio_dev,\n> +\t\t\t\t\t       &iio_pollfunc_store_time,\n> +\t\t\t\t\t       &ad4691_trigger_handler,\n> +\t\t\t\t\t       &ad4691_manual_buffer_setup_ops);\n> +}\n> +","headers":{"Return-Path":"\n <linux-gpio+bounces-34664-incoming=patchwork.ozlabs.org@vger.kernel.org>","X-Original-To":["incoming@patchwork.ozlabs.org","linux-gpio@vger.kernel.org"],"Delivered-To":"patchwork-incoming@legolas.ozlabs.org","Authentication-Results":["legolas.ozlabs.org;\n\tdkim=pass (2048-bit key;\n unprotected) header.d=baylibre-com.20251104.gappssmtp.com\n header.i=@baylibre-com.20251104.gappssmtp.com header.a=rsa-sha256\n header.s=20251104 header.b=LuNKqbtK;\n\tdkim-atps=neutral","legolas.ozlabs.org;\n spf=pass (sender SPF authorized) smtp.mailfrom=vger.kernel.org\n (client-ip=172.105.105.114; helo=tor.lore.kernel.org;\n envelope-from=linux-gpio+bounces-34664-incoming=patchwork.ozlabs.org@vger.kernel.org;\n receiver=patchwork.ozlabs.org)","smtp.subspace.kernel.org;\n\tdkim=pass (2048-bit key) header.d=baylibre-com.20251104.gappssmtp.com\n header.i=@baylibre-com.20251104.gappssmtp.com header.b=\"LuNKqbtK\"","smtp.subspace.kernel.org;\n arc=none smtp.client-ip=209.85.167.182","smtp.subspace.kernel.org;\n dmarc=none (p=none dis=none) header.from=baylibre.com","smtp.subspace.kernel.org;\n spf=pass smtp.mailfrom=baylibre.com"],"Received":["from tor.lore.kernel.org (tor.lore.kernel.org [172.105.105.114])\n\t(using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)\n\t key-exchange x25519)\n\t(No client certificate requested)\n\tby legolas.ozlabs.org (Postfix) with ESMTPS id 4fnzbN2bLbz1xtJ\n\tfor <incoming@patchwork.ozlabs.org>; Sun, 05 Apr 2026 02:12:16 +1100 (AEDT)","from smtp.subspace.kernel.org (conduit.subspace.kernel.org\n [100.90.174.1])\n\tby tor.lore.kernel.org (Postfix) with ESMTP id 58E99300A4C2\n\tfor <incoming@patchwork.ozlabs.org>; Sat,  4 Apr 2026 15:12:14 +0000 (UTC)","from localhost.localdomain (localhost.localdomain [127.0.0.1])\n\tby smtp.subspace.kernel.org (Postfix) with ESMTP id 4D9AC2ED15D;\n\tSat,  4 Apr 2026 15:12:11 +0000 (UTC)","from mail-oi1-f182.google.com (mail-oi1-f182.google.com\n [209.85.167.182])\n\t(using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits))\n\t(No client certificate requested)\n\tby smtp.subspace.kernel.org (Postfix) with ESMTPS id AE2F92D9EDB\n\tfor <linux-gpio@vger.kernel.org>; Sat,  4 Apr 2026 15:12:07 +0000 (UTC)","by mail-oi1-f182.google.com with SMTP id\n 5614622812f47-46eca92a29bso506796b6e.0\n        for <linux-gpio@vger.kernel.org>;\n Sat, 04 Apr 2026 08:12:07 -0700 (PDT)","from ?IPV6:2600:8803:e7e4:500:e14e:bcc6:3f95:26eb?\n ([2600:8803:e7e4:500:e14e:bcc6:3f95:26eb])\n        by smtp.gmail.com with ESMTPSA id\n 5614622812f47-46ff2f94b71sm2098137b6e.5.2026.04.04.08.12.04\n        (version=TLS1_3 cipher=TLS_AES_128_GCM_SHA256 bits=128/128);\n        Sat, 04 Apr 2026 08:12:05 -0700 (PDT)"],"ARC-Seal":"i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116;\n\tt=1775315531; cv=none;\n b=QnRBNoBzxdPMHFE7icIWM0tCyumGaaIzM2Scz3XuAV4IGH8qpaV7gyaWFVPXLZzagT90B0cd+8SPZeWp/Ps5N7FavefcmzpOOVvW/j31Cq3PhCauJ91ziLVz2FPzdEvYVsDoCQljwtmTzTsoOCzPLZ6y/RV1/ASPoNs/MEIIZNk=","ARC-Message-Signature":"i=1; a=rsa-sha256; d=subspace.kernel.org;\n\ts=arc-20240116; t=1775315531; c=relaxed/simple;\n\tbh=1lTJmCCXmdvQEIZLpII1oLol49s0u5TRwm6Vre4zhdc=;\n\th=Message-ID:Date:MIME-Version:Subject:To:Cc:References:From:\n\t In-Reply-To:Content-Type;\n b=oQ2vy8WekWViXzywfh4yYhGT49UMWxPddFEhC5dQbJh3lUcyRrsSK1Znp7PzugRca/bckPEB7YQNWpUs5B2GLMTieor9gJ93+UU/tql5C84mAmDHALw0/maVmHeDTEQHL5J61BpL+7GbyCYLNMrdXihW0fOHzjvNlyw2Sy+xLzs=","ARC-Authentication-Results":"i=1; smtp.subspace.kernel.org;\n dmarc=none (p=none dis=none) header.from=baylibre.com;\n spf=pass smtp.mailfrom=baylibre.com;\n dkim=pass (2048-bit key) header.d=baylibre-com.20251104.gappssmtp.com\n header.i=@baylibre-com.20251104.gappssmtp.com header.b=LuNKqbtK;\n arc=none smtp.client-ip=209.85.167.182","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed;\n        d=baylibre-com.20251104.gappssmtp.com; s=20251104; t=1775315526;\n x=1775920326; darn=vger.kernel.org;\n        h=content-transfer-encoding:in-reply-to:from:content-language\n         :references:cc:to:subject:user-agent:mime-version:date:message-id\n         :from:to:cc:subject:date:message-id:reply-to;\n        bh=HURTYXitYF3odN2Wvm4ypVxVht9AsZTMGe0A8ZJ7Z50=;\n        b=LuNKqbtKI5tZXvNPzYQbszNxxcVbu8zuYFXC6fl4ApP7RNNxswoqJfjzlReMQWaWkA\n         qSWwh42AEQs5MfJGojRcj1Q0UAqZdxoP2BYthfk0Rh+f80OkrNG/7NaVKxVU+11KmHkk\n         jT9BaSO4T48vgbc3oL6y7rKfeIue/xNbc6fEiq6z/6ia57oC+EDX0vTs2B49fG4Q7+xv\n         QFZrNV4ljqsac+P5SINbNn+zjw57qwow14E11aVkMD57jYMhrpCUHG+x25GesqiFTP7t\n         27AOu0CsBGsOhq4FNc4ojZ/IKdZZhGJiG+grH+90O/E1wwfg87M3E3ZQ/kVS7d9Tjk7L\n         geWQ==","X-Google-DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed;\n        d=1e100.net; s=20251104; t=1775315526; x=1775920326;\n        h=content-transfer-encoding:in-reply-to:from:content-language\n         :references:cc:to:subject:user-agent:mime-version:date:message-id\n         :x-gm-gg:x-gm-message-state:from:to:cc:subject:date:message-id\n         :reply-to;\n        bh=HURTYXitYF3odN2Wvm4ypVxVht9AsZTMGe0A8ZJ7Z50=;\n        b=gCQLRt0WXLNIYanKuhHqStdIxAe5i7CmODdVxqRBeHixnbgfZIjaT+7FGcB19kqs5K\n         VeUaHjjt8sKRI308EjzS69DVwXsQFK9ZdFOBJ8Vpy1vI2mCgbCEeGLI4bEsc/DGZP6Cm\n         zXYZjKcWZyjnjEi2Ns663bMbniptjlGcvVXIQoI7oZY17MASYk4WzEFIhA9YgpNTlYfY\n         56cTb/iXz1s2JfUk7R/uXoxvpBTQSkVZZrLG8JMlpB36cPN9QO2+fph9HRKki3vP50q8\n         R7tt+9pQrNPONemyn4sxazHeUv5+vFJ/FIm/oc3BRwFnSqf/jFcWfWv4WXG6nPSpACuh\n         nBpg==","X-Forwarded-Encrypted":"i=1;\n AJvYcCVKmgiu+d5mL84KA4/72GObpXYA0DuDhwz6CSkHQEZikaHdCDZRrD7IiMDDKzU1//KgNG61JUfOfwuS@vger.kernel.org","X-Gm-Message-State":"AOJu0Yw3SEghnJe6lD8rbsjZInnIPayYIpZ0O2pr7r9A+hegg5gxoiqe\n\t2qQlsYbhIgddIsBvpFUzPZy7mxBi85FYlOQhTvj4Aq/h6jb1VDaassjgI33zFH7/DPw=","X-Gm-Gg":"AeBDieuL9MwM2Cuf6bH7VXJuvo1eurQ1vWW0VVXO9IpKosxtEPGxQnwn4gZIF7UDYTU\n\tn/aWiOaI+6E5xw3/4N+NMJZQmk2ye5iAwo5nIbp7J9qgi21T4nfRbP41mfzAqiYldNcDazbts3e\n\t/ATsYxSRsTwS//UhhsXdT3rmf3SDBRmXEfsD0TWhInhGPPufUBOu9PctJRTMAzKeB0jSv76Bt/j\n\tiZp/j+w7fCyS5h90DEqx6V1l607I0xE9O/thup7S9dAB8TAmdSxkaIkY+pqIw5MCEY/lc+m1Miz\n\t9NfxDGvffS5EdBezdeGo4gUiYMrT6Zt/J12BtNOxszRaF61384fJ2uXlA8CQTmYT5EaDPgznWz8\n\tKcfFTJkQEpYFH2iXO826zX3FCFCddmgRJ243xzhv+5Iy629TTfxdqK7OxrUEZSENyOKeBQjY1Ir\n\txf+6CdWXyhZtDrdyUrwT2tPufZNluHe7q8s+RyeJECrd40gy8Se5CAwUTpZlnLS+A/nLyByqwsC\n\tw==","X-Received":"by 2002:a05:6808:4fc8:b0:468:bfcc:11fa with SMTP id\n 5614622812f47-46ef821476emr3206674b6e.38.1775315526319;\n        Sat, 04 Apr 2026 08:12:06 -0700 (PDT)","Message-ID":"<e38e5b97-e90f-4613-a15e-6c3d08cd77f7@baylibre.com>","Date":"Sat, 4 Apr 2026 10:12:04 -0500","Precedence":"bulk","X-Mailing-List":"linux-gpio@vger.kernel.org","List-Id":"<linux-gpio.vger.kernel.org>","List-Subscribe":"<mailto:linux-gpio+subscribe@vger.kernel.org>","List-Unsubscribe":"<mailto:linux-gpio+unsubscribe@vger.kernel.org>","MIME-Version":"1.0","User-Agent":"Mozilla Thunderbird","Subject":"Re: [PATCH v6 3/4] iio: adc: ad4691: add triggered buffer support","To":"radu.sabau@analog.com, Lars-Peter Clausen <lars@metafoo.de>,\n Michael Hennerich <Michael.Hennerich@analog.com>,\n Jonathan Cameron <jic23@kernel.org>,\n =?utf-8?q?Nuno_S=C3=A1?= <nuno.sa@analog.com>,\n Andy Shevchenko <andy@kernel.org>, Rob Herring <robh@kernel.org>,\n Krzysztof Kozlowski <krzk+dt@kernel.org>, Conor Dooley <conor+dt@kernel.org>,\n =?utf-8?q?Uwe_Kleine-K=C3=B6nig?= <ukleinek@kernel.org>,\n Liam Girdwood <lgirdwood@gmail.com>, Mark Brown <broonie@kernel.org>,\n Linus Walleij <linusw@kernel.org>, Bartosz Golaszewski <brgl@kernel.org>,\n Philipp Zabel <p.zabel@pengutronix.de>, Jonathan Corbet <corbet@lwn.net>,\n Shuah Khan <skhan@linuxfoundation.org>","Cc":"linux-iio@vger.kernel.org, devicetree@vger.kernel.org,\n linux-kernel@vger.kernel.org, linux-pwm@vger.kernel.org,\n linux-gpio@vger.kernel.org, linux-doc@vger.kernel.org","References":"\n <20260403-ad4692-multichannel-sar-adc-driver-v6-0-fa2a01a57c4e@analog.com>\n <20260403-ad4692-multichannel-sar-adc-driver-v6-3-fa2a01a57c4e@analog.com>","Content-Language":"en-US","From":"David Lechner <dlechner@baylibre.com>","In-Reply-To":"\n <20260403-ad4692-multichannel-sar-adc-driver-v6-3-fa2a01a57c4e@analog.com>","Content-Type":"text/plain; charset=UTF-8","Content-Transfer-Encoding":"8bit"}},{"id":3673542,"web_url":"http://patchwork.ozlabs.org/comment/3673542/","msgid":"<adIkBcEoOJdvxa3Y@ashevche-desk.local>","list_archive_url":null,"date":"2026-04-05T08:57:41","subject":"Re: [PATCH v6 3/4] iio: adc: ad4691: add triggered buffer support","submitter":{"id":46495,"url":"http://patchwork.ozlabs.org/api/people/46495/","name":"Andy Shevchenko","email":"andriy.shevchenko@intel.com"},"content":"On Sat, Apr 04, 2026 at 10:12:04AM -0500, David Lechner wrote:\n> On 4/3/26 6:03 AM, Radu Sabau via B4 Relay wrote:\n\n> > Add buffered capture support using the IIO triggered buffer framework.\n> > \n> > CNV Burst Mode: the GP pin identified by interrupt-names in the device\n> > tree is configured as DATA_READY output. The IRQ handler stops\n> > conversions and fires the IIO trigger; the trigger handler executes a\n> > pre-built SPI message that reads all active channels from the AVG_IN\n> > accumulator registers and then resets accumulator state and restarts\n> > conversions for the next cycle.\n> > \n> > Manual Mode: CNV is tied to SPI CS so each transfer simultaneously\n> > reads the previous result and starts the next conversion (pipelined\n> > N+1 scheme). At preenable time a pre-built, optimised SPI message of\n> > N+1 transfers is constructed (N channel reads plus one NOOP to drain\n> > the pipeline). The trigger handler executes the message in a single\n> > spi_sync() call and collects the results. An external trigger (e.g.\n> > iio-trig-hrtimer) is required to drive the trigger at the desired\n> > sample rate.\n> > \n> > Both modes share the same trigger handler and push a complete scan —\n> > one u16 slot per channel at its scan_index position, followed by a\n> > timestamp — to the IIO buffer via iio_push_to_buffers_with_ts().\n> > \n> > The CNV Burst Mode sampling frequency (PWM period) is exposed as a\n> > buffer-level attribute via IIO_DEVICE_ATTR.\n\nTried my best to avoid clashes with David's review.\n\n...\n\n> >  #include <linux/array_size.h>\n> >  #include <linux/bitfield.h>\n\n> > +#include <linux/bitmap.h>\n> >  #include <linux/bitops.h>\n\nWhen bitmap.h is present, it implies bitops.h, hence the latter can be simply\nreplaced.\n\n> >  #include <linux/cleanup.h>\n> >  #include <linux/delay.h>\n> >  #include <linux/dev_printk.h>\n> >  #include <linux/device/devres.h>\n> >  #include <linux/err.h>\n> > +#include <linux/interrupt.h>\n> >  #include <linux/math.h>\n> >  #include <linux/module.h>\n> >  #include <linux/mod_devicetable.h>\n> > +#include <linux/property.h>\n> > +#include <linux/pwm.h>\n> >  #include <linux/regmap.h>\n> >  #include <linux/regulator/consumer.h>\n> >  #include <linux/reset.h>\n\n...\n\n> >  \t\t.info_mask_separate = BIT(IIO_CHAN_INFO_RAW)\t\t\\\n> > -\t\t\t\t    | BIT(IIO_CHAN_INFO_SAMP_FREQ),\t\\\n> > +\t\t\t\t    | BIT(IIO_CHAN_INFO_SAMP_FREQ)\t\\\n> > +\t\t\t\t    | BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO),\t\\\n> >  \t\t.info_mask_separate_available =\t\t\t\t\\\n> > -\t\t\t\t      BIT(IIO_CHAN_INFO_SAMP_FREQ),\t\\\n> > +\t\t\t\t      BIT(IIO_CHAN_INFO_SAMP_FREQ)\t\\\n> > +\t\t\t\t    | BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO),\t\\\n\nYou may reduce churn by squeezing a new ones in between existing ones.\nAlso consider use usual patter of placing the operator on the same line where\nleft operand is (currently it goes with the right operand).\n\n...\n\n> >  struct ad4691_state {\n\nJust to double check, when add a new field or fields into the data structure\ncheck with `pahole` that the new members placed at the best or at least good\nenough locations.\n\n> >  \tconst struct ad4691_chip_info *info;\n> >  \tstruct regmap *regmap;\n> > +\n> > +\tstruct pwm_device *conv_trigger;\n> > +\tint irq;\n> > +\n> > +\tbool manual_mode;\n> > +\n> >  \tint vref_uV;\n> > +\tu8 osr[16];\n> >  \tbool refbuf_en;\n> >  \tbool ldo_en;\n> > +\tu32 cnv_period_ns;\n> >  \t/*\n> >  \t * Synchronize access to members of the driver state, and ensure\n> >  \t * atomicity of consecutive SPI operations.\n> >  \t */\n> >  \tstruct mutex lock;\n> > +\t/*\n> > +\t * Per-buffer-enable lifetime resources:\n> > +\t * Manual Mode - a pre-built SPI message that clocks out N+1\n> > +\t *\t\t transfers in one go.\n> > +\t * CNV Burst Mode - a pre-built SPI message that clocks out 2*N\n> > +\t *\t\t    transfers in one go.\n> > +\t */\n> > +\tstruct spi_message scan_msg;\n> > +\tstruct spi_transfer *scan_xfers;\n> > +\t__be16 *scan_tx;\n> > +\t__be16 *scan_rx;\n> \n> Why not embed these arrays here? Then we don't have to deal with\n> alloc/free later.\n> \n> > +\t/* Scan buffer: one slot per channel plus timestamp */\n> > +\tstruct {\n> > +\t\tu16 vals[16];\n> > +\t\taligned_s64 ts;\n> > +\t} scan __aligned(IIO_DMA_MINALIGN);\n> \n> Better would be IIO_DECLARE_BUFFER_WITH_TS() since we don't always\n> use all vals.\n> \n> Also, current usage doesn't need to be DMA-safe because scan_tx\n> is being used for the actual SPI xfer.\n> \n> >  };\n\n...\n\n> > +static int ad4691_gpio_setup(struct ad4691_state *st, unsigned int gp_num)\n> > +{\n> > +\tunsigned int shift = 4 * (gp_num % 2);\n> > +\n> > +\treturn regmap_update_bits(st->regmap,\n> > +\t\t\t\t  AD4691_GPIO_MODE1_REG + gp_num / 2,\n> > +\t\t\t\t  AD4691_GP_MODE_MASK << shift,\n> > +\t\t\t\t  AD4691_GP_MODE_DATA_READY << shift);\n\nNot sure if compiler will see % and / together, I would go with two more\ntemporary variables to make it clear to it:\n\n\t... _bit_off = % 2;\n\t... _reg_off = / 2;\n\nThe practical example is described, for example, here:\n9b3cd5c7099f (\"regmap: place foo / 8 and foo % 8 closer to each other\").\n\n> > +}\n\n...\n\n> > +static int ad4691_manual_buffer_preenable(struct iio_dev *indio_dev)\n> > +{\n> > +\tstruct ad4691_state *st = iio_priv(indio_dev);\n> > +\tstruct device *dev = regmap_get_device(st->regmap);\n> > +\tstruct spi_device *spi = to_spi_device(dev);\n\n> > +\tunsigned int n_active = bitmap_weight(indio_dev->active_scan_mask,\n> > +\t\t\t\t\t      iio_get_masklength(indio_dev));\n\nIn such cases please split definition and assignment. Will take two lines, but\nreadability will be better.\n\n> > +\tunsigned int n_xfers = n_active + 1;\n> > +\tunsigned int k, i;\n> > +\tint ret;\n> > +\n> > +\tst->scan_xfers = kcalloc(n_xfers, sizeof(*st->scan_xfers), GFP_KERNEL);\n> \n> Usually, we make st->scan_xfers a fixed array with the max number of possible\n> xfers. Then we don't have to deal with alloc/free.\n\nAnd please if it's still be required to allocate and possible use kzalloc_objs().\n\n> > +\tif (!st->scan_xfers)\n> > +\t\treturn -ENOMEM;\n> > +\n> > +\tst->scan_tx = kcalloc(n_xfers, sizeof(*st->scan_tx), GFP_KERNEL);\n> > +\tif (!st->scan_tx) {\n> > +\t\tkfree(st->scan_xfers);\n> > +\t\treturn -ENOMEM;\n> > +\t}\n> > +\n> > +\tst->scan_rx = kcalloc(n_xfers, sizeof(*st->scan_rx), GFP_KERNEL);\n> > +\tif (!st->scan_rx) {\n> > +\t\tkfree(st->scan_tx);\n> > +\t\tkfree(st->scan_xfers);\n> > +\t\treturn -ENOMEM;\n> > +\t}\n> > +\n> > +\tspi_message_init(&st->scan_msg);\n> > +\n> > +\tk = 0;\n> > +\tiio_for_each_active_channel(indio_dev, i) {\n> > +\t\tst->scan_tx[k] = cpu_to_be16(AD4691_ADC_CHAN(i));\n> > +\t\tst->scan_xfers[k].tx_buf = &st->scan_tx[k];\n> > +\t\tst->scan_xfers[k].rx_buf = &st->scan_rx[k];\n> > +\t\tst->scan_xfers[k].len = sizeof(__be16);\n> > +\t\tst->scan_xfers[k].cs_change = 1;\n> > +\t\tspi_message_add_tail(&st->scan_xfers[k], &st->scan_msg);\n> > +\t\tk++;\n> > +\t}\n> > +\n> > +\t/* Final NOOP transfer to retrieve last channel's result. */\n> > +\tst->scan_tx[k] = cpu_to_be16(AD4691_NOOP);\n> > +\tst->scan_xfers[k].tx_buf = &st->scan_tx[k];\n> > +\tst->scan_xfers[k].rx_buf = &st->scan_rx[k];\n> > +\tst->scan_xfers[k].len = sizeof(__be16);\n> > +\tspi_message_add_tail(&st->scan_xfers[k], &st->scan_msg);\n> > +\n> > +\tst->scan_msg.spi = spi;\n> \n> This isn't how the SPI framework is intended to be used. We should\n> have st->spi = spi in probe instead.\n> \n> > +\n> > +\tret = spi_optimize_message(spi, &st->scan_msg);\n> > +\tif (ret) {\n> > +\t\tad4691_free_scan_bufs(st);\n> > +\t\treturn ret;\n> > +\t}\n> > +\n> > +\tret = ad4691_enter_conversion_mode(st);\n> > +\tif (ret) {\n> > +\t\tspi_unoptimize_message(&st->scan_msg);\n> > +\t\tad4691_free_scan_bufs(st);\n> > +\t\treturn ret;\n> > +\t}\n> > +\n> > +\treturn 0;\n> > +}\n\n...\n\n> > +static int ad4691_cnv_burst_buffer_preenable(struct iio_dev *indio_dev)\n\nAs per above.\n\n...\n\n> > +static ssize_t sampling_frequency_show(struct device *dev,\n> > +\t\t\t\t       struct device_attribute *attr,\n> > +\t\t\t\t       char *buf)\n> > +{\n> > +\tstruct iio_dev *indio_dev = dev_to_iio_dev(dev);\n> > +\tstruct ad4691_state *st = iio_priv(indio_dev);\n> > +\n> > +\treturn sysfs_emit(buf, \"%u\\n\", (u32)(NSEC_PER_SEC / st->cnv_period_ns));\n\nWhy casting?\n\n> > +}\n\n...\n\n> > +static IIO_DEVICE_ATTR(sampling_frequency, 0644,\n> > +\t\t       sampling_frequency_show,\n> > +\t\t       sampling_frequency_store, 0);\n\nIIO_DEVICE_ATTR_RW().\n\n...\n\n> > +static int ad4691_read_scan(struct iio_dev *indio_dev, s64 timestamp)\n> > +{\n> > +\tstruct ad4691_state *st = iio_priv(indio_dev);\n> > +\tunsigned int i, k = 0;\n> > +\tint ret;\n> > +\n> > +\tguard(mutex)(&st->lock);\n> > +\n> > +\tret = spi_sync(st->scan_msg.spi, &st->scan_msg);\n> > +\tif (ret)\n> > +\t\treturn ret;\n> > +\n> > +\tif (st->manual_mode) {\n> > +\t\tiio_for_each_active_channel(indio_dev, i) {\n> > +\t\t\tst->scan.vals[i] = be16_to_cpu(st->scan_rx[k + 1]);\n> > +\t\t\tk++;\n> > +\t\t}\n> > +\t} else {\n> > +\t\tiio_for_each_active_channel(indio_dev, i) {\n> > +\t\t\tst->scan.vals[i] = be16_to_cpu(st->scan_rx[k]);\n> > +\t\t\tk++;\n> > +\t\t}\n> \n> I suppose this is fine, but we usually try to avoid extra copiying and\n> byte swapping of bufferes like this if we can. It seems completly doable\n> in both modes. Manual mode will just one extra two-byte buffer for the\n> throw-away conversion on the first read xfer (or just write to the same\n> element twice).\n\nAnd in case it's still needed, we may introduce a helper in\ninclude/linux/byteorder/generic.h calling it memcpy_to/from_be16().\n\n> > +\t\tret = regmap_write(st->regmap, AD4691_STATE_RESET_REG,\n> > +\t\t\t\t   AD4691_STATE_RESET_ALL);\n> > +\t\tif (ret)\n> > +\t\t\treturn ret;\n> > +\n> > +\t\tret = ad4691_sampling_enable(st, true);\n> > +\t\tif (ret)\n> > +\t\t\treturn ret;\n> > +\t}\n> > +\n> > +\tiio_push_to_buffers_with_ts(indio_dev, &st->scan, sizeof(st->scan),\n> > +\t\t\t\t    timestamp);\n> > +\treturn 0;\n> > +}\n\n...\n\n> > +static int ad4691_setup_triggered_buffer(struct iio_dev *indio_dev,\n> > +\t\t\t\t\t struct ad4691_state *st)\n> > +{\n> > +\tstruct device *dev = regmap_get_device(st->regmap);\n> > +\tstruct iio_trigger *trig;\n> > +\tunsigned int i;\n> > +\tint irq, ret;\n> > +\n> > +\ttrig = devm_iio_trigger_alloc(dev, \"%s-dev%d\",\n> > +\t\t\t\t      indio_dev->name,\n> > +\t\t\t\t      iio_device_id(indio_dev));\n> > +\tif (!trig)\n> > +\t\treturn -ENOMEM;\n> > +\n> > +\ttrig->ops = &ad4691_trigger_ops;\n> > +\tiio_trigger_set_drvdata(trig, st);\n> > +\n> > +\tret = devm_iio_trigger_register(dev, trig);\n> > +\tif (ret)\n> > +\t\treturn dev_err_probe(dev, ret, \"IIO trigger register failed\\n\");\n> > +\n> > +\tindio_dev->trig = iio_trigger_get(trig);\n> > +\n> > +\tif (!st->manual_mode) {\n> \n> I would invert the if since the other case is shorter.\n\n+1.\n\n> > +\t\t/*\n> > +\t\t * The GP pin named in interrupt-names asserts at end-of-conversion.\n> > +\t\t * The IRQ handler stops conversions and fires the IIO trigger so\n> > +\t\t * the trigger handler can read and push the sample to the buffer.\n> > +\t\t * The IRQ is kept disabled until the buffer is enabled.\n> > +\t\t */\n> > +\t\tirq = -ENODEV;\n> > +\t\tfor (i = 0; i < ARRAY_SIZE(ad4691_gp_names); i++) {\n> > +\t\t\tirq = fwnode_irq_get_byname(dev_fwnode(dev),\n> > +\t\t\t\t\t\t    ad4691_gp_names[i]);\n> > +\t\t\tif (irq > 0)\n> > +\t\t\t\tbreak;\n> > +\t\t}\n> > +\t\tif (irq <= 0)\n> > +\t\t\treturn dev_err_probe(dev, irq < 0 ? irq : -ENODEV,\n> > +\t\t\t\t\t     \"failed to get GP interrupt\\n\");\n> \n> Usually we would usually just use spi->irq since it already\n> has been looked up. But I guess it is OK to do it like this.\n\nNo, it's not. (Linux) IRQ shouldn't ever be 0, so this check is effectively a\ndead code.\n\n\t\tirq = -ENXIO; // Note, this is the error code used by core for\n\t\t\t      // IRQ not found.\n\t\t...\n\t\tif (irq < 0)\n\t\t\treturn dev_err_probe(dev, irq, \"failed to get GP interrupt\\n\");\n\n> > +\t\tst->irq = irq;\n> > +\n> > +\t\tret = ad4691_gpio_setup(st, i);\n> > +\t\tif (ret)\n> > +\t\t\treturn ret;\n> > +\n> > +\t\t/*\n> > +\t\t * IRQ is kept disabled until the buffer is enabled to prevent\n> > +\t\t * spurious DATA_READY events before the SPI message is set up.\n> > +\t\t */\n> > +\t\tret = devm_request_threaded_irq(dev, irq, NULL,\n> > +\t\t\t\t\t\t&ad4691_irq,\n> > +\t\t\t\t\t\tIRQF_ONESHOT | IRQF_NO_AUTOEN,\n> > +\t\t\t\t\t\tindio_dev->name, indio_dev);\n> > +\t\tif (ret)\n> > +\t\t\treturn ret;\n> > +\n> > +\t\treturn devm_iio_triggered_buffer_setup_ext(dev, indio_dev,\n> > +\t\t\t\t\t\t\t   &iio_pollfunc_store_time,\n> > +\t\t\t\t\t\t\t   &ad4691_trigger_handler,\n> > +\t\t\t\t\t\t\t   IIO_BUFFER_DIRECTION_IN,\n> > +\t\t\t\t\t\t\t   &ad4691_cnv_burst_buffer_setup_ops,\n> > +\t\t\t\t\t\t\t   ad4691_buffer_attrs);\n> > +\t}\n> > +\n> > +\treturn devm_iio_triggered_buffer_setup(dev, indio_dev,\n> > +\t\t\t\t\t       &iio_pollfunc_store_time,\n> > +\t\t\t\t\t       &ad4691_trigger_handler,\n> > +\t\t\t\t\t       &ad4691_manual_buffer_setup_ops);\n> > +}\n> > +","headers":{"Return-Path":"\n <linux-gpio+bounces-34678-incoming=patchwork.ozlabs.org@vger.kernel.org>","X-Original-To":["incoming@patchwork.ozlabs.org","linux-gpio@vger.kernel.org"],"Delivered-To":"patchwork-incoming@legolas.ozlabs.org","Authentication-Results":["legolas.ozlabs.org;\n\tdkim=pass (2048-bit key;\n unprotected) header.d=intel.com header.i=@intel.com header.a=rsa-sha256\n header.s=Intel header.b=booqZUxe;\n\tdkim-atps=neutral","legolas.ozlabs.org;\n spf=pass (sender SPF authorized) smtp.mailfrom=vger.kernel.org\n (client-ip=172.234.253.10; helo=sea.lore.kernel.org;\n envelope-from=linux-gpio+bounces-34678-incoming=patchwork.ozlabs.org@vger.kernel.org;\n receiver=patchwork.ozlabs.org)","smtp.subspace.kernel.org;\n\tdkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com\n header.b=\"booqZUxe\"","smtp.subspace.kernel.org;\n arc=none smtp.client-ip=198.175.65.9","smtp.subspace.kernel.org;\n dmarc=pass (p=none dis=none) header.from=intel.com","smtp.subspace.kernel.org;\n spf=pass smtp.mailfrom=intel.com"],"Received":["from sea.lore.kernel.org (sea.lore.kernel.org [172.234.253.10])\n\t(using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)\n\t key-exchange x25519 server-signature ECDSA (secp384r1) server-digest SHA384)\n\t(No client certificate requested)\n\tby legolas.ozlabs.org (Postfix) with ESMTPS id 4fpRF53pdbz1yD3\n\tfor <incoming@patchwork.ozlabs.org>; Sun, 05 Apr 2026 18:58:01 +1000 (AEST)","from smtp.subspace.kernel.org (conduit.subspace.kernel.org\n [100.90.174.1])\n\tby sea.lore.kernel.org (Postfix) with ESMTP id E2FF03009FAE\n\tfor <incoming@patchwork.ozlabs.org>; Sun,  5 Apr 2026 08:57:54 +0000 (UTC)","from localhost.localdomain (localhost.localdomain [127.0.0.1])\n\tby smtp.subspace.kernel.org (Postfix) with ESMTP id 45F39370D48;\n\tSun,  5 Apr 2026 08:57:53 +0000 (UTC)","from mgamail.intel.com (mgamail.intel.com [198.175.65.9])\n\t(using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits))\n\t(No client certificate requested)\n\tby smtp.subspace.kernel.org (Postfix) with ESMTPS id B492813DDAA;\n\tSun,  5 Apr 2026 08:57:50 +0000 (UTC)","from orviesa004.jf.intel.com ([10.64.159.144])\n  by orvoesa101.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384;\n 05 Apr 2026 01:57:49 -0700","from abityuts-desk.ger.corp.intel.com (HELO localhost)\n ([10.245.245.247])\n  by orviesa004-auth.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384;\n 05 Apr 2026 01:57:44 -0700"],"ARC-Seal":"i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116;\n\tt=1775379473; cv=none;\n b=lL6JWwil4KhYB8L148tQ1eFyumDAZnbSmsjduO9WQl7fXfkTHwd1XzbV64t/uAZvgxPE72T/TyDzd78OOpnGRVraW+4svb/i5me5COrnN2flSDZN9fVSyXSigGTZibSkBOkhY/qm4ksGaOS3Aly4jSUq3vD/q/9qWL8m9TAz7K4=","ARC-Message-Signature":"i=1; a=rsa-sha256; d=subspace.kernel.org;\n\ts=arc-20240116; t=1775379473; c=relaxed/simple;\n\tbh=XvSZnnRQNFjQPKZmdyglaM8/uF3se4KKpvHFWt5+pfs=;\n\th=Date:From:To:Cc:Subject:Message-ID:References:MIME-Version:\n\t Content-Type:Content-Disposition:In-Reply-To;\n b=gNLxNwsJVGR/R54DNQfUkuNB5YY795LJzhhD6A+eflHdjPEMmotwUBquQnz8pYwKX3UTFqKmFCzrJ9IYB3AkvCXUVwWkBez8yulAxjz79aL+S9vepSkMDPBpMUuM432KrAW/nzEaIUF9xwv7yuUJSi/RwNviIlI8mszPOirPFSU=","ARC-Authentication-Results":"i=1; smtp.subspace.kernel.org;\n dmarc=pass (p=none dis=none) header.from=intel.com;\n spf=pass smtp.mailfrom=intel.com;\n dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com\n header.b=booqZUxe; arc=none smtp.client-ip=198.175.65.9","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple;\n  d=intel.com; i=@intel.com; q=dns/txt; s=Intel;\n  t=1775379470; x=1806915470;\n  h=date:from:to:cc:subject:message-id:references:\n   mime-version:content-transfer-encoding:in-reply-to;\n  bh=XvSZnnRQNFjQPKZmdyglaM8/uF3se4KKpvHFWt5+pfs=;\n  b=booqZUxe7mqPQnVe99FHeaIGUD7f8V/6q9QZDy95HwbiY+otVq+clImF\n   /NpvSGmbIc6Ll/JlS0mCCIOMqhKY0gdnnGZmvo+bnzmHmKivvuLPZidI2\n   tVyyI3Ct+N6ZTHCdb4bYDYid3tXWCPp+AwO2izgwjVui0v0Br6TOKi0L6\n   G4PK89o6BEiy43btDvgRbJdjhxIcrG3tVpBToObPGF4O5+YChoyZwl0YD\n   lUinxhgxtIntqVqW7HP7Q9vardoAj/TFgTtwo9YXw2QYZ/fY4SWJopASO\n   IAyGo3lwfH17m4fw8T6kKqJMLZyl3D4R++/dd5FOpZZw1ekUeLbMCCMHu\n   A==;","X-CSE-ConnectionGUID":["ZGn0pq/GSsKfDpN+jCRAjg==","LyOW3uRQTTmltuGGRf0HWw=="],"X-CSE-MsgGUID":["+tN/fUckQFSadln9kALN/Q==","FfaNSo2ZRaKE1oFNif9B8A=="],"X-IronPort-AV":["E=McAfee;i=\"6800,10657,11749\"; a=\"98992875\"","E=Sophos;i=\"6.23,161,1770624000\";\n   d=\"scan'208\";a=\"98992875\"","E=Sophos;i=\"6.23,161,1770624000\";\n   d=\"scan'208\";a=\"232026285\""],"X-ExtLoop1":"1","Date":"Sun, 5 Apr 2026 11:57:41 +0300","From":"Andy Shevchenko <andriy.shevchenko@intel.com>","To":"David Lechner <dlechner@baylibre.com>","Cc":"radu.sabau@analog.com, Lars-Peter Clausen <lars@metafoo.de>,\n Michael Hennerich <Michael.Hennerich@analog.com>,\n Jonathan Cameron <jic23@kernel.org>,\n Nuno =?iso-8859-1?q?S=E1?= <nuno.sa@analog.com>,\n Andy Shevchenko <andy@kernel.org>, Rob Herring <robh@kernel.org>,\n Krzysztof Kozlowski <krzk+dt@kernel.org>, Conor Dooley <conor+dt@kernel.org>,\n Uwe =?iso-8859-1?q?Kleine-K=F6nig?= <ukleinek@kernel.org>,\n Liam Girdwood <lgirdwood@gmail.com>, Mark Brown <broonie@kernel.org>,\n Linus Walleij <linusw@kernel.org>, Bartosz Golaszewski <brgl@kernel.org>,\n Philipp Zabel <p.zabel@pengutronix.de>, Jonathan Corbet <corbet@lwn.net>,\n Shuah Khan <skhan@linuxfoundation.org>, linux-iio@vger.kernel.org,\n devicetree@vger.kernel.org, linux-kernel@vger.kernel.org,\n linux-pwm@vger.kernel.org, linux-gpio@vger.kernel.org,\n linux-doc@vger.kernel.org","Subject":"Re: [PATCH v6 3/4] iio: adc: ad4691: add triggered buffer support","Message-ID":"<adIkBcEoOJdvxa3Y@ashevche-desk.local>","References":"\n <20260403-ad4692-multichannel-sar-adc-driver-v6-0-fa2a01a57c4e@analog.com>\n <20260403-ad4692-multichannel-sar-adc-driver-v6-3-fa2a01a57c4e@analog.com>\n <e38e5b97-e90f-4613-a15e-6c3d08cd77f7@baylibre.com>","Precedence":"bulk","X-Mailing-List":"linux-gpio@vger.kernel.org","List-Id":"<linux-gpio.vger.kernel.org>","List-Subscribe":"<mailto:linux-gpio+subscribe@vger.kernel.org>","List-Unsubscribe":"<mailto:linux-gpio+unsubscribe@vger.kernel.org>","MIME-Version":"1.0","Content-Type":"text/plain; charset=utf-8","Content-Disposition":"inline","Content-Transfer-Encoding":"8bit","In-Reply-To":"<e38e5b97-e90f-4613-a15e-6c3d08cd77f7@baylibre.com>","Organization":"Intel Finland Oy - BIC 0357606-4 - c/o Alberga Business Park, 6\n krs, Bertel Jungin Aukio 5, 02600 Espoo"}}]