]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
iio: adc: ad4691: add oversampling support
authorRadu Sabau <radu.sabau@analog.com>
Fri, 29 May 2026 10:15:04 +0000 (13:15 +0300)
committerJonathan Cameron <jic23@kernel.org>
Sun, 31 May 2026 10:14:05 +0000 (11:14 +0100)
Add oversampling ratio (OSR) support for CNV burst mode. The accumulator
depth register (ACC_DEPTH_IN(0)) is programmed with the selected OSR at
buffer enable time and before each single-shot read.

Supported OSR values: 1, 2, 4, 8, 16, 32.

Introduce AD4691_MANUAL_CHANNEL() for manual mode channels, which do
not expose the oversampling_ratio attribute since OSR is not applicable
in that mode. A separate manual_channels array is added to
struct ad4691_channel_info and selected at probe time.

The OSR is shared across all channels (in_voltage_sampling_frequency
and in_voltage_oversampling_ratio are info_mask_shared_by_all) because
the chip has one internal oscillator and a single accumulator depth
register (ACC_DEPTH_IN(0)) for all channels.

in_voltage_sampling_frequency represents the effective output rate,
defined as osc_freq / osr. Writing it computes needed_osc = freq * osr
and snaps down to the largest oscillator table entry that satisfies both
osc <= needed_osc and osc % osr == 0, guaranteeing an exact integer
read-back. The result is stored in target_osc_freq_Hz and written to
OSC_FREQ_REG at buffer enable and single-shot time, so sampling_frequency
and oversampling_ratio can be set in any order.

in_voltage_sampling_frequency_available is precomputed at probe for
each OSR value, listing only oscillator table entries that divide
evenly by that OSR, expressed as effective rates (osc_freq / osr).
The list becomes sparser as OSR increases, capping at max_rate / osr.
read_avail picks the precomputed list for the current OSR, making the
returned pointer stable and race-free.

Writing oversampling_ratio stores the new shared OSR and snaps
target_osc_freq_Hz to the largest oscillator table entry that is both
<= old_effective_rate * new_osr and evenly divisible by new_osr. This
preserves an integer read-back of in_voltage_sampling_frequency after
the OSR change while keeping the oscillator as close as possible to the
previous effective rate.

OSR defaults to 1 (no accumulation).

Signed-off-by: Radu Sabau <radu.sabau@analog.com>
Signed-off-by: Jonathan Cameron <jic23@kernel.org>
drivers/iio/adc/ad4691.c

index aa4263fa17bf49c9707af0122bf2e3f10de11e9c..548678adc2a46ce08de3610531dcf49ab9f7680b 100644 (file)
@@ -122,6 +122,7 @@ enum ad4691_ref_ctrl {
 
 struct ad4691_channel_info {
        const struct iio_chan_spec *channels __counted_by_ptr(num_channels);
+       const struct iio_chan_spec *manual_channels __counted_by_ptr(num_channels);
        unsigned int num_channels;
 };
 
@@ -132,7 +133,34 @@ struct ad4691_chip_info {
        const struct ad4691_channel_info *offload_info;
 };
 
+/* CNV burst mode channel — exposes oversampling ratio. */
 #define AD4691_CHANNEL(ch)                                             \
+       {                                                               \
+               .type = IIO_VOLTAGE,                                    \
+               .indexed = 1,                                           \
+               .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),           \
+               .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SCALE)     \
+                                   | BIT(IIO_CHAN_INFO_SAMP_FREQ)      \
+                                   | BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \
+               .info_mask_shared_by_all_available =                    \
+                                     BIT(IIO_CHAN_INFO_SAMP_FREQ)      \
+                                   | BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \
+               .channel = ch,                                          \
+               .scan_index = ch,                                       \
+               .scan_type = {                                          \
+                       .format = 'u',                                  \
+                       .realbits = 16,                                 \
+                       .storagebits = 16,                              \
+                       .endianness = IIO_BE,                           \
+               },                                                      \
+       }
+
+/*
+ * Manual mode channel — no oversampling ratio attribute. OSR is not
+ * supported in manual mode; ACC_DEPTH_IN is not configured during manual
+ * buffer enable.
+ */
+#define AD4691_MANUAL_CHANNEL(ch)                                      \
        {                                                               \
                .type = IIO_VOLTAGE,                                    \
                .indexed = 1,                                           \
@@ -156,8 +184,33 @@ struct ad4691_chip_info {
  * bits into native 16-bit words before DMA, so samples are in
  * CPU-native byte order (IIO_CPU). storagebits=16 matches the 16-bit
  * DMA word size.
+ *
+ * CNV burst offload configures ACC_DEPTH_IN per channel, so the
+ * oversampling_ratio attribute is exposed. Manual offload does not;
+ * use AD4691_OFFLOAD_MANUAL_CHANNEL for that path.
  */
 #define AD4691_OFFLOAD_CHANNEL(ch)                                     \
+       {                                                               \
+               .type = IIO_VOLTAGE,                                    \
+               .indexed = 1,                                           \
+               .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),           \
+               .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SCALE)     \
+                                   | BIT(IIO_CHAN_INFO_SAMP_FREQ)      \
+                                   | BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \
+               .info_mask_shared_by_all_available =                    \
+                                     BIT(IIO_CHAN_INFO_SAMP_FREQ)      \
+                                   | BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \
+               .channel = ch,                                          \
+               .scan_index = ch,                                       \
+               .scan_type = {                                          \
+                       .format = 'u',                                  \
+                       .realbits = 16,                                 \
+                       .storagebits = 16,                              \
+               },                                                      \
+       }
+
+/* Manual offload — same IIO_CPU layout but no oversampling_ratio attribute. */
+#define AD4691_OFFLOAD_MANUAL_CHANNEL(ch)                              \
        {                                                               \
                .type = IIO_VOLTAGE,                                    \
                .indexed = 1,                                           \
@@ -241,23 +294,91 @@ static const struct iio_chan_spec ad4693_offload_channels[] = {
        AD4691_OFFLOAD_CHANNEL(7),
 };
 
+static const struct iio_chan_spec ad4691_manual_channels[] = {
+       AD4691_MANUAL_CHANNEL(0),
+       AD4691_MANUAL_CHANNEL(1),
+       AD4691_MANUAL_CHANNEL(2),
+       AD4691_MANUAL_CHANNEL(3),
+       AD4691_MANUAL_CHANNEL(4),
+       AD4691_MANUAL_CHANNEL(5),
+       AD4691_MANUAL_CHANNEL(6),
+       AD4691_MANUAL_CHANNEL(7),
+       AD4691_MANUAL_CHANNEL(8),
+       AD4691_MANUAL_CHANNEL(9),
+       AD4691_MANUAL_CHANNEL(10),
+       AD4691_MANUAL_CHANNEL(11),
+       AD4691_MANUAL_CHANNEL(12),
+       AD4691_MANUAL_CHANNEL(13),
+       AD4691_MANUAL_CHANNEL(14),
+       AD4691_MANUAL_CHANNEL(15),
+       IIO_CHAN_SOFT_TIMESTAMP(16),
+};
+
+static const struct iio_chan_spec ad4693_manual_channels[] = {
+       AD4691_MANUAL_CHANNEL(0),
+       AD4691_MANUAL_CHANNEL(1),
+       AD4691_MANUAL_CHANNEL(2),
+       AD4691_MANUAL_CHANNEL(3),
+       AD4691_MANUAL_CHANNEL(4),
+       AD4691_MANUAL_CHANNEL(5),
+       AD4691_MANUAL_CHANNEL(6),
+       AD4691_MANUAL_CHANNEL(7),
+       IIO_CHAN_SOFT_TIMESTAMP(8),
+};
+
+static const struct iio_chan_spec ad4691_offload_manual_channels[] = {
+       AD4691_OFFLOAD_MANUAL_CHANNEL(0),
+       AD4691_OFFLOAD_MANUAL_CHANNEL(1),
+       AD4691_OFFLOAD_MANUAL_CHANNEL(2),
+       AD4691_OFFLOAD_MANUAL_CHANNEL(3),
+       AD4691_OFFLOAD_MANUAL_CHANNEL(4),
+       AD4691_OFFLOAD_MANUAL_CHANNEL(5),
+       AD4691_OFFLOAD_MANUAL_CHANNEL(6),
+       AD4691_OFFLOAD_MANUAL_CHANNEL(7),
+       AD4691_OFFLOAD_MANUAL_CHANNEL(8),
+       AD4691_OFFLOAD_MANUAL_CHANNEL(9),
+       AD4691_OFFLOAD_MANUAL_CHANNEL(10),
+       AD4691_OFFLOAD_MANUAL_CHANNEL(11),
+       AD4691_OFFLOAD_MANUAL_CHANNEL(12),
+       AD4691_OFFLOAD_MANUAL_CHANNEL(13),
+       AD4691_OFFLOAD_MANUAL_CHANNEL(14),
+       AD4691_OFFLOAD_MANUAL_CHANNEL(15),
+};
+
+static const struct iio_chan_spec ad4693_offload_manual_channels[] = {
+       AD4691_OFFLOAD_MANUAL_CHANNEL(0),
+       AD4691_OFFLOAD_MANUAL_CHANNEL(1),
+       AD4691_OFFLOAD_MANUAL_CHANNEL(2),
+       AD4691_OFFLOAD_MANUAL_CHANNEL(3),
+       AD4691_OFFLOAD_MANUAL_CHANNEL(4),
+       AD4691_OFFLOAD_MANUAL_CHANNEL(5),
+       AD4691_OFFLOAD_MANUAL_CHANNEL(6),
+       AD4691_OFFLOAD_MANUAL_CHANNEL(7),
+};
+
+static const int ad4691_oversampling_ratios[] = { 1, 2, 4, 8, 16, 32 };
+
 static const struct ad4691_channel_info ad4691_sw_info = {
        .channels = ad4691_channels,
+       .manual_channels = ad4691_manual_channels,
        .num_channels = ARRAY_SIZE(ad4691_channels),
 };
 
 static const struct ad4691_channel_info ad4693_sw_info = {
        .channels = ad4693_channels,
+       .manual_channels = ad4693_manual_channels,
        .num_channels = ARRAY_SIZE(ad4693_channels),
 };
 
 static const struct ad4691_channel_info ad4691_offload_info = {
        .channels = ad4691_offload_channels,
+       .manual_channels = ad4691_offload_manual_channels,
        .num_channels = ARRAY_SIZE(ad4691_offload_channels),
 };
 
 static const struct ad4691_channel_info ad4693_offload_info = {
        .channels = ad4693_offload_channels,
+       .manual_channels = ad4693_offload_manual_channels,
        .num_channels = ARRAY_SIZE(ad4693_offload_channels),
 };
 
@@ -324,6 +445,25 @@ struct ad4691_state {
        int irq;
        int vref_uV;
        u32 cnv_period_ns;
+       /*
+        * Snapped oscillator frequency (Hz) shared by all channels. Set when
+        * sampling_frequency or oversampling_ratio is written; written to
+        * OSC_FREQ_REG at buffer enable and single-shot time so both attributes
+        * can be set in any order. Reading in_voltage_sampling_frequency
+        * returns target_osc_freq_Hz / osr — the effective rate given the
+        * shared oversampling ratio.
+        */
+       u32 target_osc_freq_Hz;
+       /* Shared oversampling ratio across all channels; always 1 in manual mode. */
+       unsigned int osr;
+       /*
+        * Precomputed effective-rate lists, one row per entry in
+        * ad4691_oversampling_ratios[]. Populated at probe; read_avail picks
+        * the row for the current shared OSR. The tables are stable after
+        * probe so returning a pointer into them from read_avail is race-free.
+        */
+       int samp_freq_avail[ARRAY_SIZE(ad4691_oversampling_ratios)][ARRAY_SIZE(ad4691_osc_freqs_Hz)];
+       int samp_freq_avail_len[ARRAY_SIZE(ad4691_oversampling_ratios)];
 
        bool manual_mode;
        bool irq_enabled;
@@ -580,35 +720,95 @@ static unsigned int ad4691_samp_freq_start(const struct ad4691_chip_info *info)
        return (info->max_rate == 1 * HZ_PER_MHZ) ? 0 : 1;
 }
 
-static int ad4691_get_sampling_freq(struct ad4691_state *st, int *val)
+/*
+ * Find the largest oscillator table entry that is both <= needed_osc and
+ * evenly divisible by osr (guaranteeing an integer effective rate on
+ * read-back). Returns 0 if no such entry exists in the chip's valid range.
+ */
+static unsigned int ad4691_find_osc_freq(struct ad4691_state *st,
+                                        unsigned int needed_osc,
+                                        unsigned int osr)
 {
-       unsigned int reg_val;
-       int ret;
+       unsigned int start = ad4691_samp_freq_start(st->info);
 
-       guard(mutex)(&st->lock);
+       for (unsigned int i = start; i < ARRAY_SIZE(ad4691_osc_freqs_Hz); i++) {
+               if ((unsigned int)ad4691_osc_freqs_Hz[i] > needed_osc)
+                       continue;
+               if (ad4691_osc_freqs_Hz[i] % osr)
+                       continue;
+               return ad4691_osc_freqs_Hz[i];
+       }
+       return 0;
+}
 
-       ret = regmap_read(st->regmap, AD4691_OSC_FREQ_REG, &reg_val);
-       if (ret)
-               return ret;
+/* Write target_osc_freq_Hz to OSC_FREQ_REG. Called at use time. */
+static int ad4691_write_osc_freq(struct ad4691_state *st)
+{
+       for (unsigned int i = 0; i < ARRAY_SIZE(ad4691_osc_freqs_Hz); i++) {
+               if (ad4691_osc_freqs_Hz[i] == st->target_osc_freq_Hz)
+                       return regmap_write(st->regmap, AD4691_OSC_FREQ_REG, i);
+       }
+       return -EINVAL;
+}
 
-       *val = ad4691_osc_freqs_Hz[FIELD_GET(AD4691_OSC_FREQ_MASK, reg_val)];
-       return IIO_VAL_INT;
+/* Return the index of osr in ad4691_oversampling_ratios[], defaulting to 0. */
+static unsigned int ad4691_osr_index(unsigned int osr)
+{
+       for (unsigned int i = 0; i < ARRAY_SIZE(ad4691_oversampling_ratios) - 1; i++) {
+               if ((unsigned int)ad4691_oversampling_ratios[i] == osr)
+                       return i;
+       }
+       return ARRAY_SIZE(ad4691_oversampling_ratios) - 1;
 }
 
-static int ad4691_set_sampling_freq(struct ad4691_state *st, int freq)
+/*
+ * Precompute samp_freq_avail[][]: for each OSR value, list the oscillator
+ * table entries that divide evenly by that OSR, expressed as effective rates
+ * (osc_freq / osr). Called once at probe after st->info is set.
+ */
+static void ad4691_precompute_samp_freq_avail(struct ad4691_state *st)
 {
        unsigned int start = ad4691_samp_freq_start(st->info);
 
-       guard(mutex)(&st->lock);
+       for (unsigned int i = 0; i < ARRAY_SIZE(ad4691_oversampling_ratios); i++) {
+               unsigned int osr = ad4691_oversampling_ratios[i];
+               int n = 0;
 
-       for (unsigned int i = start; i < ARRAY_SIZE(ad4691_osc_freqs_Hz); i++) {
-               if (ad4691_osc_freqs_Hz[i] != freq)
-                       continue;
-               return regmap_update_bits(st->regmap, AD4691_OSC_FREQ_REG,
-                                         AD4691_OSC_FREQ_MASK, i);
+               for (unsigned int j = start; j < ARRAY_SIZE(ad4691_osc_freqs_Hz); j++) {
+                       if (ad4691_osc_freqs_Hz[j] % osr)
+                               continue;
+                       st->samp_freq_avail[i][n++] = ad4691_osc_freqs_Hz[j] / osr;
+               }
+               st->samp_freq_avail_len[i] = n;
        }
+}
 
-       return -EINVAL;
+static int ad4691_set_sampling_freq(struct ad4691_state *st, int freq)
+{
+       unsigned int osr, found;
+
+       /*
+        * Read osr under st->lock: osr and target_osc_freq_Hz are modified
+        * together under the lock; reading after acquiring it ensures we see
+        * a consistent snapshot with no concurrent write racing us.
+        */
+       guard(mutex)(&st->lock);
+       osr = st->osr;
+
+       if (freq <= 0 || (unsigned int)freq > st->info->max_rate / osr)
+               return -EINVAL;
+
+       found = ad4691_find_osc_freq(st, (unsigned int)freq * osr, osr);
+       if (!found)
+               return -EINVAL;
+
+       /*
+        * Store the snapped oscillator frequency; OSC_FREQ_REG is written at
+        * buffer enable and single-shot time so that sampling_frequency and
+        * oversampling_ratio can be set in any order.
+        */
+       st->target_osc_freq_Hz = found;
+       return 0;
 }
 
 static int ad4691_read_avail(struct iio_dev *indio_dev,
@@ -617,13 +817,27 @@ 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 = ad4691_samp_freq_start(st->info);
 
        switch (mask) {
-       case IIO_CHAN_INFO_SAMP_FREQ:
-               *vals = &ad4691_osc_freqs_Hz[start];
+       case IIO_CHAN_INFO_SAMP_FREQ: {
+               unsigned int osr_idx;
+
+               /*
+                * The precomputed tables are stable after probe; only the
+                * current OSR needs to be read under the lock to pick the
+                * right row atomically.
+                */
+               guard(mutex)(&st->lock);
+               osr_idx = ad4691_osr_index(st->osr);
+               *vals = st->samp_freq_avail[osr_idx];
+               *type = IIO_VAL_INT;
+               *length = st->samp_freq_avail_len[osr_idx];
+               return IIO_AVAIL_LIST;
+       }
+       case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
+               *vals = ad4691_oversampling_ratios;
                *type = IIO_VAL_INT;
-               *length = ARRAY_SIZE(ad4691_osc_freqs_Hz) - start;
+               *length = ARRAY_SIZE(ad4691_oversampling_ratios);
                return IIO_AVAIL_LIST;
        default:
                return -EINVAL;
@@ -634,7 +848,7 @@ static int ad4691_single_shot_read(struct iio_dev *indio_dev,
                                   struct iio_chan_spec const *chan, int *val)
 {
        struct ad4691_state *st = iio_priv(indio_dev);
-       unsigned int reg_val, osc_idx, period_us;
+       unsigned int reg_val, period_us;
        int ret;
 
        guard(mutex)(&st->lock);
@@ -654,7 +868,11 @@ static int ad4691_single_shot_read(struct iio_dev *indio_dev,
        if (ret)
                return ret;
 
-       ret = regmap_read(st->regmap, AD4691_OSC_FREQ_REG, &reg_val);
+       ret = regmap_write(st->regmap, AD4691_ACC_DEPTH_IN(0), st->osr);
+       if (ret)
+               return ret;
+
+       ret = ad4691_write_osc_freq(st);
        if (ret)
                return ret;
 
@@ -662,9 +880,12 @@ static int ad4691_single_shot_read(struct iio_dev *indio_dev,
        if (ret)
                return ret;
 
-       osc_idx = FIELD_GET(AD4691_OSC_FREQ_MASK, reg_val);
-       /* Wait 2 oscillator periods for the conversion to complete. */
-       period_us = DIV_ROUND_UP(2UL * USEC_PER_SEC, ad4691_osc_freqs_Hz[osc_idx]);
+       /*
+        * Wait osr + 1 oscillator periods: osr for accumulation, +1 for the
+        * pipeline margin (one extra period ensures the final result is ready).
+        */
+       period_us = DIV_ROUND_UP((st->osr + 1) * USEC_PER_SEC,
+                                st->target_osc_freq_Hz);
        fsleep(period_us);
 
        ret = regmap_write(st->regmap, AD4691_OSC_EN_REG, 0);
@@ -698,8 +919,22 @@ static int ad4691_read_raw(struct iio_dev *indio_dev,
 
                return ad4691_single_shot_read(indio_dev, chan, val);
        }
-       case IIO_CHAN_INFO_SAMP_FREQ:
-               return ad4691_get_sampling_freq(st, val);
+       case IIO_CHAN_INFO_SAMP_FREQ: {
+               /*
+                * Read target_osc_freq_Hz and osr under st->lock to get a
+                * consistent snapshot: write_raw for SAMP_FREQ or OSR modifies
+                * both fields under the lock, so a concurrent read without the
+                * lock could observe a new oscillator frequency with the old OSR.
+                */
+               guard(mutex)(&st->lock);
+               *val = st->target_osc_freq_Hz / st->osr;
+               return IIO_VAL_INT;
+       }
+       case IIO_CHAN_INFO_OVERSAMPLING_RATIO: {
+               guard(mutex)(&st->lock);
+               *val = st->osr;
+               return IIO_VAL_INT;
+       }
        case IIO_CHAN_INFO_SCALE:
                *val = st->vref_uV / (MICRO / MILLI);
                *val2 = chan->scan_type.realbits;
@@ -722,6 +957,33 @@ static int ad4691_write_raw(struct iio_dev *indio_dev,
        switch (mask) {
        case IIO_CHAN_INFO_SAMP_FREQ:
                return ad4691_set_sampling_freq(st, val);
+       case IIO_CHAN_INFO_OVERSAMPLING_RATIO: {
+               unsigned int old_effective, found, osr_idx;
+
+               osr_idx = ad4691_osr_index(val);
+               if (ad4691_oversampling_ratios[osr_idx] != val)
+                       return -EINVAL;
+
+               /*
+                * Hold st->lock while computing the new oscillator frequency
+                * and updating both target_osc_freq_Hz and osr atomically:
+                * read_raw for SAMP_FREQ reads both fields under the lock and
+                * must see a consistent pair (new osc ↔ new osr).
+                *
+                * Snap target_osc_freq_Hz to the largest table entry that is
+                * both <= old_effective * new_osr and evenly divisible by
+                * new_osr, preserving an integer read-back of
+                * in_voltage_sampling_frequency after the OSR change.
+                */
+               guard(mutex)(&st->lock);
+               old_effective = st->target_osc_freq_Hz / st->osr;
+               found = ad4691_find_osc_freq(st, old_effective * (unsigned int)val, val);
+               if (!found)
+                       return -EINVAL;
+               st->target_osc_freq_Hz = found;
+               st->osr = val;
+               return 0;
+       }
        default:
                return -EINVAL;
        }
@@ -776,6 +1038,10 @@ static int ad4691_enter_conversion_mode(struct ad4691_state *st)
                return regmap_update_bits(st->regmap, AD4691_DEVICE_SETUP,
                                          AD4691_MANUAL_MODE, AD4691_MANUAL_MODE);
 
+       ret = ad4691_write_osc_freq(st);
+       if (ret)
+               return ret;
+
        ret = regmap_update_bits(st->regmap, AD4691_ADC_SETUP,
                                 AD4691_ADC_MODE_MASK, AD4691_CNV_BURST_MODE);
        if (ret)
@@ -943,6 +1209,10 @@ static int ad4691_cnv_burst_buffer_preenable(struct iio_dev *indio_dev)
        if (ret)
                goto err_unoptimize;
 
+       ret = regmap_write(st->regmap, AD4691_ACC_DEPTH_IN(0), st->osr);
+       if (ret)
+               goto err_unoptimize;
+
        ret = ad4691_enter_conversion_mode(st);
        if (ret)
                goto err_unoptimize;
@@ -1122,6 +1392,10 @@ static int ad4691_cnv_burst_offload_buffer_postenable(struct iio_dev *indio_dev)
        if (ret)
                return ret;
 
+       ret = regmap_write(st->regmap, AD4691_ACC_DEPTH_IN(0), st->osr);
+       if (ret)
+               return ret;
+
        ret = ad4691_enter_conversion_mode(st);
        if (ret)
                return ret;
@@ -1538,11 +1812,15 @@ static int ad4691_config(struct ad4691_state *st)
        if (ret)
                return dev_err_probe(dev, ret, "Failed to write OSC_FREQ\n");
 
+       st->target_osc_freq_Hz = ad4691_osc_freqs_Hz[ad4691_samp_freq_start(st->info)];
+
        ret = regmap_update_bits(st->regmap, AD4691_ADC_SETUP,
                                 AD4691_ADC_MODE_MASK, AD4691_AUTONOMOUS_MODE);
        if (ret)
                return dev_err_probe(dev, ret, "Failed to write ADC_SETUP\n");
 
+       ad4691_precompute_samp_freq_avail(st);
+
        return 0;
 }
 
@@ -1554,7 +1832,14 @@ static int ad4691_setup_triggered_buffer(struct iio_dev *indio_dev,
        unsigned int i;
        int irq, ret;
 
-       indio_dev->channels = st->info->sw_info->channels;
+       /*
+        * Manual mode exposes channels without the oversampling_ratio attribute
+        * because ACC_DEPTH_IN is not configured in manual mode.
+        */
+       if (st->manual_mode)
+               indio_dev->channels = st->info->sw_info->manual_channels;
+       else
+               indio_dev->channels = st->info->sw_info->channels;
        indio_dev->num_channels = st->info->sw_info->num_channels;
        indio_dev->info = st->manual_mode ? &ad4691_manual_info : &ad4691_cnv_burst_info;
 
@@ -1636,7 +1921,18 @@ static int ad4691_setup_offload(struct iio_dev *indio_dev,
 
        st->offload = spi_offload;
 
-       indio_dev->channels = st->info->offload_info->channels;
+       /*
+        * CNV burst offload exposes oversampling_ratio (ACC_DEPTH_IN is
+        * configured per channel at buffer enable). Manual offload does not
+        * configure ACC_DEPTH_IN, so it uses a separate channel array
+        * without the oversampling_ratio attribute. Both paths use IIO_CPU
+        * (no .endianness annotation) because bits_per_word=16 causes the
+        * SPI Engine to produce native 16-bit DMA words.
+        */
+       if (st->manual_mode)
+               indio_dev->channels = st->info->offload_info->manual_channels;
+       else
+               indio_dev->channels = st->info->offload_info->channels;
        indio_dev->num_channels = st->info->offload_info->num_channels;
        /*
         * Offload path uses DMA directly; no IIO trigger is involved, so
@@ -1710,6 +2006,7 @@ static int ad4691_probe(struct spi_device *spi)
        st->info = spi_get_device_match_data(spi);
        if (!st->info)
                return -ENODEV;
+       st->osr = 1;
 
        ret = devm_mutex_init(dev, &st->lock);
        if (ret)