iio: adc: ad4030: add averaging support

This add support for the averaging mode of AD4030 using oversampling IIO
attribute

Signed-off-by: Esteban Blanc <eblanc@baylibre.com>
Link: https://patch.msgid.link/20250214-eblanc-ad4630_v1-v4-3-135dd66cab6a@baylibre.com
Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
This commit is contained in:
Esteban Blanc
2025-02-14 13:22:33 +01:00
committed by Jonathan Cameron
parent 0cb8b32485
commit 949abd1ca5

View File

@@ -112,6 +112,11 @@ enum ad4030_out_mode {
AD4030_OUT_DATA_MD_32_PATTERN, AD4030_OUT_DATA_MD_32_PATTERN,
}; };
enum {
AD4030_SCAN_TYPE_NORMAL,
AD4030_SCAN_TYPE_AVG,
};
struct ad4030_chip_info { struct ad4030_chip_info {
const char *name; const char *name;
const unsigned long *available_masks; const unsigned long *available_masks;
@@ -127,10 +132,12 @@ struct ad4030_state {
struct spi_device *spi; struct spi_device *spi;
struct regmap *regmap; struct regmap *regmap;
const struct ad4030_chip_info *chip; const struct ad4030_chip_info *chip;
const struct iio_scan_type *current_scan_type;
struct gpio_desc *cnv_gpio; struct gpio_desc *cnv_gpio;
int vref_uv; int vref_uv;
int vio_uv; int vio_uv;
int offset_avail[3]; int offset_avail[3];
unsigned int avg_log2;
enum ad4030_out_mode mode; enum ad4030_out_mode mode;
/* /*
@@ -184,7 +191,11 @@ struct ad4030_state {
* - voltage0-voltage1 * - voltage0-voltage1
* - voltage2-voltage3 * - voltage2-voltage3
*/ */
#define AD4030_CHAN_DIFF(_idx, _storage, _real, _shift) { \ #define AD4030_CHAN_DIFF(_idx, _scan_type) { \
.info_mask_shared_by_all = \
BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \
.info_mask_shared_by_all_available = \
BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \
.info_mask_separate = BIT(IIO_CHAN_INFO_SCALE) | \ .info_mask_separate = BIT(IIO_CHAN_INFO_SCALE) | \
BIT(IIO_CHAN_INFO_CALIBSCALE) | \ BIT(IIO_CHAN_INFO_CALIBSCALE) | \
BIT(IIO_CHAN_INFO_CALIBBIAS) | \ BIT(IIO_CHAN_INFO_CALIBBIAS) | \
@@ -198,15 +209,17 @@ struct ad4030_state {
.channel2 = (_idx) * 2 + 1, \ .channel2 = (_idx) * 2 + 1, \
.scan_index = (_idx), \ .scan_index = (_idx), \
.differential = true, \ .differential = true, \
.scan_type = { \ .has_ext_scan_type = 1, \
.sign = 's', \ .ext_scan_type = _scan_type, \
.storagebits = _storage, \ .num_ext_scan_type = ARRAY_SIZE(_scan_type), \
.realbits = _real, \
.shift = _shift, \
.endianness = IIO_BE, \
}, \
} }
static const int ad4030_average_modes[] = {
1, 2, 4, 8, 16, 32, 64, 128,
256, 512, 1024, 2048, 4096, 8192, 16384, 32768,
65536,
};
static int ad4030_enter_config_mode(struct ad4030_state *st) static int ad4030_enter_config_mode(struct ad4030_state *st)
{ {
st->tx_data[0] = AD4030_REG_ACCESS; st->tx_data[0] = AD4030_REG_ACCESS;
@@ -356,10 +369,13 @@ static int ad4030_get_chan_scale(struct iio_dev *indio_dev,
int *val2) int *val2)
{ {
struct ad4030_state *st = iio_priv(indio_dev); struct ad4030_state *st = iio_priv(indio_dev);
const struct iio_scan_type *scan_type;
if (chan->differential) { if (chan->differential) {
scan_type = iio_get_current_scan_type(indio_dev,
st->chip->channels);
*val = (st->vref_uv * 2) / MILLI; *val = (st->vref_uv * 2) / MILLI;
*val2 = chan->scan_type.realbits; *val2 = scan_type->realbits;
return IIO_VAL_FRACTIONAL_LOG2; return IIO_VAL_FRACTIONAL_LOG2;
} }
@@ -474,6 +490,27 @@ static int ad4030_set_chan_calibbias(struct iio_dev *indio_dev,
st->tx_data, AD4030_REG_OFFSET_BYTES_NB); st->tx_data, AD4030_REG_OFFSET_BYTES_NB);
} }
static int ad4030_set_avg_frame_len(struct iio_dev *dev, int avg_val)
{
struct ad4030_state *st = iio_priv(dev);
unsigned int avg_log2 = ilog2(avg_val);
unsigned int last_avg_idx = ARRAY_SIZE(ad4030_average_modes) - 1;
int ret;
if (avg_val < 0 || avg_val > ad4030_average_modes[last_avg_idx])
return -EINVAL;
ret = regmap_write(st->regmap, AD4030_REG_AVG,
AD4030_REG_AVG_MASK_AVG_SYNC |
FIELD_PREP(AD4030_REG_AVG_MASK_AVG_VAL, avg_log2));
if (ret)
return ret;
st->avg_log2 = avg_log2;
return 0;
}
static bool ad4030_is_common_byte_asked(struct ad4030_state *st, static bool ad4030_is_common_byte_asked(struct ad4030_state *st,
unsigned int mask) unsigned int mask)
{ {
@@ -484,11 +521,18 @@ static int ad4030_set_mode(struct iio_dev *indio_dev, unsigned long mask)
{ {
struct ad4030_state *st = iio_priv(indio_dev); struct ad4030_state *st = iio_priv(indio_dev);
if (ad4030_is_common_byte_asked(st, mask)) if (st->avg_log2 > 0)
st->mode = AD4030_OUT_DATA_MD_30_AVERAGED_DIFF;
else if (ad4030_is_common_byte_asked(st, mask))
st->mode = AD4030_OUT_DATA_MD_24_DIFF_8_COM; st->mode = AD4030_OUT_DATA_MD_24_DIFF_8_COM;
else else
st->mode = AD4030_OUT_DATA_MD_DIFF; st->mode = AD4030_OUT_DATA_MD_DIFF;
st->current_scan_type = iio_get_current_scan_type(indio_dev,
st->chip->channels);
if (IS_ERR(st->current_scan_type))
return PTR_ERR(st->current_scan_type);
return regmap_update_bits(st->regmap, AD4030_REG_MODES, return regmap_update_bits(st->regmap, AD4030_REG_MODES,
AD4030_REG_MODES_MASK_OUT_DATA_MODE, AD4030_REG_MODES_MASK_OUT_DATA_MODE,
st->mode); st->mode);
@@ -497,9 +541,11 @@ static int ad4030_set_mode(struct iio_dev *indio_dev, unsigned long mask)
static int ad4030_conversion(struct iio_dev *indio_dev) static int ad4030_conversion(struct iio_dev *indio_dev)
{ {
struct ad4030_state *st = iio_priv(indio_dev); struct ad4030_state *st = iio_priv(indio_dev);
const struct iio_scan_type scan_type = indio_dev->channels->scan_type; unsigned char diff_realbytes =
unsigned char diff_realbytes = BITS_TO_BYTES(scan_type.realbits); BITS_TO_BYTES(st->current_scan_type->realbits);
unsigned int bytes_to_read; unsigned int bytes_to_read;
unsigned long cnv_nb = BIT(st->avg_log2);
unsigned int i;
int ret; int ret;
/* Number of bytes for one differential channel */ /* Number of bytes for one differential channel */
@@ -510,10 +556,12 @@ static int ad4030_conversion(struct iio_dev *indio_dev)
/* Mulitiply by the number of hardware channels */ /* Mulitiply by the number of hardware channels */
bytes_to_read *= st->chip->num_voltage_inputs; bytes_to_read *= st->chip->num_voltage_inputs;
gpiod_set_value_cansleep(st->cnv_gpio, 1); for (i = 0; i < cnv_nb; i++) {
ndelay(AD4030_TCNVH_NS); gpiod_set_value_cansleep(st->cnv_gpio, 1);
gpiod_set_value_cansleep(st->cnv_gpio, 0); ndelay(AD4030_TCNVH_NS);
ndelay(st->chip->tcyc_ns); gpiod_set_value_cansleep(st->cnv_gpio, 0);
ndelay(st->chip->tcyc_ns);
}
ret = spi_read(st->spi, st->rx_data.raw, bytes_to_read); ret = spi_read(st->spi, st->rx_data.raw, bytes_to_read);
if (ret) if (ret)
@@ -593,6 +641,12 @@ static int ad4030_read_avail(struct iio_dev *indio_dev,
*type = IIO_VAL_INT_PLUS_NANO; *type = IIO_VAL_INT_PLUS_NANO;
return IIO_AVAIL_RANGE; return IIO_AVAIL_RANGE;
case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
*vals = ad4030_average_modes;
*type = IIO_VAL_INT;
*length = ARRAY_SIZE(ad4030_average_modes);
return IIO_AVAIL_LIST;
default: default:
return -EINVAL; return -EINVAL;
} }
@@ -602,6 +656,8 @@ static int ad4030_read_raw_dispatch(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan, int *val, struct iio_chan_spec const *chan, int *val,
int *val2, long info) int *val2, long info)
{ {
struct ad4030_state *st = iio_priv(indio_dev);
switch (info) { switch (info) {
case IIO_CHAN_INFO_RAW: case IIO_CHAN_INFO_RAW:
return ad4030_single_conversion(indio_dev, chan, val); return ad4030_single_conversion(indio_dev, chan, val);
@@ -612,6 +668,10 @@ static int ad4030_read_raw_dispatch(struct iio_dev *indio_dev,
case IIO_CHAN_INFO_CALIBBIAS: case IIO_CHAN_INFO_CALIBBIAS:
return ad4030_get_chan_calibbias(indio_dev, chan, val); return ad4030_get_chan_calibbias(indio_dev, chan, val);
case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
*val = BIT(st->avg_log2);
return IIO_VAL_INT;
default: default:
return -EINVAL; return -EINVAL;
} }
@@ -650,6 +710,9 @@ static int ad4030_write_raw_dispatch(struct iio_dev *indio_dev,
return -EINVAL; return -EINVAL;
return ad4030_set_chan_calibbias(indio_dev, chan, val); return ad4030_set_chan_calibbias(indio_dev, chan, val);
case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
return ad4030_set_avg_frame_len(indio_dev, val);
default: default:
return -EINVAL; return -EINVAL;
} }
@@ -701,12 +764,21 @@ static int ad4030_read_label(struct iio_dev *indio_dev,
return sprintf(label, "common-mode%lu\n", chan->address); return sprintf(label, "common-mode%lu\n", chan->address);
} }
static int ad4030_get_current_scan_type(const struct iio_dev *indio_dev,
const struct iio_chan_spec *chan)
{
struct ad4030_state *st = iio_priv(indio_dev);
return st->avg_log2 ? AD4030_SCAN_TYPE_AVG : AD4030_SCAN_TYPE_NORMAL;
}
static const struct iio_info ad4030_iio_info = { static const struct iio_info ad4030_iio_info = {
.read_avail = ad4030_read_avail, .read_avail = ad4030_read_avail,
.read_raw = ad4030_read_raw, .read_raw = ad4030_read_raw,
.write_raw = ad4030_write_raw, .write_raw = ad4030_write_raw,
.debugfs_reg_access = ad4030_reg_access, .debugfs_reg_access = ad4030_reg_access,
.read_label = ad4030_read_label, .read_label = ad4030_read_label,
.get_current_scan_type = ad4030_get_current_scan_type,
}; };
static int ad4030_buffer_preenable(struct iio_dev *indio_dev) static int ad4030_buffer_preenable(struct iio_dev *indio_dev)
@@ -714,8 +786,21 @@ static int ad4030_buffer_preenable(struct iio_dev *indio_dev)
return ad4030_set_mode(indio_dev, *indio_dev->active_scan_mask); return ad4030_set_mode(indio_dev, *indio_dev->active_scan_mask);
} }
static bool ad4030_validate_scan_mask(struct iio_dev *indio_dev,
const unsigned long *scan_mask)
{
struct ad4030_state *st = iio_priv(indio_dev);
/* Asking for both common channels and averaging */
if (st->avg_log2 && ad4030_is_common_byte_asked(st, *scan_mask))
return false;
return true;
}
static const struct iio_buffer_setup_ops ad4030_buffer_setup_ops = { static const struct iio_buffer_setup_ops ad4030_buffer_setup_ops = {
.preenable = ad4030_buffer_preenable, .preenable = ad4030_buffer_preenable,
.validate_scan_mask = ad4030_validate_scan_mask,
}; };
static int ad4030_regulators_get(struct ad4030_state *st) static int ad4030_regulators_get(struct ad4030_state *st)
@@ -881,11 +966,28 @@ static const unsigned long ad4030_channel_masks[] = {
0, 0,
}; };
static const struct iio_scan_type ad4030_24_scan_types[] = {
[AD4030_SCAN_TYPE_NORMAL] = {
.sign = 's',
.storagebits = 32,
.realbits = 24,
.shift = 8,
.endianness = IIO_BE,
},
[AD4030_SCAN_TYPE_AVG] = {
.sign = 's',
.storagebits = 32,
.realbits = 30,
.shift = 2,
.endianness = IIO_BE,
},
};
static const struct ad4030_chip_info ad4030_24_chip_info = { static const struct ad4030_chip_info ad4030_24_chip_info = {
.name = "ad4030-24", .name = "ad4030-24",
.available_masks = ad4030_channel_masks, .available_masks = ad4030_channel_masks,
.channels = { .channels = {
AD4030_CHAN_DIFF(0, 32, 24, 8), AD4030_CHAN_DIFF(0, ad4030_24_scan_types),
AD4030_CHAN_CMO(1, 0), AD4030_CHAN_CMO(1, 0),
IIO_CHAN_SOFT_TIMESTAMP(2), IIO_CHAN_SOFT_TIMESTAMP(2),
}, },