Skip to content

Commit ff06b39

Browse files
dlechjic23
authored andcommitted
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>
1 parent 27901cb commit ff06b39

1 file changed

Lines changed: 181 additions & 5 deletions

File tree

drivers/iio/adc/ad7173.c

Lines changed: 181 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
* AD7175-8/AD7176-2/AD7177-2
99
*
1010
* Copyright (C) 2015, 2024 Analog Devices, Inc.
11+
* Copyright (C) 2025 BayLibre, SAS
1112
*/
1213

1314
#include <linux/array_size.h>
@@ -149,7 +150,12 @@
149150
(pin2) < st->info->num_voltage_in && \
150151
(pin2) >= st->info->num_voltage_in_div)
151152

152-
#define AD7173_FILTER_ODR0_MASK GENMASK(5, 0)
153+
#define AD7173_FILTER_SINC3_MAP BIT(15)
154+
#define AD7173_FILTER_SINC3_MAP_DIV GENMASK(14, 0)
155+
#define AD7173_FILTER_ENHFILTEN BIT(11)
156+
#define AD7173_FILTER_ENHFILT_MASK GENMASK(10, 8)
157+
#define AD7173_FILTER_ORDER BIT(6)
158+
#define AD7173_FILTER_ODR_MASK GENMASK(5, 0)
153159
#define AD7173_MAX_CONFIGS 8
154160
#define AD4111_OW_DET_THRSH_MV 300
155161

@@ -190,6 +196,15 @@ struct ad7173_device_info {
190196
u8 num_gpios;
191197
};
192198

199+
enum ad7173_filter_type {
200+
AD7173_FILTER_SINC3,
201+
AD7173_FILTER_SINC5_SINC1,
202+
AD7173_FILTER_SINC5_SINC1_PF1,
203+
AD7173_FILTER_SINC5_SINC1_PF2,
204+
AD7173_FILTER_SINC5_SINC1_PF3,
205+
AD7173_FILTER_SINC5_SINC1_PF4,
206+
};
207+
193208
struct ad7173_channel_config {
194209
/* Openwire detection threshold */
195210
unsigned int openwire_thrsh_raw;
@@ -205,8 +220,10 @@ struct ad7173_channel_config {
205220
struct_group(config_props,
206221
bool bipolar;
207222
bool input_buf;
223+
u16 sinc3_odr_div;
208224
u8 sinc5_odr_index;
209225
u8 ref_sel;
226+
enum ad7173_filter_type filter_type;
210227
);
211228
};
212229

@@ -266,6 +283,24 @@ static const unsigned int ad7175_sinc5_data_rates[] = {
266283
5000, /* 20 */
267284
};
268285

286+
/**
287+
* ad7173_sinc3_odr_div_from_odr() - Convert ODR to divider value
288+
* @odr_millihz: ODR (sampling_frequency) in milliHz
289+
* Returns: Divider value for SINC3 filter to pass.
290+
*/
291+
static u16 ad7173_sinc3_odr_div_from_odr(u32 odr_millihz)
292+
{
293+
/*
294+
* Divider is f_MOD (1 MHz) / 32 / ODR. ODR freq is in milliHz, so
295+
* we need to convert f_MOD to the same units. When SING_CYC=1 or
296+
* multiple channels are enabled (currently always the case), there
297+
* is an additional factor of 3.
298+
*/
299+
u32 div = DIV_ROUND_CLOSEST(MEGA * MILLI, odr_millihz * 32 * 3);
300+
/* Avoid divide by 0 and limit to register field size. */
301+
return clamp(div, 1U, AD7173_FILTER_SINC3_MAP_DIV);
302+
}
303+
269304
static unsigned int ad4111_current_channel_config[] = {
270305
/* Ain sel: pos neg */
271306
0x1E8, /* 15:IIN0+ 8:IIN0− */
@@ -369,6 +404,47 @@ static const struct iio_enum ad7173_syscalib_mode_enum = {
369404
.get = ad7173_get_syscalib_mode
370405
};
371406

407+
static const char * const ad7173_filter_types_str[] = {
408+
[AD7173_FILTER_SINC3] = "sinc3",
409+
[AD7173_FILTER_SINC5_SINC1] = "sinc5+sinc1",
410+
[AD7173_FILTER_SINC5_SINC1_PF1] = "sinc5+sinc1+pf1",
411+
[AD7173_FILTER_SINC5_SINC1_PF2] = "sinc5+sinc1+pf2",
412+
[AD7173_FILTER_SINC5_SINC1_PF3] = "sinc5+sinc1+pf3",
413+
[AD7173_FILTER_SINC5_SINC1_PF4] = "sinc5+sinc1+pf4",
414+
};
415+
416+
static int ad7173_set_filter_type(struct iio_dev *indio_dev,
417+
const struct iio_chan_spec *chan,
418+
unsigned int val)
419+
{
420+
struct ad7173_state *st = iio_priv(indio_dev);
421+
422+
if (!iio_device_claim_direct(indio_dev))
423+
return -EBUSY;
424+
425+
st->channels[chan->address].cfg.filter_type = val;
426+
st->channels[chan->address].cfg.live = false;
427+
428+
iio_device_release_direct(indio_dev);
429+
430+
return 0;
431+
}
432+
433+
static int ad7173_get_filter_type(struct iio_dev *indio_dev,
434+
const struct iio_chan_spec *chan)
435+
{
436+
struct ad7173_state *st = iio_priv(indio_dev);
437+
438+
return st->channels[chan->address].cfg.filter_type;
439+
}
440+
441+
static const struct iio_enum ad7173_filter_type_enum = {
442+
.items = ad7173_filter_types_str,
443+
.num_items = ARRAY_SIZE(ad7173_filter_types_str),
444+
.set = ad7173_set_filter_type,
445+
.get = ad7173_get_filter_type,
446+
};
447+
372448
static const struct iio_chan_spec_ext_info ad7173_chan_spec_ext_info[] = {
373449
{
374450
.name = "sys_calibration",
@@ -379,6 +455,16 @@ static const struct iio_chan_spec_ext_info ad7173_chan_spec_ext_info[] = {
379455
&ad7173_syscalib_mode_enum),
380456
IIO_ENUM_AVAILABLE("sys_calibration_mode", IIO_SHARED_BY_TYPE,
381457
&ad7173_syscalib_mode_enum),
458+
IIO_ENUM("filter_type", IIO_SEPARATE, &ad7173_filter_type_enum),
459+
IIO_ENUM_AVAILABLE("filter_type", IIO_SHARED_BY_TYPE,
460+
&ad7173_filter_type_enum),
461+
{ }
462+
};
463+
464+
static const struct iio_chan_spec_ext_info ad7173_temp_chan_spec_ext_info[] = {
465+
IIO_ENUM("filter_type", IIO_SEPARATE, &ad7173_filter_type_enum),
466+
IIO_ENUM_AVAILABLE("filter_type", IIO_SHARED_BY_TYPE,
467+
&ad7173_filter_type_enum),
382468
{ }
383469
};
384470

@@ -582,14 +668,18 @@ static bool ad7173_is_setup_equal(const struct ad7173_channel_config *cfg1,
582668
sizeof(struct {
583669
bool bipolar;
584670
bool input_buf;
671+
u16 sinc3_odr_div;
585672
u8 sinc5_odr_index;
586673
u8 ref_sel;
674+
enum ad7173_filter_type filter_type;
587675
}));
588676

589677
return cfg1->bipolar == cfg2->bipolar &&
590678
cfg1->input_buf == cfg2->input_buf &&
679+
cfg1->sinc3_odr_div == cfg2->sinc3_odr_div &&
591680
cfg1->sinc5_odr_index == cfg2->sinc5_odr_index &&
592-
cfg1->ref_sel == cfg2->ref_sel;
681+
cfg1->ref_sel == cfg2->ref_sel &&
682+
cfg1->filter_type == cfg2->filter_type;
593683
}
594684

595685
static struct ad7173_channel_config *
@@ -630,6 +720,7 @@ static int ad7173_load_config(struct ad7173_state *st,
630720
{
631721
unsigned int config;
632722
int free_cfg_slot, ret;
723+
u8 post_filter_enable, post_filter_select;
633724

634725
free_cfg_slot = ida_alloc_range(&st->cfg_slots_status, 0,
635726
st->info->num_configs - 1, GFP_KERNEL);
@@ -649,8 +740,49 @@ static int ad7173_load_config(struct ad7173_state *st,
649740
if (ret)
650741
return ret;
651742

743+
/*
744+
* When SINC3_MAP flag is enabled, the rest of the register has a
745+
* different meaning. We are using this option to allow the most
746+
* possible sampling frequencies with SINC3 filter.
747+
*/
748+
if (cfg->filter_type == AD7173_FILTER_SINC3)
749+
return ad_sd_write_reg(&st->sd, AD7173_REG_FILTER(free_cfg_slot), 2,
750+
FIELD_PREP(AD7173_FILTER_SINC3_MAP, 1) |
751+
FIELD_PREP(AD7173_FILTER_SINC3_MAP_DIV,
752+
cfg->sinc3_odr_div));
753+
754+
switch (cfg->filter_type) {
755+
case AD7173_FILTER_SINC5_SINC1_PF1:
756+
post_filter_enable = 1;
757+
post_filter_select = 2;
758+
break;
759+
case AD7173_FILTER_SINC5_SINC1_PF2:
760+
post_filter_enable = 1;
761+
post_filter_select = 3;
762+
break;
763+
case AD7173_FILTER_SINC5_SINC1_PF3:
764+
post_filter_enable = 1;
765+
post_filter_select = 5;
766+
break;
767+
case AD7173_FILTER_SINC5_SINC1_PF4:
768+
post_filter_enable = 1;
769+
post_filter_select = 6;
770+
break;
771+
default:
772+
post_filter_enable = 0;
773+
post_filter_select = 0;
774+
break;
775+
}
776+
652777
return ad_sd_write_reg(&st->sd, AD7173_REG_FILTER(free_cfg_slot), 2,
653-
AD7173_FILTER_ODR0_MASK & cfg->sinc5_odr_index);
778+
FIELD_PREP(AD7173_FILTER_SINC3_MAP, 0) |
779+
FIELD_PREP(AD7173_FILTER_ENHFILT_MASK,
780+
post_filter_enable) |
781+
FIELD_PREP(AD7173_FILTER_ENHFILTEN,
782+
post_filter_select) |
783+
FIELD_PREP(AD7173_FILTER_ORDER, 0) |
784+
FIELD_PREP(AD7173_FILTER_ODR_MASK,
785+
cfg->sinc5_odr_index));
654786
}
655787

656788
static int ad7173_config_channel(struct ad7173_state *st, int addr)
@@ -1183,6 +1315,13 @@ static int ad7173_read_raw(struct iio_dev *indio_dev,
11831315
return -EINVAL;
11841316
}
11851317
case IIO_CHAN_INFO_SAMP_FREQ:
1318+
if (st->channels[chan->address].cfg.filter_type == AD7173_FILTER_SINC3) {
1319+
/* Inverse operation of ad7173_sinc3_odr_div_from_odr() */
1320+
*val = MEGA;
1321+
*val2 = 3 * 32 * st->channels[chan->address].cfg.sinc3_odr_div;
1322+
return IIO_VAL_FRACTIONAL;
1323+
}
1324+
11861325
reg = st->channels[chan->address].cfg.sinc5_odr_index;
11871326

11881327
*val = st->info->sinc5_data_rates[reg] / MILLI;
@@ -1221,6 +1360,10 @@ static int ad7173_write_raw(struct iio_dev *indio_dev,
12211360
*
12221361
* This will cause the reading of CH1 to be actually done once every
12231362
* 200.16ms, an effective rate of 4.99sps.
1363+
*
1364+
* Both the sinc5 and sinc3 rates are set here so that if the filter
1365+
* type is changed, the requested rate will still be set (aside from
1366+
* rounding differences).
12241367
*/
12251368
case IIO_CHAN_INFO_SAMP_FREQ:
12261369
freq = val * MILLI + val2 / MILLI;
@@ -1230,6 +1373,7 @@ static int ad7173_write_raw(struct iio_dev *indio_dev,
12301373

12311374
cfg = &st->channels[chan->address].cfg;
12321375
cfg->sinc5_odr_index = i;
1376+
cfg->sinc3_odr_div = ad7173_sinc3_odr_div_from_odr(freq);
12331377
cfg->live = false;
12341378
break;
12351379

@@ -1246,17 +1390,40 @@ static int ad7173_update_scan_mode(struct iio_dev *indio_dev,
12461390
const unsigned long *scan_mask)
12471391
{
12481392
struct ad7173_state *st = iio_priv(indio_dev);
1393+
u16 sinc3_count = 0;
1394+
u16 sinc3_div = 0;
12491395
int i, j, k, ret;
12501396

12511397
for (i = 0; i < indio_dev->num_channels; i++) {
1252-
if (test_bit(i, scan_mask))
1398+
const struct ad7173_channel_config *cfg = &st->channels[i].cfg;
1399+
1400+
if (test_bit(i, scan_mask)) {
1401+
if (cfg->filter_type == AD7173_FILTER_SINC3) {
1402+
sinc3_count++;
1403+
1404+
if (sinc3_div == 0) {
1405+
sinc3_div = cfg->sinc3_odr_div;
1406+
} else if (sinc3_div != cfg->sinc3_odr_div) {
1407+
dev_err(&st->sd.spi->dev,
1408+
"All enabled channels must have the same sampling_frequency for sinc3 filter_type\n");
1409+
return -EINVAL;
1410+
}
1411+
}
1412+
12531413
ret = ad7173_set_channel(&st->sd, i);
1254-
else
1414+
} else {
12551415
ret = ad_sd_write_reg(&st->sd, AD7173_REG_CH(i), 2, 0);
1416+
}
12561417
if (ret < 0)
12571418
return ret;
12581419
}
12591420

1421+
if (sinc3_count && sinc3_count < bitmap_weight(scan_mask, indio_dev->num_channels)) {
1422+
dev_err(&st->sd.spi->dev,
1423+
"All enabled channels must have sinc3 filter_type\n");
1424+
return -EINVAL;
1425+
}
1426+
12601427
/*
12611428
* On some chips, there are more channels that setups, so if there were
12621429
* more unique setups requested than the number of available slots,
@@ -1415,6 +1582,7 @@ static const struct iio_chan_spec ad7173_temp_iio_channel_template = {
14151582
.storagebits = 32,
14161583
.endianness = IIO_BE,
14171584
},
1585+
.ext_info = ad7173_temp_chan_spec_ext_info,
14181586
};
14191587

14201588
static void ad7173_disable_regulators(void *data)
@@ -1655,7 +1823,11 @@ static int ad7173_fw_parse_channel_config(struct iio_dev *indio_dev)
16551823
chan_st_priv->cfg.bipolar = false;
16561824
chan_st_priv->cfg.input_buf = st->info->has_input_buf;
16571825
chan_st_priv->cfg.ref_sel = AD7173_SETUP_REF_SEL_INT_REF;
1826+
chan_st_priv->cfg.sinc3_odr_div = ad7173_sinc3_odr_div_from_odr(
1827+
st->info->sinc5_data_rates[st->info->odr_start_value]
1828+
);
16581829
chan_st_priv->cfg.sinc5_odr_index = st->info->odr_start_value;
1830+
chan_st_priv->cfg.filter_type = AD7173_FILTER_SINC5_SINC1;
16591831
chan_st_priv->cfg.openwire_comp_chan = -1;
16601832
st->adc_mode |= AD7173_ADC_MODE_REF_EN;
16611833
if (st->info->data_reg_only_16bit)
@@ -1727,7 +1899,11 @@ static int ad7173_fw_parse_channel_config(struct iio_dev *indio_dev)
17271899
chan->scan_index = chan_index;
17281900
chan->channel = ain[0];
17291901
chan_st_priv->cfg.input_buf = st->info->has_input_buf;
1902+
chan_st_priv->cfg.sinc3_odr_div = ad7173_sinc3_odr_div_from_odr(
1903+
st->info->sinc5_data_rates[st->info->odr_start_value]
1904+
);
17301905
chan_st_priv->cfg.sinc5_odr_index = st->info->odr_start_value;
1906+
chan_st_priv->cfg.filter_type = AD7173_FILTER_SINC5_SINC1;
17311907
chan_st_priv->cfg.openwire_comp_chan = -1;
17321908

17331909
chan_st_priv->cfg.bipolar = fwnode_property_read_bool(child, "bipolar");

0 commit comments

Comments
 (0)