iio: adc: ad7173: add calibration support
authorGuillaume Ranquet <granquet@baylibre.com>
Mon, 2 Dec 2024 10:09:52 +0000 (11:09 +0100)
committerJonathan Cameron <Jonathan.Cameron@huawei.com>
Wed, 11 Dec 2024 19:20:49 +0000 (19:20 +0000)
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>
drivers/iio/adc/ad7173.c

index d48b5d98207eabbd45d54ec99d3902a9c7b56be0..b8ab41f3226dfac6679987676a479b38179d3507 100644 (file)
 #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 = {