mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-07-22 02:17:36 -04:00
iio: adc: ad4691: add oversampling support
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>
This commit is contained in:
committed by
Jonathan Cameron
parent
ad4f8513aa
commit
6d9e7161bf
@@ -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, ®_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;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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);
|
||||
|
||||
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 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;
|
||||
}
|
||||
}
|
||||
|
||||
static int ad4691_set_sampling_freq(struct ad4691_state *st, int freq)
|
||||
{
|
||||
unsigned int start = ad4691_samp_freq_start(st->info);
|
||||
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;
|
||||
|
||||
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);
|
||||
}
|
||||
if (freq <= 0 || (unsigned int)freq > st->info->max_rate / osr)
|
||||
return -EINVAL;
|
||||
|
||||
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 = ARRAY_SIZE(ad4691_osc_freqs_Hz) - start;
|
||||
*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_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, ®_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)
|
||||
|
||||
Reference in New Issue
Block a user