Skip to content

Commit 5913828

Browse files
dlechgregkh
authored andcommitted
iio: adc: ad7173: prevent scan if too many setups requested
commit 1cfb22c upstream. Add a check to ad7173_update_scan_mode() to ensure that we didn't exceed the maximum number of unique channel configurations. In the AD7173 family of chips, there are some chips that have 16 CHANNELx registers but only 8 setups (combination of CONFIGx, FILTERx, GAINx and OFFSETx registers). Since commit 92c2472 ("iio: adc: ad7173: fix num_slots"), it is possible to have more than 8 channels enabled in a scan at the same time, so it is possible to get a bad configuration when more than 8 channels are using unique configurations. This happens because the algorithm to allocate the setup slots only takes into account which slot has been least recently used and doesn't know about the maximum number of slots available. Since the algorithm to allocate the setup slots is quite complex, it is simpler to check after the fact if the current state is valid or not. So this patch adds a check in ad7173_update_scan_mode() after setting up all of the configurations to make sure that the actual setup still matches the requested setup for each enabled channel. If not, we prevent the scan from being enabled and return an error. The setup comparison in ad7173_setup_equal() is refactored to a separate function since we need to call it in two places now. Fixes: 92c2472 ("iio: adc: ad7173: fix num_slots") Signed-off-by: David Lechner <dlechner@baylibre.com> Link: https://patch.msgid.link/20250722-iio-adc-ad7173-fix-setup-use-limits-v2-1-8e96bdb72a9c@baylibre.com Cc: <Stable@vger.kernel.org> Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
1 parent 4266666 commit 5913828

File tree

1 file changed

+75
-12
lines changed

1 file changed

+75
-12
lines changed

drivers/iio/adc/ad7173.c

Lines changed: 75 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -200,7 +200,7 @@ struct ad7173_channel_config {
200200
/*
201201
* Following fields are used to compare equality. If you
202202
* make adaptations in it, you most likely also have to adapt
203-
* ad7173_find_live_config(), too.
203+
* ad7173_is_setup_equal(), too.
204204
*/
205205
struct_group(config_props,
206206
bool bipolar;
@@ -562,12 +562,19 @@ static void ad7173_reset_usage_cnts(struct ad7173_state *st)
562562
st->config_usage_counter = 0;
563563
}
564564

565-
static struct ad7173_channel_config *
566-
ad7173_find_live_config(struct ad7173_state *st, struct ad7173_channel_config *cfg)
565+
/**
566+
* ad7173_is_setup_equal - Compare two channel setups
567+
* @cfg1: First channel configuration
568+
* @cfg2: Second channel configuration
569+
*
570+
* Compares all configuration options that affect the registers connected to
571+
* SETUP_SEL, namely CONFIGx, FILTERx, GAINx and OFFSETx.
572+
*
573+
* Returns: true if the setups are identical, false otherwise
574+
*/
575+
static bool ad7173_is_setup_equal(const struct ad7173_channel_config *cfg1,
576+
const struct ad7173_channel_config *cfg2)
567577
{
568-
struct ad7173_channel_config *cfg_aux;
569-
int i;
570-
571578
/*
572579
* This is just to make sure that the comparison is adapted after
573580
* struct ad7173_channel_config was changed.
@@ -580,14 +587,22 @@ ad7173_find_live_config(struct ad7173_state *st, struct ad7173_channel_config *c
580587
u8 ref_sel;
581588
}));
582589

590+
return cfg1->bipolar == cfg2->bipolar &&
591+
cfg1->input_buf == cfg2->input_buf &&
592+
cfg1->odr == cfg2->odr &&
593+
cfg1->ref_sel == cfg2->ref_sel;
594+
}
595+
596+
static struct ad7173_channel_config *
597+
ad7173_find_live_config(struct ad7173_state *st, struct ad7173_channel_config *cfg)
598+
{
599+
struct ad7173_channel_config *cfg_aux;
600+
int i;
601+
583602
for (i = 0; i < st->num_channels; i++) {
584603
cfg_aux = &st->channels[i].cfg;
585604

586-
if (cfg_aux->live &&
587-
cfg->bipolar == cfg_aux->bipolar &&
588-
cfg->input_buf == cfg_aux->input_buf &&
589-
cfg->odr == cfg_aux->odr &&
590-
cfg->ref_sel == cfg_aux->ref_sel)
605+
if (cfg_aux->live && ad7173_is_setup_equal(cfg, cfg_aux))
591606
return cfg_aux;
592607
}
593608
return NULL;
@@ -1229,7 +1244,7 @@ static int ad7173_update_scan_mode(struct iio_dev *indio_dev,
12291244
const unsigned long *scan_mask)
12301245
{
12311246
struct ad7173_state *st = iio_priv(indio_dev);
1232-
int i, ret;
1247+
int i, j, k, ret;
12331248

12341249
for (i = 0; i < indio_dev->num_channels; i++) {
12351250
if (test_bit(i, scan_mask))
@@ -1240,6 +1255,54 @@ static int ad7173_update_scan_mode(struct iio_dev *indio_dev,
12401255
return ret;
12411256
}
12421257

1258+
/*
1259+
* On some chips, there are more channels that setups, so if there were
1260+
* more unique setups requested than the number of available slots,
1261+
* ad7173_set_channel() will have written over some of the slots. We
1262+
* can detect this by making sure each assigned cfg_slot matches the
1263+
* requested configuration. If it doesn't, we know that the slot was
1264+
* overwritten by a different channel.
1265+
*/
1266+
for_each_set_bit(i, scan_mask, indio_dev->num_channels) {
1267+
const struct ad7173_channel_config *cfg1, *cfg2;
1268+
1269+
cfg1 = &st->channels[i].cfg;
1270+
1271+
for_each_set_bit(j, scan_mask, indio_dev->num_channels) {
1272+
cfg2 = &st->channels[j].cfg;
1273+
1274+
/*
1275+
* Only compare configs that are assigned to the same
1276+
* SETUP_SEL slot and don't compare channel to itself.
1277+
*/
1278+
if (i == j || cfg1->cfg_slot != cfg2->cfg_slot)
1279+
continue;
1280+
1281+
/*
1282+
* If we find two different configs trying to use the
1283+
* same SETUP_SEL slot, then we know that the that we
1284+
* have too many unique configurations requested for
1285+
* the available slots and at least one was overwritten.
1286+
*/
1287+
if (!ad7173_is_setup_equal(cfg1, cfg2)) {
1288+
/*
1289+
* At this point, there isn't a way to tell
1290+
* which setups are actually programmed in the
1291+
* ADC anymore, so we could read them back to
1292+
* see, but it is simpler to just turn off all
1293+
* of the live flags so that everything gets
1294+
* reprogramed on the next attempt read a sample.
1295+
*/
1296+
for (k = 0; k < st->num_channels; k++)
1297+
st->channels[k].cfg.live = false;
1298+
1299+
dev_err(&st->sd.spi->dev,
1300+
"Too many unique channel configurations requested for scan\n");
1301+
return -EINVAL;
1302+
}
1303+
}
1304+
}
1305+
12431306
return 0;
12441307
}
12451308

0 commit comments

Comments
 (0)