#define AD7124_ADC_CTRL_MODE_MSK GENMASK(5, 2)
#define AD7124_ADC_CTRL_MODE(x) FIELD_PREP(AD7124_ADC_CTRL_MODE_MSK, x)
+#define AD7124_MODE_CAL_INT_ZERO 0x5 /* Internal Zero-Scale Calibration */
+#define AD7124_MODE_CAL_INT_FULL 0x6 /* Internal Full-Scale Calibration */
+#define AD7124_MODE_CAL_SYS_ZERO 0x7 /* System Zero-Scale Calibration */
+#define AD7124_MODE_CAL_SYS_FULL 0x8 /* System Full-Scale Calibration */
+
/* AD7124 ID */
#define AD7124_DEVICE_ID_MSK GENMASK(7, 4)
#define AD7124_DEVICE_ID_GET(x) FIELD_GET(AD7124_DEVICE_ID_MSK, x)
unsigned int odr;
unsigned int odr_sel_bits;
unsigned int filter_type;
+ unsigned int calibration_offset;
+ unsigned int calibration_gain;
);
};
unsigned int num_channels;
struct mutex cfgs_lock; /* lock for configs access */
unsigned long cfg_slots_status; /* bitmap with slot status (1 means it is used) */
+
+ /*
+ * Stores the power-on reset value for the GAIN(x) registers which are
+ * needed for measurements at gain 1 (i.e. CONFIG(x).PGA == 0)
+ */
+ unsigned int gain_default;
DECLARE_KFIFO(live_cfgs_fifo, struct ad7124_channel_config *, AD7124_MAX_CONFIGS);
};
unsigned int odr;
unsigned int odr_sel_bits;
unsigned int filter_type;
+ unsigned int calibration_offset;
+ unsigned int calibration_gain;
}));
for (i = 0; i < st->num_channels; i++) {
cfg->pga_bits == cfg_aux->pga_bits &&
cfg->odr == cfg_aux->odr &&
cfg->odr_sel_bits == cfg_aux->odr_sel_bits &&
- cfg->filter_type == cfg_aux->filter_type)
+ cfg->filter_type == cfg_aux->filter_type &&
+ cfg->calibration_offset == cfg_aux->calibration_offset &&
+ cfg->calibration_gain == cfg_aux->calibration_gain)
return cfg_aux;
}
cfg->cfg_slot = cfg_slot;
+ ret = ad_sd_write_reg(&st->sd, AD7124_OFFSET(cfg->cfg_slot), 3, cfg->calibration_offset);
+ if (ret)
+ return ret;
+
+ ret = ad_sd_write_reg(&st->sd, AD7124_GAIN(cfg->cfg_slot), 3, cfg->calibration_gain);
+ if (ret)
+ return ret;
+
tmp = (cfg->buf_positive << 1) + cfg->buf_negative;
val = AD7124_CONFIG_BIPOLAR(cfg->bipolar) | AD7124_CONFIG_REF_SEL(cfg->refsel) |
AD7124_CONFIG_IN_BUFF(tmp) | AD7124_CONFIG_PGA(cfg->pga_bits);
return dev_err_probe(dev, ret, "Error reading status register\n");
if (!(readval & AD7124_STATUS_POR_FLAG_MSK))
- return 0;
+ break;
/* The AD7124 requires typically 2ms to power up and settle */
usleep_range(100, 2000);
} while (--timeout);
- return dev_err_probe(dev, -EIO, "Soft reset failed\n");
+ if (readval & AD7124_STATUS_POR_FLAG_MSK)
+ return dev_err_probe(dev, -EIO, "Soft reset failed\n");
+
+ ret = ad_sd_read_reg(&st->sd, AD7124_GAIN(0), 3, &st->gain_default);
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "Error reading gain register\n");
+
+ dev_dbg(dev, "Reset value of GAIN register is 0x%x\n", st->gain_default);
+
+ return 0;
}
static int ad7124_check_chip_id(struct ad7124_state *st)
return ret;
}
+static int __ad7124_calibrate_all(struct ad7124_state *st, struct iio_dev *indio_dev)
+{
+ struct device *dev = &st->sd.spi->dev;
+ int ret, i;
+
+ for (i = 0; i < st->num_channels; i++) {
+
+ if (indio_dev->channels[i].type != IIO_VOLTAGE)
+ continue;
+
+ /*
+ * For calibration the OFFSET register should hold its reset default
+ * value. For the GAIN register there is no such requirement but
+ * for gain 1 it should hold the reset default value, too. So to
+ * simplify matters use the reset default value for both.
+ */
+ st->channels[i].cfg.calibration_offset = 0x800000;
+ st->channels[i].cfg.calibration_gain = st->gain_default;
+
+ /*
+ * Full-scale calibration isn't supported at gain 1, so skip in
+ * that case. Note that untypically full-scale calibration has
+ * to happen before zero-scale calibration. This only applies to
+ * the internal calibration. For system calibration it's as
+ * usual: first zero-scale then full-scale calibration.
+ */
+ if (st->channels[i].cfg.pga_bits > 0) {
+ ret = ad_sd_calibrate(&st->sd, AD7124_MODE_CAL_INT_FULL, i);
+ if (ret < 0)
+ return ret;
+
+ /*
+ * read out the resulting value of GAIN
+ * after full-scale calibration because the next
+ * ad_sd_calibrate() call overwrites this via
+ * ad_sigma_delta_set_channel() -> ad7124_set_channel()
+ * ... -> ad7124_enable_channel().
+ */
+ ret = ad_sd_read_reg(&st->sd, AD7124_GAIN(st->channels[i].cfg.cfg_slot), 3,
+ &st->channels[i].cfg.calibration_gain);
+ if (ret < 0)
+ return ret;
+ }
+
+ ret = ad_sd_calibrate(&st->sd, AD7124_MODE_CAL_INT_ZERO, i);
+ if (ret < 0)
+ return ret;
+
+ ret = ad_sd_read_reg(&st->sd, AD7124_OFFSET(st->channels[i].cfg.cfg_slot), 3,
+ &st->channels[i].cfg.calibration_offset);
+ if (ret < 0)
+ return ret;
+
+ dev_dbg(dev, "offset and gain for channel %d = 0x%x + 0x%x\n", i,
+ st->channels[i].cfg.calibration_offset,
+ st->channels[i].cfg.calibration_gain);
+ }
+
+ return 0;
+}
+
+static int ad7124_calibrate_all(struct ad7124_state *st, struct iio_dev *indio_dev)
+{
+ int ret;
+ unsigned int adc_control = st->adc_control;
+
+ /*
+ * Calibration isn't supported at full power, so speed down a bit.
+ * Setting .adc_control is enough here because the control register is
+ * written as part of ad_sd_calibrate() -> ad_sigma_delta_set_mode().
+ * The resulting calibration is then also valid for high-speed, so just
+ * restore adc_control afterwards.
+ */
+ if (FIELD_GET(AD7124_ADC_CTRL_PWR_MSK, adc_control) >= AD7124_FULL_POWER) {
+ st->adc_control &= ~AD7124_ADC_CTRL_PWR_MSK;
+ st->adc_control |= AD7124_ADC_CTRL_PWR(AD7124_MID_POWER);
+ }
+
+ ret = __ad7124_calibrate_all(st, indio_dev);
+
+ st->adc_control = adc_control;
+
+ return ret;
+}
+
static void ad7124_reg_disable(void *r)
{
regulator_disable(r);
if (ret < 0)
return dev_err_probe(dev, ret, "Failed to setup triggers\n");
+ ret = ad7124_calibrate_all(st, indio_dev);
+ if (ret)
+ return ret;
+
ret = devm_iio_device_register(&spi->dev, indio_dev);
if (ret < 0)
return dev_err_probe(dev, ret, "Failed to register iio device\n");