From: Radu Sabau <radu.sabau@analog.com>
Add buffered capture support using the IIO triggered buffer framework.
CNV Burst Mode: the GP pin identified by interrupt-names in the device
tree is configured as DATA_READY output. The IRQ handler stops
conversions and fires the IIO trigger; the trigger handler executes a
pre-built SPI message that reads all active channels from the AVG_IN
accumulator registers and then resets accumulator state and restarts
conversions for the next cycle.
Manual Mode: CNV is tied to SPI CS so each transfer simultaneously
reads the previous result and starts the next conversion (pipelined
N+1 scheme). At preenable time a pre-built, optimised SPI message of
N+1 transfers is constructed (N channel reads plus one NOOP to drain
the pipeline). The trigger handler executes the message in a single
spi_sync() call and collects the results. An external trigger (e.g.
iio-trig-hrtimer) is required to drive the trigger at the desired
sample rate.
Both modes share the same trigger handler and push a complete scan —
one u16 slot per channel at its scan_index position, followed by a
timestamp — to the IIO buffer via iio_push_to_buffers_with_ts().
The CNV Burst Mode sampling frequency (PWM period) is exposed as a
buffer-level attribute via IIO_DEVICE_ATTR.
Signed-off-by: Radu Sabau <radu.sabau@analog.com>
---
drivers/iio/adc/Kconfig | 2 +
drivers/iio/adc/ad4691.c | 584 +++++++++++++++++++++++++++++++++++++++++++++--
2 files changed, 571 insertions(+), 15 deletions(-)
diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
index 3685a03aa8dc..d498f16c0816 100644
--- a/drivers/iio/adc/Kconfig
+++ b/drivers/iio/adc/Kconfig
@@ -142,6 +142,8 @@ config AD4170_4
config AD4691
tristate "Analog Devices AD4691 Family ADC Driver"
depends on SPI
+ select IIO_BUFFER
+ select IIO_TRIGGERED_BUFFER
select REGMAP
help
Say yes here to build support for Analog Devices AD4691 Family MuxSAR
diff --git a/drivers/iio/adc/ad4691.c b/drivers/iio/adc/ad4691.c
index 5e02eb44ca44..db776de32846 100644
--- a/drivers/iio/adc/ad4691.c
+++ b/drivers/iio/adc/ad4691.c
@@ -9,9 +9,12 @@
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/err.h>
+#include <linux/interrupt.h>
#include <linux/math.h>
#include <linux/module.h>
#include <linux/mod_devicetable.h>
+#include <linux/property.h>
+#include <linux/pwm.h>
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
#include <linux/reset.h>
@@ -19,7 +22,12 @@
#include <linux/units.h>
#include <linux/unaligned.h>
+#include <linux/iio/buffer.h>
#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/trigger.h>
+#include <linux/iio/triggered_buffer.h>
+#include <linux/iio/trigger_consumer.h>
#define AD4691_VREF_uV_MIN 2400000
#define AD4691_VREF_uV_MAX 5250000
@@ -28,6 +36,8 @@
#define AD4691_VREF_3P3_uV_MAX 3750000
#define AD4691_VREF_4P096_uV_MAX 4500000
+#define AD4691_CNV_DUTY_CYCLE_NS 380
+
#define AD4691_SPI_CONFIG_A_REG 0x000
#define AD4691_SW_RESET (BIT(7) | BIT(0))
@@ -35,6 +45,7 @@
#define AD4691_CLAMP_STATUS1_REG 0x01A
#define AD4691_CLAMP_STATUS2_REG 0x01B
#define AD4691_DEVICE_SETUP 0x020
+#define AD4691_MANUAL_MODE BIT(2)
#define AD4691_LDO_EN BIT(4)
#define AD4691_REF_CTRL 0x021
#define AD4691_REF_CTRL_MASK GENMASK(4, 2)
@@ -42,21 +53,29 @@
#define AD4691_OSC_FREQ_REG 0x023
#define AD4691_OSC_FREQ_MASK GENMASK(3, 0)
#define AD4691_STD_SEQ_CONFIG 0x025
+#define AD4691_SEQ_ALL_CHANNELS_OFF 0x00
#define AD4691_SPARE_CONTROL 0x02A
+#define AD4691_NOOP 0x00
+#define AD4691_ADC_CHAN(ch) ((0x10 + (ch)) << 3)
+
#define AD4691_OSC_EN_REG 0x180
#define AD4691_STATE_RESET_REG 0x181
#define AD4691_STATE_RESET_ALL 0x01
#define AD4691_ADC_SETUP 0x182
-#define AD4691_AUTONOMOUS_MODE_VAL 0x02
+#define AD4691_CNV_BURST_MODE 0x01
+#define AD4691_AUTONOMOUS_MODE 0x02
/*
* ACC_MASK_REG covers both mask bytes via ADDR_DESCENDING SPI: writing a
* 16-bit BE value to 0x185 auto-decrements to 0x184 for the second byte.
*/
#define AD4691_ACC_MASK_REG 0x185
#define AD4691_ACC_COUNT_LIMIT(n) (0x186 + (n))
+#define AD4691_ACC_COUNT_VAL 0x1
#define AD4691_GPIO_MODE1_REG 0x196
#define AD4691_GPIO_MODE2_REG 0x197
+#define AD4691_GP_MODE_MASK GENMASK(3, 0)
+#define AD4691_GP_MODE_DATA_READY 0x06
#define AD4691_GPIO_READ 0x1A0
#define AD4691_ACC_STATUS_FULL1_REG 0x1B0
#define AD4691_ACC_STATUS_FULL2_REG 0x1B1
@@ -121,6 +140,7 @@ static const struct iio_chan_spec ad4691_channels[] = {
AD4691_CHANNEL(13),
AD4691_CHANNEL(14),
AD4691_CHANNEL(15),
+ IIO_CHAN_SOFT_TIMESTAMP(16),
};
static const struct iio_chan_spec ad4693_channels[] = {
@@ -132,6 +152,7 @@ static const struct iio_chan_spec ad4693_channels[] = {
AD4691_CHANNEL(5),
AD4691_CHANNEL(6),
AD4691_CHANNEL(7),
+ IIO_CHAN_SOFT_TIMESTAMP(16),
};
/*
@@ -189,16 +210,63 @@ static const struct ad4691_chip_info ad4694_chip_info = {
struct ad4691_state {
const struct ad4691_chip_info *info;
struct regmap *regmap;
+
+ struct pwm_device *conv_trigger;
+ struct iio_trigger *trig;
+ int irq;
+
+ bool manual_mode;
+
int vref_uV;
bool refbuf_en;
bool ldo_en;
+ u32 cnv_period_ns;
/*
* Synchronize access to members of the driver state, and ensure
* atomicity of consecutive SPI operations.
*/
struct mutex lock;
+ /*
+ * Per-buffer-enabl ree lifetimesources:
+ * Manual Mode - a pre-built SPI message that clocks out N+1
+ * transfers in one go.
+ * CNV Burst Mode - a pre-built SPI message that clocks out 2*N
+ * transfers in one go.
+ */
+ void *scan_devm_group;
+ struct spi_message scan_msg;
+ struct spi_transfer *scan_xfers;
+ __be16 *scan_tx;
+ __be16 *scan_rx;
+ /* Scan buffer: one slot per channel (u16) plus timestamp */
+ struct {
+ u16 vals[16];
+ s64 ts __aligned(8);
+ } scan __aligned(IIO_DMA_MINALIGN);
};
+/*
+ * Configure the given GP pin (0-3) as DATA_READY output.
+ * GP0/GP1 → GPIO_MODE1_REG, GP2/GP3 → GPIO_MODE2_REG.
+ * Even pins occupy bits [3:0], odd pins bits [7:4].
+ */
+static int ad4691_gpio_setup(struct ad4691_state *st, unsigned int gp_num)
+{
+ unsigned int shift = 4 * (gp_num % 2);
+
+ return regmap_update_bits(st->regmap,
+ AD4691_GPIO_MODE1_REG + gp_num / 2,
+ AD4691_GP_MODE_MASK << shift,
+ AD4691_GP_MODE_DATA_READY << shift);
+}
+
+static void ad4691_disable_pwm(void *data)
+{
+ struct pwm_state state = { .enabled = false };
+
+ pwm_apply_might_sleep(data, &state);
+}
+
static int ad4691_reg_read(void *context, unsigned int reg, unsigned int *val)
{
struct spi_device *spi = context;
@@ -341,14 +409,16 @@ static int ad4691_get_sampling_freq(struct ad4691_state *st, int *val)
static int ad4691_set_sampling_freq(struct iio_dev *indio_dev, int freq)
{
struct ad4691_state *st = iio_priv(indio_dev);
- unsigned int start = (st->info->max_rate == HZ_PER_MHZ) ? 0 : 1;
unsigned int i;
+ if (freq > st->info->max_rate)
+ return -EINVAL;
+
IIO_DEV_ACQUIRE_DIRECT_MODE(indio_dev, claim);
if (IIO_DEV_ACQUIRE_FAILED(claim))
return -EBUSY;
- for (i = start; i < ARRAY_SIZE(ad4691_osc_freqs); i++) {
+ for (i = 0; i < ARRAY_SIZE(ad4691_osc_freqs); i++) {
if ((int)ad4691_osc_freqs[i] == freq)
return regmap_update_bits(st->regmap, AD4691_OSC_FREQ_REG,
AD4691_OSC_FREQ_MASK, i);
@@ -363,7 +433,10 @@ static int ad4691_read_avail(struct iio_dev *indio_dev,
int *length, long mask)
{
struct ad4691_state *st = iio_priv(indio_dev);
- unsigned int start = (st->info->max_rate == HZ_PER_MHZ) ? 0 : 1;
+ unsigned int start;
+
+ /* Skip frequencies that exceed this chip's maximum rate. */
+ start = (ad4691_osc_freqs[0] > st->info->max_rate) ? 1 : 0;
switch (mask) {
case IIO_CHAN_INFO_SAMP_FREQ:
@@ -386,8 +459,7 @@ static int ad4691_single_shot_read(struct iio_dev *indio_dev,
guard(mutex)(&st->lock);
/*
- * Use AUTONOMOUS mode for single-shot reads. The chip always
- * operates in AUTONOMOUS mode in this driver revision.
+ * Use AUTONOMOUS mode for single-shot reads.
*/
ret = regmap_write(st->regmap, AD4691_STATE_RESET_REG,
AD4691_STATE_RESET_ALL);
@@ -417,8 +489,7 @@ static int ad4691_single_shot_read(struct iio_dev *indio_dev,
* conversion to complete.
*/
fsleep(DIV_ROUND_UP(2 * USEC_PER_SEC,
- ad4691_osc_freqs[FIELD_GET(AD4691_OSC_FREQ_MASK,
- reg_val)]));
+ ad4691_osc_freqs[FIELD_GET(AD4691_OSC_FREQ_MASK, reg_val)]));
ret = regmap_write(st->regmap, AD4691_OSC_EN_REG, 0);
if (ret)
@@ -488,6 +559,374 @@ static int ad4691_reg_access(struct iio_dev *indio_dev, unsigned int reg,
return regmap_write(st->regmap, reg, writeval);
}
+static int ad4691_set_pwm_freq(struct ad4691_state *st, int freq)
+{
+ if (!freq)
+ return -EINVAL;
+
+ st->cnv_period_ns = DIV_ROUND_CLOSEST(NSEC_PER_SEC, freq);
+ return 0;
+}
+
+static int ad4691_sampling_enable(struct ad4691_state *st, bool enable)
+{
+ struct pwm_state conv_state = { };
+
+ conv_state.period = st->cnv_period_ns;
+ conv_state.duty_cycle = AD4691_CNV_DUTY_CYCLE_NS;
+ conv_state.polarity = PWM_POLARITY_NORMAL;
+ conv_state.enabled = enable;
+
+ return pwm_apply_might_sleep(st->conv_trigger, &conv_state);
+}
+
+/*
+ * ad4691_enter_conversion_mode - Switch the chip to its buffer conversion mode.
+ *
+ * Configures the ADC hardware registers for the mode selected at probe
+ * (CNV_BURST or MANUAL). Called from buffer preenable before starting
+ * sampling. The chip is in AUTONOMOUS mode during idle (for read_raw).
+ */
+static int ad4691_enter_conversion_mode(struct ad4691_state *st)
+{
+ int ret;
+
+ if (st->manual_mode)
+ return regmap_update_bits(st->regmap, AD4691_DEVICE_SETUP,
+ AD4691_MANUAL_MODE, AD4691_MANUAL_MODE);
+
+ ret = regmap_write(st->regmap, AD4691_ADC_SETUP, AD4691_CNV_BURST_MODE);
+ if (ret)
+ return ret;
+
+ return regmap_write(st->regmap, AD4691_STATE_RESET_REG,
+ AD4691_STATE_RESET_ALL);
+}
+
+/*
+ * ad4691_exit_conversion_mode - Return the chip to AUTONOMOUS mode.
+ *
+ * Called from buffer postdisable to restore the chip to the
+ * idle state used by read_raw. Clears the sequencer and resets state.
+ */
+static int ad4691_exit_conversion_mode(struct ad4691_state *st)
+{
+ if (st->manual_mode)
+ return regmap_update_bits(st->regmap, AD4691_DEVICE_SETUP,
+ AD4691_MANUAL_MODE, 0);
+
+ return regmap_write(st->regmap, AD4691_ADC_SETUP,
+ AD4691_AUTONOMOUS_MODE);
+}
+
+static int ad4691_manual_buffer_preenable(struct iio_dev *indio_dev)
+{
+ struct ad4691_state *st = iio_priv(indio_dev);
+ struct device *dev = regmap_get_device(st->regmap);
+ struct spi_device *spi = to_spi_device(dev);
+ unsigned int n_active = hweight_long(*indio_dev->active_scan_mask);
+ unsigned int n_xfers = n_active + 1;
+ unsigned int k, i;
+ int ret;
+
+ st->scan_devm_group = devres_open_group(dev, NULL, GFP_KERNEL);
+ if (!st->scan_devm_group)
+ return -ENOMEM;
+
+ st->scan_xfers = devm_kcalloc(dev, n_xfers, sizeof(*st->scan_xfers),
+ GFP_KERNEL);
+ st->scan_tx = devm_kcalloc(dev, n_xfers, sizeof(*st->scan_tx),
+ GFP_KERNEL);
+ st->scan_rx = devm_kcalloc(dev, n_xfers, sizeof(*st->scan_rx),
+ GFP_KERNEL);
+ if (!st->scan_xfers || !st->scan_tx || !st->scan_rx) {
+ devres_release_group(dev, st->scan_devm_group);
+ return -ENOMEM;
+ }
+
+ spi_message_init(&st->scan_msg);
+
+ k = 0;
+ iio_for_each_active_channel(indio_dev, i) {
+ st->scan_tx[k] = cpu_to_be16(AD4691_ADC_CHAN(i));
+ st->scan_xfers[k].tx_buf = &st->scan_tx[k];
+ st->scan_xfers[k].rx_buf = &st->scan_rx[k];
+ st->scan_xfers[k].len = sizeof(__be16);
+ st->scan_xfers[k].cs_change = 1;
+ spi_message_add_tail(&st->scan_xfers[k], &st->scan_msg);
+ k++;
+ }
+
+ /* Final NOOP transfer to retrieve last channel's result. */
+ st->scan_tx[k] = cpu_to_be16(AD4691_NOOP);
+ st->scan_xfers[k].tx_buf = &st->scan_tx[k];
+ st->scan_xfers[k].rx_buf = &st->scan_rx[k];
+ st->scan_xfers[k].len = sizeof(__be16);
+ spi_message_add_tail(&st->scan_xfers[k], &st->scan_msg);
+
+ devres_close_group(dev, st->scan_devm_group);
+
+ st->scan_msg.spi = spi;
+
+ ret = spi_optimize_message(spi, &st->scan_msg);
+ if (ret) {
+ devres_release_group(dev, st->scan_devm_group);
+ return ret;
+ }
+
+ ret = ad4691_enter_conversion_mode(st);
+ if (ret) {
+ spi_unoptimize_message(&st->scan_msg);
+ devres_release_group(dev, st->scan_devm_group);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int ad4691_manual_buffer_postdisable(struct iio_dev *indio_dev)
+{
+ struct ad4691_state *st = iio_priv(indio_dev);
+ struct device *dev = regmap_get_device(st->regmap);
+ int ret;
+
+ ret = ad4691_exit_conversion_mode(st);
+ spi_unoptimize_message(&st->scan_msg);
+ devres_release_group(dev, st->scan_devm_group);
+ return ret;
+}
+
+static const struct iio_buffer_setup_ops ad4691_manual_buffer_setup_ops = {
+ .preenable = &ad4691_manual_buffer_preenable,
+ .postdisable = &ad4691_manual_buffer_postdisable,
+};
+
+static int ad4691_cnv_burst_buffer_preenable(struct iio_dev *indio_dev)
+{
+ struct ad4691_state *st = iio_priv(indio_dev);
+ struct device *dev = regmap_get_device(st->regmap);
+ struct spi_device *spi = to_spi_device(dev);
+ unsigned int n_active = hweight_long(*indio_dev->active_scan_mask);
+ unsigned int bit, k, i;
+ int ret;
+
+ st->scan_devm_group = devres_open_group(dev, NULL, GFP_KERNEL);
+ if (!st->scan_devm_group)
+ return -ENOMEM;
+
+ st->scan_xfers = devm_kcalloc(dev, 2 * n_active, sizeof(*st->scan_xfers),
+ GFP_KERNEL);
+ st->scan_tx = devm_kcalloc(dev, n_active, sizeof(*st->scan_tx),
+ GFP_KERNEL);
+ st->scan_rx = devm_kcalloc(dev, n_active, sizeof(*st->scan_rx),
+ GFP_KERNEL);
+ if (!st->scan_xfers || !st->scan_tx || !st->scan_rx) {
+ devres_release_group(dev, st->scan_devm_group);
+ return -ENOMEM;
+ }
+
+ spi_message_init(&st->scan_msg);
+
+ /*
+ * Each AVG_IN read needs two transfers: a 2-byte address write phase
+ * followed by a 2-byte data read phase. CS toggles between channels
+ * (cs_change=1 on the read phase of all but the last channel).
+ */
+ k = 0;
+ iio_for_each_active_channel(indio_dev, i) {
+ st->scan_tx[k] = cpu_to_be16(0x8000 | AD4691_AVG_IN(i));
+ st->scan_xfers[2 * k].tx_buf = &st->scan_tx[k];
+ st->scan_xfers[2 * k].len = sizeof(__be16);
+ spi_message_add_tail(&st->scan_xfers[2 * k], &st->scan_msg);
+ st->scan_xfers[2 * k + 1].rx_buf = &st->scan_rx[k];
+ st->scan_xfers[2 * k + 1].len = sizeof(__be16);
+ if (k < n_active - 1)
+ st->scan_xfers[2 * k + 1].cs_change = 1;
+ spi_message_add_tail(&st->scan_xfers[2 * k + 1], &st->scan_msg);
+ k++;
+ }
+
+ devres_close_group(dev, st->scan_devm_group);
+
+ st->scan_msg.spi = spi;
+
+ ret = spi_optimize_message(spi, &st->scan_msg);
+ if (ret) {
+ devres_release_group(dev, st->scan_devm_group);
+ return ret;
+ }
+
+ ret = regmap_write(st->regmap, AD4691_ACC_MASK_REG,
+ (u16)~(*indio_dev->active_scan_mask));
+ if (ret)
+ goto err;
+
+ ret = regmap_write(st->regmap, AD4691_STD_SEQ_CONFIG,
+ *indio_dev->active_scan_mask);
+ if (ret)
+ goto err;
+
+ iio_for_each_active_channel(indio_dev, bit) {
+ ret = regmap_write(st->regmap, AD4691_ACC_COUNT_LIMIT(bit),
+ AD4691_ACC_COUNT_VAL);
+ if (ret)
+ goto err;
+ }
+
+ ret = ad4691_enter_conversion_mode(st);
+ if (ret)
+ goto err;
+
+ ret = ad4691_sampling_enable(st, true);
+ if (ret)
+ goto err;
+
+ enable_irq(st->irq);
+ return 0;
+err:
+ spi_unoptimize_message(&st->scan_msg);
+ devres_release_group(dev, st->scan_devm_group);
+ return ret;
+}
+
+static int ad4691_cnv_burst_buffer_postdisable(struct iio_dev *indio_dev)
+{
+ struct ad4691_state *st = iio_priv(indio_dev);
+ struct device *dev = regmap_get_device(st->regmap);
+ int ret;
+
+ disable_irq(st->irq);
+
+ ret = ad4691_sampling_enable(st, false);
+ if (ret)
+ return ret;
+
+ ret = regmap_write(st->regmap, AD4691_STD_SEQ_CONFIG,
+ AD4691_SEQ_ALL_CHANNELS_OFF);
+ if (ret)
+ return ret;
+
+ ret = ad4691_exit_conversion_mode(st);
+ spi_unoptimize_message(&st->scan_msg);
+ devres_release_group(dev, st->scan_devm_group);
+ return ret;
+}
+
+static const struct iio_buffer_setup_ops ad4691_cnv_burst_buffer_setup_ops = {
+ .preenable = &ad4691_cnv_burst_buffer_preenable,
+ .postdisable = &ad4691_cnv_burst_buffer_postdisable,
+};
+
+static ssize_t sampling_frequency_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct ad4691_state *st = iio_priv(indio_dev);
+
+ if (st->manual_mode)
+ return -ENODEV;
+
+ return sysfs_emit(buf, "%u\n", (u32)(NSEC_PER_SEC / st->cnv_period_ns));
+}
+
+static ssize_t sampling_frequency_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t len)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct ad4691_state *st = iio_priv(indio_dev);
+ int freq, ret;
+
+ if (st->manual_mode)
+ return -ENODEV;
+
+ ret = kstrtoint(buf, 10, &freq);
+ if (ret)
+ return ret;
+
+ guard(mutex)(&st->lock);
+
+ ret = ad4691_set_pwm_freq(st, freq);
+ if (ret)
+ return ret;
+
+ return len;
+}
+
+static IIO_DEVICE_ATTR(sampling_frequency, 0644,
+ sampling_frequency_show,
+ sampling_frequency_store, 0);
+
+static const struct iio_dev_attr *ad4691_buffer_attrs[] = {
+ &iio_dev_attr_sampling_frequency,
+ NULL,
+};
+
+static irqreturn_t ad4691_irq(int irq, void *private)
+{
+ struct iio_dev *indio_dev = private;
+ struct ad4691_state *st = iio_priv(indio_dev);
+
+ /*
+ * GPx has asserted: stop conversions before reading so the
+ * accumulator does not continue sampling while the trigger handler
+ * processes the data. Then fire the IIO trigger to push the sample
+ * to the buffer.
+ */
+ ad4691_sampling_enable(st, false);
+ iio_trigger_poll(st->trig);
+
+ return IRQ_HANDLED;
+}
+
+static const struct iio_trigger_ops ad4691_trigger_ops = {
+ .validate_device = iio_trigger_validate_own_device,
+};
+
+static irqreturn_t ad4691_trigger_handler(int irq, void *p)
+{
+ struct iio_poll_func *pf = p;
+ struct iio_dev *indio_dev = pf->indio_dev;
+ struct ad4691_state *st = iio_priv(indio_dev);
+ unsigned int i, k = 0;
+ int ret;
+
+ guard(mutex)(&st->lock);
+
+ ret = spi_sync(st->scan_msg.spi, &st->scan_msg);
+ if (ret)
+ goto done;
+
+ if (st->manual_mode) {
+ iio_for_each_active_channel(indio_dev, i) {
+ st->scan.vals[i] = be16_to_cpu(st->scan_rx[k + 1]);
+ k++;
+ }
+ } else {
+ iio_for_each_active_channel(indio_dev, i) {
+ st->scan.vals[i] = be16_to_cpu(st->scan_rx[k]);
+ k++;
+ }
+
+ ret = regmap_write(st->regmap, AD4691_STATE_RESET_REG,
+ AD4691_STATE_RESET_ALL);
+ if (ret)
+ goto done;
+
+ ret = ad4691_sampling_enable(st, true);
+ if (ret)
+ goto done;
+ }
+
+ iio_push_to_buffers_with_ts(indio_dev, &st->scan, sizeof(st->scan),
+ pf->timestamp);
+
+done:
+ iio_trigger_notify_done(indio_dev->trig);
+ return IRQ_HANDLED;
+}
+
static const struct iio_info ad4691_info = {
.read_raw = &ad4691_read_raw,
.write_raw = &ad4691_write_raw,
@@ -495,6 +934,25 @@ static const struct iio_info ad4691_info = {
.debugfs_reg_access = &ad4691_reg_access,
};
+static int ad4691_pwm_setup(struct ad4691_state *st)
+{
+ struct device *dev = regmap_get_device(st->regmap);
+ int ret;
+
+ st->conv_trigger = devm_pwm_get(dev, "cnv");
+ if (IS_ERR(st->conv_trigger))
+ return dev_err_probe(dev, PTR_ERR(st->conv_trigger),
+ "Failed to get cnv pwm\n");
+
+ ret = devm_add_action_or_reset(dev, ad4691_disable_pwm,
+ st->conv_trigger);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "Failed to register PWM disable action\n");
+
+ return ad4691_set_pwm_freq(st, st->info->max_rate);
+}
+
static int ad4691_regulator_setup(struct ad4691_state *st)
{
struct device *dev = regmap_get_device(st->regmap);
@@ -555,12 +1013,30 @@ static int ad4691_reset(struct ad4691_state *st)
return 0;
}
-static int ad4691_config(struct ad4691_state *st)
+static int ad4691_config(struct ad4691_state *st, u32 max_speed_hz)
{
struct device *dev = regmap_get_device(st->regmap);
enum ad4691_ref_ctrl ref_val;
+ const char *irq_name;
+ unsigned int gp_num;
int ret;
+ /*
+ * Determine buffer conversion mode from DT: if a PWM is provided it
+ * drives the CNV pin (CNV_BURST_MODE); otherwise CNV is tied to CS
+ * and each SPI transfer triggers a conversion (MANUAL_MODE).
+ * Both modes idle in AUTONOMOUS mode so that read_raw can use the
+ * internal oscillator without disturbing the hardware configuration.
+ */
+ if (device_property_present(dev, "pwms")) {
+ st->manual_mode = false;
+ ret = ad4691_pwm_setup(st);
+ if (ret)
+ return ret;
+ } else {
+ st->manual_mode = true;
+ }
+
switch (st->vref_uV) {
case AD4691_VREF_uV_MIN ... AD4691_VREF_2P5_uV_MAX:
ref_val = AD4691_VREF_2P5;
@@ -595,17 +1071,91 @@ static int ad4691_config(struct ad4691_state *st)
return dev_err_probe(dev, ret, "Failed to write DEVICE_SETUP\n");
/*
- * Set the internal oscillator to the highest valid rate for this chip.
- * Index 0 (1 MHz) is valid only for AD4692/AD4694; AD4691/AD4693 start
- * at index 1 (500 kHz).
+ * Set the internal oscillator to the highest rate this chip supports.
+ * Index 0 (1 MHz) exceeds the 500 kHz max of AD4691/AD4693, so those
+ * chips start at index 1 (500 kHz).
*/
ret = regmap_write(st->regmap, AD4691_OSC_FREQ_REG,
- (st->info->max_rate == HZ_PER_MHZ) ? 0 : 1);
+ (ad4691_osc_freqs[0] > st->info->max_rate) ? 1 : 0);
if (ret)
return dev_err_probe(dev, ret, "Failed to write OSC_FREQ\n");
/* Device always operates in AUTONOMOUS mode. */
- return regmap_write(st->regmap, AD4691_ADC_SETUP, AD4691_AUTONOMOUS_MODE_VAL);
+ ret = regmap_write(st->regmap, AD4691_ADC_SETUP, AD4691_AUTONOMOUS_MODE);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to write ADC_SETUp\n");
+
+ if (st->manual_mode)
+ return 0;
+
+ ret = device_property_read_string_array(dev, "interrupt-names",
+ &irq_name, 1);
+ if (ret < 0)
+ return dev_err_probe(dev, ret,
+ "Failed to read interrupt-names\n");
+
+ if (strncmp(irq_name, "gp", 2) != 0 ||
+ kstrtouint(irq_name + 2, 10, &gp_num) || gp_num > 3)
+ return dev_err_probe(dev, -EINVAL,
+ "Invalid interrupt name '%s'\n", irq_name);
+
+ return ad4691_gpio_setup(st, gp_num);
+}
+
+static int ad4691_setup_triggered_buffer(struct iio_dev *indio_dev,
+ struct ad4691_state *st)
+{
+ struct device *dev = regmap_get_device(st->regmap);
+ int irq, ret;
+
+ st->trig = devm_iio_trigger_alloc(dev, "%s-dev%d",
+ indio_dev->name,
+ iio_device_id(indio_dev));
+ if (!st->trig)
+ return -ENOMEM;
+
+ st->trig->ops = &ad4691_trigger_ops;
+ iio_trigger_set_drvdata(st->trig, st);
+
+ ret = devm_iio_trigger_register(dev, st->trig);
+ if (ret)
+ return dev_err_probe(dev, ret, "IIO trigger register failed\n");
+
+ indio_dev->trig = iio_trigger_get(st->trig);
+
+ if (!st->manual_mode) {
+ /*
+ * GP0 asserts at end-of-conversion. The IRQ handler stops
+ * conversions and fires the IIO trigger so the trigger handler
+ * can read and push the sample to the buffer. The IRQ is kept
+ * disabled until the buffer is enabled.
+ */
+ irq = fwnode_irq_get(dev_fwnode(dev), 0);
+ if (irq < 0)
+ return dev_err_probe(dev, irq,
+ "failed to get GP interrupt\n");
+
+ st->irq = irq;
+
+ ret = devm_request_threaded_irq(dev, irq, NULL,
+ &ad4691_irq,
+ IRQF_ONESHOT | IRQF_NO_AUTOEN,
+ indio_dev->name, indio_dev);
+ if (ret)
+ return ret;
+
+ return devm_iio_triggered_buffer_setup_ext(dev, indio_dev,
+ &iio_pollfunc_store_time,
+ &ad4691_trigger_handler,
+ IIO_BUFFER_DIRECTION_IN,
+ &ad4691_cnv_burst_buffer_setup_ops,
+ ad4691_buffer_attrs);
+ }
+
+ return devm_iio_triggered_buffer_setup(dev, indio_dev,
+ &iio_pollfunc_store_time,
+ &ad4691_trigger_handler,
+ &ad4691_manual_buffer_setup_ops);
}
static int ad4691_probe(struct spi_device *spi)
@@ -639,7 +1189,7 @@ static int ad4691_probe(struct spi_device *spi)
if (ret)
return ret;
- ret = ad4691_config(st);
+ ret = ad4691_config(st, spi->max_speed_hz);
if (ret)
return ret;
@@ -650,6 +1200,10 @@ static int ad4691_probe(struct spi_device *spi)
indio_dev->channels = st->info->channels;
indio_dev->num_channels = st->info->num_channels;
+ ret = ad4691_setup_triggered_buffer(indio_dev, st);
+ if (ret)
+ return ret;
+
return devm_iio_device_register(dev, indio_dev);
}
--
2.43.0
Hi Radu,
Some comments from me.
On Fri, 2026-03-20 at 13:03 +0200, Radu Sabau via B4 Relay wrote:
> From: Radu Sabau <radu.sabau@analog.com>
>
> Add buffered capture support using the IIO triggered buffer framework.
>
> CNV Burst Mode: the GP pin identified by interrupt-names in the device
> tree is configured as DATA_READY output. The IRQ handler stops
> conversions and fires the IIO trigger; the trigger handler executes a
> pre-built SPI message that reads all active channels from the AVG_IN
> accumulator registers and then resets accumulator state and restarts
> conversions for the next cycle.
>
> Manual Mode: CNV is tied to SPI CS so each transfer simultaneously
> reads the previous result and starts the next conversion (pipelined
> N+1 scheme). At preenable time a pre-built, optimised SPI message of
> N+1 transfers is constructed (N channel reads plus one NOOP to drain
> the pipeline). The trigger handler executes the message in a single
> spi_sync() call and collects the results. An external trigger (e.g.
> iio-trig-hrtimer) is required to drive the trigger at the desired
> sample rate.
>
> Both modes share the same trigger handler and push a complete scan —
> one u16 slot per channel at its scan_index position, followed by a
> timestamp — to the IIO buffer via iio_push_to_buffers_with_ts().
>
> The CNV Burst Mode sampling frequency (PWM period) is exposed as a
> buffer-level attribute via IIO_DEVICE_ATTR.
>
> Signed-off-by: Radu Sabau <radu.sabau@analog.com>
> ---
> drivers/iio/adc/Kconfig | 2 +
> drivers/iio/adc/ad4691.c | 584 +++++++++++++++++++++++++++++++++++++++++++++--
> 2 files changed, 571 insertions(+), 15 deletions(-)
>
> diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
> index 3685a03aa8dc..d498f16c0816 100644
> --- a/drivers/iio/adc/Kconfig
> +++ b/drivers/iio/adc/Kconfig
> @@ -142,6 +142,8 @@ config AD4170_4
> config AD4691
> tristate "Analog Devices AD4691 Family ADC Driver"
> depends on SPI
> + select IIO_BUFFER
> + select IIO_TRIGGERED_BUFFER
> select REGMAP
> help
> Say yes here to build support for Analog Devices AD4691 Family MuxSAR
> diff --git a/drivers/iio/adc/ad4691.c b/drivers/iio/adc/ad4691.c
> index 5e02eb44ca44..db776de32846 100644
> --- a/drivers/iio/adc/ad4691.c
> +++ b/drivers/iio/adc/ad4691.c
> @@ -9,9 +9,12 @@
> #include <linux/delay.h>
> #include <linux/device.h>
> #include <linux/err.h>
> +#include <linux/interrupt.h>
> #include <linux/math.h>
> #include <linux/module.h>
> #include <linux/mod_devicetable.h>
> +#include <linux/property.h>
> +#include <linux/pwm.h>
> #include <linux/regmap.h>
> #include <linux/regulator/consumer.h>
> #include <linux/reset.h>
> @@ -19,7 +22,12 @@
> #include <linux/units.h>
> #include <linux/unaligned.h>
>
> +#include <linux/iio/buffer.h>
> #include <linux/iio/iio.h>
> +#include <linux/iio/sysfs.h>
> +#include <linux/iio/trigger.h>
> +#include <linux/iio/triggered_buffer.h>
> +#include <linux/iio/trigger_consumer.h>
>
> #define AD4691_VREF_uV_MIN 2400000
> #define AD4691_VREF_uV_MAX 5250000
> @@ -28,6 +36,8 @@
> #define AD4691_VREF_3P3_uV_MAX 3750000
> #define AD4691_VREF_4P096_uV_MAX 4500000
>
> +#define AD4691_CNV_DUTY_CYCLE_NS 380
> +
> #define AD4691_SPI_CONFIG_A_REG 0x000
> #define AD4691_SW_RESET (BIT(7) | BIT(0))
>
...
>
> +static int ad4691_cnv_burst_buffer_preenable(struct iio_dev *indio_dev)
> +{
> + struct ad4691_state *st = iio_priv(indio_dev);
> + struct device *dev = regmap_get_device(st->regmap);
> + struct spi_device *spi = to_spi_device(dev);
> + unsigned int n_active = hweight_long(*indio_dev->active_scan_mask);
> + unsigned int bit, k, i;
> + int ret;
> +
> + st->scan_devm_group = devres_open_group(dev, NULL, GFP_KERNEL);
> + if (!st->scan_devm_group)
> + return -ENOMEM;
Agree with Jonathan. Not seeing a valid reason for the above.
> +
> + st->scan_xfers = devm_kcalloc(dev, 2 * n_active, sizeof(*st->scan_xfers),
> + GFP_KERNEL);
> + st->scan_tx = devm_kcalloc(dev, n_active, sizeof(*st->scan_tx),
> + GFP_KERNEL);
> + st->scan_rx = devm_kcalloc(dev, n_active, sizeof(*st->scan_rx),
> + GFP_KERNEL);
> + if (!st->scan_xfers || !st->scan_tx || !st->scan_rx) {
> + devres_release_group(dev, st->scan_devm_group);
> + return -ENOMEM;
> + }
> +
> + spi_message_init(&st->scan_msg);
> +
> + /*
> + * Each AVG_IN read needs two transfers: a 2-byte address write phase
> + * followed by a 2-byte data read phase. CS toggles between channels
> + * (cs_change=1 on the read phase of all but the last channel).
> + */
> + k = 0;
> + iio_for_each_active_channel(indio_dev, i) {
> + st->scan_tx[k] = cpu_to_be16(0x8000 | AD4691_AVG_IN(i));
> + st->scan_xfers[2 * k].tx_buf = &st->scan_tx[k];
> + st->scan_xfers[2 * k].len = sizeof(__be16);
> + spi_message_add_tail(&st->scan_xfers[2 * k], &st->scan_msg);
> + st->scan_xfers[2 * k + 1].rx_buf = &st->scan_rx[k];
> + st->scan_xfers[2 * k + 1].len = sizeof(__be16);
> + if (k < n_active - 1)
> + st->scan_xfers[2 * k + 1].cs_change = 1;
> + spi_message_add_tail(&st->scan_xfers[2 * k + 1], &st->scan_msg);
> + k++;
> + }
> +
> + devres_close_group(dev, st->scan_devm_group);
> +
> + st->scan_msg.spi = spi;
> +
> + ret = spi_optimize_message(spi, &st->scan_msg);
> + if (ret) {
> + devres_release_group(dev, st->scan_devm_group);
> + return ret;
> + }
> +
> + ret = regmap_write(st->regmap, AD4691_ACC_MASK_REG,
> + (u16)~(*indio_dev->active_scan_mask));
> + if (ret)
> + goto err;
> +
> + ret = regmap_write(st->regmap, AD4691_STD_SEQ_CONFIG,
> + *indio_dev->active_scan_mask);
> + if (ret)
> + goto err;
> +
> + iio_for_each_active_channel(indio_dev, bit) {
> + ret = regmap_write(st->regmap, AD4691_ACC_COUNT_LIMIT(bit),
> + AD4691_ACC_COUNT_VAL);
> + if (ret)
> + goto err;
> + }
> +
> + ret = ad4691_enter_conversion_mode(st);
> + if (ret)
> + goto err;
> +
> + ret = ad4691_sampling_enable(st, true);
> + if (ret)
> + goto err;
> +
> + enable_irq(st->irq);
> + return 0;
> +err:
> + spi_unoptimize_message(&st->scan_msg);
> + devres_release_group(dev, st->scan_devm_group);
> + return ret;
> +}
> +
> +static int ad4691_cnv_burst_buffer_postdisable(struct iio_dev *indio_dev)
> +{
> + struct ad4691_state *st = iio_priv(indio_dev);
> + struct device *dev = regmap_get_device(st->regmap);
> + int ret;
> +
> + disable_irq(st->irq);
Should we use disable_irq_sync()?
> +
> + ret = ad4691_sampling_enable(st, false);
> + if (ret)
> + return ret;
> +
> + ret = regmap_write(st->regmap, AD4691_STD_SEQ_CONFIG,
> + AD4691_SEQ_ALL_CHANNELS_OFF);
> + if (ret)
> + return ret;
> +
> + ret = ad4691_exit_conversion_mode(st);
> + spi_unoptimize_message(&st->scan_msg);
> + devres_release_group(dev, st->scan_devm_group);
> + return ret;
> +}
> +
> +static const struct iio_buffer_setup_ops ad4691_cnv_burst_buffer_setup_ops = {
> + .preenable = &ad4691_cnv_burst_buffer_preenable,
> + .postdisable = &ad4691_cnv_burst_buffer_postdisable,
> +};
> +
> +static ssize_t sampling_frequency_show(struct device *dev,
> + struct device_attribute *attr,
> + char *buf)
> +{
> + struct iio_dev *indio_dev = dev_to_iio_dev(dev);
> + struct ad4691_state *st = iio_priv(indio_dev);
> +
> + if (st->manual_mode)
> + return -ENODEV;
Can the above happen at all? I think you're making sure (at probe) this interface
never get's exposed in manual mode.
> +
> + return sysfs_emit(buf, "%u\n", (u32)(NSEC_PER_SEC / st->cnv_period_ns));
> +}
> +
> +static ssize_t sampling_frequency_store(struct device *dev,
> + struct device_attribute *attr,
> + const char *buf, size_t len)
> +{
> + struct iio_dev *indio_dev = dev_to_iio_dev(dev);
> + struct ad4691_state *st = iio_priv(indio_dev);
> + int freq, ret;
> +
> + if (st->manual_mode)
> + return -ENODEV;
> +
> + ret = kstrtoint(buf, 10, &freq);
> + if (ret)
> + return ret;
> +
> + guard(mutex)(&st->lock);
> +
> + ret = ad4691_set_pwm_freq(st, freq);
> + if (ret)
> + return ret;
> +
> + return len;
> +}
> +
> +static IIO_DEVICE_ATTR(sampling_frequency, 0644,
> + sampling_frequency_show,
> + sampling_frequency_store, 0);
> +
> +static const struct iio_dev_attr *ad4691_buffer_attrs[] = {
> + &iio_dev_attr_sampling_frequency,
> + NULL,
> +};
> +
> +static irqreturn_t ad4691_irq(int irq, void *private)
> +{
> + struct iio_dev *indio_dev = private;
> + struct ad4691_state *st = iio_priv(indio_dev);
> +
> + /*
> + * GPx has asserted: stop conversions before reading so the
> + * accumulator does not continue sampling while the trigger handler
> + * processes the data. Then fire the IIO trigger to push the sample
> + * to the buffer.
> + */
> + ad4691_sampling_enable(st, false);
> + iio_trigger_poll(st->trig);
Not sure you need to save trig in your struct. We already have it in indio_dev->trig. Sure,
it is a private member but still fairly common to see (this patch included):
indio_dev->trig = iio_trigger_get(trig);
So I would say we either assume it's public or start to not allow the above
pattern.
Alternatively, I don't think you're using indio_dev driverdata right? Could save it
in there.
> +
> + return IRQ_HANDLED;
> +}
> +
> +static const struct iio_trigger_ops ad4691_trigger_ops = {
> + .validate_device = iio_trigger_validate_own_device,
> +};
> +
> +static irqreturn_t ad4691_trigger_handler(int irq, void *p)
> +{
> + struct iio_poll_func *pf = p;
> + struct iio_dev *indio_dev = pf->indio_dev;
> + struct ad4691_state *st = iio_priv(indio_dev);
> + unsigned int i, k = 0;
> + int ret;
> +
> + guard(mutex)(&st->lock);
> +
> + ret = spi_sync(st->scan_msg.spi, &st->scan_msg);
> + if (ret)
> + goto done;
> +
> + if (st->manual_mode) {
> + iio_for_each_active_channel(indio_dev, i) {
> + st->scan.vals[i] = be16_to_cpu(st->scan_rx[k + 1]);
> + k++;
> + }
> + } else {
> + iio_for_each_active_channel(indio_dev, i) {
> + st->scan.vals[i] = be16_to_cpu(st->scan_rx[k]);
> + k++;
> + }
> +
> + ret = regmap_write(st->regmap, AD4691_STATE_RESET_REG,
> + AD4691_STATE_RESET_ALL);
> + if (ret)
> + goto done;
> +
> + ret = ad4691_sampling_enable(st, true);
> + if (ret)
> + goto done;
> + }
> +
> + iio_push_to_buffers_with_ts(indio_dev, &st->scan, sizeof(st->scan),
> + pf->timestamp);
> +
> +done:
> + iio_trigger_notify_done(indio_dev->trig);
> + return IRQ_HANDLED;
> +}
> +
> static const struct iio_info ad4691_info = {
> .read_raw = &ad4691_read_raw,
> .write_raw = &ad4691_write_raw,
> @@ -495,6 +934,25 @@ static const struct iio_info ad4691_info = {
> .debugfs_reg_access = &ad4691_reg_access,
> };
>
> +static int ad4691_pwm_setup(struct ad4691_state *st)
> +{
> + struct device *dev = regmap_get_device(st->regmap);
> + int ret;
> +
> + st->conv_trigger = devm_pwm_get(dev, "cnv");
> + if (IS_ERR(st->conv_trigger))
> + return dev_err_probe(dev, PTR_ERR(st->conv_trigger),
> + "Failed to get cnv pwm\n");
> +
> + ret = devm_add_action_or_reset(dev, ad4691_disable_pwm,
> + st->conv_trigger);
This is a suspicious pattern. But I do see it's used like this in more places and it's a no-op
if PWM is already disabled. Still, not sure if agree with this kind "unbalanced" handling.
> + if (ret)
> + return dev_err_probe(dev, ret,
> + "Failed to register PWM disable action\n");
> +
> + return ad4691_set_pwm_freq(st, st->info->max_rate);
> +}
> +
> static int ad4691_regulator_setup(struct ad4691_state *st)
> {
> struct device *dev = regmap_get_device(st->regmap);
> @@ -555,12 +1013,30 @@ static int ad4691_reset(struct ad4691_state *st)
> return 0;
> }
>
> -static int ad4691_config(struct ad4691_state *st)
> +static int ad4691_config(struct ad4691_state *st, u32 max_speed_hz)
My eyes might be failing me but where is 'max_speed_hz' used?
> {
> struct device *dev = regmap_get_device(st->regmap);
> enum ad4691_ref_ctrl ref_val;
> + const char *irq_name;
> + unsigned int gp_num;
> int ret;
>
> + /*
> + * Determine buffer conversion mode from DT: if a PWM is provided it
> + * drives the CNV pin (CNV_BURST_MODE); otherwise CNV is tied to CS
> + * and each SPI transfer triggers a conversion (MANUAL_MODE).
> + * Both modes idle in AUTONOMOUS mode so that read_raw can use the
> + * internal oscillator without disturbing the hardware configuration.
> + */
> + if (device_property_present(dev, "pwms")) {
> + st->manual_mode = false;
> + ret = ad4691_pwm_setup(st);
> + if (ret)
> + return ret;
> + } else {
> + st->manual_mode = true;
> + }
> +
> switch (st->vref_uV) {
> case AD4691_VREF_uV_MIN ... AD4691_VREF_2P5_uV_MAX:
> ref_val = AD4691_VREF_2P5;
> @@ -595,17 +1071,91 @@ static int ad4691_config(struct ad4691_state *st)
> return dev_err_probe(dev, ret, "Failed to write DEVICE_SETUP\n");
>
> /*
> - * Set the internal oscillator to the highest valid rate for this chip.
> - * Index 0 (1 MHz) is valid only for AD4692/AD4694; AD4691/AD4693 start
> - * at index 1 (500 kHz).
> + * Set the internal oscillator to the highest rate this chip supports.
> + * Index 0 (1 MHz) exceeds the 500 kHz max of AD4691/AD4693, so those
> + * chips start at index 1 (500 kHz).
> */
> ret = regmap_write(st->regmap, AD4691_OSC_FREQ_REG,
> - (st->info->max_rate == HZ_PER_MHZ) ? 0 : 1);
> + (ad4691_osc_freqs[0] > st->info->max_rate) ? 1 : 0);
Does this belong to this commit?
> if (ret)
> return dev_err_probe(dev, ret, "Failed to write OSC_FREQ\n");
>
> /* Device always operates in AUTONOMOUS mode. */
> - return regmap_write(st->regmap, AD4691_ADC_SETUP, AD4691_AUTONOMOUS_MODE_VAL);
> + ret = regmap_write(st->regmap, AD4691_ADC_SETUP, AD4691_AUTONOMOUS_MODE);
> + if (ret)
> + return dev_err_probe(dev, ret, "Failed to write ADC_SETUp\n");
> +
> + if (st->manual_mode)
> + return 0;
> +
> + ret = device_property_read_string_array(dev, "interrupt-names",
> + &irq_name, 1);
> + if (ret < 0)
> + return dev_err_probe(dev, ret,
> + "Failed to read interrupt-names\n");
> +
> + if (strncmp(irq_name, "gp", 2) != 0 ||
> + kstrtouint(irq_name + 2, 10, &gp_num) || gp_num > 3)
> + return dev_err_probe(dev, -EINVAL,
> + "Invalid interrupt name '%s'\n", irq_name);
> +
I would likely prefer something like [1] rather than the string parsing.
[1]: https://elixir.bootlin.com/linux/v7.0-rc5/source/drivers/iio/imu/adis16480.c#L1582
- Nuno Sá
On Fri, 20 Mar 2026 13:03:57 +0200
Radu Sabau via B4 Relay <devnull+radu.sabau.analog.com@kernel.org> wrote:
> From: Radu Sabau <radu.sabau@analog.com>
>
> Add buffered capture support using the IIO triggered buffer framework.
>
> CNV Burst Mode: the GP pin identified by interrupt-names in the device
The first I assume? Might be up to 4 of them.
> tree is configured as DATA_READY output. The IRQ handler stops
> conversions and fires the IIO trigger; the trigger handler executes a
> pre-built SPI message that reads all active channels from the AVG_IN
> accumulator registers and then resets accumulator state and restarts
> conversions for the next cycle.
No oversampling configuration? If it's a fixed length burst I'd still
expect to see an indication of what it is and if we can flip back to
no oversampling by changing mode, that should be oversampling == 1.
Seems there is a depth setting for the averaging filters, that superficially
at least appears to be the right control for this.
Seems like there is an SPI burst mode as well? That feels like very
standard oversampling.
>
> Manual Mode: CNV is tied to SPI CS so each transfer simultaneously
> reads the previous result and starts the next conversion (pipelined
> N+1 scheme). At preenable time a pre-built, optimised SPI message of
> N+1 transfers is constructed (N channel reads plus one NOOP to drain
> the pipeline). The trigger handler executes the message in a single
> spi_sync() call and collects the results. An external trigger (e.g.
> iio-trig-hrtimer) is required to drive the trigger at the desired
> sample rate.
>
> Both modes share the same trigger handler and push a complete scan —
> one u16 slot per channel at its scan_index position, followed by a
> timestamp — to the IIO buffer via iio_push_to_buffers_with_ts().
>
> The CNV Burst Mode sampling frequency (PWM period) is exposed as a
> buffer-level attribute via IIO_DEVICE_ATTR.
>
> Signed-off-by: Radu Sabau <radu.sabau@analog.com>
A few comments inline. All the modes etc in this driver are complex
enough I think this one needs a driver specific document in Documentation/iio
Thanks,
Jonathan
> diff --git a/drivers/iio/adc/ad4691.c b/drivers/iio/adc/ad4691.c
> index 5e02eb44ca44..db776de32846 100644
> --- a/drivers/iio/adc/ad4691.c
> +++ b/drivers/iio/adc/ad4691.c
> @@ -121,6 +140,7 @@ static const struct iio_chan_spec ad4691_channels[] = {
> AD4691_CHANNEL(13),
> AD4691_CHANNEL(14),
> AD4691_CHANNEL(15),
> + IIO_CHAN_SOFT_TIMESTAMP(16),
> };
>
> static const struct iio_chan_spec ad4693_channels[] = {
> @@ -132,6 +152,7 @@ static const struct iio_chan_spec ad4693_channels[] = {
> AD4691_CHANNEL(5),
> AD4691_CHANNEL(6),
> AD4691_CHANNEL(7),
> + IIO_CHAN_SOFT_TIMESTAMP(16),
Any big advantage in pushing it all the way down to 16? It's ABI compliant
as these only have to be monotonic, but I'd feel it was more natural as 8 unless
there is something I'm missing.
> };
>
> /*
> @@ -189,16 +210,63 @@ static const struct ad4691_chip_info ad4694_chip_info = {
> struct ad4691_state {
> const struct ad4691_chip_info *info;
> struct regmap *regmap;
> +
> + struct pwm_device *conv_trigger;
> + struct iio_trigger *trig;
> + int irq;
> +
> + bool manual_mode;
> +
> int vref_uV;
> bool refbuf_en;
> bool ldo_en;
> + u32 cnv_period_ns;
> /*
> * Synchronize access to members of the driver state, and ensure
> * atomicity of consecutive SPI operations.
> */
> struct mutex lock;
> + /*
> + * Per-buffer-enabl ree lifetimesources:
> + * Manual Mode - a pre-built SPI message that clocks out N+1
> + * transfers in one go.
> + * CNV Burst Mode - a pre-built SPI message that clocks out 2*N
> + * transfers in one go.
> + */
> + void *scan_devm_group;
> + struct spi_message scan_msg;
> + struct spi_transfer *scan_xfers;
> + __be16 *scan_tx;
> + __be16 *scan_rx;
> + /* Scan buffer: one slot per channel (u16) plus timestamp */
> + struct {
> + u16 vals[16];
> + s64 ts __aligned(8);
aligned_s64
That was introduced last year IIRC.
> + } scan __aligned(IIO_DMA_MINALIGN);
> };
> static int ad4691_reg_read(void *context, unsigned int reg, unsigned int *val)
> {
> struct spi_device *spi = context;
> @@ -341,14 +409,16 @@ static int ad4691_get_sampling_freq(struct ad4691_state *st, int *val)
> static int ad4691_set_sampling_freq(struct iio_dev *indio_dev, int freq)
> {
> struct ad4691_state *st = iio_priv(indio_dev);
> - unsigned int start = (st->info->max_rate == HZ_PER_MHZ) ? 0 : 1;
> unsigned int i;
>
> + if (freq > st->info->max_rate)
> + return -EINVAL;
> +
> IIO_DEV_ACQUIRE_DIRECT_MODE(indio_dev, claim);
> if (IIO_DEV_ACQUIRE_FAILED(claim))
> return -EBUSY;
>
> - for (i = start; i < ARRAY_SIZE(ad4691_osc_freqs); i++) {
> + for (i = 0; i < ARRAY_SIZE(ad4691_osc_freqs); i++) {
Why is the start offset no longer relevant?
> if ((int)ad4691_osc_freqs[i] == freq)
> return regmap_update_bits(st->regmap, AD4691_OSC_FREQ_REG,
> AD4691_OSC_FREQ_MASK, i);
> @@ -386,8 +459,7 @@ static int ad4691_single_shot_read(struct iio_dev *indio_dev,
> guard(mutex)(&st->lock);
>
> /*
> - * Use AUTONOMOUS mode for single-shot reads. The chip always
> - * operates in AUTONOMOUS mode in this driver revision.
> + * Use AUTONOMOUS mode for single-shot reads.
This edit doesn't seem to belong in this patch. I'd just put the new
text in place in the earlier patch.
> +static int ad4691_manual_buffer_preenable(struct iio_dev *indio_dev)
> +{
> + struct ad4691_state *st = iio_priv(indio_dev);
> + struct device *dev = regmap_get_device(st->regmap);
> + struct spi_device *spi = to_spi_device(dev);
> + unsigned int n_active = hweight_long(*indio_dev->active_scan_mask);
> + unsigned int n_xfers = n_active + 1;
> + unsigned int k, i;
> + int ret;
> +
> + st->scan_devm_group = devres_open_group(dev, NULL, GFP_KERNEL);
> + if (!st->scan_devm_group)
> + return -ENOMEM;
> +
> + st->scan_xfers = devm_kcalloc(dev, n_xfers, sizeof(*st->scan_xfers),
> + GFP_KERNEL);
> + st->scan_tx = devm_kcalloc(dev, n_xfers, sizeof(*st->scan_tx),
> + GFP_KERNEL);
> + st->scan_rx = devm_kcalloc(dev, n_xfers, sizeof(*st->scan_rx),
> + GFP_KERNEL);
> + if (!st->scan_xfers || !st->scan_tx || !st->scan_rx) {
> + devres_release_group(dev, st->scan_devm_group);
> + return -ENOMEM;
> + }
> +
> + spi_message_init(&st->scan_msg);
> +
> + k = 0;
> + iio_for_each_active_channel(indio_dev, i) {
> + st->scan_tx[k] = cpu_to_be16(AD4691_ADC_CHAN(i));
> + st->scan_xfers[k].tx_buf = &st->scan_tx[k];
> + st->scan_xfers[k].rx_buf = &st->scan_rx[k];
> + st->scan_xfers[k].len = sizeof(__be16);
> + st->scan_xfers[k].cs_change = 1;
> + spi_message_add_tail(&st->scan_xfers[k], &st->scan_msg);
> + k++;
> + }
> +
> + /* Final NOOP transfer to retrieve last channel's result. */
> + st->scan_tx[k] = cpu_to_be16(AD4691_NOOP);
> + st->scan_xfers[k].tx_buf = &st->scan_tx[k];
> + st->scan_xfers[k].rx_buf = &st->scan_rx[k];
> + st->scan_xfers[k].len = sizeof(__be16);
> + spi_message_add_tail(&st->scan_xfers[k], &st->scan_msg);
> +
> + devres_close_group(dev, st->scan_devm_group);
Similar comment to below on devres groups not really being appropriate
here.
> +
> + st->scan_msg.spi = spi;
> +
> + ret = spi_optimize_message(spi, &st->scan_msg);
> + if (ret) {
> + devres_release_group(dev, st->scan_devm_group);
If you were keeping this, better to do an error handling block via
gotos like you do for the other similar code.
> + return ret;
> + }
> +
> + ret = ad4691_enter_conversion_mode(st);
> + if (ret) {
> + spi_unoptimize_message(&st->scan_msg);
> + devres_release_group(dev, st->scan_devm_group);
> + return ret;
> + }
> +
> + return 0;
> +}
> +
> +static int ad4691_manual_buffer_postdisable(struct iio_dev *indio_dev)
> +{
> + struct ad4691_state *st = iio_priv(indio_dev);
> + struct device *dev = regmap_get_device(st->regmap);
> + int ret;
> +
> + ret = ad4691_exit_conversion_mode(st);
> + spi_unoptimize_message(&st->scan_msg);
> + devres_release_group(dev, st->scan_devm_group);
> + return ret;
> +}
;
> +
> +static int ad4691_cnv_burst_buffer_preenable(struct iio_dev *indio_dev)
> +{
> + struct ad4691_state *st = iio_priv(indio_dev);
> + struct device *dev = regmap_get_device(st->regmap);
> + struct spi_device *spi = to_spi_device(dev);
> + unsigned int n_active = hweight_long(*indio_dev->active_scan_mask);
> + unsigned int bit, k, i;
> + int ret;
> +
> + st->scan_devm_group = devres_open_group(dev, NULL, GFP_KERNEL);
> + if (!st->scan_devm_group)
> + return -ENOMEM;
> +
> + st->scan_xfers = devm_kcalloc(dev, 2 * n_active, sizeof(*st->scan_xfers),
> + GFP_KERNEL);
See later. I don't see a benefit in using a devres group here. It's nothing
to do with device lifetime.
> + st->scan_tx = devm_kcalloc(dev, n_active, sizeof(*st->scan_tx),
> + GFP_KERNEL);
> + st->scan_rx = devm_kcalloc(dev, n_active, sizeof(*st->scan_rx),
> + GFP_KERNEL);
> + if (!st->scan_xfers || !st->scan_tx || !st->scan_rx) {
> + devres_release_group(dev, st->scan_devm_group);
> + return -ENOMEM;
> + }
> +
> + spi_message_init(&st->scan_msg);
> +
> + /*
> + * Each AVG_IN read needs two transfers: a 2-byte address write phase
> + * followed by a 2-byte data read phase. CS toggles between channels
> + * (cs_change=1 on the read phase of all but the last channel).
> + */
> + k = 0;
> + iio_for_each_active_channel(indio_dev, i) {
> + st->scan_tx[k] = cpu_to_be16(0x8000 | AD4691_AVG_IN(i));
> + st->scan_xfers[2 * k].tx_buf = &st->scan_tx[k];
> + st->scan_xfers[2 * k].len = sizeof(__be16);
> + spi_message_add_tail(&st->scan_xfers[2 * k], &st->scan_msg);
> + st->scan_xfers[2 * k + 1].rx_buf = &st->scan_rx[k];
> + st->scan_xfers[2 * k + 1].len = sizeof(__be16);
> + if (k < n_active - 1)
> + st->scan_xfers[2 * k + 1].cs_change = 1;
> + spi_message_add_tail(&st->scan_xfers[2 * k + 1], &st->scan_msg);
> + k++;
> + }
> +
> + devres_close_group(dev, st->scan_devm_group);
If you were keeping this (against comment below) why close it here? Close it once
devm calls are done above.
> +
> + st->scan_msg.spi = spi;
> +
> + ret = spi_optimize_message(spi, &st->scan_msg);
> + if (ret) {
> + devres_release_group(dev, st->scan_devm_group);
> + return ret;
> + }
> +
> + ret = regmap_write(st->regmap, AD4691_ACC_MASK_REG,
> + (u16)~(*indio_dev->active_scan_mask));
> + if (ret)
> + goto err;
> +
> + ret = regmap_write(st->regmap, AD4691_STD_SEQ_CONFIG,
> + *indio_dev->active_scan_mask);
> + if (ret)
> + goto err;
> +
> + iio_for_each_active_channel(indio_dev, bit) {
> + ret = regmap_write(st->regmap, AD4691_ACC_COUNT_LIMIT(bit),
> + AD4691_ACC_COUNT_VAL);
> + if (ret)
> + goto err;
> + }
> +
> + ret = ad4691_enter_conversion_mode(st);
> + if (ret)
> + goto err;
> +
> + ret = ad4691_sampling_enable(st, true);
> + if (ret)
> + goto err;
> +
> + enable_irq(st->irq);
> + return 0;
> +err:
> + spi_unoptimize_message(&st->scan_msg);
> + devres_release_group(dev, st->scan_devm_group);
> + return ret;
> +}
> +
> +static int ad4691_cnv_burst_buffer_postdisable(struct iio_dev *indio_dev)
> +{
> + struct ad4691_state *st = iio_priv(indio_dev);
> + struct device *dev = regmap_get_device(st->regmap);
> + int ret;
> +
> + disable_irq(st->irq);
> +
> + ret = ad4691_sampling_enable(st, false);
> + if (ret)
> + return ret;
> +
> + ret = regmap_write(st->regmap, AD4691_STD_SEQ_CONFIG,
> + AD4691_SEQ_ALL_CHANNELS_OFF);
> + if (ret)
> + return ret;
> +
> + ret = ad4691_exit_conversion_mode(st);
> + spi_unoptimize_message(&st->scan_msg);
> + devres_release_group(dev, st->scan_devm_group);
I'm not seeing an obvious reason for devres group handling here.
Normally we do that if there is a path to remove() in which the cleanup
still needs to be done. Here there isn't as we always turn the buffer
off earlier in the remove flow. So I'd do things the old fashioned way
and free them by hand.
> + return ret;
> +}
> +static IIO_DEVICE_ATTR(sampling_frequency, 0644,
> + sampling_frequency_show,
> + sampling_frequency_store, 0);
> +
> +static const struct iio_dev_attr *ad4691_buffer_attrs[] = {
> + &iio_dev_attr_sampling_frequency,
> + NULL,
No comma.
> +};
> +
> +static irqreturn_t ad4691_trigger_handler(int irq, void *p)
> +{
> + struct iio_poll_func *pf = p;
> + struct iio_dev *indio_dev = pf->indio_dev;
> + struct ad4691_state *st = iio_priv(indio_dev);
> + unsigned int i, k = 0;
> + int ret;
> +
> + guard(mutex)(&st->lock);
General rule (see cleanup.h comments) is don't use guard() or __free()
stuff in a function that also does goto. Just handle the mutex()
the old fashioned way. Alternative is to use a helper function and
just leave the iio_trigger_notify_done() in this outer call.
> +
> + ret = spi_sync(st->scan_msg.spi, &st->scan_msg);
> + if (ret)
> + goto done;
> +
> + if (st->manual_mode) {
> + iio_for_each_active_channel(indio_dev, i) {
> + st->scan.vals[i] = be16_to_cpu(st->scan_rx[k + 1]);
> + k++;
> + }
> + } else {
> + iio_for_each_active_channel(indio_dev, i) {
> + st->scan.vals[i] = be16_to_cpu(st->scan_rx[k]);
> + k++;
> + }
> +
> + ret = regmap_write(st->regmap, AD4691_STATE_RESET_REG,
> + AD4691_STATE_RESET_ALL);
> + if (ret)
> + goto done;
> +
> + ret = ad4691_sampling_enable(st, true);
> + if (ret)
> + goto done;
> + }
> +
> + iio_push_to_buffers_with_ts(indio_dev, &st->scan, sizeof(st->scan),
> + pf->timestamp);
> +
> +done:
> + iio_trigger_notify_done(indio_dev->trig);
> + return IRQ_HANDLED;
> +}
> +
> @@ -595,17 +1071,91 @@ static int ad4691_config(struct ad4691_state *st)
> return dev_err_probe(dev, ret, "Failed to write DEVICE_SETUP\n");
>
> /*
> - * Set the internal oscillator to the highest valid rate for this chip.
> - * Index 0 (1 MHz) is valid only for AD4692/AD4694; AD4691/AD4693 start
> - * at index 1 (500 kHz).
> + * Set the internal oscillator to the highest rate this chip supports.
This reword doesn't obviously fit in this patch. Move it to earlier one.
> + * Index 0 (1 MHz) exceeds the 500 kHz max of AD4691/AD4693, so those
> + * chips start at index 1 (500 kHz).
> */
> ret = regmap_write(st->regmap, AD4691_OSC_FREQ_REG,
> - (st->info->max_rate == HZ_PER_MHZ) ? 0 : 1);
> + (ad4691_osc_freqs[0] > st->info->max_rate) ? 1 : 0);
> if (ret)
> return dev_err_probe(dev, ret, "Failed to write OSC_FREQ\n");
>
> /* Device always operates in AUTONOMOUS mode. */
> - return regmap_write(st->regmap, AD4691_ADC_SETUP, AD4691_AUTONOMOUS_MODE_VAL);
> + ret = regmap_write(st->regmap, AD4691_ADC_SETUP, AD4691_AUTONOMOUS_MODE);
> + if (ret)
> + return dev_err_probe(dev, ret, "Failed to write ADC_SETUp\n");
> +
> + if (st->manual_mode)
> + return 0;
> +
> + ret = device_property_read_string_array(dev, "interrupt-names",
> + &irq_name, 1);
> + if (ret < 0)
> + return dev_err_probe(dev, ret,
> + "Failed to read interrupt-names\n");
> +
> + if (strncmp(irq_name, "gp", 2) != 0 ||
> + kstrtouint(irq_name + 2, 10, &gp_num) || gp_num > 3)
No need to check this. Just request the individual GPIOs when needed by name.
> + return dev_err_probe(dev, -EINVAL,
> + "Invalid interrupt name '%s'\n", irq_name);
> +
> + return ad4691_gpio_setup(st, gp_num);
> +}
> +
> +static int ad4691_setup_triggered_buffer(struct iio_dev *indio_dev,
> + struct ad4691_state *st)
> +{
> + struct device *dev = regmap_get_device(st->regmap);
> + int irq, ret;
> +
> + st->trig = devm_iio_trigger_alloc(dev, "%s-dev%d",
> + indio_dev->name,
> + iio_device_id(indio_dev));
> + if (!st->trig)
> + return -ENOMEM;
> +
> + st->trig->ops = &ad4691_trigger_ops;
> + iio_trigger_set_drvdata(st->trig, st);
> +
> + ret = devm_iio_trigger_register(dev, st->trig);
> + if (ret)
> + return dev_err_probe(dev, ret, "IIO trigger register failed\n");
> +
> + indio_dev->trig = iio_trigger_get(st->trig);
> +
> + if (!st->manual_mode) {
> + /*
> + * GP0 asserts at end-of-conversion. The IRQ handler stops
> + * conversions and fires the IIO trigger so the trigger handler
> + * can read and push the sample to the buffer. The IRQ is kept
> + * disabled until the buffer is enabled.
> + */
> + irq = fwnode_irq_get(dev_fwnode(dev), 0);
Given the presence of interrupt names, there is no guarantee that this is GP0.
You need to get it by name to know that. They can come in any order, or indeed
any given one can be not wired.
> + if (irq < 0)
> + return dev_err_probe(dev, irq,
> + "failed to get GP interrupt\n");
> +
> + st->irq = irq;
> +
> + ret = devm_request_threaded_irq(dev, irq, NULL,
> + &ad4691_irq,
> + IRQF_ONESHOT | IRQF_NO_AUTOEN,
When we are not enabling at boot it is usually helpful to add a comment for why
not.
> + indio_dev->name, indio_dev);
> + if (ret)
> + return ret;
> +
> + return devm_iio_triggered_buffer_setup_ext(dev, indio_dev,
> + &iio_pollfunc_store_time,
> + &ad4691_trigger_handler,
> + IIO_BUFFER_DIRECTION_IN,
> + &ad4691_cnv_burst_buffer_setup_ops,
> + ad4691_buffer_attrs);
> + }
> +
> + return devm_iio_triggered_buffer_setup(dev, indio_dev,
> + &iio_pollfunc_store_time,
> + &ad4691_trigger_handler,
> + &ad4691_manual_buffer_setup_ops);
> }
On Fri, Mar 20, 2026 at 01:03:57PM +0200, Radu Sabau via B4 Relay wrote:
> From: Radu Sabau <radu.sabau@analog.com>
>
> Add buffered capture support using the IIO triggered buffer framework.
>
> CNV Burst Mode: the GP pin identified by interrupt-names in the device
> tree is configured as DATA_READY output. The IRQ handler stops
> conversions and fires the IIO trigger; the trigger handler executes a
> pre-built SPI message that reads all active channels from the AVG_IN
> accumulator registers and then resets accumulator state and restarts
> conversions for the next cycle.
>
> Manual Mode: CNV is tied to SPI CS so each transfer simultaneously
> reads the previous result and starts the next conversion (pipelined
> N+1 scheme). At preenable time a pre-built, optimised SPI message of
> N+1 transfers is constructed (N channel reads plus one NOOP to drain
> the pipeline). The trigger handler executes the message in a single
> spi_sync() call and collects the results. An external trigger (e.g.
> iio-trig-hrtimer) is required to drive the trigger at the desired
> sample rate.
>
> Both modes share the same trigger handler and push a complete scan —
> one u16 slot per channel at its scan_index position, followed by a
> timestamp — to the IIO buffer via iio_push_to_buffers_with_ts().
>
> The CNV Burst Mode sampling frequency (PWM period) is exposed as a
> buffer-level attribute via IIO_DEVICE_ATTR.
>
> Signed-off-by: Radu Sabau <radu.sabau@analog.com>
> ---
> drivers/iio/adc/Kconfig | 2 +
> drivers/iio/adc/ad4691.c | 584 +++++++++++++++++++++++++++++++++++++++++++++--
> 2 files changed, 571 insertions(+), 15 deletions(-)
>
> diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
> index 3685a03aa8dc..d498f16c0816 100644
> --- a/drivers/iio/adc/Kconfig
> +++ b/drivers/iio/adc/Kconfig
> @@ -142,6 +142,8 @@ config AD4170_4
> config AD4691
> tristate "Analog Devices AD4691 Family ADC Driver"
> depends on SPI
> + select IIO_BUFFER
> + select IIO_TRIGGERED_BUFFER
> select REGMAP
> help
> Say yes here to build support for Analog Devices AD4691 Family MuxSAR
> diff --git a/drivers/iio/adc/ad4691.c b/drivers/iio/adc/ad4691.c
> index 5e02eb44ca44..db776de32846 100644
> --- a/drivers/iio/adc/ad4691.c
> +++ b/drivers/iio/adc/ad4691.c
> @@ -9,9 +9,12 @@
> #include <linux/delay.h>
> #include <linux/device.h>
> #include <linux/err.h>
> +#include <linux/interrupt.h>
> #include <linux/math.h>
> #include <linux/module.h>
> #include <linux/mod_devicetable.h>
> +#include <linux/property.h>
> +#include <linux/pwm.h>
> #include <linux/regmap.h>
> #include <linux/regulator/consumer.h>
> #include <linux/reset.h>
> @@ -19,7 +22,12 @@
> #include <linux/units.h>
> #include <linux/unaligned.h>
>
> +#include <linux/iio/buffer.h>
> #include <linux/iio/iio.h>
> +#include <linux/iio/sysfs.h>
> +#include <linux/iio/trigger.h>
> +#include <linux/iio/triggered_buffer.h>
> +#include <linux/iio/trigger_consumer.h>
>
> #define AD4691_VREF_uV_MIN 2400000
> #define AD4691_VREF_uV_MAX 5250000
> @@ -28,6 +36,8 @@
> #define AD4691_VREF_3P3_uV_MAX 3750000
> #define AD4691_VREF_4P096_uV_MAX 4500000
>
> +#define AD4691_CNV_DUTY_CYCLE_NS 380
> +
> #define AD4691_SPI_CONFIG_A_REG 0x000
> #define AD4691_SW_RESET (BIT(7) | BIT(0))
>
> @@ -35,6 +45,7 @@
> #define AD4691_CLAMP_STATUS1_REG 0x01A
> #define AD4691_CLAMP_STATUS2_REG 0x01B
> #define AD4691_DEVICE_SETUP 0x020
> +#define AD4691_MANUAL_MODE BIT(2)
> #define AD4691_LDO_EN BIT(4)
> #define AD4691_REF_CTRL 0x021
> #define AD4691_REF_CTRL_MASK GENMASK(4, 2)
> @@ -42,21 +53,29 @@
> #define AD4691_OSC_FREQ_REG 0x023
> #define AD4691_OSC_FREQ_MASK GENMASK(3, 0)
> #define AD4691_STD_SEQ_CONFIG 0x025
> +#define AD4691_SEQ_ALL_CHANNELS_OFF 0x00
> #define AD4691_SPARE_CONTROL 0x02A
>
> +#define AD4691_NOOP 0x00
> +#define AD4691_ADC_CHAN(ch) ((0x10 + (ch)) << 3)
> +
> #define AD4691_OSC_EN_REG 0x180
> #define AD4691_STATE_RESET_REG 0x181
> #define AD4691_STATE_RESET_ALL 0x01
> #define AD4691_ADC_SETUP 0x182
> -#define AD4691_AUTONOMOUS_MODE_VAL 0x02
> +#define AD4691_CNV_BURST_MODE 0x01
> +#define AD4691_AUTONOMOUS_MODE 0x02
> /*
> * ACC_MASK_REG covers both mask bytes via ADDR_DESCENDING SPI: writing a
> * 16-bit BE value to 0x185 auto-decrements to 0x184 for the second byte.
> */
> #define AD4691_ACC_MASK_REG 0x185
> #define AD4691_ACC_COUNT_LIMIT(n) (0x186 + (n))
> +#define AD4691_ACC_COUNT_VAL 0x1
> #define AD4691_GPIO_MODE1_REG 0x196
> #define AD4691_GPIO_MODE2_REG 0x197
> +#define AD4691_GP_MODE_MASK GENMASK(3, 0)
> +#define AD4691_GP_MODE_DATA_READY 0x06
> #define AD4691_GPIO_READ 0x1A0
> #define AD4691_ACC_STATUS_FULL1_REG 0x1B0
> #define AD4691_ACC_STATUS_FULL2_REG 0x1B1
> @@ -121,6 +140,7 @@ static const struct iio_chan_spec ad4691_channels[] = {
> AD4691_CHANNEL(13),
> AD4691_CHANNEL(14),
> AD4691_CHANNEL(15),
> + IIO_CHAN_SOFT_TIMESTAMP(16),
> };
>
> static const struct iio_chan_spec ad4693_channels[] = {
> @@ -132,6 +152,7 @@ static const struct iio_chan_spec ad4693_channels[] = {
> AD4691_CHANNEL(5),
> AD4691_CHANNEL(6),
> AD4691_CHANNEL(7),
> + IIO_CHAN_SOFT_TIMESTAMP(16),
> };
>
> /*
> @@ -189,16 +210,63 @@ static const struct ad4691_chip_info ad4694_chip_info = {
> struct ad4691_state {
> const struct ad4691_chip_info *info;
> struct regmap *regmap;
> +
> + struct pwm_device *conv_trigger;
> + struct iio_trigger *trig;
> + int irq;
> +
> + bool manual_mode;
> +
> int vref_uV;
> bool refbuf_en;
> bool ldo_en;
> + u32 cnv_period_ns;
> /*
> * Synchronize access to members of the driver state, and ensure
> * atomicity of consecutive SPI operations.
> */
> struct mutex lock;
> + /*
> + * Per-buffer-enabl ree lifetimesources:
> + * Manual Mode - a pre-built SPI message that clocks out N+1
> + * transfers in one go.
> + * CNV Burst Mode - a pre-built SPI message that clocks out 2*N
> + * transfers in one go.
> + */
> + void *scan_devm_group;
> + struct spi_message scan_msg;
> + struct spi_transfer *scan_xfers;
> + __be16 *scan_tx;
> + __be16 *scan_rx;
> + /* Scan buffer: one slot per channel (u16) plus timestamp */
> + struct {
> + u16 vals[16];
> + s64 ts __aligned(8);
> + } scan __aligned(IIO_DMA_MINALIGN);
> };
>
> +/*
> + * Configure the given GP pin (0-3) as DATA_READY output.
> + * GP0/GP1 → GPIO_MODE1_REG, GP2/GP3 → GPIO_MODE2_REG.
> + * Even pins occupy bits [3:0], odd pins bits [7:4].
> + */
> +static int ad4691_gpio_setup(struct ad4691_state *st, unsigned int gp_num)
> +{
> + unsigned int shift = 4 * (gp_num % 2);
> +
> + return regmap_update_bits(st->regmap,
> + AD4691_GPIO_MODE1_REG + gp_num / 2,
> + AD4691_GP_MODE_MASK << shift,
> + AD4691_GP_MODE_DATA_READY << shift);
> +}
> +
> +static void ad4691_disable_pwm(void *data)
> +{
> + struct pwm_state state = { .enabled = false };
> +
> + pwm_apply_might_sleep(data, &state);
> +}
> +
> static int ad4691_reg_read(void *context, unsigned int reg, unsigned int *val)
> {
> struct spi_device *spi = context;
> @@ -341,14 +409,16 @@ static int ad4691_get_sampling_freq(struct ad4691_state *st, int *val)
> static int ad4691_set_sampling_freq(struct iio_dev *indio_dev, int freq)
> {
> struct ad4691_state *st = iio_priv(indio_dev);
> - unsigned int start = (st->info->max_rate == HZ_PER_MHZ) ? 0 : 1;
> unsigned int i;
>
> + if (freq > st->info->max_rate)
> + return -EINVAL;
> +
> IIO_DEV_ACQUIRE_DIRECT_MODE(indio_dev, claim);
> if (IIO_DEV_ACQUIRE_FAILED(claim))
> return -EBUSY;
>
> - for (i = start; i < ARRAY_SIZE(ad4691_osc_freqs); i++) {
> + for (i = 0; i < ARRAY_SIZE(ad4691_osc_freqs); i++) {
> if ((int)ad4691_osc_freqs[i] == freq)
> return regmap_update_bits(st->regmap, AD4691_OSC_FREQ_REG,
> AD4691_OSC_FREQ_MASK, i);
> @@ -363,7 +433,10 @@ static int ad4691_read_avail(struct iio_dev *indio_dev,
> int *length, long mask)
> {
> struct ad4691_state *st = iio_priv(indio_dev);
> - unsigned int start = (st->info->max_rate == HZ_PER_MHZ) ? 0 : 1;
> + unsigned int start;
> +
> + /* Skip frequencies that exceed this chip's maximum rate. */
> + start = (ad4691_osc_freqs[0] > st->info->max_rate) ? 1 : 0;
>
> switch (mask) {
> case IIO_CHAN_INFO_SAMP_FREQ:
> @@ -386,8 +459,7 @@ static int ad4691_single_shot_read(struct iio_dev *indio_dev,
> guard(mutex)(&st->lock);
>
> /*
> - * Use AUTONOMOUS mode for single-shot reads. The chip always
> - * operates in AUTONOMOUS mode in this driver revision.
> + * Use AUTONOMOUS mode for single-shot reads.
> */
> ret = regmap_write(st->regmap, AD4691_STATE_RESET_REG,
> AD4691_STATE_RESET_ALL);
> @@ -417,8 +489,7 @@ static int ad4691_single_shot_read(struct iio_dev *indio_dev,
> * conversion to complete.
> */
> fsleep(DIV_ROUND_UP(2 * USEC_PER_SEC,
> - ad4691_osc_freqs[FIELD_GET(AD4691_OSC_FREQ_MASK,
> - reg_val)]));
> + ad4691_osc_freqs[FIELD_GET(AD4691_OSC_FREQ_MASK, reg_val)]));
>
> ret = regmap_write(st->regmap, AD4691_OSC_EN_REG, 0);
> if (ret)
> @@ -488,6 +559,374 @@ static int ad4691_reg_access(struct iio_dev *indio_dev, unsigned int reg,
> return regmap_write(st->regmap, reg, writeval);
> }
>
> +static int ad4691_set_pwm_freq(struct ad4691_state *st, int freq)
> +{
> + if (!freq)
> + return -EINVAL;
> +
> + st->cnv_period_ns = DIV_ROUND_CLOSEST(NSEC_PER_SEC, freq);
I wrote something about divisions in an earlier revision already.
Ideally there was a iio-specific policy how to convert a frequency to a
period.
Until such a policy exists (maybe even with a helper function), let me
point out that if you want to pick the period that is nearest to 1/freq,
most of the time
st->cnv_period_ns = DIV_ROUND_UP(NSEC_PER_SEC, freq);
is the better value to use. And in the remaining cases this is still a
very good value.
The analysis goes as follows:
Let Π be the function mapping an integer period request to the actually
implemented period (a rational number in general).
Let P := DIV_ROUND_DOWN(NSEC_PER_SEC, freq) and ε := P - Π(P).
With Π(x) ≤ x, we have ε ≥ 0.
The analysis is moot if DIV_ROUND_CLOSEST() and DIV_ROUND_UP() yield the
same value, so we can assume
(P + ẟ) * freq = NSEC_PER_SEC
with 0 < ẟ < 0.5. The values to consider are Π(P) and Π(P+1).
The former is the better value if:
abs(P + ẟ - Π(P)) < abs(P + ẟ - Π(P+1))
⟺ ẟ + ε < abs(P + ẟ - Π(P+1))
With Π(x + 1) ≥ Π(x) this can only hold if
Π(P+1) > P + 2ẟ + ε
⟹ 1 > 2ẟ + ε
So we'd need that ε = P - Π(P) < 1, that is a possible period quite near
to P and another possible period value in the interval ]P + 2ẟ + ε; P + 1].
In that case Π(P+1) is a worse value than Π(P), but still
abs(P + ẟ - Π(P+1)) < 1
, so even in this unlikely situation using P+1 is quite good.
Another advantage of DIV_ROUND_UP(NSEC_PER_SEC, freq) over
DIV_ROUND_CLOSEST(NSEC_PER_SEC, freq) is that it yields a sensible
period for freq > 2*NSEC_PER_SEC.
> + return 0;
> +}
> +
> +static int ad4691_sampling_enable(struct ad4691_state *st, bool enable)
> +{
> + struct pwm_state conv_state = { };
> +
> + conv_state.period = st->cnv_period_ns;
> + conv_state.duty_cycle = AD4691_CNV_DUTY_CYCLE_NS;
> + conv_state.polarity = PWM_POLARITY_NORMAL;
> + conv_state.enabled = enable;
this can be written as:
static int ad4691_sampling_enable(struct ad4691_state *st, bool enable)
{
struct pwm_state conv_state = {
.period = st->cnv_period_ns;
.duty_cycle = AD4691_CNV_DUTY_CYCLE_NS;
.polarity = PWM_POLARITY_NORMAL;
.enabled = enable;
};
...
}
Best regards
Uwe
© 2016 - 2026 Red Hat, Inc.