[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <20241127-ad411x_calibration-v2-1-66412dac35aa@baylibre.com>
Date: Wed, 27 Nov 2024 10:06:13 +0100
From: Guillaume Ranquet <granquet@...libre.com>
To: Lars-Peter Clausen <lars@...afoo.de>,
Michael Hennerich <Michael.Hennerich@...log.com>,
Jonathan Cameron <jic23@...nel.org>
Cc: linux-iio@...r.kernel.org, linux-kernel@...r.kernel.org,
Guillaume Ranquet <granquet@...libre.com>
Subject: [PATCH v2 1/2] 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@...libre.com>
---
drivers/iio/adc/ad7173.c | 123 +++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 123 insertions(+)
diff --git a/drivers/iio/adc/ad7173.c b/drivers/iio/adc/ad7173.c
index a0fca16c3be07534547a5b914d525d05f7425340..11ff148cb5a315d32485acf04b8d6f7d0fb6e5fa 100644
--- a/drivers/iio/adc/ad7173.c
+++ b/drivers/iio/adc/ad7173.c
@@ -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;
};
@@ -215,6 +221,7 @@ struct ad7173_state {
struct regmap *reg_gpiocon_regmap;
struct gpio_regmap *gpio_regmap;
#endif
+ u8 *syscalib_mode;
};
static unsigned int ad4115_sinc5_data_rates[] = {
@@ -272,6 +279,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),
@@ -291,6 +299,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),
@@ -326,6 +335,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),
@@ -343,6 +353,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),
@@ -360,6 +371,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),
@@ -505,6 +517,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->syscalib_mode[chan->channel] = 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->syscalib_mode[chan->channel];
+}
+
+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->syscalib_mode[chan->channel];
+ 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 = {
@@ -1213,6 +1329,7 @@ static int ad7173_fw_parse_channel_config(struct iio_dev *indio_dev)
struct iio_chan_spec *chan_arr, *chan;
unsigned int ain[AD7173_NO_AINS_PER_CHANNEL], chan_index = 0;
int ref_sel, ret, num_channels;
+ u8 *calib_mode;
num_channels = device_get_child_node_count(dev);
@@ -1240,8 +1357,14 @@ static int ad7173_fw_parse_channel_config(struct iio_dev *indio_dev)
if (!chans_st_arr)
return -ENOMEM;
+ calib_mode = devm_kcalloc(dev, st->num_channels, sizeof(*st->syscalib_mode),
+ GFP_KERNEL);
+ if (!calib_mode)
+ return -ENOMEM;
+
indio_dev->channels = chan_arr;
st->channels = chans_st_arr;
+ st->syscalib_mode = calib_mode;
if (st->info->has_temp) {
chan_arr[chan_index] = ad7173_temp_iio_channel_template;
--
2.47.0
Powered by blists - more mailing lists