iio: adc: ad7606: add offset and phase calibration support

Add support for offset and phase calibration, only for
devices that support software mode, that are:

ad7606b
ad7606c-16
ad7606c-18

Tested-by: David Lechner <dlechner@baylibre.com>
Reviewed-by: Nuno Sá <nuno.sa@analog.com>
Signed-off-by: Angelo Dureghello <adureghello@baylibre.com>
Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
Link: https://patch.msgid.link/20250606-wip-bl-ad7606-calibration-v9-3-6e014a1f92a2@baylibre.com
Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
pull/1279/head
Angelo Dureghello 2025-06-06 16:19:18 +02:00 committed by Jonathan Cameron
parent 342c52dde2
commit 48d487dc64
2 changed files with 169 additions and 0 deletions

View File

@ -95,6 +95,22 @@ static const unsigned int ad7616_oversampling_avail[8] = {
1, 2, 4, 8, 16, 32, 64, 128,
};
static const int ad7606_calib_offset_avail[3] = {
-128, 1, 127,
};
static const int ad7606c_18bit_calib_offset_avail[3] = {
-512, 4, 508,
};
static const int ad7606b_calib_phase_avail[][2] = {
{ 0, 0 }, { 0, 1250 }, { 0, 318750 },
};
static const int ad7606c_calib_phase_avail[][2] = {
{ 0, 0 }, { 0, 1000 }, { 0, 255000 },
};
static int ad7606c_18bit_chan_scale_setup(struct iio_dev *indio_dev,
struct iio_chan_spec *chan);
static int ad7606c_16bit_chan_scale_setup(struct iio_dev *indio_dev,
@ -164,6 +180,8 @@ const struct ad7606_chip_info ad7606b_info = {
.scale_setup_cb = ad7606_16bit_chan_scale_setup,
.sw_setup_cb = ad7606b_sw_mode_setup,
.offload_storagebits = 32,
.calib_offset_avail = ad7606_calib_offset_avail,
.calib_phase_avail = ad7606b_calib_phase_avail,
};
EXPORT_SYMBOL_NS_GPL(ad7606b_info, "IIO_AD7606");
@ -177,6 +195,8 @@ const struct ad7606_chip_info ad7606c_16_info = {
.scale_setup_cb = ad7606c_16bit_chan_scale_setup,
.sw_setup_cb = ad7606b_sw_mode_setup,
.offload_storagebits = 32,
.calib_offset_avail = ad7606_calib_offset_avail,
.calib_phase_avail = ad7606c_calib_phase_avail,
};
EXPORT_SYMBOL_NS_GPL(ad7606c_16_info, "IIO_AD7606");
@ -226,6 +246,8 @@ const struct ad7606_chip_info ad7606c_18_info = {
.scale_setup_cb = ad7606c_18bit_chan_scale_setup,
.sw_setup_cb = ad7606b_sw_mode_setup,
.offload_storagebits = 32,
.calib_offset_avail = ad7606c_18bit_calib_offset_avail,
.calib_phase_avail = ad7606c_calib_phase_avail,
};
EXPORT_SYMBOL_NS_GPL(ad7606c_18_info, "IIO_AD7606");
@ -681,6 +703,40 @@ error_ret:
return ret;
}
static int ad7606_get_calib_offset(struct ad7606_state *st, int ch, int *val)
{
int ret;
ret = st->bops->reg_read(st, AD7606_CALIB_OFFSET(ch));
if (ret < 0)
return ret;
*val = st->chip_info->calib_offset_avail[0] +
ret * st->chip_info->calib_offset_avail[1];
return 0;
}
static int ad7606_get_calib_phase(struct ad7606_state *st, int ch, int *val,
int *val2)
{
int ret;
ret = st->bops->reg_read(st, AD7606_CALIB_PHASE(ch));
if (ret < 0)
return ret;
*val = 0;
/*
* ad7606b: phase delay from 0 to 318.75 μs in steps of 1.25 μs.
* ad7606c-16/18: phase delay from 0 µs to 255 µs in steps of 1 µs.
*/
*val2 = ret * st->chip_info->calib_phase_avail[1][1];
return 0;
}
static int ad7606_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int *val,
@ -715,6 +771,22 @@ static int ad7606_read_raw(struct iio_dev *indio_dev,
pwm_get_state(st->cnvst_pwm, &cnvst_pwm_state);
*val = DIV_ROUND_CLOSEST_ULL(NSEC_PER_SEC, cnvst_pwm_state.period);
return IIO_VAL_INT;
case IIO_CHAN_INFO_CALIBBIAS:
if (!iio_device_claim_direct(indio_dev))
return -EBUSY;
ret = ad7606_get_calib_offset(st, chan->scan_index, val);
iio_device_release_direct(indio_dev);
if (ret)
return ret;
return IIO_VAL_INT;
case IIO_CHAN_INFO_CONVDELAY:
if (!iio_device_claim_direct(indio_dev))
return -EBUSY;
ret = ad7606_get_calib_phase(st, chan->scan_index, val, val2);
iio_device_release_direct(indio_dev);
if (ret)
return ret;
return IIO_VAL_INT_PLUS_NANO;
}
return -EINVAL;
}
@ -765,6 +837,64 @@ static int ad7606_write_os_hw(struct iio_dev *indio_dev, int val)
return 0;
}
static int ad7606_set_calib_offset(struct ad7606_state *st, int ch, int val)
{
int start_val, step_val, stop_val;
int offset;
start_val = st->chip_info->calib_offset_avail[0];
step_val = st->chip_info->calib_offset_avail[1];
stop_val = st->chip_info->calib_offset_avail[2];
if (val < start_val || val > stop_val)
return -EINVAL;
offset = (val - start_val) / step_val;
return st->bops->reg_write(st, AD7606_CALIB_OFFSET(ch), offset);
}
static int ad7606_set_calib_phase(struct ad7606_state *st, int ch, int val,
int val2)
{
int wreg, start_ns, step_ns, stop_ns;
if (val != 0)
return -EINVAL;
start_ns = st->chip_info->calib_phase_avail[0][1];
step_ns = st->chip_info->calib_phase_avail[1][1];
stop_ns = st->chip_info->calib_phase_avail[2][1];
/*
* ad7606b: phase delay from 0 to 318.75 μs in steps of 1.25 μs.
* ad7606c-16/18: phase delay from 0 µs to 255 µs in steps of 1 µs.
*/
if (val2 < start_ns || val2 > stop_ns)
return -EINVAL;
wreg = val2 / step_ns;
return st->bops->reg_write(st, AD7606_CALIB_PHASE(ch), wreg);
}
static int ad7606_write_raw_get_fmt(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan, long info)
{
switch (info) {
case IIO_CHAN_INFO_SCALE:
return IIO_VAL_INT_PLUS_MICRO;
case IIO_CHAN_INFO_SAMP_FREQ:
case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
case IIO_CHAN_INFO_CALIBBIAS:
return IIO_VAL_INT;
case IIO_CHAN_INFO_CONVDELAY:
return IIO_VAL_INT_PLUS_NANO;
default:
return -EINVAL;
}
}
static int ad7606_write_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int val,
@ -818,6 +948,18 @@ static int ad7606_write_raw(struct iio_dev *indio_dev,
if (val < 0 && val2 != 0)
return -EINVAL;
return ad7606_set_sampling_freq(st, val);
case IIO_CHAN_INFO_CALIBBIAS:
if (!iio_device_claim_direct(indio_dev))
return -EBUSY;
ret = ad7606_set_calib_offset(st, chan->scan_index, val);
iio_device_release_direct(indio_dev);
return ret;
case IIO_CHAN_INFO_CONVDELAY:
if (!iio_device_claim_direct(indio_dev))
return -EBUSY;
ret = ad7606_set_calib_phase(st, chan->scan_index, val, val2);
iio_device_release_direct(indio_dev);
return ret;
default:
return -EINVAL;
}
@ -996,6 +1138,14 @@ static int ad7606_read_avail(struct iio_dev *indio_dev,
*type = IIO_VAL_INT_PLUS_MICRO;
return IIO_AVAIL_LIST;
case IIO_CHAN_INFO_CALIBBIAS:
*vals = st->chip_info->calib_offset_avail;
*type = IIO_VAL_INT;
return IIO_AVAIL_RANGE;
case IIO_CHAN_INFO_CONVDELAY:
*vals = (const int *)st->chip_info->calib_phase_avail;
*type = IIO_VAL_INT_PLUS_NANO;
return IIO_AVAIL_RANGE;
}
return -EINVAL;
}
@ -1058,6 +1208,7 @@ static const struct iio_info ad7606_info_sw_mode = {
.read_raw = &ad7606_read_raw,
.write_raw = &ad7606_write_raw,
.read_avail = &ad7606_read_avail,
.write_raw_get_fmt = ad7606_write_raw_get_fmt,
.debugfs_reg_access = &ad7606_reg_access,
.validate_trigger = &ad7606_validate_trigger,
.update_scan_mode = &ad7606_update_scan_mode,
@ -1250,6 +1401,15 @@ static int ad7606_probe_channels(struct iio_dev *indio_dev)
chan->info_mask_separate_available |=
BIT(IIO_CHAN_INFO_SCALE);
if (st->chip_info->calib_offset_avail) {
chan->info_mask_separate |=
BIT(IIO_CHAN_INFO_CALIBBIAS) |
BIT(IIO_CHAN_INFO_CONVDELAY);
chan->info_mask_separate_available |=
BIT(IIO_CHAN_INFO_CALIBBIAS) |
BIT(IIO_CHAN_INFO_CONVDELAY);
}
/*
* All chips with software mode support oversampling,
* so we skip the oversampling_available check. And the

View File

@ -40,6 +40,11 @@
#define AD7606_RANGE_CH_ADDR(ch) (0x03 + ((ch) >> 1))
#define AD7606_OS_MODE 0x08
#define AD7606_CALIB_GAIN(ch) (0x09 + (ch))
#define AD7606_CALIB_GAIN_MASK GENMASK(5, 0)
#define AD7606_CALIB_OFFSET(ch) (0x11 + (ch))
#define AD7606_CALIB_PHASE(ch) (0x19 + (ch))
struct ad7606_state;
typedef int (*ad7606_scale_setup_cb_t)(struct iio_dev *indio_dev,
@ -61,6 +66,8 @@ typedef int (*ad7606_sw_setup_cb_t)(struct iio_dev *indio_dev);
* @init_delay_ms: required delay in milliseconds for initialization
* after a restart
* @offload_storagebits: storage bits used by the offload hw implementation
* @calib_offset_avail: pointer to offset calibration range/limits array
* @calib_phase_avail: pointer to phase calibration range/limits array
*/
struct ad7606_chip_info {
unsigned int max_samplerate;
@ -74,6 +81,8 @@ struct ad7606_chip_info {
bool os_req_reset;
unsigned long init_delay_ms;
u8 offload_storagebits;
const int *calib_offset_avail;
const int (*calib_phase_avail)[2];
};
/**