Skip to content

Commit 1cfb22c

Browse files
dlechjic23
authored andcommitted
iio: adc: ad7173: prevent scan if too many setups requested
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 <[email protected]> Link: https://patch.msgid.link/20250722-iio-adc-ad7173-fix-setup-use-limits-v2-1-8e96bdb72a9c@baylibre.com Cc: <[email protected]> Signed-off-by: Jonathan Cameron <[email protected]>
1 parent de18e97 commit 1cfb22c

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;
@@ -561,12 +561,19 @@ static void ad7173_reset_usage_cnts(struct ad7173_state *st)
561561
st->config_usage_counter = 0;
562562
}
563563

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

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

585-
if (cfg_aux->live &&
586-
cfg->bipolar == cfg_aux->bipolar &&
587-
cfg->input_buf == cfg_aux->input_buf &&
588-
cfg->odr == cfg_aux->odr &&
589-
cfg->ref_sel == cfg_aux->ref_sel)
604+
if (cfg_aux->live && ad7173_is_setup_equal(cfg, cfg_aux))
590605
return cfg_aux;
591606
}
592607
return NULL;
@@ -1228,7 +1243,7 @@ static int ad7173_update_scan_mode(struct iio_dev *indio_dev,
12281243
const unsigned long *scan_mask)
12291244
{
12301245
struct ad7173_state *st = iio_priv(indio_dev);
1231-
int i, ret;
1246+
int i, j, k, ret;
12321247

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

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

0 commit comments

Comments
 (0)