summaryrefslogtreecommitdiff
path: root/drivers/iio/adc/ad7173.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/iio/adc/ad7173.c')
-rw-r--r--drivers/iio/adc/ad7173.c165
1 files changed, 116 insertions, 49 deletions
diff --git a/drivers/iio/adc/ad7173.c b/drivers/iio/adc/ad7173.c
index b3e6bd2a55d7..683146e83ab2 100644
--- a/drivers/iio/adc/ad7173.c
+++ b/drivers/iio/adc/ad7173.c
@@ -200,7 +200,7 @@ struct ad7173_channel_config {
/*
* Following fields are used to compare equality. If you
* make adaptations in it, you most likely also have to adapt
- * ad7173_find_live_config(), too.
+ * ad7173_is_setup_equal(), too.
*/
struct_group(config_props,
bool bipolar;
@@ -228,7 +228,6 @@ struct ad7173_state {
struct ida cfg_slots_status;
unsigned long long config_usage_counter;
unsigned long long *config_cnts;
- struct clk *ext_clk;
struct clk_hw int_clk_hw;
struct regmap *reg_gpiocon_regmap;
struct gpio_regmap *gpio_regmap;
@@ -319,7 +318,7 @@ static int ad7173_set_syscalib_mode(struct iio_dev *indio_dev,
{
struct ad7173_state *st = iio_priv(indio_dev);
- st->channels[chan->channel].syscalib_mode = mode;
+ st->channels[chan->address].syscalib_mode = mode;
return 0;
}
@@ -329,7 +328,7 @@ static int ad7173_get_syscalib_mode(struct iio_dev *indio_dev,
{
struct ad7173_state *st = iio_priv(indio_dev);
- return st->channels[chan->channel].syscalib_mode;
+ return st->channels[chan->address].syscalib_mode;
}
static ssize_t ad7173_write_syscalib(struct iio_dev *indio_dev,
@@ -348,7 +347,7 @@ static ssize_t ad7173_write_syscalib(struct iio_dev *indio_dev,
if (!iio_device_claim_direct(indio_dev))
return -EBUSY;
- mode = st->channels[chan->channel].syscalib_mode;
+ mode = st->channels[chan->address].syscalib_mode;
if (sys_calib) {
if (mode == AD7173_SYSCALIB_ZERO_SCALE)
ret = ad_sd_calibrate(&st->sd, AD7173_MODE_CAL_SYS_ZERO,
@@ -392,13 +391,12 @@ static int ad7173_calibrate_all(struct ad7173_state *st, struct iio_dev *indio_d
if (indio_dev->channels[i].type != IIO_VOLTAGE)
continue;
- ret = ad_sd_calibrate(&st->sd, AD7173_MODE_CAL_INT_ZERO, st->channels[i].ain);
+ ret = ad_sd_calibrate(&st->sd, AD7173_MODE_CAL_INT_ZERO, i);
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);
+ ret = ad_sd_calibrate(&st->sd, AD7173_MODE_CAL_INT_FULL, i);
if (ret < 0)
return ret;
}
@@ -563,12 +561,19 @@ static void ad7173_reset_usage_cnts(struct ad7173_state *st)
st->config_usage_counter = 0;
}
-static struct ad7173_channel_config *
-ad7173_find_live_config(struct ad7173_state *st, struct ad7173_channel_config *cfg)
+/**
+ * ad7173_is_setup_equal - Compare two channel setups
+ * @cfg1: First channel configuration
+ * @cfg2: Second channel configuration
+ *
+ * Compares all configuration options that affect the registers connected to
+ * SETUP_SEL, namely CONFIGx, FILTERx, GAINx and OFFSETx.
+ *
+ * Returns: true if the setups are identical, false otherwise
+ */
+static bool ad7173_is_setup_equal(const struct ad7173_channel_config *cfg1,
+ const struct ad7173_channel_config *cfg2)
{
- struct ad7173_channel_config *cfg_aux;
- int i;
-
/*
* This is just to make sure that the comparison is adapted after
* struct ad7173_channel_config was changed.
@@ -581,14 +586,22 @@ ad7173_find_live_config(struct ad7173_state *st, struct ad7173_channel_config *c
u8 ref_sel;
}));
+ return cfg1->bipolar == cfg2->bipolar &&
+ cfg1->input_buf == cfg2->input_buf &&
+ cfg1->odr == cfg2->odr &&
+ cfg1->ref_sel == cfg2->ref_sel;
+}
+
+static struct ad7173_channel_config *
+ad7173_find_live_config(struct ad7173_state *st, struct ad7173_channel_config *cfg)
+{
+ struct ad7173_channel_config *cfg_aux;
+ int i;
+
for (i = 0; i < st->num_channels; i++) {
cfg_aux = &st->channels[i].cfg;
- if (cfg_aux->live &&
- cfg->bipolar == cfg_aux->bipolar &&
- cfg->input_buf == cfg_aux->input_buf &&
- cfg->odr == cfg_aux->odr &&
- cfg->ref_sel == cfg_aux->ref_sel)
+ if (cfg_aux->live && ad7173_is_setup_equal(cfg, cfg_aux))
return cfg_aux;
}
return NULL;
@@ -772,10 +785,26 @@ static const struct ad_sigma_delta_info ad7173_sigma_delta_info_8_slots = {
.num_slots = 8,
};
+static const struct ad_sigma_delta_info ad7173_sigma_delta_info_16_slots = {
+ .set_channel = ad7173_set_channel,
+ .append_status = ad7173_append_status,
+ .disable_all = ad7173_disable_all,
+ .disable_one = ad7173_disable_one,
+ .set_mode = ad7173_set_mode,
+ .has_registers = true,
+ .has_named_irqs = true,
+ .addr_shift = 0,
+ .read_mask = BIT(6),
+ .status_ch_mask = GENMASK(3, 0),
+ .data_reg = AD7173_REG_DATA,
+ .num_resetclks = 64,
+ .num_slots = 16,
+};
+
static const struct ad7173_device_info ad4111_device_info = {
.name = "ad4111",
.id = AD4111_ID,
- .sd_info = &ad7173_sigma_delta_info_8_slots,
+ .sd_info = &ad7173_sigma_delta_info_16_slots,
.num_voltage_in_div = 8,
.num_channels = 16,
.num_configs = 8,
@@ -797,7 +826,7 @@ static const struct ad7173_device_info ad4111_device_info = {
static const struct ad7173_device_info ad4112_device_info = {
.name = "ad4112",
.id = AD4112_ID,
- .sd_info = &ad7173_sigma_delta_info_8_slots,
+ .sd_info = &ad7173_sigma_delta_info_16_slots,
.num_voltage_in_div = 8,
.num_channels = 16,
.num_configs = 8,
@@ -818,7 +847,7 @@ static const struct ad7173_device_info ad4112_device_info = {
static const struct ad7173_device_info ad4113_device_info = {
.name = "ad4113",
.id = AD4113_ID,
- .sd_info = &ad7173_sigma_delta_info_8_slots,
+ .sd_info = &ad7173_sigma_delta_info_16_slots,
.num_voltage_in_div = 8,
.num_channels = 16,
.num_configs = 8,
@@ -837,7 +866,7 @@ static const struct ad7173_device_info ad4113_device_info = {
static const struct ad7173_device_info ad4114_device_info = {
.name = "ad4114",
.id = AD4114_ID,
- .sd_info = &ad7173_sigma_delta_info_8_slots,
+ .sd_info = &ad7173_sigma_delta_info_16_slots,
.num_voltage_in_div = 16,
.num_channels = 16,
.num_configs = 8,
@@ -856,7 +885,7 @@ static const struct ad7173_device_info ad4114_device_info = {
static const struct ad7173_device_info ad4115_device_info = {
.name = "ad4115",
.id = AD4115_ID,
- .sd_info = &ad7173_sigma_delta_info_8_slots,
+ .sd_info = &ad7173_sigma_delta_info_16_slots,
.num_voltage_in_div = 16,
.num_channels = 16,
.num_configs = 8,
@@ -875,7 +904,7 @@ static const struct ad7173_device_info ad4115_device_info = {
static const struct ad7173_device_info ad4116_device_info = {
.name = "ad4116",
.id = AD4116_ID,
- .sd_info = &ad7173_sigma_delta_info_8_slots,
+ .sd_info = &ad7173_sigma_delta_info_16_slots,
.num_voltage_in_div = 11,
.num_channels = 16,
.num_configs = 8,
@@ -894,7 +923,7 @@ static const struct ad7173_device_info ad4116_device_info = {
static const struct ad7173_device_info ad7172_2_device_info = {
.name = "ad7172-2",
.id = AD7172_2_ID,
- .sd_info = &ad7173_sigma_delta_info_8_slots,
+ .sd_info = &ad7173_sigma_delta_info_4_slots,
.num_voltage_in = 5,
.num_channels = 4,
.num_configs = 4,
@@ -927,7 +956,7 @@ static const struct ad7173_device_info ad7172_4_device_info = {
static const struct ad7173_device_info ad7173_8_device_info = {
.name = "ad7173-8",
.id = AD7173_ID,
- .sd_info = &ad7173_sigma_delta_info_8_slots,
+ .sd_info = &ad7173_sigma_delta_info_16_slots,
.num_voltage_in = 17,
.num_channels = 16,
.num_configs = 8,
@@ -944,7 +973,7 @@ static const struct ad7173_device_info ad7173_8_device_info = {
static const struct ad7173_device_info ad7175_2_device_info = {
.name = "ad7175-2",
.id = AD7175_2_ID,
- .sd_info = &ad7173_sigma_delta_info_8_slots,
+ .sd_info = &ad7173_sigma_delta_info_4_slots,
.num_voltage_in = 5,
.num_channels = 4,
.num_configs = 4,
@@ -961,7 +990,7 @@ static const struct ad7173_device_info ad7175_2_device_info = {
static const struct ad7173_device_info ad7175_8_device_info = {
.name = "ad7175-8",
.id = AD7175_8_ID,
- .sd_info = &ad7173_sigma_delta_info_8_slots,
+ .sd_info = &ad7173_sigma_delta_info_16_slots,
.num_voltage_in = 17,
.num_channels = 16,
.num_configs = 8,
@@ -1214,7 +1243,7 @@ static int ad7173_update_scan_mode(struct iio_dev *indio_dev,
const unsigned long *scan_mask)
{
struct ad7173_state *st = iio_priv(indio_dev);
- int i, ret;
+ int i, j, k, ret;
for (i = 0; i < indio_dev->num_channels; i++) {
if (test_bit(i, scan_mask))
@@ -1225,6 +1254,54 @@ static int ad7173_update_scan_mode(struct iio_dev *indio_dev,
return ret;
}
+ /*
+ * On some chips, there are more channels that setups, so if there were
+ * more unique setups requested than the number of available slots,
+ * ad7173_set_channel() will have written over some of the slots. We
+ * can detect this by making sure each assigned cfg_slot matches the
+ * requested configuration. If it doesn't, we know that the slot was
+ * overwritten by a different channel.
+ */
+ for_each_set_bit(i, scan_mask, indio_dev->num_channels) {
+ const struct ad7173_channel_config *cfg1, *cfg2;
+
+ cfg1 = &st->channels[i].cfg;
+
+ for_each_set_bit(j, scan_mask, indio_dev->num_channels) {
+ cfg2 = &st->channels[j].cfg;
+
+ /*
+ * Only compare configs that are assigned to the same
+ * SETUP_SEL slot and don't compare channel to itself.
+ */
+ if (i == j || cfg1->cfg_slot != cfg2->cfg_slot)
+ continue;
+
+ /*
+ * If we find two different configs trying to use the
+ * same SETUP_SEL slot, then we know that the that we
+ * have too many unique configurations requested for
+ * the available slots and at least one was overwritten.
+ */
+ if (!ad7173_is_setup_equal(cfg1, cfg2)) {
+ /*
+ * At this point, there isn't a way to tell
+ * which setups are actually programmed in the
+ * ADC anymore, so we could read them back to
+ * see, but it is simpler to just turn off all
+ * of the live flags so that everything gets
+ * reprogramed on the next attempt read a sample.
+ */
+ for (k = 0; k < st->num_channels; k++)
+ st->channels[k].cfg.live = false;
+
+ dev_err(&st->sd.spi->dev,
+ "Too many unique channel configurations requested for scan\n");
+ return -EINVAL;
+ }
+ }
+ }
+
return 0;
}
@@ -1344,11 +1421,6 @@ static void ad7173_disable_regulators(void *data)
regulator_bulk_disable(ARRAY_SIZE(st->regulators), st->regulators);
}
-static void ad7173_clk_disable_unprepare(void *clk)
-{
- clk_disable_unprepare(clk);
-}
-
static unsigned long ad7173_sel_clk(struct ad7173_state *st,
unsigned int clk_sel)
{
@@ -1580,6 +1652,7 @@ static int ad7173_fw_parse_channel_config(struct iio_dev *indio_dev)
chan_st_priv->cfg.bipolar = false;
chan_st_priv->cfg.input_buf = st->info->has_input_buf;
chan_st_priv->cfg.ref_sel = AD7173_SETUP_REF_SEL_INT_REF;
+ chan_st_priv->cfg.odr = st->info->odr_start_value;
chan_st_priv->cfg.openwire_comp_chan = -1;
st->adc_mode |= AD7173_ADC_MODE_REF_EN;
if (st->info->data_reg_only_16bit)
@@ -1646,7 +1719,7 @@ static int ad7173_fw_parse_channel_config(struct iio_dev *indio_dev)
chan->scan_index = chan_index;
chan->channel = ain[0];
chan_st_priv->cfg.input_buf = st->info->has_input_buf;
- chan_st_priv->cfg.odr = 0;
+ chan_st_priv->cfg.odr = st->info->odr_start_value;
chan_st_priv->cfg.openwire_comp_chan = -1;
chan_st_priv->cfg.bipolar = fwnode_property_read_bool(child, "bipolar");
@@ -1718,22 +1791,14 @@ static int ad7173_fw_parse_device_config(struct iio_dev *indio_dev)
AD7173_ADC_MODE_CLOCKSEL_INT);
ad7173_register_clk_provider(indio_dev);
} else {
+ struct clk *clk;
+
st->adc_mode |= FIELD_PREP(AD7173_ADC_MODE_CLOCKSEL_MASK,
AD7173_ADC_MODE_CLOCKSEL_EXT + ret);
- st->ext_clk = devm_clk_get(dev, ad7173_clk_sel[ret]);
- if (IS_ERR(st->ext_clk))
- return dev_err_probe(dev, PTR_ERR(st->ext_clk),
+ clk = devm_clk_get_enabled(dev, ad7173_clk_sel[ret]);
+ if (IS_ERR(clk))
+ return dev_err_probe(dev, PTR_ERR(clk),
"Failed to get external clock\n");
-
- ret = clk_prepare_enable(st->ext_clk);
- if (ret)
- return dev_err_probe(dev, ret,
- "Failed to enable external clock\n");
-
- ret = devm_add_action_or_reset(dev, ad7173_clk_disable_unprepare,
- st->ext_clk);
- if (ret)
- return ret;
}
return ad7173_fw_parse_channel_config(indio_dev);
@@ -1765,7 +1830,9 @@ static int ad7173_probe(struct spi_device *spi)
indio_dev->info = &ad7173_info;
spi->mode = SPI_MODE_3;
- spi_setup(spi);
+ ret = spi_setup(spi);
+ if (ret)
+ return ret;
ret = ad_sd_init(&st->sd, indio_dev, spi, st->info->sd_info);
if (ret)