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: <CAHp75VdqbhwZTjA2TQPFeoAHQv1NCO949kdxYQbUSZmjm3kP6g@mail.gmail.com>
Date: Wed, 13 Aug 2025 14:16:01 +0200
From: Andy Shevchenko <andy.shevchenko@...il.com>
To: Jonathan Santos <Jonathan.Santos@...log.com>
Cc: linux-iio@...r.kernel.org, devicetree@...r.kernel.org, 
	linux-kernel@...r.kernel.org, Michael.Hennerich@...log.com, jic23@...nel.org, 
	dlechner@...libre.com, nuno.sa@...log.com, andy@...nel.org, robh@...nel.org, 
	krzk+dt@...nel.org, conor+dt@...nel.org, jonath4nns@...il.com
Subject: Re: [PATCH 4/4] iio: adc: ad7768-1: add support for ADAQ776x-1 ADC Family

On Wed, Aug 13, 2025 at 4:49 AM Jonathan Santos
<Jonathan.Santos@...log.com> wrote:
>
> Add support for ADAQ7767/68/69-1 series, which includes PGIA and
> Anti-aliasing filter (AAF) gains.
>
> The PGA gain is configured in run-time through the scale attribute,
> if supported by the device. The scale options are updated according
> to the output data width.
>
> The AAF gain is configured in the devicetree and it should correspond to
> the AAF channel selected in hardware.
> +#define AD7768_GPIO_READ(x)            FIELD_PREP(AD7768_GPIO_READ_MSK, x)
>
>  #define AD7768_VCM_OFF                 0x07
>  #define AD7768_CHAN_INFO_NONE          0
>
> +#define ADAQ776X_GAIN_MAX_NANO         (128 * NANO)
> +#define ADAQ776X_MAX_GAIN_MODES                8
> +
>  #define AD7768_TRIGGER_SOURCE_SYNC_IDX 0
>
>  #define AD7768_MAX_CHANNELS 1
> @@ -154,6 +161,52 @@ enum ad7768_scan_type {
>         AD7768_SCAN_TYPE_HIGH_SPEED,
>  };
>
> +enum {
> +       AD7768_PGA_GAIN_0,
> +       AD7768_PGA_GAIN_1,
> +       AD7768_PGA_GAIN_2,
> +       AD7768_PGA_GAIN_3,
> +       AD7768_PGA_GAIN_4,
> +       AD7768_PGA_GAIN_5,
> +       AD7768_PGA_GAIN_6,
> +       AD7768_PGA_GAIN_7,
> +       AD7768_MAX_PGA_GAIN,
> +};
> +
> +enum {
> +       AD7768_AAF_IN1,
> +       AD7768_AAF_IN2,
> +       AD7768_AAF_IN3,
> +};
> +
> +/* PGA and AAF gains in V/V */
> +static const int adaq7768_gains[7] = {
> +       [AD7768_PGA_GAIN_0] = 325,      /* 0.325 */
> +       [AD7768_PGA_GAIN_1] = 650,      /* 0.650 */
> +       [AD7768_PGA_GAIN_2] = 1300,     /* 1.300 */
> +       [AD7768_PGA_GAIN_3] = 2600,     /* 2.600 */
> +       [AD7768_PGA_GAIN_4] = 5200,     /* 5.200 */
> +       [AD7768_PGA_GAIN_5] = 10400,    /* 10.400 */
> +       [AD7768_PGA_GAIN_6] = 20800     /* 20.800 */
> +};
> +
> +static const int adaq7769_gains[8] = {
> +       [AD7768_PGA_GAIN_0] = 1000,     /* 1.000 */
> +       [AD7768_PGA_GAIN_1] = 2000,     /* 2.000 */
> +       [AD7768_PGA_GAIN_2] = 4000,     /* 4.000 */
> +       [AD7768_PGA_GAIN_3] = 8000,     /* 8.000 */
> +       [AD7768_PGA_GAIN_4] = 16000,    /* 16.000 */
> +       [AD7768_PGA_GAIN_5] = 32000,    /* 32.000 */
> +       [AD7768_PGA_GAIN_6] = 64000,    /* 64.000 */
> +       [AD7768_PGA_GAIN_7] = 128000    /* 128.000 */
> +};
> +
> +static const int ad7768_aaf_gains[3] = {
> +       [AD7768_AAF_IN1] = 1000,        /* 1.000 */
> +       [AD7768_AAF_IN2] = 364,         /* 0.364 */
> +       [AD7768_AAF_IN3] = 143          /* 0.143 */
> +};
> +
>  /* -3dB cutoff frequency multipliers (relative to ODR) for each filter type. */
>  static const int ad7768_filter_3db_odr_multiplier[] = {
>         [AD7768_FILTER_SINC5] = 204,            /* 0.204 */
> @@ -216,6 +269,12 @@ static const struct iio_scan_type ad7768_scan_type[] = {
>
>  struct ad7768_chip_info {
>         const char *name;
> +       bool has_variable_aaf;
> +       bool has_pga;
> +       int num_pga_modes;
> +       int default_pga_mode;
> +       int pgia_mode2pin_offset;
> +       const int *pga_gains;
>         const struct iio_chan_spec *channel_spec;
>         const unsigned long *available_masks;
>         int num_channels;
> @@ -236,6 +295,9 @@ struct ad7768_state {
>         unsigned int samp_freq;
>         unsigned int samp_freq_avail[ARRAY_SIZE(ad7768_mclk_div_rates)];
>         unsigned int samp_freq_avail_len;
> +       int pga_gain_mode;
> +       int aaf_gain;
> +       int scale_tbl[ADAQ776X_MAX_GAIN_MODES][2];
>         struct completion completion;
>         struct iio_trigger *trig;
>         struct gpio_desc *gpio_sync_in;
> @@ -466,6 +528,43 @@ static int ad7768_reg_access(struct iio_dev *indio_dev,
>         return ret;
>  }
>
> +static void ad7768_fill_scale_tbl(struct iio_dev *dev)
> +{
> +       struct ad7768_state *st = iio_priv(dev);
> +       const struct iio_scan_type *scan_type;
> +       int val, val2, tmp0, tmp1, i;
> +       unsigned long denominator, numerator;
> +       u64 tmp2;
> +
> +       scan_type = iio_get_current_scan_type(dev, &dev->channels[0]);
> +       if (scan_type->sign == 's')
> +               val2 = scan_type->realbits - 1;
> +       else
> +               val2 = scan_type->realbits;
> +
> +       for (i = 0; i < st->chip->num_pga_modes; i++) {
> +               /* Convert gain to a fraction format */
> +               numerator = st->chip->pga_gains[i];
> +               denominator = MILLI;
> +               if (st->chip->has_variable_aaf) {
> +                       numerator *= ad7768_aaf_gains[st->aaf_gain];
> +                       denominator *= MILLI;
> +               }
> +
> +               rational_best_approximation(numerator, denominator, __INT_MAX__, __INT_MAX__,
> +                                           &numerator, &denominator);
> +
> +               val = st->vref_uv / 1000;
> +               /* Multiply by MILLI here to avoid losing precision */
> +               val = mult_frac(val, denominator * MILLI, numerator);
> +               /* Would multiply by NANO here but we already multiplied by MILLI */
> +               tmp2 = shift_right((u64)val * MICRO, val2);
> +               tmp0 = (int)div_s64_rem(tmp2, NANO, &tmp1);
> +               st->scale_tbl[i][0] = tmp0; /* Integer part */
> +               st->scale_tbl[i][1] = abs(tmp1); /* Fractional part */
> +       }
> +}
> +
>  static int ad7768_set_sinc3_dec_rate(struct ad7768_state *st,
>                                      unsigned int dec_rate)
>  {
> @@ -567,12 +666,68 @@ static int ad7768_configure_dig_fil(struct iio_dev *dev,
>                 st->oversampling_ratio = ad7768_dec_rate_values[dec_rate_idx];
>         }
>
> +       /* Update scale table: scale values vary according to the precision */
> +       ad7768_fill_scale_tbl(dev);
> +
>         ad7768_fill_samp_freq_tbl(st);
>
>         /* A sync-in pulse is required after every configuration change */
>         return ad7768_send_sync_pulse(st);
>  }
>
> +static int ad7768_calc_pga_gain(struct ad7768_state *st, int gain_int,
> +                               int gain_fract, int precision)
> +{
> +       u64 gain_nano, tmp;
> +       int gain_idx;
> +
> +       precision--;
> +       gain_nano = gain_int * NANO + gain_fract;
> +       if (gain_nano < 0 || gain_nano > ADAQ776X_GAIN_MAX_NANO)
> +               return -EINVAL;
> +
> +       tmp = DIV_ROUND_CLOSEST_ULL(gain_nano << precision, NANO);
> +       gain_nano = DIV_ROUND_CLOSEST_ULL(st->vref_uv, tmp);
> +       if (st->chip->has_variable_aaf)
> +               /* remove the AAF gain from the overall gain */
> +               gain_nano = DIV_ROUND_CLOSEST_ULL(gain_nano *  MILLI,
> +                                                 ad7768_aaf_gains[st->aaf_gain]);
> +       tmp = st->chip->num_pga_modes;
> +       gain_idx = find_closest(gain_nano, st->chip->pga_gains, tmp);
> +
> +       return gain_idx;
> +}
> +
> +static int ad7768_set_pga_gain(struct ad7768_state *st,
> +                              int gain_mode)
> +{
> +       int pgia_pins_value = abs(gain_mode - st->chip->pgia_mode2pin_offset);
> +       int check_val;
> +       int ret;
> +
> +       /* Check GPIO control register */
> +       ret = regmap_read(st->regmap, AD7768_REG_GPIO_CONTROL, &check_val);
> +       if (ret < 0)
> +               return ret;
> +
> +       if ((check_val & AD7768_GPIO_PGIA_EN) != AD7768_GPIO_PGIA_EN) {
> +               /* Enable PGIA GPIOs and set them as output */
> +               ret = regmap_write(st->regmap, AD7768_REG_GPIO_CONTROL, AD7768_GPIO_PGIA_EN);
> +               if (ret < 0)
> +                       return ret;
> +       }
> +
> +       /* Write the respective gain values to GPIOs 0, 1, 2 */
> +       ret = regmap_write(st->regmap, AD7768_REG_GPIO_WRITE,
> +                          AD7768_GPIO_WRITE(pgia_pins_value));
> +       if (ret < 0)
> +               return ret;
> +
> +       st->pga_gain_mode = gain_mode;
> +
> +       return 0;
> +}
> +
>  static int ad7768_gpio_direction_input(struct gpio_chip *chip, unsigned int offset)
>  {
>         struct iio_dev *indio_dev = gpiochip_get_data(chip);
> @@ -782,13 +937,17 @@ static const struct iio_chan_spec ad7768_channels[] = {
>         AD7768_CHAN(0, AD7768_CHAN_INFO_NONE),
>  };
>
> +static const struct iio_chan_spec adaq776x_channels[] = {
> +       AD7768_CHAN(0, BIT(IIO_CHAN_INFO_SCALE)),
> +};
> +
>  static int ad7768_read_raw(struct iio_dev *indio_dev,
>                            struct iio_chan_spec const *chan,
>                            int *val, int *val2, long info)
>  {
>         struct ad7768_state *st = iio_priv(indio_dev);
>         const struct iio_scan_type *scan_type;
> -       int scale_uv, ret, temp;
> +       int ret, temp;
>
>         scan_type = iio_get_current_scan_type(indio_dev, chan);
>         if (IS_ERR(scan_type))
> @@ -809,12 +968,19 @@ static int ad7768_read_raw(struct iio_dev *indio_dev,
>                 return IIO_VAL_INT;
>
>         case IIO_CHAN_INFO_SCALE:
> -               scale_uv = st->vref_uv;
> -               if (scale_uv < 0)
> -                       return scale_uv;
> -
> -               *val = (scale_uv * 2) / 1000;
> -               *val2 = scan_type->realbits;
> +               if (st->chip->has_pga) {
> +                       *val = st->scale_tbl[st->pga_gain_mode][0];
> +                       *val2 = st->scale_tbl[st->pga_gain_mode][1];
> +                       return IIO_VAL_INT_PLUS_NANO;
> +               }
> +               *val = st->vref_uv / 1000;
> +               if (st->chip->has_variable_aaf)
> +                       *val = (*val * MILLI) / ad7768_aaf_gains[st->aaf_gain];
> +               /*
> +                * ADC output code is two's complement so only (realbits - 1)
> +                * bits express voltage magnitude.
> +                */
> +               *val2 = scan_type->realbits - 1;
>
>                 return IIO_VAL_FRACTIONAL_LOG2;
>
> @@ -869,18 +1035,42 @@ static int ad7768_read_avail(struct iio_dev *indio_dev,
>                 *length = st->samp_freq_avail_len;
>                 *type = IIO_VAL_INT;
>                 return IIO_AVAIL_LIST;
> +       case IIO_CHAN_INFO_SCALE:
> +               *vals = (int *)st->scale_tbl;
> +               *length = st->chip->num_pga_modes * 2;
> +               *type = IIO_VAL_INT_PLUS_NANO;
> +               return IIO_AVAIL_LIST;
>         default:
>                 return -EINVAL;
>         }
>  }
>
> +static int ad7768_write_raw_get_fmt(struct iio_dev *indio_dev,
> +                                   struct iio_chan_spec const *chan, long mask)
> +{
> +       switch (mask) {
> +       case IIO_CHAN_INFO_SCALE:
> +               return IIO_VAL_INT_PLUS_NANO;
> +       default:
> +               return IIO_VAL_INT_PLUS_MICRO;
> +       }
> +
> +       return -EINVAL;
> +}
> +
>  static int __ad7768_write_raw(struct iio_dev *indio_dev,
>                               struct iio_chan_spec const *chan,
>                               int val, int val2, long info)
>  {
>         struct ad7768_state *st = iio_priv(indio_dev);
> +       const struct iio_scan_type *scan_type;
> +       int gain_mode;
>         int ret;
>
> +       scan_type = iio_get_current_scan_type(indio_dev, chan);
> +       if (IS_ERR(scan_type))
> +               return PTR_ERR(scan_type);
> +
>         switch (info) {
>         case IIO_CHAN_INFO_SAMP_FREQ:
>                 return ad7768_set_freq(st, val);
> @@ -892,6 +1082,13 @@ static int __ad7768_write_raw(struct iio_dev *indio_dev,
>
>                 /* Update sampling frequency */
>                 return ad7768_set_freq(st, st->samp_freq);
> +       case IIO_CHAN_INFO_SCALE:
> +               if (!st->chip->has_pga)
> +                       return -EOPNOTSUPP;
> +
> +               gain_mode = ad7768_calc_pga_gain(st, val, val2,
> +                                                scan_type->realbits);
> +               return ad7768_set_pga_gain(st, gain_mode);
>         default:
>                 return -EINVAL;
>         }
> @@ -933,6 +1130,7 @@ static const struct iio_info ad7768_info = {
>         .read_raw = &ad7768_read_raw,
>         .read_avail = &ad7768_read_avail,
>         .write_raw = &ad7768_write_raw,
> +       .write_raw_get_fmt = &ad7768_write_raw_get_fmt,
>         .read_label = ad7768_read_label,
>         .get_current_scan_type = &ad7768_get_current_scan_type,
>         .debugfs_reg_access = &ad7768_reg_access,
> @@ -1351,10 +1549,46 @@ static const struct ad7768_chip_info ad7768_chip_info = {
>         .available_masks = ad7768_channel_masks,
>  };
>
> +static const struct ad7768_chip_info adaq7767_chip_info = {
> +       .name = "adaq7767-1",
> +       .channel_spec = ad7768_channels,
> +       .num_channels = ARRAY_SIZE(ad7768_channels),
> +       .available_masks = ad7768_channel_masks,
> +       .has_pga = false,
> +       .has_variable_aaf = true
> +};
> +
> +static const struct ad7768_chip_info adaq7768_chip_info = {
> +       .name = "adaq7768-1",
> +       .channel_spec = adaq776x_channels,
> +       .num_channels = ARRAY_SIZE(adaq776x_channels),
> +       .available_masks = ad7768_channel_masks,
> +       .pga_gains = adaq7768_gains,
> +       .default_pga_mode = AD7768_PGA_GAIN_2,
> +       .num_pga_modes = ARRAY_SIZE(adaq7768_gains),
> +       .pgia_mode2pin_offset = 6,
> +       .has_pga = true,
> +       .has_variable_aaf = false
> +};
> +
> +static const struct ad7768_chip_info adaq7769_chip_info = {
> +       .name = "adaq7769-1",
> +       .channel_spec = adaq776x_channels,
> +       .num_channels = ARRAY_SIZE(adaq776x_channels),
> +       .available_masks = ad7768_channel_masks,
> +       .pga_gains = adaq7769_gains,
> +       .default_pga_mode = AD7768_PGA_GAIN_0,
> +       .num_pga_modes = ARRAY_SIZE(adaq7769_gains),
> +       .pgia_mode2pin_offset = 0,
> +       .has_pga = true,
> +       .has_variable_aaf = true
> +};
> +
>  static int ad7768_probe(struct spi_device *spi)
>  {
>         struct ad7768_state *st;
>         struct iio_dev *indio_dev;
> +       u32 val;
>         int ret;
>

...

> +       st->aaf_gain = AD7768_AAF_IN1;
> +       ret = device_property_read_u32(&spi->dev, "adi,aaf-gain", &val);
> +       if (ret) {
> +               /* AAF gain required, but not specified */
> +               if (st->chip->has_variable_aaf)
> +                       return dev_err_probe(&spi->dev, -EINVAL, "AAF gain not specified\n");

> +

Stray blank line.

> +       } else if (!st->chip->has_variable_aaf) {


> +               /* AAF gain provided, but not supported */
> +               return dev_err_probe(&spi->dev, -EINVAL, "AAF gain not supported for %s\n",
> +                                    st->chip->name);

I would rewrite these conditionals as

  if (ret && ->has_variable_aaf)
    return dev_err_probe(...);
  if (!ret && ! ->has_variable_aaf)
    return dev_err_probe(...);

> +       } else {

It will be redundant 'else' after the above being applied.

> +               /* Device supports variable AAF gain, validate and set the gain */
> +               switch (val) {
> +               case 1000:
> +                       st->aaf_gain = AD7768_AAF_IN1;
> +                       break;
> +               case 364:
> +                       st->aaf_gain = AD7768_AAF_IN2;
> +                       break;
> +               case 143:
> +                       st->aaf_gain = AD7768_AAF_IN3;
> +                       break;
> +               default:
> +                       return dev_err_probe(&spi->dev, -EINVAL,
> +                                            "Invalid firmware provided gain\n");
> +               }
> +       }


-- 
With Best Regards,
Andy Shevchenko

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ