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: Guillaume Ranquet <granquet@baylibre.com>
Link: https://patch.msgid.link/20241202-ad411x_calibration-v3-1-beb6aeec39e2@baylibre.com
Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
This commit is contained in:
Guillaume Ranquet 2024-12-02 11:09:52 +01:00 committed by Jonathan Cameron
parent c3948d0900
commit 031bdc8aee

View file

@ -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 = {