Commit 949abd1c authored by Esteban Blanc's avatar Esteban Blanc Committed by Jonathan Cameron
Browse files

iio: adc: ad4030: add averaging support



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

Signed-off-by: default avatarEsteban Blanc <eblanc@baylibre.com>
Link: https://patch.msgid.link/20250214-eblanc-ad4630_v1-v4-3-135dd66cab6a@baylibre.com


Signed-off-by: default avatarJonathan Cameron <Jonathan.Cameron@huawei.com>
parent 0cb8b324
Loading
Loading
Loading
Loading
+119 −17
Original line number Diff line number Diff line
@@ -112,6 +112,11 @@ enum ad4030_out_mode {
	AD4030_OUT_DATA_MD_32_PATTERN,
};

enum {
	AD4030_SCAN_TYPE_NORMAL,
	AD4030_SCAN_TYPE_AVG,
};

struct ad4030_chip_info {
	const char *name;
	const unsigned long *available_masks;
@@ -127,10 +132,12 @@ struct ad4030_state {
	struct spi_device *spi;
	struct regmap *regmap;
	const struct ad4030_chip_info *chip;
	const struct iio_scan_type *current_scan_type;
	struct gpio_desc *cnv_gpio;
	int vref_uv;
	int vio_uv;
	int offset_avail[3];
	unsigned int avg_log2;
	enum ad4030_out_mode mode;

	/*
@@ -184,7 +191,11 @@ struct ad4030_state {
 * - voltage0-voltage1
 * - 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) |		\
		BIT(IIO_CHAN_INFO_CALIBSCALE) |				\
		BIT(IIO_CHAN_INFO_CALIBBIAS) |				\
@@ -198,15 +209,17 @@ struct ad4030_state {
	.channel2 = (_idx) * 2 + 1,					\
	.scan_index = (_idx),						\
	.differential = true,						\
	.scan_type = {							\
		.sign = 's',						\
		.storagebits = _storage,				\
		.realbits = _real,					\
		.shift = _shift,					\
		.endianness = IIO_BE,					\
	},								\
	.has_ext_scan_type = 1,						\
	.ext_scan_type = _scan_type,					\
	.num_ext_scan_type = ARRAY_SIZE(_scan_type),			\
}

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)
{
	st->tx_data[0] = AD4030_REG_ACCESS;
@@ -356,10 +369,13 @@ static int ad4030_get_chan_scale(struct iio_dev *indio_dev,
				 int *val2)
{
	struct ad4030_state *st = iio_priv(indio_dev);
	const struct iio_scan_type *scan_type;

	if (chan->differential) {
		scan_type = iio_get_current_scan_type(indio_dev,
						      st->chip->channels);
		*val = (st->vref_uv * 2) / MILLI;
		*val2 = chan->scan_type.realbits;
		*val2 = scan_type->realbits;
		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);
}

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,
					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);

	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;
	else
		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,
				  AD4030_REG_MODES_MASK_OUT_DATA_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)
{
	struct ad4030_state *st = iio_priv(indio_dev);
	const struct iio_scan_type scan_type = indio_dev->channels->scan_type;
	unsigned char diff_realbytes = BITS_TO_BYTES(scan_type.realbits);
	unsigned char diff_realbytes =
		BITS_TO_BYTES(st->current_scan_type->realbits);
	unsigned int bytes_to_read;
	unsigned long cnv_nb = BIT(st->avg_log2);
	unsigned int i;
	int ret;

	/* 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 */
	bytes_to_read *= st->chip->num_voltage_inputs;

	for (i = 0; i < cnv_nb; i++) {
		gpiod_set_value_cansleep(st->cnv_gpio, 1);
		ndelay(AD4030_TCNVH_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);
	if (ret)
@@ -593,6 +641,12 @@ static int ad4030_read_avail(struct iio_dev *indio_dev,
		*type = IIO_VAL_INT_PLUS_NANO;
		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:
		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,
				    int *val2, long info)
{
	struct ad4030_state *st = iio_priv(indio_dev);

	switch (info) {
	case IIO_CHAN_INFO_RAW:
		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:
		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:
		return -EINVAL;
	}
@@ -650,6 +710,9 @@ static int ad4030_write_raw_dispatch(struct iio_dev *indio_dev,
			return -EINVAL;
		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:
		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);
}

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 = {
	.read_avail = ad4030_read_avail,
	.read_raw = ad4030_read_raw,
	.write_raw = ad4030_write_raw,
	.debugfs_reg_access = ad4030_reg_access,
	.read_label = ad4030_read_label,
	.get_current_scan_type = ad4030_get_current_scan_type,
};

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);
}

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 = {
	.preenable = ad4030_buffer_preenable,
	.validate_scan_mask = ad4030_validate_scan_mask,
};

static int ad4030_regulators_get(struct ad4030_state *st)
@@ -881,11 +966,28 @@ static const unsigned long ad4030_channel_masks[] = {
	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 = {
	.name = "ad4030-24",
	.available_masks = ad4030_channel_masks,
	.channels = {
		AD4030_CHAN_DIFF(0, 32, 24, 8),
		AD4030_CHAN_DIFF(0, ad4030_24_scan_types),
		AD4030_CHAN_CMO(1, 0),
		IIO_CHAN_SOFT_TIMESTAMP(2),
	},