Re: [PATCH v5 3/3] drivers: iio: adc: add support for ad777x family

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Thu, Sep 12, 2024 at 3:17 PM Ramona Alexandra Nechita
<ramona.nechita@xxxxxxxxxx> wrote:
>
> Add support for AD7770, AD7771, AD7779 ADCs. The device is capable of

"..., and AD7779..."

> sending out data both on DOUT lines interface,as on the SDO line.
> The driver currently implements only the SDO data streaming mode. SPI
> communication is used alternatively for accessing registers and streaming
> data. Register access are protected by crc8.

accesses

...

> +/*
> + * AD7770, AD7771, AD7779 ADC

"..., and AD7779..."

> + *
> + * Copyright 2023-2024 Analog Devices Inc.
> + */

...

> +#define AD7779_MAXCLK_LOWPOWER                 4096000

Units? _HZ? _uV?

...

> +#define GAIN_REL                               0x555555

Is it something like making value for 12 channels? Can you elaborate a
bit (perhaps in the comment)?

...

> +struct ad7779_state {
> +       struct spi_device *spi;
> +       const struct ad7779_chip_info *chip_info;
> +       struct clk *mclk;
> +       struct iio_trigger *trig;
> +       struct completion completion;
> +       unsigned int sampling_freq;
> +       enum ad7779_filter filter_enabled;
> +       /*
> +        * DMA (thus cache coherency maintenance) requires the
> +        * transfer buffers to live in their own cache lines.
> +        */
> +       u8                      reg_rx_buf[3] __aligned(IIO_DMA_MINALIGN);
> +       u8                      reg_tx_buf[3];
> +       u32                     spidata_rx[8];
> +       u32                     spidata_tx[8];
> +       u8                      reset_buf[8];
> +};

Have you run `pahole` to check if this can be optimised in size?

...

> +static int ad7779_spi_read(struct ad7779_state *st, u8 reg, u8 *rbuf)
> +{
> +       int ret;
> +       int length = 3;
> +       u8 crc_buf[2];
> +       u8 exp_crc = 0;
> +       struct spi_transfer reg_read_tr[] = {
> +               {
> +                       .tx_buf = st->reg_tx_buf,
> +                       .rx_buf = st->reg_rx_buf,
> +               },
> +       };
> +
> +       if (reg == AD7779_REG_GEN_ERR_REG_1_EN)
> +               length = 2;

Does it mean the crc byte will be ignored? If so, why do we even
bother to spend resources on calculating it in that case? Same Q for
other similar cases.

> +       reg_read_tr[0].len = length;
> +
> +       st->reg_tx_buf[0] = AD7779_SPI_READ_CMD | FIELD_GET(AD7779_REG_MSK, reg);
> +       st->reg_tx_buf[1] = 0;
> +       st->reg_tx_buf[2] = crc8(ad7779_crc8_table, st->reg_tx_buf, 2, 0);
> +
> +       ret = spi_sync_transfer(st->spi, reg_read_tr, ARRAY_SIZE(reg_read_tr));
> +       if (ret)
> +               return ret;
> +
> +       crc_buf[0] = AD7779_SPI_READ_CMD | FIELD_GET(AD7779_REG_MSK, reg);
> +       crc_buf[1] = st->reg_rx_buf[1];
> +       exp_crc = crc8(ad7779_crc8_table, crc_buf, 2, 0);
> +       if (reg != AD7779_REG_GEN_ERR_REG_1_EN && exp_crc != st->reg_rx_buf[2]) {
> +               dev_err(&st->spi->dev, "Bad CRC %x, expected %x",
> +                       st->reg_rx_buf[2], exp_crc);
> +               return -EINVAL;
> +       }
> +       *rbuf = st->reg_rx_buf[1];
> +
> +       return 0;
> +}

...

> +       regval = data;
> +       regval &= ~mask;
> +       regval |= val;

Traditional pattern is to have this as

    regval = (data & ~mask) | (val & mask);

...

> +static int ad7779_set_sampling_frequency(struct ad7779_state *st,
> +                                        unsigned int sampling_freq)
> +{
> +       int ret;
> +       unsigned int dec;
> +       unsigned int div;
> +       unsigned int decimal;
> +       int temp;
> +       unsigned int kfreq;

freq_khz will be better name

> +       if (st->filter_enabled == AD7779_SINC3 &&
> +           sampling_freq > AD7779_SINC3_MAXFREQ)
> +               return -EINVAL;
> +
> +       if (st->filter_enabled == AD7779_SINC5 &&
> +           sampling_freq > AD7779_SINC5_MAXFREQ)
> +               return -EINVAL;
> +
> +       if (sampling_freq > AD7779_SPIMODE_MAX_SAMP_FREQ)
> +               return -EINVAL;
> +
> +       div = AD7779_HIGHPOWER_DIV;
> +
> +       kfreq = sampling_freq / KILO;
> +       dec = div / kfreq;
> +
> +       ret = ad7779_spi_write(st, AD7779_REG_SRC_N_MSB,
> +                              FIELD_GET(AD7779_FREQ_MSB_MSK, dec));
> +       if (ret)
> +               return ret;
> +       ret = ad7779_spi_write(st, AD7779_REG_SRC_N_LSB,
> +                              FIELD_GET(AD7779_FREQ_LSB_MSK, dec));
> +       if (ret)
> +               return ret;

> +       if (div % kfreq) {

It's better to combine with / above, because some ISAs may have a
single assembly instruction to do both at the same time. I'm not sure
compiler (os some versions of it) may see this.

  dec = div / freq_khz;
  frac = div % freq_khz;
  ...
  if (frac) {
      ...
  }

> +               temp = (div * KILO) / kfreq;
> +               decimal = ((temp -  dec * KILO) << 16) / KILO;

I would add a small comment to show the formula behind this.

> +               ret = ad7779_spi_write(st, AD7779_REG_SRC_N_MSB,
> +                                      FIELD_GET(AD7779_FREQ_MSB_MSK, decimal));
> +               if (ret)
> +                       return ret;
> +               ret = ad7779_spi_write(st, AD7779_REG_SRC_N_LSB,
> +                                      FIELD_GET(AD7779_FREQ_LSB_MSK, decimal));
> +               if (ret)
> +                       return ret;
> +       } else {
> +               ret = ad7779_spi_write(st, AD7779_REG_SRC_N_MSB,
> +                                      FIELD_GET(AD7779_FREQ_MSB_MSK, 0x0));
> +               if (ret)
> +                       return ret;
> +               ret = ad7779_spi_write(st, AD7779_REG_SRC_N_LSB,
> +                                      FIELD_GET(AD7779_FREQ_LSB_MSK, 0x0));
> +               if (ret)
> +                       return ret;
> +       }
> +       ret = ad7779_spi_write(st, AD7779_REG_SRC_UPDATE, 0x1);
> +       if (ret)
> +               return ret;
> +
> +       /* SRC update settling time */
> +       fsleep(15);

+ Blank line, otherwise it's unclear how fsleep() is applied (or to
which sections).

> +       ret = ad7779_spi_write(st, AD7779_REG_SRC_UPDATE, 0x0);
> +       if (ret)
> +               return ret;
> +
> +       /* SRC update settling time */
> +       fsleep(15);

Alternatively make a helper to avoid potential desynchronisation in
the code pieces

statuc int ad7779_update_src(..., val)
{
    ...
    /* ... */
    fsleep(...);

    return 0;
}

> +       st->sampling_freq = sampling_freq;
> +
> +       return 0;
> +}

...

> +static int ad7779_set_calibscale(struct ad7779_state *st, int channel, int val)
> +{
> +       int ret;
> +       unsigned int gain;
> +       unsigned long long tmp;
> +       u8 gain_bytes[3];
> +
> +       tmp = val * 5592405LL;
> +       gain = DIV_ROUND_CLOSEST_ULL(tmp, MEGA);

Formula to be explained?
Magic number to be described (in the same comment), please?

> +       put_unaligned_be24(gain, gain_bytes);
> +       ret = ad7779_spi_write(st,
> +                              AD7779_REG_CH_GAIN_UPPER_BYTE(channel),
> +                              gain_bytes[0]);
> +       if (ret)
> +               return ret;
> +
> +       ret = ad7779_spi_write(st,
> +                              AD7779_REG_CH_GAIN_MID_BYTE(channel),
> +                              gain_bytes[1]);
> +       if (ret)
> +               return ret;
> +
> +       return ad7779_spi_write(st,
> +                               AD7779_REG_CH_GAIN_LOWER_BYTE(channel),
> +                               gain_bytes[2]);
> +}

...

> +       iio_device_claim_direct_scoped(return -EBUSY, indio_dev) {
> +               switch (mask) {
> +               case IIO_CHAN_INFO_CALIBSCALE:
> +                       *val = ad7779_get_calibscale(st, chan->channel);
> +                       if (*val < 0)
> +                               return -EINVAL;
> +                       *val2 = GAIN_REL;
> +                       return IIO_VAL_FRACTIONAL;
> +               case IIO_CHAN_INFO_CALIBBIAS:
> +                       *val = ad7779_get_calibbias(st, chan->channel);
> +                       if (*val < 0)
> +                               return -EINVAL;
> +                       return IIO_VAL_INT;
> +               case IIO_CHAN_INFO_SAMP_FREQ:
> +                       *val = st->sampling_freq;
> +                       if (*val < 0)
> +                               return -EINVAL;
> +                       return IIO_VAL_INT;
> +               }
> +               return -EINVAL;
> +       }

> +       unreachable();

Hmm... Is it necessary? Same Q for other similar cases. I.o.w. what
will be if we don't add this line?

> +}

...

> +static irqreturn_t ad7779_trigger_handler(int irq, void *p)
> +{
> +       struct iio_poll_func *pf = p;
> +       struct iio_dev *indio_dev = pf->indio_dev;
> +       struct ad7779_state *st = iio_priv(indio_dev);
> +       int ret;
> +       int bit;
> +       int k = 0;
> +       /*
> +        * Each channel shifts out HEADER + 24 bits of data therefore 8 * u32
> +        * for the data and 64 bits for the timestamp
> +        */

May you do the respective structure and use aligned_s64 for the timestamp?

> +       u32 tmp[10];
> +
> +       struct spi_transfer sd_readback_tr[] = {
> +               {
> +                       .rx_buf = st->spidata_rx,
> +                       .tx_buf = st->spidata_tx,
> +                       .len = AD7779_NUM_CHANNELS * AD7779_CHAN_DATA_SIZE,
> +               }
> +       };
> +
> +       if (!iio_buffer_enabled(indio_dev))
> +               goto exit_handler;
> +
> +       st->spidata_tx[0] = AD7779_SPI_READ_CMD;
> +       ret = spi_sync_transfer(st->spi, sd_readback_tr,
> +                               ARRAY_SIZE(sd_readback_tr));
> +       if (ret) {
> +               dev_err(&st->spi->dev,
> +                       "spi transfer error in irq handler");
> +               goto exit_handler;
> +       }
> +
> +       for_each_set_bit(bit, indio_dev->active_scan_mask, AD7779_NUM_CHANNELS - 1)
> +               tmp[k++] = st->spidata_rx[bit];
> +
> +       iio_push_to_buffers_with_timestamp(indio_dev, &tmp[0], pf->timestamp);
> +
> +exit_handler:
> +       iio_trigger_notify_done(indio_dev->trig);
> +       return IRQ_HANDLED;
> +}

...

> +       st->mclk = devm_clk_get_enabled(&spi->dev, "mclk");
> +       if (IS_ERR(st->mclk))
> +               return PTR_ERR(st->mclk);

> +       if (!spi->irq)
> +               return dev_err_probe(&spi->dev, ret,
> +                                    "DRDY irq not present\n");

This can be done much earlier and avoid unneeded resource allocations.

...

> +static int ad7779_suspend(struct device *dev)
> +{
> +       struct iio_dev *indio_dev = dev_get_drvdata(dev);
> +       struct ad7779_state *st = iio_priv(indio_dev);
> +       int ret;
> +
> +       ret = ad7779_spi_write_mask(st, AD7779_REG_GENERAL_USER_CONFIG_1,
> +                                   AD7779_MOD_POWERMODE_MSK,
> +                                   FIELD_PREP(AD7779_MOD_POWERMODE_MSK,
> +                                              AD7779_LOW_POWER));
> +       if (ret)
> +               return ret;
> +
> +       return 0;

  return ad7779_spi_write_mask(...);

> +}
> +
> +static int ad7779_resume(struct device *dev)
> +{
> +       struct iio_dev *indio_dev = dev_get_drvdata(dev);
> +       struct ad7779_state *st = iio_priv(indio_dev);
> +       int ret;
> +
> +       ret = ad7779_spi_write_mask(st, AD7779_REG_GENERAL_USER_CONFIG_1,
> +                                   AD7779_MOD_POWERMODE_MSK,
> +                                   FIELD_PREP(AD7779_MOD_POWERMODE_MSK,
> +                                              AD7779_HIGH_POWER));
> +       if (ret)
> +               return ret;
> +
> +       return 0;

Ditto.

> +}

...

> +static const struct spi_device_id ad7779_id[] = {
> +       {
> +               .name = "ad7770",
> +               .driver_data = (kernel_ulong_t)&ad7770_chip_info

Leave trailing comma.

> +       },
> +       {
> +               .name = "ad7771",
> +               .driver_data = (kernel_ulong_t)&ad7771_chip_info

Ditto.

> +       },
> +       {
> +               .name = "ad7779",
> +               .driver_data = (kernel_ulong_t)&ad7779_chip_info

Ditto.

> +       },
> +       { }
> +};

-- 
With Best Regards,
Andy Shevchenko





[Index of Archives]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Input]     [Linux Kernel]     [Linux SCSI]     [X.org]

  Powered by Linux