Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

iio: adc: ad7173: support changing filter type

Add support for changing the filter type to the ad7173 driver.

This family of chips by default uses a sinc5+sinc1 filter. There are
also optional post-filters that can be added in this configuration for
various 50/60Hz rejection purposes. The sinc3 filter doesn't have any
post-filters and handles the output data rate (ODR) a bit differently.
Here, we've opted to use SINC3_MAPx to get the maximum possible
sampling frequencies with the SINC3 filter.

Adding support consists of adding the filter_type and
filter_type_available attributes, making the sampling_frequency
attribute aware of the filter type, and programming the filter
parameters when we configure the channel of the ADC for reading
a sample.

Reviewed-by: Nuno Sá <nuno.sa@analog.com>
Signed-off-by: David Lechner <dlechner@baylibre.com>
Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>

authored by

David Lechner and committed by
Jonathan Cameron
ff06b39b 27901cbc

+181 -5
+181 -5
drivers/iio/adc/ad7173.c
··· 8 8 * AD7175-8/AD7176-2/AD7177-2 9 9 * 10 10 * Copyright (C) 2015, 2024 Analog Devices, Inc. 11 + * Copyright (C) 2025 BayLibre, SAS 11 12 */ 12 13 13 14 #include <linux/array_size.h> ··· 150 149 (pin2) < st->info->num_voltage_in && \ 151 150 (pin2) >= st->info->num_voltage_in_div) 152 151 153 - #define AD7173_FILTER_ODR0_MASK GENMASK(5, 0) 152 + #define AD7173_FILTER_SINC3_MAP BIT(15) 153 + #define AD7173_FILTER_SINC3_MAP_DIV GENMASK(14, 0) 154 + #define AD7173_FILTER_ENHFILTEN BIT(11) 155 + #define AD7173_FILTER_ENHFILT_MASK GENMASK(10, 8) 156 + #define AD7173_FILTER_ORDER BIT(6) 157 + #define AD7173_FILTER_ODR_MASK GENMASK(5, 0) 154 158 #define AD7173_MAX_CONFIGS 8 155 159 #define AD4111_OW_DET_THRSH_MV 300 156 160 ··· 196 190 u8 num_gpios; 197 191 }; 198 192 193 + enum ad7173_filter_type { 194 + AD7173_FILTER_SINC3, 195 + AD7173_FILTER_SINC5_SINC1, 196 + AD7173_FILTER_SINC5_SINC1_PF1, 197 + AD7173_FILTER_SINC5_SINC1_PF2, 198 + AD7173_FILTER_SINC5_SINC1_PF3, 199 + AD7173_FILTER_SINC5_SINC1_PF4, 200 + }; 201 + 199 202 struct ad7173_channel_config { 200 203 /* Openwire detection threshold */ 201 204 unsigned int openwire_thrsh_raw; ··· 220 205 struct_group(config_props, 221 206 bool bipolar; 222 207 bool input_buf; 208 + u16 sinc3_odr_div; 223 209 u8 sinc5_odr_index; 224 210 u8 ref_sel; 211 + enum ad7173_filter_type filter_type; 225 212 ); 226 213 }; 227 214 ··· 282 265 49960, 20000, 16666, 10000, /* 16-19 */ 283 266 5000, /* 20 */ 284 267 }; 268 + 269 + /** 270 + * ad7173_sinc3_odr_div_from_odr() - Convert ODR to divider value 271 + * @odr_millihz: ODR (sampling_frequency) in milliHz 272 + * Returns: Divider value for SINC3 filter to pass. 273 + */ 274 + static u16 ad7173_sinc3_odr_div_from_odr(u32 odr_millihz) 275 + { 276 + /* 277 + * Divider is f_MOD (1 MHz) / 32 / ODR. ODR freq is in milliHz, so 278 + * we need to convert f_MOD to the same units. When SING_CYC=1 or 279 + * multiple channels are enabled (currently always the case), there 280 + * is an additional factor of 3. 281 + */ 282 + u32 div = DIV_ROUND_CLOSEST(MEGA * MILLI, odr_millihz * 32 * 3); 283 + /* Avoid divide by 0 and limit to register field size. */ 284 + return clamp(div, 1U, AD7173_FILTER_SINC3_MAP_DIV); 285 + } 285 286 286 287 static unsigned int ad4111_current_channel_config[] = { 287 288 /* Ain sel: pos neg */ ··· 404 369 .get = ad7173_get_syscalib_mode 405 370 }; 406 371 372 + static const char * const ad7173_filter_types_str[] = { 373 + [AD7173_FILTER_SINC3] = "sinc3", 374 + [AD7173_FILTER_SINC5_SINC1] = "sinc5+sinc1", 375 + [AD7173_FILTER_SINC5_SINC1_PF1] = "sinc5+sinc1+pf1", 376 + [AD7173_FILTER_SINC5_SINC1_PF2] = "sinc5+sinc1+pf2", 377 + [AD7173_FILTER_SINC5_SINC1_PF3] = "sinc5+sinc1+pf3", 378 + [AD7173_FILTER_SINC5_SINC1_PF4] = "sinc5+sinc1+pf4", 379 + }; 380 + 381 + static int ad7173_set_filter_type(struct iio_dev *indio_dev, 382 + const struct iio_chan_spec *chan, 383 + unsigned int val) 384 + { 385 + struct ad7173_state *st = iio_priv(indio_dev); 386 + 387 + if (!iio_device_claim_direct(indio_dev)) 388 + return -EBUSY; 389 + 390 + st->channels[chan->address].cfg.filter_type = val; 391 + st->channels[chan->address].cfg.live = false; 392 + 393 + iio_device_release_direct(indio_dev); 394 + 395 + return 0; 396 + } 397 + 398 + static int ad7173_get_filter_type(struct iio_dev *indio_dev, 399 + const struct iio_chan_spec *chan) 400 + { 401 + struct ad7173_state *st = iio_priv(indio_dev); 402 + 403 + return st->channels[chan->address].cfg.filter_type; 404 + } 405 + 406 + static const struct iio_enum ad7173_filter_type_enum = { 407 + .items = ad7173_filter_types_str, 408 + .num_items = ARRAY_SIZE(ad7173_filter_types_str), 409 + .set = ad7173_set_filter_type, 410 + .get = ad7173_get_filter_type, 411 + }; 412 + 407 413 static const struct iio_chan_spec_ext_info ad7173_chan_spec_ext_info[] = { 408 414 { 409 415 .name = "sys_calibration", ··· 455 379 &ad7173_syscalib_mode_enum), 456 380 IIO_ENUM_AVAILABLE("sys_calibration_mode", IIO_SHARED_BY_TYPE, 457 381 &ad7173_syscalib_mode_enum), 382 + IIO_ENUM("filter_type", IIO_SEPARATE, &ad7173_filter_type_enum), 383 + IIO_ENUM_AVAILABLE("filter_type", IIO_SHARED_BY_TYPE, 384 + &ad7173_filter_type_enum), 385 + { } 386 + }; 387 + 388 + static const struct iio_chan_spec_ext_info ad7173_temp_chan_spec_ext_info[] = { 389 + IIO_ENUM("filter_type", IIO_SEPARATE, &ad7173_filter_type_enum), 390 + IIO_ENUM_AVAILABLE("filter_type", IIO_SHARED_BY_TYPE, 391 + &ad7173_filter_type_enum), 458 392 { } 459 393 }; 460 394 ··· 668 582 sizeof(struct { 669 583 bool bipolar; 670 584 bool input_buf; 585 + u16 sinc3_odr_div; 671 586 u8 sinc5_odr_index; 672 587 u8 ref_sel; 588 + enum ad7173_filter_type filter_type; 673 589 })); 674 590 675 591 return cfg1->bipolar == cfg2->bipolar && 676 592 cfg1->input_buf == cfg2->input_buf && 593 + cfg1->sinc3_odr_div == cfg2->sinc3_odr_div && 677 594 cfg1->sinc5_odr_index == cfg2->sinc5_odr_index && 678 - cfg1->ref_sel == cfg2->ref_sel; 595 + cfg1->ref_sel == cfg2->ref_sel && 596 + cfg1->filter_type == cfg2->filter_type; 679 597 } 680 598 681 599 static struct ad7173_channel_config * ··· 720 630 { 721 631 unsigned int config; 722 632 int free_cfg_slot, ret; 633 + u8 post_filter_enable, post_filter_select; 723 634 724 635 free_cfg_slot = ida_alloc_range(&st->cfg_slots_status, 0, 725 636 st->info->num_configs - 1, GFP_KERNEL); ··· 740 649 if (ret) 741 650 return ret; 742 651 652 + /* 653 + * When SINC3_MAP flag is enabled, the rest of the register has a 654 + * different meaning. We are using this option to allow the most 655 + * possible sampling frequencies with SINC3 filter. 656 + */ 657 + if (cfg->filter_type == AD7173_FILTER_SINC3) 658 + return ad_sd_write_reg(&st->sd, AD7173_REG_FILTER(free_cfg_slot), 2, 659 + FIELD_PREP(AD7173_FILTER_SINC3_MAP, 1) | 660 + FIELD_PREP(AD7173_FILTER_SINC3_MAP_DIV, 661 + cfg->sinc3_odr_div)); 662 + 663 + switch (cfg->filter_type) { 664 + case AD7173_FILTER_SINC5_SINC1_PF1: 665 + post_filter_enable = 1; 666 + post_filter_select = 2; 667 + break; 668 + case AD7173_FILTER_SINC5_SINC1_PF2: 669 + post_filter_enable = 1; 670 + post_filter_select = 3; 671 + break; 672 + case AD7173_FILTER_SINC5_SINC1_PF3: 673 + post_filter_enable = 1; 674 + post_filter_select = 5; 675 + break; 676 + case AD7173_FILTER_SINC5_SINC1_PF4: 677 + post_filter_enable = 1; 678 + post_filter_select = 6; 679 + break; 680 + default: 681 + post_filter_enable = 0; 682 + post_filter_select = 0; 683 + break; 684 + } 685 + 743 686 return ad_sd_write_reg(&st->sd, AD7173_REG_FILTER(free_cfg_slot), 2, 744 - AD7173_FILTER_ODR0_MASK & cfg->sinc5_odr_index); 687 + FIELD_PREP(AD7173_FILTER_SINC3_MAP, 0) | 688 + FIELD_PREP(AD7173_FILTER_ENHFILT_MASK, 689 + post_filter_enable) | 690 + FIELD_PREP(AD7173_FILTER_ENHFILTEN, 691 + post_filter_select) | 692 + FIELD_PREP(AD7173_FILTER_ORDER, 0) | 693 + FIELD_PREP(AD7173_FILTER_ODR_MASK, 694 + cfg->sinc5_odr_index)); 745 695 } 746 696 747 697 static int ad7173_config_channel(struct ad7173_state *st, int addr) ··· 1315 1183 return -EINVAL; 1316 1184 } 1317 1185 case IIO_CHAN_INFO_SAMP_FREQ: 1186 + if (st->channels[chan->address].cfg.filter_type == AD7173_FILTER_SINC3) { 1187 + /* Inverse operation of ad7173_sinc3_odr_div_from_odr() */ 1188 + *val = MEGA; 1189 + *val2 = 3 * 32 * st->channels[chan->address].cfg.sinc3_odr_div; 1190 + return IIO_VAL_FRACTIONAL; 1191 + } 1192 + 1318 1193 reg = st->channels[chan->address].cfg.sinc5_odr_index; 1319 1194 1320 1195 *val = st->info->sinc5_data_rates[reg] / MILLI; ··· 1360 1221 * 1361 1222 * This will cause the reading of CH1 to be actually done once every 1362 1223 * 200.16ms, an effective rate of 4.99sps. 1224 + * 1225 + * Both the sinc5 and sinc3 rates are set here so that if the filter 1226 + * type is changed, the requested rate will still be set (aside from 1227 + * rounding differences). 1363 1228 */ 1364 1229 case IIO_CHAN_INFO_SAMP_FREQ: 1365 1230 freq = val * MILLI + val2 / MILLI; ··· 1373 1230 1374 1231 cfg = &st->channels[chan->address].cfg; 1375 1232 cfg->sinc5_odr_index = i; 1233 + cfg->sinc3_odr_div = ad7173_sinc3_odr_div_from_odr(freq); 1376 1234 cfg->live = false; 1377 1235 break; 1378 1236 ··· 1390 1246 const unsigned long *scan_mask) 1391 1247 { 1392 1248 struct ad7173_state *st = iio_priv(indio_dev); 1249 + u16 sinc3_count = 0; 1250 + u16 sinc3_div = 0; 1393 1251 int i, j, k, ret; 1394 1252 1395 1253 for (i = 0; i < indio_dev->num_channels; i++) { 1396 - if (test_bit(i, scan_mask)) 1254 + const struct ad7173_channel_config *cfg = &st->channels[i].cfg; 1255 + 1256 + if (test_bit(i, scan_mask)) { 1257 + if (cfg->filter_type == AD7173_FILTER_SINC3) { 1258 + sinc3_count++; 1259 + 1260 + if (sinc3_div == 0) { 1261 + sinc3_div = cfg->sinc3_odr_div; 1262 + } else if (sinc3_div != cfg->sinc3_odr_div) { 1263 + dev_err(&st->sd.spi->dev, 1264 + "All enabled channels must have the same sampling_frequency for sinc3 filter_type\n"); 1265 + return -EINVAL; 1266 + } 1267 + } 1268 + 1397 1269 ret = ad7173_set_channel(&st->sd, i); 1398 - else 1270 + } else { 1399 1271 ret = ad_sd_write_reg(&st->sd, AD7173_REG_CH(i), 2, 0); 1272 + } 1400 1273 if (ret < 0) 1401 1274 return ret; 1275 + } 1276 + 1277 + if (sinc3_count && sinc3_count < bitmap_weight(scan_mask, indio_dev->num_channels)) { 1278 + dev_err(&st->sd.spi->dev, 1279 + "All enabled channels must have sinc3 filter_type\n"); 1280 + return -EINVAL; 1402 1281 } 1403 1282 1404 1283 /* ··· 1582 1415 .storagebits = 32, 1583 1416 .endianness = IIO_BE, 1584 1417 }, 1418 + .ext_info = ad7173_temp_chan_spec_ext_info, 1585 1419 }; 1586 1420 1587 1421 static void ad7173_disable_regulators(void *data) ··· 1823 1655 chan_st_priv->cfg.bipolar = false; 1824 1656 chan_st_priv->cfg.input_buf = st->info->has_input_buf; 1825 1657 chan_st_priv->cfg.ref_sel = AD7173_SETUP_REF_SEL_INT_REF; 1658 + chan_st_priv->cfg.sinc3_odr_div = ad7173_sinc3_odr_div_from_odr( 1659 + st->info->sinc5_data_rates[st->info->odr_start_value] 1660 + ); 1826 1661 chan_st_priv->cfg.sinc5_odr_index = st->info->odr_start_value; 1662 + chan_st_priv->cfg.filter_type = AD7173_FILTER_SINC5_SINC1; 1827 1663 chan_st_priv->cfg.openwire_comp_chan = -1; 1828 1664 st->adc_mode |= AD7173_ADC_MODE_REF_EN; 1829 1665 if (st->info->data_reg_only_16bit) ··· 1899 1727 chan->scan_index = chan_index; 1900 1728 chan->channel = ain[0]; 1901 1729 chan_st_priv->cfg.input_buf = st->info->has_input_buf; 1730 + chan_st_priv->cfg.sinc3_odr_div = ad7173_sinc3_odr_div_from_odr( 1731 + st->info->sinc5_data_rates[st->info->odr_start_value] 1732 + ); 1902 1733 chan_st_priv->cfg.sinc5_odr_index = st->info->odr_start_value; 1734 + chan_st_priv->cfg.filter_type = AD7173_FILTER_SINC5_SINC1; 1903 1735 chan_st_priv->cfg.openwire_comp_chan = -1; 1904 1736 1905 1737 chan_st_priv->cfg.bipolar = fwnode_property_read_bool(child, "bipolar");