Commit 031bdc8a authored by Guillaume Ranquet's avatar Guillaume Ranquet Committed by Jonathan Cameron
Browse files

iio: adc: ad7173: add calibration support



The ad7173 family of chips has up to four calibration modes.

Internal zero scale: removes ADC core offset errors.
Internal full scale: removes ADC core gain errors.
System zero scale: reduces offset error to the order of channel noise.
System full scale: reduces gain error to the order of channel noise.

All voltage channels will undergo an internal zero/full scale
calibration at bootup.

System zero/full scale can be done after bootup using the newly created
iio interface 'sys_calibration' and 'sys_calibration_mode'

Signed-off-by: default avatarGuillaume Ranquet <granquet@baylibre.com>
Link: https://patch.msgid.link/20241202-ad411x_calibration-v3-1-beb6aeec39e2@baylibre.com


Signed-off-by: default avatarJonathan Cameron <Jonathan.Cameron@huawei.com>
parent c3948d09
Loading
Loading
Loading
Loading
+116 −0
Original line number Diff line number Diff line
@@ -150,6 +150,11 @@
#define AD7173_FILTER_ODR0_MASK		GENMASK(5, 0)
#define AD7173_MAX_CONFIGS		8

#define AD7173_MODE_CAL_INT_ZERO		0x4 /* Internal Zero-Scale Calibration */
#define AD7173_MODE_CAL_INT_FULL		0x5 /* Internal Full-Scale Calibration */
#define AD7173_MODE_CAL_SYS_ZERO		0x6 /* System Zero-Scale Calibration */
#define AD7173_MODE_CAL_SYS_FULL		0x7 /* System Full-Scale Calibration */

struct ad7173_device_info {
	const unsigned int *sinc5_data_rates;
	unsigned int num_sinc5_data_rates;
@@ -175,6 +180,7 @@ struct ad7173_device_info {
	bool has_input_buf;
	bool has_int_ref;
	bool has_ref2;
	bool has_internal_fs_calibration;
	bool higher_gpio_bits;
	u8 num_gpios;
};
@@ -195,6 +201,7 @@ struct ad7173_channel_config {
struct ad7173_channel {
	unsigned int ain;
	struct ad7173_channel_config cfg;
	u8 syscalib_mode;
};

struct ad7173_state {
@@ -271,6 +278,7 @@ static const struct ad7173_device_info ad4111_device_info = {
	.has_input_buf = true,
	.has_current_inputs = true,
	.has_int_ref = true,
	.has_internal_fs_calibration = true,
	.clock = 2 * HZ_PER_MHZ,
	.sinc5_data_rates = ad7173_sinc5_data_rates,
	.num_sinc5_data_rates = ARRAY_SIZE(ad7173_sinc5_data_rates),
@@ -290,6 +298,7 @@ static const struct ad7173_device_info ad4112_device_info = {
	.has_input_buf = true,
	.has_current_inputs = true,
	.has_int_ref = true,
	.has_internal_fs_calibration = true,
	.clock = 2 * HZ_PER_MHZ,
	.sinc5_data_rates = ad7173_sinc5_data_rates,
	.num_sinc5_data_rates = ARRAY_SIZE(ad7173_sinc5_data_rates),
@@ -325,6 +334,7 @@ static const struct ad7173_device_info ad4114_device_info = {
	.has_temp = true,
	.has_input_buf = true,
	.has_int_ref = true,
	.has_internal_fs_calibration = true,
	.clock = 2 * HZ_PER_MHZ,
	.sinc5_data_rates = ad7173_sinc5_data_rates,
	.num_sinc5_data_rates = ARRAY_SIZE(ad7173_sinc5_data_rates),
@@ -342,6 +352,7 @@ static const struct ad7173_device_info ad4115_device_info = {
	.has_temp = true,
	.has_input_buf = true,
	.has_int_ref = true,
	.has_internal_fs_calibration = true,
	.clock = 8 * HZ_PER_MHZ,
	.sinc5_data_rates = ad4115_sinc5_data_rates,
	.num_sinc5_data_rates = ARRAY_SIZE(ad4115_sinc5_data_rates),
@@ -359,6 +370,7 @@ static const struct ad7173_device_info ad4116_device_info = {
	.has_temp = true,
	.has_input_buf = true,
	.has_int_ref = true,
	.has_internal_fs_calibration = true,
	.clock = 4 * HZ_PER_MHZ,
	.sinc5_data_rates = ad4116_sinc5_data_rates,
	.num_sinc5_data_rates = ARRAY_SIZE(ad4116_sinc5_data_rates),
@@ -504,6 +516,105 @@ static const struct regmap_config ad7173_regmap_config = {
	.read_flag_mask = BIT(6),
};

enum {
	AD7173_SYSCALIB_ZERO_SCALE,
	AD7173_SYSCALIB_FULL_SCALE,
};

static const char * const ad7173_syscalib_modes[] = {
	[AD7173_SYSCALIB_ZERO_SCALE] = "zero_scale",
	[AD7173_SYSCALIB_FULL_SCALE] = "full_scale",
};

static int ad7173_set_syscalib_mode(struct iio_dev *indio_dev,
				    const struct iio_chan_spec *chan,
				    unsigned int mode)
{
	struct ad7173_state *st = iio_priv(indio_dev);

	st->channels[chan->channel].syscalib_mode = mode;

	return 0;
}

static int ad7173_get_syscalib_mode(struct iio_dev *indio_dev,
				    const struct iio_chan_spec *chan)
{
	struct ad7173_state *st = iio_priv(indio_dev);

	return st->channels[chan->channel].syscalib_mode;
}

static ssize_t ad7173_write_syscalib(struct iio_dev *indio_dev,
				     uintptr_t private,
				     const struct iio_chan_spec *chan,
				     const char *buf, size_t len)
{
	struct ad7173_state *st = iio_priv(indio_dev);
	bool sys_calib;
	int ret, mode;

	ret = kstrtobool(buf, &sys_calib);
	if (ret)
		return ret;

	mode = st->channels[chan->channel].syscalib_mode;
	if (sys_calib) {
		if (mode == AD7173_SYSCALIB_ZERO_SCALE)
			ret = ad_sd_calibrate(&st->sd, AD7173_MODE_CAL_SYS_ZERO,
					      chan->address);
		else
			ret = ad_sd_calibrate(&st->sd, AD7173_MODE_CAL_SYS_FULL,
					      chan->address);
	}

	return ret ? : len;
}

static const struct iio_enum ad7173_syscalib_mode_enum = {
	.items = ad7173_syscalib_modes,
	.num_items = ARRAY_SIZE(ad7173_syscalib_modes),
	.set = ad7173_set_syscalib_mode,
	.get = ad7173_get_syscalib_mode
};

static const struct iio_chan_spec_ext_info ad7173_calibsys_ext_info[] = {
	{
		.name = "sys_calibration",
		.write = ad7173_write_syscalib,
		.shared = IIO_SEPARATE,
	},
	IIO_ENUM("sys_calibration_mode", IIO_SEPARATE,
		 &ad7173_syscalib_mode_enum),
	IIO_ENUM_AVAILABLE("sys_calibration_mode", IIO_SHARED_BY_TYPE,
			   &ad7173_syscalib_mode_enum),
	{ }
};

static int ad7173_calibrate_all(struct ad7173_state *st, struct iio_dev *indio_dev)
{
	int ret;
	int i;

	for (i = 0; i < st->num_channels; i++) {
		if (indio_dev->channels[i].type != IIO_VOLTAGE)
			continue;

		ret = ad_sd_calibrate(&st->sd, AD7173_MODE_CAL_INT_ZERO, st->channels[i].ain);
		if (ret < 0)
			return ret;

		if (st->info->has_internal_fs_calibration) {
			ret = ad_sd_calibrate(&st->sd, AD7173_MODE_CAL_INT_FULL,
					      st->channels[i].ain);
			if (ret < 0)
				return ret;
		}
	}

	return 0;
}

static int ad7173_mask_xlate(struct gpio_regmap *gpio, unsigned int base,
			     unsigned int offset, unsigned int *reg,
			     unsigned int *mask)
@@ -801,6 +912,10 @@ static int ad7173_setup(struct iio_dev *indio_dev)
	if (!st->config_cnts)
		return -ENOMEM;

	ret = ad7173_calibrate_all(st, indio_dev);
	if (ret)
		return ret;

	/* All channels are enabled by default after a reset */
	return ad7173_disable_all(&st->sd);
}
@@ -1023,6 +1138,7 @@ static const struct iio_chan_spec ad7173_channel_template = {
		.storagebits = 32,
		.endianness = IIO_BE,
	},
	.ext_info = ad7173_calibsys_ext_info,
};

static const struct iio_chan_spec ad7173_temp_iio_channel_template = {