lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <6d0ff620-ec1a-4b17-9b5d-b9c48078271a@baylibre.com>
Date: Fri, 25 Apr 2025 10:43:29 -0500
From: David Lechner <dlechner@...libre.com>
To: Alisa-Dariana Roman <alisadariana@...il.com>,
 Jonathan Cameron <Jonathan.Cameron@...wei.com>,
 Alisa-Dariana Roman <alisa.roman@...log.com>, linux-iio@...r.kernel.org,
 linux-kernel@...r.kernel.org
Cc: Lars-Peter Clausen <lars@...afoo.de>,
 Michael Hennerich <Michael.Hennerich@...log.com>,
 Jonathan Cameron <jic23@...nel.org>, Nuno Sá
 <nuno.sa@...log.com>, Andy Shevchenko <andy@...nel.org>
Subject: Re: [PATCH v1 1/1] iio: adc: ad7192: Refactor filter config

On 4/25/25 8:20 AM, Alisa-Dariana Roman wrote:
> It is not useful for users to set the 3db filter frequency or the
> oversampling value. Remove the option for these to be set by the user.
> 
> The available arrays for 3db filter frequency and oversampling value are
> not removed for backward compatibility.
> 
> The available array for 3db filter frequency is dynamic now, since some
> chips have 4 filter modes and others have 16.

The available array only makes sense if the matching attribute is writeable.
As mentioned in my reply to the cover letter, I think we should keep it
writeable for backwards compatibility. But we don't need to extend it to allow
writing new options, so keeping the previous available array seems fine to me.

> 
> Expose the filter mode to user, providing an intuitive way to select
> filter behaviour.
> 
> Signed-off-by: Alisa-Dariana Roman <alisa.roman@...log.com>
> ---
>  drivers/iio/adc/ad7192.c | 455 +++++++++++++++++++++++++--------------
>  1 file changed, 288 insertions(+), 167 deletions(-)
> 
> diff --git a/drivers/iio/adc/ad7192.c b/drivers/iio/adc/ad7192.c
> index 530e1d307860..1846dc4e90b0 100644
> --- a/drivers/iio/adc/ad7192.c
> +++ b/drivers/iio/adc/ad7192.c
> @@ -165,9 +165,9 @@
>  #define AD7192_EXT_FREQ_MHZ_MAX	5120000
>  #define AD7192_INT_FREQ_MHZ	4915200
>  
> -#define AD7192_NO_SYNC_FILTER	1
> -#define AD7192_SYNC3_FILTER	3
> -#define AD7192_SYNC4_FILTER	4
> +#define AD7192_FILTER_DIV	1024
> +#define AD7192_FS_MIN		1
> +#define AD7192_FS_MAX		1023
>  
>  /* NOTE:
>   * The AD7190/2/5 features a dual use data out ready DOUT/RDY output.
> @@ -182,6 +182,25 @@ enum {
>  	AD7192_SYSCALIB_FULL_SCALE,
>  };
>  
> +enum ad7192_filter_mode {
> +	AD7192_FILTER_SINC4,
> +	AD7192_FILTER_SINC3,
> +	AD7192_FILTER_SINC4_CHOP,
> +	AD7192_FILTER_SINC3_CHOP,
> +	AD7192_FILTER_SINC4_AVG2,
> +	AD7192_FILTER_SINC3_AVG2,
> +	AD7192_FILTER_SINC4_CHOP_AVG2,
> +	AD7192_FILTER_SINC3_CHOP_AVG2,
> +	AD7192_FILTER_SINC4_AVG8,
> +	AD7192_FILTER_SINC3_AVG8,
> +	AD7192_FILTER_SINC4_CHOP_AVG8,
> +	AD7192_FILTER_SINC3_CHOP_AVG8,
> +	AD7192_FILTER_SINC4_AVG16,
> +	AD7192_FILTER_SINC3_AVG16,
> +	AD7192_FILTER_SINC4_CHOP_AVG16,
> +	AD7192_FILTER_SINC3_CHOP_AVG16,
> +};
> +
>  enum {
>  	ID_AD7190,
>  	ID_AD7192,
> @@ -190,11 +209,24 @@ enum {
>  	ID_AD7195,
>  };
>  
> +struct ad7192_filter_config {
> +	enum ad7192_filter_mode		filter_mode;
> +	unsigned int			f_order;

I assume f means filter? Can we spell that out everywhere we have f_order (in
function names too)?

> +	u8				sinc3_en;
> +	u8				chop_en;
> +	u8				avg_val;
> +	enum iio_available_type		samp_freq_avail_type;

If this is always IIO_AVAIL_RANGE, then we don't need this field.

> +	int				samp_freq_avail_len;

If this is always 2, we don't need this field either.

> +	int				samp_freq_avail[2][2];

Range has 3 elements, start, step, stop. This only has 2.

> +};
> +
>  struct ad7192_chip_info {
>  	unsigned int			chip_id;
>  	const char			*name;
>  	const struct iio_chan_spec	*channels;
>  	u8				num_channels;
> +	const struct ad7192_filter_config	*filter_configs;
> +	u8					num_filter_modes;
>  	const struct ad_sigma_delta_info	*sigma_delta_info;
>  	const struct iio_info		*info;
>  	int (*parse_channels)(struct iio_dev *indio_dev);
> @@ -210,13 +242,16 @@ struct ad7192_state {
>  	u32				mode;
>  	u32				conf;
>  	u32				scale_avail[8][2];
> -	u32				filter_freq_avail[4][2];
> +	u32				(*filter_freq_avail)[2];
> +	u8				num_filter_modes;
>  	u32				oversampling_ratio_avail[4];
>  	u8				gpocon;
>  	u8				clock_sel;
>  	struct mutex			lock;	/* protect sensor state */
>  	u8				syscalib_mode[8];
>  
> +	enum ad7192_filter_mode		filter_mode;
> +
>  	struct ad_sigma_delta		sd;
>  };
>  
> @@ -282,7 +317,200 @@ static const struct iio_enum ad7192_syscalib_mode_enum = {
>  	.get = ad7192_get_syscalib_mode
>  };
>  
> -static const struct iio_chan_spec_ext_info ad7192_calibsys_ext_info[] = {
> +#define AD7192_FILTER_CONFIG(_filter_mode, _f_order, _sinc3_en, _chop_en, _avg_val)	\
> +{									\
> +		.filter_mode = (_filter_mode),				\
> +		.f_order = (_f_order),					\
> +		.sinc3_en = (_sinc3_en),				\
> +		.chop_en = (_chop_en),					\
> +		.avg_val = (_avg_val),					\
> +		.samp_freq_avail_type = IIO_AVAIL_RANGE,		\
> +		.samp_freq_avail_len = 2,		\
> +		.samp_freq_avail = {					\
> +			{ AD7192_INT_FREQ_MHZ,				\
> +				(_f_order) * AD7192_FILTER_DIV * AD7192_FS_MAX },	\
> +			{ AD7192_INT_FREQ_MHZ,				\
> +				(_f_order) * AD7192_FILTER_DIV * AD7192_FS_MIN },	\

These ranges don't make sense to me. Shouldn't we be calculating it during probe
based on the actual clock rate?

> +		},							\
> +}
> +
> +static const struct ad7192_filter_config ad7192_filter_configs[] = {
> +	AD7192_FILTER_CONFIG(AD7192_FILTER_SINC4,		1, 0, 0, 1),
> +	AD7192_FILTER_CONFIG(AD7192_FILTER_SINC3,		1, 1, 0, 1),
> +	AD7192_FILTER_CONFIG(AD7192_FILTER_SINC4_CHOP,		4, 0, 1, 1),
> +	AD7192_FILTER_CONFIG(AD7192_FILTER_SINC3_CHOP,		3, 1, 1, 1),
> +};
> +
> +static const struct ad7192_filter_config ad7192_filter_configs_avg[] = {
> +	AD7192_FILTER_CONFIG(AD7192_FILTER_SINC4,		1, 0, 0, 1),
> +	AD7192_FILTER_CONFIG(AD7192_FILTER_SINC3,		1, 1, 0, 1),
> +	AD7192_FILTER_CONFIG(AD7192_FILTER_SINC4_CHOP,		4, 0, 1, 1),
> +	AD7192_FILTER_CONFIG(AD7192_FILTER_SINC3_CHOP,		3, 1, 1, 1),
> +
> +	AD7192_FILTER_CONFIG(AD7192_FILTER_SINC4_AVG2,		5, 0, 0, 2),
> +	AD7192_FILTER_CONFIG(AD7192_FILTER_SINC3_AVG2,		4, 1, 0, 2),
> +	AD7192_FILTER_CONFIG(AD7192_FILTER_SINC4_CHOP_AVG2,	5, 0, 1, 2),
> +	AD7192_FILTER_CONFIG(AD7192_FILTER_SINC3_CHOP_AVG2,	4, 1, 1, 2),
> +
> +	AD7192_FILTER_CONFIG(AD7192_FILTER_SINC4_AVG8,		11, 0, 0, 8),
> +	AD7192_FILTER_CONFIG(AD7192_FILTER_SINC3_AVG8,		10, 1, 0, 8),
> +	AD7192_FILTER_CONFIG(AD7192_FILTER_SINC4_CHOP_AVG8,	11, 0, 1, 8),
> +	AD7192_FILTER_CONFIG(AD7192_FILTER_SINC3_CHOP_AVG8,	10, 1, 1, 8),
> +
> +	AD7192_FILTER_CONFIG(AD7192_FILTER_SINC4_AVG16,		19, 0, 0, 16),
> +	AD7192_FILTER_CONFIG(AD7192_FILTER_SINC3_AVG16,		18, 1, 0, 16),
> +	AD7192_FILTER_CONFIG(AD7192_FILTER_SINC4_CHOP_AVG16,	19, 0, 1, 16),
> +	AD7192_FILTER_CONFIG(AD7192_FILTER_SINC3_CHOP_AVG16,	18, 1, 1, 16),
> +};
> +
> +static const char *const ad7192_filter_modes_str[] = {
> +	[AD7192_FILTER_SINC4] =			"sinc4",
> +	[AD7192_FILTER_SINC3] =			"sinc3",
> +	[AD7192_FILTER_SINC4_CHOP] =		"sinc4+chop",
> +	[AD7192_FILTER_SINC3_CHOP] =		"sinc3+chop",
> +	[AD7192_FILTER_SINC4_AVG2] =		"sinc4+avg2",
> +	[AD7192_FILTER_SINC3_AVG2] =		"sinc3+avg2",
> +	[AD7192_FILTER_SINC4_CHOP_AVG2] =	"sinc4+chop+avg2",
> +	[AD7192_FILTER_SINC3_CHOP_AVG2] =	"sinc3+chop+avg2",
> +	[AD7192_FILTER_SINC4_AVG8] =		"sinc4+avg8",
> +	[AD7192_FILTER_SINC3_AVG8] =		"sinc3+avg8",
> +	[AD7192_FILTER_SINC4_CHOP_AVG8] =	"sinc4+chop+avg8",
> +	[AD7192_FILTER_SINC3_CHOP_AVG8] =	"sinc3+chop+avg8",
> +	[AD7192_FILTER_SINC4_AVG16] =		"sinc4+avg16",
> +	[AD7192_FILTER_SINC3_AVG16] =		"sinc3+avg16",
> +	[AD7192_FILTER_SINC4_CHOP_AVG16] =	"sinc4+chop+avg16",
> +	[AD7192_FILTER_SINC3_CHOP_AVG16] =	"sinc3+chop+avg16",
> +};

We need to make these match the values already defined in the ABI docs as much
as we can.

I see in the datasheets that there is a REJ60 bit in the MODE register, so I
would expect to see "sinc3+rej60" in this list as well.

We already have "sinc3+sinc1" that is defined as 'Sinc3 + averaging by 8' so
"sinc3+avg8" would be redunant. And given that this driver already uses
the oversampling_ratio attribute to control the avg2/8/16, I'm wondering if we
can keep that instead of introducing more filter types.

I also wonder if "sinc3+pf1" could be used for "sinc3+chop" since it is defined
as a device-specific post filter. Or make the case that "chop" is common enough
that it deseres it's own name.

I'm not the best expert on filters though, so I'm sure Jonathan will have some
better wisdom to share here.

Here is the existing list. If we have any filter types that don't fit into the
existing ones, we will need to have a separate patch to add those to the
documentation too.


What:		/sys/bus/iio/devices/iio:deviceX/filter_type_available
What:		/sys/bus/iio/devices/iio:deviceX/in_voltage-voltage_filter_type_available
KernelVersion:	6.1
Contact:	linux-iio@...r.kernel.org
Description:
		Reading returns a list with the possible filter modes. Options
		for the attribute:

		* "sinc3" - The digital sinc3 filter. Moderate 1st
		  conversion time. Good noise performance.
		* "sinc4" - Sinc 4. Excellent noise performance. Long
		  1st conversion time.
		* "sinc5" - The digital sinc5 filter. Excellent noise
		  performance
		* "sinc4+sinc1" - Sinc4 + averaging by 8. Low 1st conversion
		  time.
		* "sinc3+rej60" - Sinc3 + 60Hz rejection.
		* "sinc3+sinc1" - Sinc3 + averaging by 8. Low 1st conversion
		  time.
		* "sinc3+pf1" - Sinc3 + device specific Post Filter 1.
		* "sinc3+pf2" - Sinc3 + device specific Post Filter 2.
		* "sinc3+pf3" - Sinc3 + device specific Post Filter 3.
		* "sinc3+pf4" - Sinc3 + device specific Post Filter 4.
		* "wideband" - filter with wideband low ripple passband
		  and sharp transition band.



> +static int ad7192_set_filter_mode(struct iio_dev *indio_dev,
> +				  const struct iio_chan_spec *chan,
> +				  unsigned int val)
> +{
> +	struct ad7192_state *st = iio_priv(indio_dev);
> +	const struct ad7192_filter_config *filter_config = &st->chip_info->filter_configs[val];

Seems dangerous to access an array without checking that val is in range first,
especially since it comes from userspace.

> +	u16 old_samp_freq, div;
> +	int i, ret;

Probably should have iio_device_claim_direct() here to make sure we can't change
the filter mode in the middle of a buffer read.

> +
> +	old_samp_freq = ad7192_get_f_adc(st);
> +
> +	st->filter_mode = val;
> +
> +	div = DIV_ROUND_CLOSEST(st->fclk, ad7192_get_f_order(st) * old_samp_freq);
> +	if (div < AD7192_FS_MIN || div > AD7192_FS_MAX)
> +		return -EINVAL;
> +
> +	st->mode &= ~AD7192_MODE_RATE_MASK;
> +	st->mode |= FIELD_PREP(AD7192_MODE_RATE_MASK, div);
> +
> +	st->mode &= ~AD7192_MODE_SINC3;
> +	st->mode |= FIELD_PREP(AD7192_MODE_SINC3, filter_config->sinc3_en);
> +
> +	st->conf &= ~AD7192_CONF_CHOP;
> +	st->conf |= FIELD_PREP(AD7192_CONF_CHOP, filter_config->chop_en);
> +
> +	for (i = 0; i < ARRAY_SIZE(st->oversampling_ratio_avail); i++) {
> +		if (filter_config->avg_val != st->oversampling_ratio_avail[i])
> +			continue;
> +
> +		st->mode &= ~AD7192_MODE_AVG_MASK;
> +		st->mode |= FIELD_PREP(AD7192_MODE_AVG_MASK, i);
> +	}
> +
> +	ret = ad_sd_write_reg(&st->sd, AD7192_REG_MODE, 3, st->mode);
> +	if (ret < 0)
> +		return ret;
> +
> +	ret = ad_sd_write_reg(&st->sd, AD7192_REG_CONF, 3, st->conf);
> +	if (ret < 0)
> +		return ret;
> +
> +	ad7192_update_filter_freq_avail(st);
> +
> +	return 0;
> +}
> +
> +static int ad7192_get_filter_mode(struct iio_dev *indio_dev,
> +				  const struct iio_chan_spec *chan)
> +{
> +	struct ad7192_state *st = iio_priv(indio_dev);
> +
> +	return st->filter_mode;
> +}
> +
> +static const struct iio_enum ad7192_filter_mode_enum = {
> +	.items = ad7192_filter_modes_str,
> +	.num_items = ARRAY_SIZE(ad7192_filter_modes_str),
> +	.set = ad7192_set_filter_mode,
> +	.get = ad7192_get_filter_mode,
> +};
> +
> +static const struct iio_chan_spec_ext_info ad7192_ext_info[] = {
>  	{
>  		.name = "sys_calibration",
>  		.write = ad7192_write_syscalib,
> @@ -292,6 +520,9 @@ static const struct iio_chan_spec_ext_info ad7192_calibsys_ext_info[] = {
>  		 &ad7192_syscalib_mode_enum),
>  	IIO_ENUM_AVAILABLE("sys_calibration_mode", IIO_SHARED_BY_TYPE,
>  			   &ad7192_syscalib_mode_enum),
> +	IIO_ENUM("filter_mode", IIO_SHARED_BY_ALL, &ad7192_filter_mode_enum),
> +	IIO_ENUM_AVAILABLE("filter_mode", IIO_SHARED_BY_ALL,
> +			   &ad7192_filter_mode_enum),

As in the ABI docs above, this needs to be "filter_type", not "filter_mode". It
would make sense to rename the functions and variables as well. (We recently
standardized this across all existing drivers.)

>  	{ }
>  };

Since some chips don't support averaging, we will need two different ext_info
now so that filter_type_available is correct for those chips.

>  
> @@ -650,15 +881,22 @@ static int ad7192_setup(struct iio_dev *indio_dev, struct device *dev)
>  	st->oversampling_ratio_avail[2] = 8;
>  	st->oversampling_ratio_avail[3] = 16;
>  
> -	st->filter_freq_avail[0][0] = 600;
> -	st->filter_freq_avail[1][0] = 800;
> -	st->filter_freq_avail[2][0] = 2300;
> -	st->filter_freq_avail[3][0] = 2720;
> +	return 0;
> +}
>  
> -	st->filter_freq_avail[0][1] = 1000;
> -	st->filter_freq_avail[1][1] = 1000;
> -	st->filter_freq_avail[2][1] = 1000;
> -	st->filter_freq_avail[3][1] = 1000;
> +static int ad7192_filter_setup(struct ad7192_state *st)
> +{
> +	unsigned int num_modes = st->chip_info->num_filter_modes;
> +
> +	st->filter_freq_avail = devm_kcalloc(&st->sd.spi->dev, num_modes,
> +					     sizeof(*st->filter_freq_avail),
> +					     GFP_KERNEL);
> +	if (!st->filter_freq_avail)
> +		return -ENOMEM;
> +
> +	st->num_filter_modes = num_modes;
> +
> +	ad7192_update_filter_freq_avail(st);

As mentioned elsewhere, I would just keep the existing implementation for the
3db_frequency_available attribute and add some comments that it is for backwards
compatibility only.



> @@ -936,7 +1050,7 @@ static int ad7192_read_raw(struct iio_dev *indio_dev,
>  			return -EINVAL;
>  		}
>  	case IIO_CHAN_INFO_SAMP_FREQ:
> -		*val = DIV_ROUND_CLOSEST(ad7192_get_f_adc(st), 1024);
> +		*val = DIV_ROUND_CLOSEST(ad7192_get_f_adc(st), AD7192_FILTER_DIV);
>  		return IIO_VAL_INT;
>  	case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY:
>  		*val = ad7192_get_3db_filter_freq(st);
> @@ -957,7 +1071,7 @@ static int __ad7192_write_raw(struct iio_dev *indio_dev,
>  			      long mask)
>  {
>  	struct ad7192_state *st = iio_priv(indio_dev);
> -	int i, div;
> +	int i, div, ret;
>  	unsigned int tmp;
>  
>  	guard(mutex)(&st->lock);
> @@ -982,32 +1096,20 @@ static int __ad7192_write_raw(struct iio_dev *indio_dev,
>  		if (!val)
>  			return -EINVAL;
>  
> -		div = st->fclk / (val * ad7192_get_f_order(st) * 1024);
> -		if (div < 1 || div > 1023)
> +		div = st->fclk / (val * ad7192_get_f_order(st) * AD7192_FILTER_DIV);
> +		if (div < AD7192_FS_MIN || div > AD7192_FS_MAX)
>  			return -EINVAL;
>  
>  		st->mode &= ~AD7192_MODE_RATE_MASK;
>  		st->mode |= FIELD_PREP(AD7192_MODE_RATE_MASK, div);
> -		ad_sd_write_reg(&st->sd, AD7192_REG_MODE, 3, st->mode);
> +
> +		ret = ad_sd_write_reg(&st->sd, AD7192_REG_MODE, 3, st->mode);
> +		if (ret)
> +			return ret;

This looks like an unrelated fixup, so please put that in a separate patch.
Actually 2 patches, one for adding the macros and one for checking the return
value.


Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ