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