diff mbox series

[v3,1/2] iio: adc: Add driver for Texas Instruments ADS131E0x ADC family

Message ID 20210112095128.7112-2-tomislav.denis@avl.com (mailing list archive)
State New, archived
Headers show
Series Add support for ADS131E0x ADC family | expand

Commit Message

Denis, Tomislav AVL DiTEST Jan. 12, 2021, 9:51 a.m. UTC
From: Tomislav Denis <tomislav.denis@avl.com>

The ADS131E0x are a family of multichannel, simultaneous sampling,
24-bit, delta-sigma, analog-to-digital converters (ADCs) with a
built-in programmable gain amplifier (PGA), internal reference
and an onboard oscillator.

Signed-off-by: Tomislav Denis <tomislav.denis@avl.com>
---
 MAINTAINERS                    |   6 +
 drivers/iio/adc/Kconfig        |  12 +
 drivers/iio/adc/Makefile       |   1 +
 drivers/iio/adc/ti-ads131e08.c | 960 +++++++++++++++++++++++++++++++++++++++++
 4 files changed, 979 insertions(+)
 create mode 100644 drivers/iio/adc/ti-ads131e08.c

Comments

Jonathan Cameron Jan. 17, 2021, 12:31 p.m. UTC | #1
On Tue, 12 Jan 2021 10:51:27 +0100
<tomislav.denis@avl.com> wrote:

> From: Tomislav Denis <tomislav.denis@avl.com>
> 
> The ADS131E0x are a family of multichannel, simultaneous sampling,
> 24-bit, delta-sigma, analog-to-digital converters (ADCs) with a
> built-in programmable gain amplifier (PGA), internal reference
> and an onboard oscillator.
> 
> Signed-off-by: Tomislav Denis <tomislav.denis@avl.com>
Hi Tomislav,

I previous failed to look in detail at how this driver will interact with
triggers that are not it's own data ready signal.  There are a few corners
around that which need fixing up.  You could just not allow that case, 
but it can be very handy to use an hrtimer trigger to do slow polling for
example and it's not that hard to enable.

Although I haven't done the necessary audit I suspect we have a few drivers
that don't get this right that got through review :(

A few other minor things below which I'd probably just have tidied up whilst
applying if not for the above.

...

> diff --git a/drivers/iio/adc/ti-ads131e08.c b/drivers/iio/adc/ti-ads131e08.c
> new file mode 100644
> index 0000000..dcd5d34
> --- /dev/null
> +++ b/drivers/iio/adc/ti-ads131e08.c
> @@ -0,0 +1,960 @@
...

> +
> +static int ads131e08_initial_config(struct iio_dev *indio_dev)
> +{
> +	struct ads131e08_state *st = iio_priv(indio_dev);
> +	const struct iio_chan_spec *channel = indio_dev->channels;
> +	int ret, i;
> +	u8 active_channels = 0;
> +
> +	ret = ads131e08_exec_cmd(st, ADS131E08_CMD_RESET);
> +	if (ret)
> +		return ret;
> +
> +	udelay(st->reset_delay_us);
> +
> +	/* Disable read data in continuous mode (enabled by default) */
> +	ret = ads131e08_exec_cmd(st, ADS131E08_CMD_SDATAC);
> +	if (ret)
> +		return ret;
> +
> +	ret = ads131e08_set_data_rate(st, ADS131E08_DEFAULT_DATA_RATE);
> +	if (ret)
> +		return ret;
> +
> +	ret = ads131e08_config_reference_voltage(st);
> +	if (ret)
> +		return ret;
> +
> +	for (i = 0;  i < indio_dev->num_channels; ++i) {
> +		ret = ads131e08_set_pga_gain(st, channel->channel,
> +			st->channel_config[i].pga_gain);
> +		if (ret)
> +			return ret;
> +
> +		ret = ads131e08_set_channel_mux(st, channel->channel,
> +			st->channel_config[i].mux);
> +		if (ret)
> +			return ret;
> +
> +		active_channels |= BIT(channel->channel);
> +		channel++;
> +	}
> +
> +	/* Power down unused channels */
> +	for (i = 0; i < st->info->max_channels; ++i) {
> +		if (!(active_channels & BIT(i))) {
> +			ret = ads131e08_power_down_channel(st, i, true);
> +			if (ret)
> +				return ret;
> +		}
> +	}
> +
> +	/* Request channel offset calibration */
> +	ret = ads131e08_exec_cmd(st, ADS131E08_CMD_OFFSETCAL);
> +	if (ret)
> +		return ret;
> +
> +	/*
> +	 * Channel offset calibration is triggered with first START command.
> +	 * Since calibration take more time than settling operation,
> +	 * this causes timeout error when command START is sent first
> +	 * time (e.g. first call of the ads131e08_read_direct method).
> +	 * To avoid this problem offset calibration is triggered here.
> +	 */
> +	ret = ads131e08_exec_cmd(st, ADS131E08_CMD_START);
> +	if (ret)
> +		return ret;
> +
> +	msleep(ADS131E08_WAIT_OFFSETCAL_MS);
> +
> +	ret = ads131e08_exec_cmd(st, ADS131E08_CMD_STOP);
> +	if (ret)
> +		return ret;

trivial but nicer to just do

return ads131e...

> +
> +	return 0;
> +}
> +
> +static int ads131e08_read_direct(struct iio_dev *indio_dev,
> +	struct iio_chan_spec const *channel, int *value)
> +{
> +	struct ads131e08_state *st = iio_priv(indio_dev);
> +	int ret;
> +	u8 num_bits, *src;
> +
> +	reinit_completion(&st->completion);
> +
> +	ret = ads131e08_exec_cmd(st, ADS131E08_CMD_START);
> +	if (ret)
> +		return ret;
> +
> +	ret = wait_for_completion_timeout(&st->completion,
> +		msecs_to_jiffies(ADS131E08_MAX_SETTLING_TIME_MS));
> +	if (!ret)
> +		return -ETIMEDOUT;
> +
> +	ret = ads131e08_read_data(st, st->readback_len);
> +	if (ret)
> +		return ret;
> +
> +	ret = ads131e08_exec_cmd(st, ADS131E08_CMD_STOP);
> +	if (ret)
> +		return ret;
> +
> +	src = st->rx_buf + ADS131E08_NUM_STATUS_BYTES +
> +		channel->channel * ADS131E08_NUM_DATA_BYTES(st->data_rate);
> +
> +	num_bits = ADS131E08_NUM_DATA_BITS(st->data_rate);
> +	*value = sign_extend32(
> +		get_unaligned_be32(src) >> (32 - num_bits), num_bits - 1);
> +
> +	return ret;
> +}
> +

...

> +
> +static int ads131e08_set_trigger_state(struct iio_trigger *trig, bool state)
> +{
> +	struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig);
> +	struct ads131e08_state *st = iio_priv(indio_dev);
> +	u8 cmd = state ? ADS131E08_CMD_START : ADS131E08_CMD_STOP;
> +
> +	return ads131e08_exec_cmd(st, cmd);
> +}
> +
> +static const struct iio_trigger_ops ads131e08_trigger_ops = {
> +	.set_trigger_state = &ads131e08_set_trigger_state,
.validate_device = &iio_trigger_validate_own_device,

(see below for why!)

> +};
> +
> +static irqreturn_t ads131e08_trigger_handler(int irq, void *private)
> +{
> +	struct iio_poll_func *pf = private;
> +	struct iio_dev *indio_dev = pf->indio_dev;
> +	struct ads131e08_state *st = iio_priv(indio_dev);
> +	int ret;
> +	unsigned int chn, i = 0;
> +	u8 *src, *dest;
> +	/*
> +	 * The number of data bits per channel depends on the data rate.
> +	 * For 32 and 64 ksps data rates, number of data bits per channel
> +	 * is 16. This case is not compliant with used (fixed) scan element
> +	 * type (be:s24/32>>8). So we use a litle tweek to pack properly
> +	 * 16 bits of data into the buffer.
> +	 */
> +	unsigned int num_bytes = ADS131E08_NUM_DATA_BYTES(st->data_rate);
> +	u8 tweek_offset = num_bytes == 2 ? 1 : 0;
> +
> +	ret = ads131e08_read_data(st, st->readback_len);
> +	if (ret)
> +		goto out;
> +
> +	for_each_set_bit(chn, indio_dev->active_scan_mask,
> +		indio_dev->masklength) {
> +		src = st->rx_buf + ADS131E08_NUM_STATUS_BYTES + chn * num_bytes;
> +		dest = st->tmp_buf.data + i * ADS131E08_NUM_STORAGE_BYTES;
> +
> +		/*
> +		 * Tweek offset is 0:
> +		 * +---+---+---+---+
> +		 * |D0 |D1 |D2 | X | (3 data bytes)
> +		 * +---+---+---+---+
> +		 *  a+0 a+1 a+2 a+3
> +		 *
> +		 * Tweek offset is 1:
> +		 * +---+---+---+---+
> +		 * |P0 |D0 |D1 | X | (one padding byte and 2 data bytes)
> +		 * +---+---+---+---+
> +		 *  a+0 a+1 a+2 a+3
> +		 */
> +		memcpy(dest + tweek_offset, src, num_bytes);
> +
> +		/*
> +		 * Data conversion from 16 bits of data to 24 bits of data
> +		 * is done by sign etension (properly filling padding byte).
> +		 */
> +		if (tweek_offset)
> +			memset(dest, *src & BIT(7) ? 0xff : 0x00, 1);
> +
> +		i++;
> +	}
> +
> +	iio_push_to_buffers_with_timestamp(indio_dev, st->tmp_buf.data,
> +		iio_get_time_ns(indio_dev));
> +
> +out:
> +	iio_trigger_notify_done(indio_dev->trig);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static irqreturn_t ads131e08_interrupt(int irq, void *private)
> +{
> +	struct iio_dev *indio_dev = private;
> +	struct ads131e08_state *st = iio_priv(indio_dev);
> +
> +	if (iio_buffer_enabled(indio_dev))
> +		iio_trigger_poll(st->trig);

There is a corner case here we may want to close
I don't think you are currently ensuring that another device
isn't using this trigger.  In theory that could happen whilst this
device doesn't have it's buffer enabled.  This corner case is common
with devices that use the interrupt for polled reads.

If you add a validate_device() callback to the trigger it will stop
that happening. 

That will still let this device be used with other triggers (e.g. sysfs
or hrtimer) However, I think there is a problem there as well.

Currently you do the start for the device in the set_trigger_state() cb
and that won't be called if we are using another device.

So to enable the use of other triggers, you would need to check whether
the trigger in use is this one provided by this driver and if it isn't
you will need to so the extra steps to initialize a capture on demand
(same as read_raw more or less)

Look at iio_trigger_using_own() and the users of that function to see
how they handle this.


> +	else
> +		complete(&st->completion);
> +
> +	return IRQ_HANDLED;
> +}
> +

...


> +
> +static int ads131e08_probe(struct spi_device *spi)
> +{
> +	const struct ads131e08_info *info;
> +	struct iio_dev *indio_dev;
> +	struct ads131e08_state *st;
> +	int ret;
> +	unsigned long adc_clk_hz, adc_clk_ns;
> +
> +	info = of_device_get_match_data(&spi->dev);
> +	if (!info) {
> +		dev_err(&spi->dev, "failed to get match data\n");
> +		return -ENODEV;
> +	}
> +
> +	indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
> +	if (!indio_dev) {
> +		dev_err(&spi->dev, "failed to allocate IIO device\n");
> +		return -ENOMEM;
> +	}
> +
> +	st = iio_priv(indio_dev);
> +	st->info = info;
> +	st->spi = spi;
> +
> +	ret = ads131e08_parse_dt(indio_dev, spi->dev.of_node);
> +	if (ret)
> +		return ret;
> +
> +	indio_dev->name = st->info->name;
> +	indio_dev->dev.parent = &spi->dev;
> +	indio_dev->dev.of_node = spi->dev.of_node;
> +	indio_dev->info = &ads131e08_iio_info;
> +	indio_dev->modes = INDIO_DIRECT_MODE;
> +
> +	init_completion(&st->completion);
> +
> +	if (spi->irq) {
> +		ret = devm_request_irq(&spi->dev, spi->irq,
> +			ads131e08_interrupt,
> +			IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
> +			spi->dev.driver->name, indio_dev);
> +		if (ret) {
> +			dev_err(&spi->dev, "failed to allocate IRQ\n");

Very small tweak here. dev_err_probe() so that we don't print a message
if the return value indicates that we are just deferring whilst waiting
for the irq chip driver to load.

> +			return ret;
> +		}
> +	} else {
> +		dev_err(&spi->dev, "data ready IRQ missing\n");
> +		return -ENODEV;
> +	}
> +
> +	st->trig = devm_iio_trigger_alloc(&spi->dev, "%s-dev%d",
> +		indio_dev->name, indio_dev->id);
> +	if (!st->trig) {
> +		dev_err(&spi->dev, "failed to allocate IIO trigger\n");
> +		return -ENOMEM;
> +	}
> +
> +	st->trig->ops = &ads131e08_trigger_ops;
> +	st->trig->dev.parent = &spi->dev;
> +	iio_trigger_set_drvdata(st->trig, indio_dev);
> +	ret = devm_iio_trigger_register(&spi->dev, st->trig);
> +	if (ret) {
> +		dev_err(&spi->dev, "failed to register IIO trigger\n");
> +		return -ENOMEM;
> +	}
> +
> +	indio_dev->trig = iio_trigger_get(st->trig);
> +
> +	ret = devm_iio_triggered_buffer_setup(&spi->dev, indio_dev,
> +		NULL, &ads131e08_trigger_handler, NULL);
> +	if (ret) {
> +		dev_err(&spi->dev, "failed to setup IIO buffer\n");
> +		return ret;
> +	}
> +
> +	st->vref_reg = devm_regulator_get_optional(&spi->dev, "vref");
> +	if (!IS_ERR(st->vref_reg)) {
> +		ret = regulator_enable(st->vref_reg);
> +		if (ret) {
> +			dev_err(&spi->dev,
> +				"failed to enable external vref supply\n");
> +			return ret;
> +		}
> +
> +		ret = devm_add_action_or_reset(&spi->dev,
> +			ads131e08_regulator_disable, st);
> +		if (ret) {
> +			regulator_disable(st->vref_reg);
> +			return ret;
> +		}
> +	} else {
> +		if (PTR_ERR(st->vref_reg) != -ENODEV)
> +			return PTR_ERR(st->vref_reg);
> +	}
> +
> +	st->adc_clk = devm_clk_get(&spi->dev, "adc-clk");
> +	if (IS_ERR(st->adc_clk)) {
> +		dev_err(&spi->dev, "failed to get the ADC clock\n");

Here again, dev_err_probe() to avoid the message in the -EPROBE_DEFER case.

> +		return PTR_ERR(st->adc_clk);
> +	}
> +
> +	ret = clk_prepare_enable(st->adc_clk);
> +	if (ret) {
> +		dev_err(&spi->dev, "failed to prepare/enable the ADC clock\n");
> +		return ret;
> +	}
> +
> +	ret = devm_add_action_or_reset(&spi->dev, ads131e08_clk_disable, st);
> +	if (ret) {
> +		clk_disable_unprepare(st->adc_clk);
> +		return ret;
> +	}
> +
> +	adc_clk_hz = clk_get_rate(st->adc_clk);
> +	if (!adc_clk_hz) {
> +		dev_err(&spi->dev, "failed to get the ADC clock rate\n");
> +		return  -EINVAL;
> +	}
> +
> +	adc_clk_ns = NSEC_PER_SEC / adc_clk_hz;
> +	st->sdecode_delay_us = DIV_ROUND_UP(
> +		ADS131E08_WAIT_SDECODE_CYCLES * adc_clk_ns, 1000);
> +	st->reset_delay_us = DIV_ROUND_UP(
> +		ADS131E08_WAIT_RESET_CYCLES * adc_clk_ns, 1000);
> +
> +	ret = ads131e08_initial_config(indio_dev);
> +	if (ret) {
> +		dev_err(&spi->dev, "initial configuration failed\n");
> +		return ret;
> +	}
> +
> +	ret = devm_iio_device_register(&spi->dev, indio_dev);
> +	if (ret) {
> +		dev_err(&spi->dev, "failed to register IIO device\n");
> +		return ret;
> +	}
> +
> +	return 0;
> +}
...
Andy Shevchenko Jan. 21, 2021, 4:42 p.m. UTC | #2
On Tue, Jan 12, 2021 at 2:37 PM <tomislav.denis@avl.com> wrote:
>
> From: Tomislav Denis <tomislav.denis@avl.com>
>
> The ADS131E0x are a family of multichannel, simultaneous sampling,
> 24-bit, delta-sigma, analog-to-digital converters (ADCs) with a
> built-in programmable gain amplifier (PGA), internal reference
> and an onboard oscillator.

...

> +config TI_ADS131E08
> +       tristate "Texas Instruments ADS131E08"
> +       depends on SPI && OF

Why OF?!

> +       select IIO_BUFFER
> +       select IIO_TRIGGERED_BUFFER
> +       help
> +         Say yes here to get support for Texas Instruments ADS131E04, ADS131E06
> +         and ADS131E08 chips.
> +
> +         This driver can also be built as a module. If so, the module will be
> +         called ti-ads131e08.

...

> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Texas Instruments ADS131E0x 4-, 6- and 8-Channel ADCs
> + *
> + * Copyright (c) 2020 AVL DiTEST GmbH
> + *   Tomislav Denis <tomislav.denis@avl.com>
> + *
> + * Datasheet: https://www.ti.com/lit/ds/symlink/ads131e08.pdf

Please, put this as a tag in the commit message as well

> + */
> +
> +#include <linux/bitfield.h>
> +#include <linux/clk.h>
> +#include <linux/delay.h>
> +#include <linux/module.h>

> +#include <linux/of_device.h>

Why?!

> +#include <linux/iio/buffer.h>
> +#include <linux/iio/iio.h>
> +#include <linux/iio/sysfs.h>
> +#include <linux/iio/trigger.h>
> +#include <linux/iio/trigger_consumer.h>
> +#include <linux/iio/triggered_buffer.h>
> +#include <linux/regulator/consumer.h>
> +#include <linux/spi/spi.h>
> +#include <asm/unaligned.h>

Can we group these to something like (wit blank lines between groups)

linux/*.h

linux/iio*.h

asm/*.h

?

...

> +#define ADS131E08_CMD_RREG(r)          (0x20 | (r & GENMASK(4, 0)))
> +#define ADS131E08_CMD_WREG(r)          (0x40 | (r & GENMASK(4, 0)))

BIT(5)
BIT(6)

...

> +#define ADS131E08_VREF_2V4_MV          2400
> +#define ADS131E08_VREF_4V_MV           4000

I prefer mV despite this being defined constant.

...

> +struct ads131e08_state {
> +       const struct ads131e08_info *info;
> +       struct spi_device *spi;
> +       struct iio_trigger *trig;
> +       struct clk *adc_clk;
> +       struct regulator *vref_reg;
> +       struct ads131e08_channel_config *channel_config;
> +       unsigned int data_rate;
> +       unsigned int vref_mv;
> +       unsigned int sdecode_delay_us;
> +       unsigned int reset_delay_us;
> +       unsigned int readback_len;
> +       struct completion completion;

> +

Redundant blank line. But it's up to you.

> +       struct {
> +               u8 data[ADS131E08_NUM_DATA_BYTES_MAX];
> +               s64 ts __aligned(8);
> +       } tmp_buf;
> +
> +       u8 tx_buf[3] ____cacheline_aligned;
> +       /*
> +        * Add extra one padding byte to be able to access the last channel
> +        * value using u32 pointer
> +        */
> +       u8 rx_buf[ADS131E08_NUM_STATUS_BYTES +
> +               ADS131E08_NUM_DATA_BYTES_MAX + 1];
> +};

...

> +static const struct ads131e08_data_rate_desc ads131e08_data_rate_tbl[] = {
> +       { .rate = 64,   .reg = 0x00 },
> +       { .rate = 32,   .reg = 0x01 },
> +       { .rate = 16,   .reg = 0x02 },
> +       { .rate = 8,    .reg = 0x03 },
> +       { .rate = 4,    .reg = 0x04 },
> +       { .rate = 2,    .reg = 0x05 },
> +       { .rate = 1,    .reg = 0x06 },
> +};

Can't you use simple bit operations on this?

rate = BIT(6 - index)
reg = index

...

> +static const struct ads131e08_pga_gain_desc ads131e08_pga_gain_tbl[] = {
> +       { .gain = 1,   .reg = 0x01 },
> +       { .gain = 2,   .reg = 0x02 },

reg == 3 valid?

> +       { .gain = 4,   .reg = 0x04 },
> +       { .gain = 8,   .reg = 0x05 },
> +       { .gain = 12,  .reg = 0x06 },
> +};

Also can be changed by formula, but I remember that in some cases
tables are preferable.

...

> +static int ads131e08_exec_cmd(struct ads131e08_state *st, u8 cmd)
> +{
> +       int ret;
> +
> +       ret = spi_write_then_read(st->spi, &cmd, 1, NULL, 0);
> +       if (ret) {
> +               dev_err(&st->spi->dev,
> +                       "%s: SPI write failed for cmd: %02x\n", __func__, cmd);

__func__ is not needed.

> +               return ret;
> +       }
> +
> +       return 0;

The last is redundant, simply
  return ret;
instead of both of them.

> +}
> +
> +static int ads131e08_read_reg(struct ads131e08_state *st, u8 reg)
> +{
> +       int ret;

> +

Redundant blank line.

> +       struct spi_transfer transfer[] = {
> +               {
> +                       .tx_buf = &st->tx_buf,
> +                       .len = 2,
> +                       .delay_usecs = st->sdecode_delay_us,
> +               }, {
> +                       .rx_buf = &st->rx_buf,
> +                       .len = 1,
> +               },
> +       };
> +
> +       st->tx_buf[0] = ADS131E08_CMD_RREG(reg);
> +       st->tx_buf[1] = 0;
> +
> +       ret = spi_sync_transfer(st->spi, transfer, ARRAY_SIZE(transfer));
> +       if (ret) {
> +               dev_err(&st->spi->dev, "%s: SPI transfer failed\n", __func__);

__func__ is almost useless. Better to think about the uniqueness of
the messages across the driver.

> +               return ret;
> +       }
> +
> +       return st->rx_buf[0];
> +}

> +static int ads131e08_write_reg(struct ads131e08_state *st, u8 reg, u8 value)
> +{

Similar comments as per previous function.

> +       ret = spi_sync_transfer(st->spi, transfer, ARRAY_SIZE(transfer));
> +       if (ret) {
> +               dev_err(&st->spi->dev, "%s: SPI transfer failed\n", __func__);

> +               return ret;
> +       }
> +
> +       return 0;

return ret;

> +}
> +
> +static int ads131e08_read_data(struct ads131e08_state *st, int rx_len)
> +{

As above.

> +}

...

> +       for (i = 0; i < ARRAY_SIZE(ads131e08_data_rate_tbl); ++i) {

i++ works the same and more regular pattern.
Ditto for other occurrences.

> +               if (ads131e08_data_rate_tbl[i].rate == data_rate)
> +                       break;
> +       }

...

> +       struct ads131e08_state *st = iio_priv(indio_dev);
> +       const struct iio_chan_spec *channel = indio_dev->channels;
> +       int ret, i;
> +       u8 active_channels = 0;

Reversed xmas tree order?
Also for the rest of the driver.

...

> +       /* Power down unused channels */
> +       for (i = 0; i < st->info->max_channels; ++i) {
> +               if (!(active_channels & BIT(i))) {

NIH of for_each_clear_bit()

> +                       ret = ads131e08_power_down_channel(st, i, true);
> +                       if (ret)
> +                               return ret;
> +               }
> +       }

...

> +       /*
> +        * Channel offset calibration is triggered with first START command.

the first

> +        * Since calibration take more time than settling operation,

takes

> +        * this causes timeout error when command START is sent first
> +        * time (e.g. first call of the ads131e08_read_direct method).
> +        * To avoid this problem offset calibration is triggered here.
> +        */

...

> +       ret = ads131e08_exec_cmd(st, ADS131E08_CMD_STOP);
> +       if (ret)
> +               return ret;
> +
> +       return 0;

return _exec_cmd(...);

...

> +       ret = wait_for_completion_timeout(&st->completion,
> +               msecs_to_jiffies(ADS131E08_MAX_SETTLING_TIME_MS));

With a temporary variable is easier to read.

... timeout = msecs_to_jiffies(...);

       ret = wait_for_completion_timeout(&st->completion, timeout);

> +       if (!ret)
> +               return -ETIMEDOUT;

...

> +       *value = sign_extend32(
> +               get_unaligned_be32(src) >> (32 - num_bits), num_bits - 1);

One line, please.

> +       return ret;

Expected other than 0?

...

> +       case IIO_CHAN_INFO_SCALE:
> +               if (!IS_ERR(st->vref_reg)) {

Why not positive conditional?

> +                       ret = regulator_get_voltage(st->vref_reg);
> +                       if (ret < 0)
> +                               return ret;
> +
> +                       *value = ret / 1000;
> +               } else {
> +                       *value = st->vref_mv;
> +               }

...

> +       return -EINVAL;

Can you use rather
  default:
   return -EINVAL;
instead?

...

> +       switch (mask) {
> +       case IIO_CHAN_INFO_SAMP_FREQ:
> +               ret = iio_device_claim_direct_mode(indio_dev);
> +               if (ret)
> +                       return ret;
> +
> +               ret = ads131e08_set_data_rate(st, value);
> +               iio_device_release_direct_mode(indio_dev);
> +               return ret;
> +       }
> +
> +       return -EINVAL;

Ditto.

...

> +static int ads131e08_debugfs_reg_access(struct iio_dev *indio_dev,
> +       unsigned int reg, unsigned int writeval, unsigned int *readval)
> +{
> +       struct ads131e08_state *st = iio_priv(indio_dev);
> +
> +       if (readval) {
> +               int ret = ads131e08_read_reg(st, (u8)reg);

Is casting needed?

> +               *readval = ret;
> +               return ret;
> +       }

...

> +       /*
> +        * The number of data bits per channel depends on the data rate.
> +        * For 32 and 64 ksps data rates, number of data bits per channel
> +        * is 16. This case is not compliant with used (fixed) scan element
> +        * type (be:s24/32>>8). So we use a litle tweek to pack properly

little tweak

> +        * 16 bits of data into the buffer.
> +        */

...

> +       for_each_set_bit(chn, indio_dev->active_scan_mask,
> +               indio_dev->masklength) {

One line.

...

> +               /*
> +                * Data conversion from 16 bits of data to 24 bits of data
> +                * is done by sign etension (properly filling padding byte).

extension

> +                */
> +               if (tweek_offset)
> +                       memset(dest, *src & BIT(7) ? 0xff : 0x00, 1);

One byte with memset()?!

...

> +       ret = of_property_read_u32(node, "ti,vref-internal", &tmp);

device_property_read_u32()

> +       if (ret)
> +               tmp = 0;

...

> +       num_channels = of_get_available_child_count(node);

fwnode API has something OF agnostic.

> +       if (num_channels == 0) {
> +               dev_err(&st->spi->dev, "no channel children\n");
> +               return -ENODEV;
> +       }

...

> +       for_each_available_child_of_node(node, child) {
> +               ret = of_property_read_u32(child, "reg", &channel);

> +               ret = of_property_read_u32(child, "ti,gain", &tmp);

> +               ret = of_property_read_u32(child, "ti,mux", &tmp);

> +       }

Easy to convert to fwnode API.

...

> +       info = of_device_get_match_data(&spi->dev);

device_get_match_data()

> +       if (!info) {
> +               dev_err(&spi->dev, "failed to get match data\n");
> +               return -ENODEV;
> +       }

...

> +       indio_dev->dev.of_node = spi->dev.of_node;

Do you need to copy fwnode as well?

...

> +       st->adc_clk = devm_clk_get(&spi->dev, "adc-clk");
> +       if (IS_ERR(st->adc_clk)) {
> +               dev_err(&spi->dev, "failed to get the ADC clock\n");

Spam when deferred.

> +               return PTR_ERR(st->adc_clk);
> +       }

...

> +       ret = clk_prepare_enable(st->adc_clk);
> +       if (ret) {
> +               dev_err(&spi->dev, "failed to prepare/enable the ADC clock\n");
> +               return ret;
> +       }
> +
> +       ret = devm_add_action_or_reset(&spi->dev, ads131e08_clk_disable, st);
> +       if (ret) {

> +               clk_disable_unprepare(st->adc_clk);

"_or_reset" does call this, no?

> +               return ret;
> +       }

Actually both calls can be moved to a separate helper.

...

> +       adc_clk_ns = NSEC_PER_SEC / adc_clk_hz;
> +       st->sdecode_delay_us = DIV_ROUND_UP(
> +               ADS131E08_WAIT_SDECODE_CYCLES * adc_clk_ns, 1000);

NSEC_PER_USEC

> +       st->reset_delay_us = DIV_ROUND_UP(
> +               ADS131E08_WAIT_RESET_CYCLES * adc_clk_ns, 1000);

Ditto.


> +       ret = devm_iio_device_register(&spi->dev, indio_dev);
> +       if (ret) {
> +               dev_err(&spi->dev, "failed to register IIO device\n");
> +               return ret;
> +       }
> +
> +       return 0;

return devm_iio_device_register(...);
Denis, Tomislav AVL DiTEST Jan. 23, 2021, 8:28 a.m. UTC | #3
Hi Andy,

First, thanks for the great review. I agree with all of your comments, just one question/comment inline.

Best Regards,
Tomislav

> -----Original Message-----
> From: Andy Shevchenko <andy.shevchenko@gmail.com>
> Sent: 21 January 2021 17:42
> To: Denis, Tomislav AVL DiTEST <Tomislav.Denis@avl.com>
> Cc: Jonathan Cameron <jic23@kernel.org>; linux-iio <linux-iio@vger.kernel.org>;
> devicetree <devicetree@vger.kernel.org>
> Subject: Re: [PATCH v3 1/2] iio: adc: Add driver for Texas Instruments
> ADS131E0x ADC family
> 
> On Tue, Jan 12, 2021 at 2:37 PM <tomislav.denis@avl.com> wrote:
> >
> > From: Tomislav Denis <tomislav.denis@avl.com>
> >
> > The ADS131E0x are a family of multichannel, simultaneous sampling,
> > 24-bit, delta-sigma, analog-to-digital converters (ADCs) with a
> > built-in programmable gain amplifier (PGA), internal reference and an
> > onboard oscillator.
> 

...

> 
> > +static const struct ads131e08_data_rate_desc ads131e08_data_rate_tbl[] = {
> > +       { .rate = 64,   .reg = 0x00 },
> > +       { .rate = 32,   .reg = 0x01 },
> > +       { .rate = 16,   .reg = 0x02 },
> > +       { .rate = 8,    .reg = 0x03 },
> > +       { .rate = 4,    .reg = 0x04 },
> > +       { .rate = 2,    .reg = 0x05 },
> > +       { .rate = 1,    .reg = 0x06 },
> > +};
> 
> Can't you use simple bit operations on this?
> 
> rate = BIT(6 - index)
> reg = index
> 
> ...
> 
> > +static const struct ads131e08_pga_gain_desc ads131e08_pga_gain_tbl[] = {
> > +       { .gain = 1,   .reg = 0x01 },
> > +       { .gain = 2,   .reg = 0x02 },
> 
> reg == 3 valid?
> 
> > +       { .gain = 4,   .reg = 0x04 },
> > +       { .gain = 8,   .reg = 0x05 },
> > +       { .gain = 12,  .reg = 0x06 },
> > +};
> 
> Also can be changed by formula, but I remember that in some cases tables are
> preferable.
> 
>

I would like to keep those tables as they are, because I find them more intuitive and easy understandable!?

>
> > +static int ads131e08_exec_cmd(struct ads131e08_state *st, u8 cmd) {
> > +       int ret;
> > +

....

> 
> --
> With Best Regards,
> Andy Shevchenko
diff mbox series

Patch

diff --git a/MAINTAINERS b/MAINTAINERS
index 2ac5688..167aabf 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -17367,6 +17367,12 @@  M:	Robert Richter <rric@kernel.org>
 S:	Odd Fixes
 F:	drivers/gpio/gpio-thunderx.c
 
+TI ADS131E0X ADC SERIES DRIVER
+M:	Tomislav Denis <tomislav.denis@avl.com>
+L:	linux-iio@vger.kernel.org
+S:	Maintained
+F:	drivers/iio/adc/ti-ads131e08.c
+
 TI AM437X VPFE DRIVER
 M:	"Lad, Prabhakar" <prabhakar.csengg@gmail.com>
 L:	linux-media@vger.kernel.org
diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
index 15587a1..af16be8 100644
--- a/drivers/iio/adc/Kconfig
+++ b/drivers/iio/adc/Kconfig
@@ -1151,6 +1151,18 @@  config TI_ADS124S08
 	  This driver can also be built as a module. If so, the module will be
 	  called ti-ads124s08.
 
+config TI_ADS131E08
+	tristate "Texas Instruments ADS131E08"
+	depends on SPI && OF
+	select IIO_BUFFER
+	select IIO_TRIGGERED_BUFFER
+	help
+	  Say yes here to get support for Texas Instruments ADS131E04, ADS131E06
+	  and ADS131E08 chips.
+
+	  This driver can also be built as a module. If so, the module will be
+	  called ti-ads131e08.
+
 config TI_AM335X_ADC
 	tristate "TI's AM335X ADC driver"
 	depends on MFD_TI_AM335X_TSCADC && HAS_DMA
diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
index 5fca90a..a226657 100644
--- a/drivers/iio/adc/Makefile
+++ b/drivers/iio/adc/Makefile
@@ -103,6 +103,7 @@  obj-$(CONFIG_TI_ADS7950) += ti-ads7950.o
 obj-$(CONFIG_TI_ADS8344) += ti-ads8344.o
 obj-$(CONFIG_TI_ADS8688) += ti-ads8688.o
 obj-$(CONFIG_TI_ADS124S08) += ti-ads124s08.o
+obj-$(CONFIG_TI_ADS131E08) += ti-ads131e08.o
 obj-$(CONFIG_TI_AM335X_ADC) += ti_am335x_adc.o
 obj-$(CONFIG_TI_TLC4541) += ti-tlc4541.o
 obj-$(CONFIG_TWL4030_MADC) += twl4030-madc.o
diff --git a/drivers/iio/adc/ti-ads131e08.c b/drivers/iio/adc/ti-ads131e08.c
new file mode 100644
index 0000000..dcd5d34
--- /dev/null
+++ b/drivers/iio/adc/ti-ads131e08.c
@@ -0,0 +1,960 @@ 
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Texas Instruments ADS131E0x 4-, 6- and 8-Channel ADCs
+ *
+ * Copyright (c) 2020 AVL DiTEST GmbH
+ *   Tomislav Denis <tomislav.denis@avl.com>
+ *
+ * Datasheet: https://www.ti.com/lit/ds/symlink/ads131e08.pdf
+ */
+
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/trigger.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
+#include <linux/regulator/consumer.h>
+#include <linux/spi/spi.h>
+#include <asm/unaligned.h>
+
+/* Commands */
+#define ADS131E08_CMD_RESET		0x06
+#define ADS131E08_CMD_START		0x08
+#define ADS131E08_CMD_STOP		0x0A
+#define ADS131E08_CMD_OFFSETCAL		0x1A
+#define ADS131E08_CMD_SDATAC		0x11
+#define ADS131E08_CMD_RDATA		0x12
+#define ADS131E08_CMD_RREG(r)		(0x20 | (r & GENMASK(4, 0)))
+#define ADS131E08_CMD_WREG(r)		(0x40 | (r & GENMASK(4, 0)))
+
+/* Registers */
+#define ADS131E08_ADR_CFG1R		0x01
+#define ADS131E08_ADR_CFG3R		0x03
+#define ADS131E08_ADR_CH0R		0x05
+
+/* Configuration register 1 */
+#define ADS131E08_CFG1R_DR_MASK		GENMASK(2, 0)
+
+/* Configuration register 3 */
+#define ADS131E08_CFG3R_PDB_REFBUF_MASK	BIT(7)
+#define ADS131E08_CFG3R_VREF_4V_MASK	BIT(5)
+
+/* Channel settings register */
+#define ADS131E08_CHR_GAIN_MASK		GENMASK(6, 4)
+#define ADS131E08_CHR_MUX_MASK		GENMASK(2, 0)
+#define ADS131E08_CHR_PWD_MASK		BIT(7)
+
+/* ADC  misc */
+#define ADS131E08_DEFAULT_DATA_RATE	1
+#define ADS131E08_DEFAULT_PGA_GAIN	1
+#define ADS131E08_DEFAULT_MUX		0
+
+#define ADS131E08_VREF_2V4_MV		2400
+#define ADS131E08_VREF_4V_MV		4000
+
+#define ADS131E08_WAIT_RESET_CYCLES	18
+#define ADS131E08_WAIT_SDECODE_CYCLES	4
+#define ADS131E08_WAIT_OFFSETCAL_MS	153
+#define ADS131E08_MAX_SETTLING_TIME_MS	6
+
+#define ADS131E08_NUM_STATUS_BYTES	3
+#define ADS131E08_NUM_DATA_BYTES_MAX	24
+#define ADS131E08_NUM_DATA_BYTES(dr)	(((dr) >= 32) ? 2 : 3)
+#define ADS131E08_NUM_DATA_BITS(dr)	(ADS131E08_NUM_DATA_BYTES(dr) * 8)
+#define ADS131E08_NUM_STORAGE_BYTES	4
+
+enum ads131e08_ids {
+	ads131e04,
+	ads131e06,
+	ads131e08,
+};
+
+struct ads131e08_info {
+	unsigned int max_channels;
+	const char *name;
+};
+
+struct ads131e08_channel_config {
+	unsigned int pga_gain;
+	unsigned int mux;
+};
+
+struct ads131e08_state {
+	const struct ads131e08_info *info;
+	struct spi_device *spi;
+	struct iio_trigger *trig;
+	struct clk *adc_clk;
+	struct regulator *vref_reg;
+	struct ads131e08_channel_config *channel_config;
+	unsigned int data_rate;
+	unsigned int vref_mv;
+	unsigned int sdecode_delay_us;
+	unsigned int reset_delay_us;
+	unsigned int readback_len;
+	struct completion completion;
+
+	struct {
+		u8 data[ADS131E08_NUM_DATA_BYTES_MAX];
+		s64 ts __aligned(8);
+	} tmp_buf;
+
+	u8 tx_buf[3] ____cacheline_aligned;
+	/*
+	 * Add extra one padding byte to be able to access the last channel
+	 * value using u32 pointer
+	 */
+	u8 rx_buf[ADS131E08_NUM_STATUS_BYTES +
+		ADS131E08_NUM_DATA_BYTES_MAX + 1];
+};
+
+static const struct ads131e08_info ads131e08_info_tbl[] = {
+	[ads131e04] = {
+		.max_channels = 4,
+		.name = "ads131e04",
+	},
+	[ads131e06] = {
+		.max_channels = 6,
+		.name = "ads131e06",
+	},
+	[ads131e08] = {
+		.max_channels = 8,
+		.name = "ads131e08",
+	},
+};
+
+struct ads131e08_data_rate_desc {
+	unsigned int rate;  /* data rate in kSPS */
+	u8 reg;             /* reg value */
+};
+
+static const struct ads131e08_data_rate_desc ads131e08_data_rate_tbl[] = {
+	{ .rate = 64,   .reg = 0x00 },
+	{ .rate = 32,   .reg = 0x01 },
+	{ .rate = 16,   .reg = 0x02 },
+	{ .rate = 8,    .reg = 0x03 },
+	{ .rate = 4,    .reg = 0x04 },
+	{ .rate = 2,    .reg = 0x05 },
+	{ .rate = 1,    .reg = 0x06 },
+};
+
+struct ads131e08_pga_gain_desc {
+	unsigned int gain;  /* PGA gain value */
+	u8 reg;             /* field value */
+};
+
+static const struct ads131e08_pga_gain_desc ads131e08_pga_gain_tbl[] = {
+	{ .gain = 1,   .reg = 0x01 },
+	{ .gain = 2,   .reg = 0x02 },
+	{ .gain = 4,   .reg = 0x04 },
+	{ .gain = 8,   .reg = 0x05 },
+	{ .gain = 12,  .reg = 0x06 },
+};
+
+static const u8 ads131e08_valid_channel_mux_values[] = { 0, 1, 3, 4 };
+
+static int ads131e08_exec_cmd(struct ads131e08_state *st, u8 cmd)
+{
+	int ret;
+
+	ret = spi_write_then_read(st->spi, &cmd, 1, NULL, 0);
+	if (ret) {
+		dev_err(&st->spi->dev,
+			"%s: SPI write failed for cmd: %02x\n", __func__, cmd);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int ads131e08_read_reg(struct ads131e08_state *st, u8 reg)
+{
+	int ret;
+
+	struct spi_transfer transfer[] = {
+		{
+			.tx_buf = &st->tx_buf,
+			.len = 2,
+			.delay_usecs = st->sdecode_delay_us,
+		}, {
+			.rx_buf = &st->rx_buf,
+			.len = 1,
+		},
+	};
+
+	st->tx_buf[0] = ADS131E08_CMD_RREG(reg);
+	st->tx_buf[1] = 0;
+
+	ret = spi_sync_transfer(st->spi, transfer, ARRAY_SIZE(transfer));
+	if (ret) {
+		dev_err(&st->spi->dev, "%s: SPI transfer failed\n", __func__);
+		return ret;
+	}
+
+	return st->rx_buf[0];
+}
+
+static int ads131e08_write_reg(struct ads131e08_state *st, u8 reg, u8 value)
+{
+	int ret;
+
+	struct spi_transfer transfer[] = {
+		{
+			.tx_buf = &st->tx_buf,
+			.len = 3,
+			.delay_usecs = st->sdecode_delay_us,
+		}
+	};
+
+	st->tx_buf[0] = ADS131E08_CMD_WREG(reg);
+	st->tx_buf[1] = 0;
+	st->tx_buf[2] = value;
+
+	ret = spi_sync_transfer(st->spi, transfer, ARRAY_SIZE(transfer));
+	if (ret) {
+		dev_err(&st->spi->dev, "%s: SPI transfer failed\n", __func__);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int ads131e08_read_data(struct ads131e08_state *st, int rx_len)
+{
+	int ret;
+
+	struct spi_transfer transfer[] = {
+		{
+			.tx_buf = &st->tx_buf,
+			.len = 1,
+		}, {
+			.rx_buf = &st->rx_buf,
+			.len = rx_len,
+		},
+	};
+
+	st->tx_buf[0] = ADS131E08_CMD_RDATA;
+
+	ret = spi_sync_transfer(st->spi, transfer, ARRAY_SIZE(transfer));
+	if (ret) {
+		dev_err(&st->spi->dev, "%s: SPI transfer failed\n", __func__);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int ads131e08_set_data_rate(struct ads131e08_state *st, int data_rate)
+{
+	int i, reg, ret;
+
+	for (i = 0; i < ARRAY_SIZE(ads131e08_data_rate_tbl); ++i) {
+		if (ads131e08_data_rate_tbl[i].rate == data_rate)
+			break;
+	}
+
+	if (i == ARRAY_SIZE(ads131e08_data_rate_tbl)) {
+		dev_err(&st->spi->dev, "invalid data rate value\n");
+		return -EINVAL;
+	}
+
+	reg = ads131e08_read_reg(st, ADS131E08_ADR_CFG1R);
+	if (reg < 0)
+		return reg;
+
+	reg &= ~ADS131E08_CFG1R_DR_MASK;
+	reg |= FIELD_PREP(ADS131E08_CFG1R_DR_MASK,
+		ads131e08_data_rate_tbl[i].reg);
+
+	ret = ads131e08_write_reg(st, ADS131E08_ADR_CFG1R, reg);
+	if (ret)
+		return ret;
+
+	st->data_rate = data_rate;
+	st->readback_len = ADS131E08_NUM_STATUS_BYTES +
+		ADS131E08_NUM_DATA_BYTES(st->data_rate) *
+		st->info->max_channels;
+
+	return 0;
+}
+
+static int ads131e08_pga_gain_to_field_value(struct ads131e08_state *st,
+	unsigned int pga_gain)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(ads131e08_pga_gain_tbl); ++i) {
+		if (ads131e08_pga_gain_tbl[i].gain == pga_gain)
+			break;
+	}
+
+	if (i == ARRAY_SIZE(ads131e08_pga_gain_tbl)) {
+		dev_err(&st->spi->dev, "invalid PGA gain value\n");
+		return -EINVAL;
+	}
+
+	return ads131e08_pga_gain_tbl[i].reg;
+}
+
+static int ads131e08_set_pga_gain(struct ads131e08_state *st,
+	unsigned int channel, unsigned int pga_gain)
+{
+	int field_value, reg;
+
+	field_value = ads131e08_pga_gain_to_field_value(st, pga_gain);
+	if (field_value < 0)
+		return field_value;
+
+	reg = ads131e08_read_reg(st, ADS131E08_ADR_CH0R + channel);
+	if (reg < 0)
+		return reg;
+
+	reg &= ~ADS131E08_CHR_GAIN_MASK;
+	reg |= FIELD_PREP(ADS131E08_CHR_GAIN_MASK, field_value);
+
+	return ads131e08_write_reg(st, ADS131E08_ADR_CH0R + channel, reg);
+}
+
+static int ads131e08_validate_channel_mux(struct ads131e08_state *st,
+	unsigned int mux)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(ads131e08_valid_channel_mux_values); ++i) {
+		if (ads131e08_valid_channel_mux_values[i] == mux)
+			break;
+	}
+
+	if (i == ARRAY_SIZE(ads131e08_valid_channel_mux_values)) {
+		dev_err(&st->spi->dev, "invalid channel mux value\n");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int ads131e08_set_channel_mux(struct ads131e08_state *st,
+	unsigned int channel, unsigned int mux)
+{
+	int reg;
+
+	reg = ads131e08_read_reg(st, ADS131E08_ADR_CH0R + channel);
+	if (reg < 0)
+		return reg;
+
+	reg &= ~ADS131E08_CHR_MUX_MASK;
+	reg |= FIELD_PREP(ADS131E08_CHR_MUX_MASK, mux);
+
+	return ads131e08_write_reg(st, ADS131E08_ADR_CH0R + channel, reg);
+}
+
+static int ads131e08_power_down_channel(struct ads131e08_state *st,
+	unsigned int channel, bool value)
+{
+	int reg;
+
+	reg = ads131e08_read_reg(st, ADS131E08_ADR_CH0R + channel);
+	if (reg < 0)
+		return reg;
+
+	reg &= ~ADS131E08_CHR_PWD_MASK;
+	reg |= FIELD_PREP(ADS131E08_CHR_PWD_MASK, value);
+
+	return ads131e08_write_reg(st, ADS131E08_ADR_CH0R + channel, reg);
+}
+
+static int ads131e08_config_reference_voltage(struct ads131e08_state *st)
+{
+	int reg;
+
+	reg = ads131e08_read_reg(st, ADS131E08_ADR_CFG3R);
+	if (reg < 0)
+		return reg;
+
+	reg &= ~ADS131E08_CFG3R_PDB_REFBUF_MASK;
+	if (IS_ERR(st->vref_reg)) {
+		reg |= FIELD_PREP(ADS131E08_CFG3R_PDB_REFBUF_MASK, 1);
+		reg &= ~ADS131E08_CFG3R_VREF_4V_MASK;
+		reg |= FIELD_PREP(ADS131E08_CFG3R_VREF_4V_MASK,
+			st->vref_mv == ADS131E08_VREF_4V_MV);
+	}
+
+	return ads131e08_write_reg(st, ADS131E08_ADR_CFG3R, reg);
+}
+
+static int ads131e08_initial_config(struct iio_dev *indio_dev)
+{
+	struct ads131e08_state *st = iio_priv(indio_dev);
+	const struct iio_chan_spec *channel = indio_dev->channels;
+	int ret, i;
+	u8 active_channels = 0;
+
+	ret = ads131e08_exec_cmd(st, ADS131E08_CMD_RESET);
+	if (ret)
+		return ret;
+
+	udelay(st->reset_delay_us);
+
+	/* Disable read data in continuous mode (enabled by default) */
+	ret = ads131e08_exec_cmd(st, ADS131E08_CMD_SDATAC);
+	if (ret)
+		return ret;
+
+	ret = ads131e08_set_data_rate(st, ADS131E08_DEFAULT_DATA_RATE);
+	if (ret)
+		return ret;
+
+	ret = ads131e08_config_reference_voltage(st);
+	if (ret)
+		return ret;
+
+	for (i = 0;  i < indio_dev->num_channels; ++i) {
+		ret = ads131e08_set_pga_gain(st, channel->channel,
+			st->channel_config[i].pga_gain);
+		if (ret)
+			return ret;
+
+		ret = ads131e08_set_channel_mux(st, channel->channel,
+			st->channel_config[i].mux);
+		if (ret)
+			return ret;
+
+		active_channels |= BIT(channel->channel);
+		channel++;
+	}
+
+	/* Power down unused channels */
+	for (i = 0; i < st->info->max_channels; ++i) {
+		if (!(active_channels & BIT(i))) {
+			ret = ads131e08_power_down_channel(st, i, true);
+			if (ret)
+				return ret;
+		}
+	}
+
+	/* Request channel offset calibration */
+	ret = ads131e08_exec_cmd(st, ADS131E08_CMD_OFFSETCAL);
+	if (ret)
+		return ret;
+
+	/*
+	 * Channel offset calibration is triggered with first START command.
+	 * Since calibration take more time than settling operation,
+	 * this causes timeout error when command START is sent first
+	 * time (e.g. first call of the ads131e08_read_direct method).
+	 * To avoid this problem offset calibration is triggered here.
+	 */
+	ret = ads131e08_exec_cmd(st, ADS131E08_CMD_START);
+	if (ret)
+		return ret;
+
+	msleep(ADS131E08_WAIT_OFFSETCAL_MS);
+
+	ret = ads131e08_exec_cmd(st, ADS131E08_CMD_STOP);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+static int ads131e08_read_direct(struct iio_dev *indio_dev,
+	struct iio_chan_spec const *channel, int *value)
+{
+	struct ads131e08_state *st = iio_priv(indio_dev);
+	int ret;
+	u8 num_bits, *src;
+
+	reinit_completion(&st->completion);
+
+	ret = ads131e08_exec_cmd(st, ADS131E08_CMD_START);
+	if (ret)
+		return ret;
+
+	ret = wait_for_completion_timeout(&st->completion,
+		msecs_to_jiffies(ADS131E08_MAX_SETTLING_TIME_MS));
+	if (!ret)
+		return -ETIMEDOUT;
+
+	ret = ads131e08_read_data(st, st->readback_len);
+	if (ret)
+		return ret;
+
+	ret = ads131e08_exec_cmd(st, ADS131E08_CMD_STOP);
+	if (ret)
+		return ret;
+
+	src = st->rx_buf + ADS131E08_NUM_STATUS_BYTES +
+		channel->channel * ADS131E08_NUM_DATA_BYTES(st->data_rate);
+
+	num_bits = ADS131E08_NUM_DATA_BITS(st->data_rate);
+	*value = sign_extend32(
+		get_unaligned_be32(src) >> (32 - num_bits), num_bits - 1);
+
+	return ret;
+}
+
+static int ads131e08_read_raw(struct iio_dev *indio_dev,
+	struct iio_chan_spec const *channel, int *value,
+	int *value2, long mask)
+{
+	struct ads131e08_state *st = iio_priv(indio_dev);
+	int ret;
+
+	switch (mask) {
+	case IIO_CHAN_INFO_RAW:
+		ret = iio_device_claim_direct_mode(indio_dev);
+		if (ret)
+			return ret;
+
+		ret = ads131e08_read_direct(indio_dev, channel, value);
+		iio_device_release_direct_mode(indio_dev);
+		if (ret)
+			return ret;
+
+		return IIO_VAL_INT;
+
+	case IIO_CHAN_INFO_SCALE:
+		if (!IS_ERR(st->vref_reg)) {
+			ret = regulator_get_voltage(st->vref_reg);
+			if (ret < 0)
+				return ret;
+
+			*value = ret / 1000;
+		} else {
+			*value = st->vref_mv;
+		}
+
+		*value /= st->channel_config[channel->address].pga_gain;
+		*value2 = ADS131E08_NUM_DATA_BITS(st->data_rate) - 1;
+
+		return IIO_VAL_FRACTIONAL_LOG2;
+
+	case IIO_CHAN_INFO_SAMP_FREQ:
+		*value = st->data_rate;
+
+		return IIO_VAL_INT;
+	}
+
+	return -EINVAL;
+}
+
+static int ads131e08_write_raw(struct iio_dev *indio_dev,
+	struct iio_chan_spec const *channel, int value,
+	int value2, long mask)
+{
+	struct ads131e08_state *st = iio_priv(indio_dev);
+	int ret;
+
+	switch (mask) {
+	case IIO_CHAN_INFO_SAMP_FREQ:
+		ret = iio_device_claim_direct_mode(indio_dev);
+		if (ret)
+			return ret;
+
+		ret = ads131e08_set_data_rate(st, value);
+		iio_device_release_direct_mode(indio_dev);
+		return ret;
+	}
+
+	return -EINVAL;
+}
+
+static IIO_CONST_ATTR_SAMP_FREQ_AVAIL("1 2 4 8 16 32 64");
+
+static struct attribute *ads131e08_attributes[] = {
+	&iio_const_attr_sampling_frequency_available.dev_attr.attr,
+	NULL
+};
+
+static const struct attribute_group ads131e08_attribute_group = {
+	.attrs = ads131e08_attributes,
+};
+
+static int ads131e08_debugfs_reg_access(struct iio_dev *indio_dev,
+	unsigned int reg, unsigned int writeval, unsigned int *readval)
+{
+	struct ads131e08_state *st = iio_priv(indio_dev);
+
+	if (readval) {
+		int ret = ads131e08_read_reg(st, (u8)reg);
+		*readval = ret;
+		return ret;
+	}
+
+	return ads131e08_write_reg(st, (u8)reg, (u8)writeval);
+}
+
+static const struct iio_info ads131e08_iio_info = {
+	.read_raw = ads131e08_read_raw,
+	.write_raw = ads131e08_write_raw,
+	.attrs = &ads131e08_attribute_group,
+	.debugfs_reg_access = &ads131e08_debugfs_reg_access,
+};
+
+static int ads131e08_set_trigger_state(struct iio_trigger *trig, bool state)
+{
+	struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig);
+	struct ads131e08_state *st = iio_priv(indio_dev);
+	u8 cmd = state ? ADS131E08_CMD_START : ADS131E08_CMD_STOP;
+
+	return ads131e08_exec_cmd(st, cmd);
+}
+
+static const struct iio_trigger_ops ads131e08_trigger_ops = {
+	.set_trigger_state = &ads131e08_set_trigger_state,
+};
+
+static irqreturn_t ads131e08_trigger_handler(int irq, void *private)
+{
+	struct iio_poll_func *pf = private;
+	struct iio_dev *indio_dev = pf->indio_dev;
+	struct ads131e08_state *st = iio_priv(indio_dev);
+	int ret;
+	unsigned int chn, i = 0;
+	u8 *src, *dest;
+	/*
+	 * The number of data bits per channel depends on the data rate.
+	 * For 32 and 64 ksps data rates, number of data bits per channel
+	 * is 16. This case is not compliant with used (fixed) scan element
+	 * type (be:s24/32>>8). So we use a litle tweek to pack properly
+	 * 16 bits of data into the buffer.
+	 */
+	unsigned int num_bytes = ADS131E08_NUM_DATA_BYTES(st->data_rate);
+	u8 tweek_offset = num_bytes == 2 ? 1 : 0;
+
+	ret = ads131e08_read_data(st, st->readback_len);
+	if (ret)
+		goto out;
+
+	for_each_set_bit(chn, indio_dev->active_scan_mask,
+		indio_dev->masklength) {
+		src = st->rx_buf + ADS131E08_NUM_STATUS_BYTES + chn * num_bytes;
+		dest = st->tmp_buf.data + i * ADS131E08_NUM_STORAGE_BYTES;
+
+		/*
+		 * Tweek offset is 0:
+		 * +---+---+---+---+
+		 * |D0 |D1 |D2 | X | (3 data bytes)
+		 * +---+---+---+---+
+		 *  a+0 a+1 a+2 a+3
+		 *
+		 * Tweek offset is 1:
+		 * +---+---+---+---+
+		 * |P0 |D0 |D1 | X | (one padding byte and 2 data bytes)
+		 * +---+---+---+---+
+		 *  a+0 a+1 a+2 a+3
+		 */
+		memcpy(dest + tweek_offset, src, num_bytes);
+
+		/*
+		 * Data conversion from 16 bits of data to 24 bits of data
+		 * is done by sign etension (properly filling padding byte).
+		 */
+		if (tweek_offset)
+			memset(dest, *src & BIT(7) ? 0xff : 0x00, 1);
+
+		i++;
+	}
+
+	iio_push_to_buffers_with_timestamp(indio_dev, st->tmp_buf.data,
+		iio_get_time_ns(indio_dev));
+
+out:
+	iio_trigger_notify_done(indio_dev->trig);
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t ads131e08_interrupt(int irq, void *private)
+{
+	struct iio_dev *indio_dev = private;
+	struct ads131e08_state *st = iio_priv(indio_dev);
+
+	if (iio_buffer_enabled(indio_dev))
+		iio_trigger_poll(st->trig);
+	else
+		complete(&st->completion);
+
+	return IRQ_HANDLED;
+}
+
+static int ads131e08_parse_dt(struct iio_dev *indio_dev,
+	struct device_node *node)
+{
+	struct ads131e08_state *st = iio_priv(indio_dev);
+	struct iio_chan_spec *channels;
+	struct ads131e08_channel_config *channel_config;
+	struct device_node *child;
+	unsigned int channel, tmp;
+	int num_channels, i, ret;
+
+	ret = of_property_read_u32(node, "ti,vref-internal", &tmp);
+	if (ret)
+		tmp = 0;
+
+	switch (tmp) {
+	case 0:
+		st->vref_mv = ADS131E08_VREF_2V4_MV;
+		break;
+	case 1:
+		st->vref_mv = ADS131E08_VREF_4V_MV;
+		break;
+	default:
+		dev_err(&st->spi->dev, "invalid internal voltage reference\n");
+		return -EINVAL;
+	}
+
+	num_channels = of_get_available_child_count(node);
+	if (num_channels == 0) {
+		dev_err(&st->spi->dev, "no channel children\n");
+		return -ENODEV;
+	}
+
+	if (num_channels > st->info->max_channels) {
+		dev_err(&st->spi->dev, "num of channel children out of range\n");
+		return -EINVAL;
+	}
+
+	channels = devm_kcalloc(&st->spi->dev, num_channels,
+		sizeof(*channels), GFP_KERNEL);
+	if (!channels)
+		return -ENOMEM;
+
+	channel_config = devm_kcalloc(&st->spi->dev, num_channels,
+		sizeof(*channel_config), GFP_KERNEL);
+	if (!channel_config)
+		return -ENOMEM;
+
+	i = 0;
+	for_each_available_child_of_node(node, child) {
+		ret = of_property_read_u32(child, "reg", &channel);
+		if (ret)
+			return ret;
+
+		ret = of_property_read_u32(child, "ti,gain", &tmp);
+		if (ret) {
+			channel_config[i].pga_gain = ADS131E08_DEFAULT_PGA_GAIN;
+		} else {
+			ret = ads131e08_pga_gain_to_field_value(st, tmp);
+			if (ret < 0)
+				return ret;
+
+			channel_config[i].pga_gain = tmp;
+		}
+
+		ret = of_property_read_u32(child, "ti,mux", &tmp);
+		if (ret) {
+			channel_config[i].mux = ADS131E08_DEFAULT_MUX;
+		} else {
+			ret = ads131e08_validate_channel_mux(st, tmp);
+			if (ret)
+				return ret;
+
+			channel_config[i].mux = tmp;
+		}
+
+		channels[i].type = IIO_VOLTAGE;
+		channels[i].indexed = 1;
+		channels[i].channel = channel;
+		channels[i].address = i;
+		channels[i].info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+						BIT(IIO_CHAN_INFO_SCALE);
+		channels[i].info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ);
+		channels[i].scan_index = channel;
+		channels[i].scan_type.sign = 's';
+		channels[i].scan_type.realbits = 24;
+		channels[i].scan_type.storagebits = 32;
+		channels[i].scan_type.shift = 8;
+		channels[i].scan_type.endianness = IIO_BE;
+		i++;
+	}
+
+	indio_dev->channels = channels;
+	indio_dev->num_channels = num_channels;
+	st->channel_config = channel_config;
+
+	return 0;
+}
+
+static void ads131e08_regulator_disable(void *data)
+{
+	struct ads131e08_state *st = data;
+
+	regulator_disable(st->vref_reg);
+}
+
+static void ads131e08_clk_disable(void *data)
+{
+	struct ads131e08_state *st = data;
+
+	clk_disable_unprepare(st->adc_clk);
+}
+
+static int ads131e08_probe(struct spi_device *spi)
+{
+	const struct ads131e08_info *info;
+	struct iio_dev *indio_dev;
+	struct ads131e08_state *st;
+	int ret;
+	unsigned long adc_clk_hz, adc_clk_ns;
+
+	info = of_device_get_match_data(&spi->dev);
+	if (!info) {
+		dev_err(&spi->dev, "failed to get match data\n");
+		return -ENODEV;
+	}
+
+	indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
+	if (!indio_dev) {
+		dev_err(&spi->dev, "failed to allocate IIO device\n");
+		return -ENOMEM;
+	}
+
+	st = iio_priv(indio_dev);
+	st->info = info;
+	st->spi = spi;
+
+	ret = ads131e08_parse_dt(indio_dev, spi->dev.of_node);
+	if (ret)
+		return ret;
+
+	indio_dev->name = st->info->name;
+	indio_dev->dev.parent = &spi->dev;
+	indio_dev->dev.of_node = spi->dev.of_node;
+	indio_dev->info = &ads131e08_iio_info;
+	indio_dev->modes = INDIO_DIRECT_MODE;
+
+	init_completion(&st->completion);
+
+	if (spi->irq) {
+		ret = devm_request_irq(&spi->dev, spi->irq,
+			ads131e08_interrupt,
+			IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+			spi->dev.driver->name, indio_dev);
+		if (ret) {
+			dev_err(&spi->dev, "failed to allocate IRQ\n");
+			return ret;
+		}
+	} else {
+		dev_err(&spi->dev, "data ready IRQ missing\n");
+		return -ENODEV;
+	}
+
+	st->trig = devm_iio_trigger_alloc(&spi->dev, "%s-dev%d",
+		indio_dev->name, indio_dev->id);
+	if (!st->trig) {
+		dev_err(&spi->dev, "failed to allocate IIO trigger\n");
+		return -ENOMEM;
+	}
+
+	st->trig->ops = &ads131e08_trigger_ops;
+	st->trig->dev.parent = &spi->dev;
+	iio_trigger_set_drvdata(st->trig, indio_dev);
+	ret = devm_iio_trigger_register(&spi->dev, st->trig);
+	if (ret) {
+		dev_err(&spi->dev, "failed to register IIO trigger\n");
+		return -ENOMEM;
+	}
+
+	indio_dev->trig = iio_trigger_get(st->trig);
+
+	ret = devm_iio_triggered_buffer_setup(&spi->dev, indio_dev,
+		NULL, &ads131e08_trigger_handler, NULL);
+	if (ret) {
+		dev_err(&spi->dev, "failed to setup IIO buffer\n");
+		return ret;
+	}
+
+	st->vref_reg = devm_regulator_get_optional(&spi->dev, "vref");
+	if (!IS_ERR(st->vref_reg)) {
+		ret = regulator_enable(st->vref_reg);
+		if (ret) {
+			dev_err(&spi->dev,
+				"failed to enable external vref supply\n");
+			return ret;
+		}
+
+		ret = devm_add_action_or_reset(&spi->dev,
+			ads131e08_regulator_disable, st);
+		if (ret) {
+			regulator_disable(st->vref_reg);
+			return ret;
+		}
+	} else {
+		if (PTR_ERR(st->vref_reg) != -ENODEV)
+			return PTR_ERR(st->vref_reg);
+	}
+
+	st->adc_clk = devm_clk_get(&spi->dev, "adc-clk");
+	if (IS_ERR(st->adc_clk)) {
+		dev_err(&spi->dev, "failed to get the ADC clock\n");
+		return PTR_ERR(st->adc_clk);
+	}
+
+	ret = clk_prepare_enable(st->adc_clk);
+	if (ret) {
+		dev_err(&spi->dev, "failed to prepare/enable the ADC clock\n");
+		return ret;
+	}
+
+	ret = devm_add_action_or_reset(&spi->dev, ads131e08_clk_disable, st);
+	if (ret) {
+		clk_disable_unprepare(st->adc_clk);
+		return ret;
+	}
+
+	adc_clk_hz = clk_get_rate(st->adc_clk);
+	if (!adc_clk_hz) {
+		dev_err(&spi->dev, "failed to get the ADC clock rate\n");
+		return  -EINVAL;
+	}
+
+	adc_clk_ns = NSEC_PER_SEC / adc_clk_hz;
+	st->sdecode_delay_us = DIV_ROUND_UP(
+		ADS131E08_WAIT_SDECODE_CYCLES * adc_clk_ns, 1000);
+	st->reset_delay_us = DIV_ROUND_UP(
+		ADS131E08_WAIT_RESET_CYCLES * adc_clk_ns, 1000);
+
+	ret = ads131e08_initial_config(indio_dev);
+	if (ret) {
+		dev_err(&spi->dev, "initial configuration failed\n");
+		return ret;
+	}
+
+	ret = devm_iio_device_register(&spi->dev, indio_dev);
+	if (ret) {
+		dev_err(&spi->dev, "failed to register IIO device\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+static const struct of_device_id ads131e08_of_match[] = {
+	{ .compatible = "ti,ads131e04",
+	  .data = &ads131e08_info_tbl[ads131e04], },
+	{ .compatible = "ti,ads131e06",
+	  .data = &ads131e08_info_tbl[ads131e06], },
+	{ .compatible = "ti,ads131e08",
+	  .data = &ads131e08_info_tbl[ads131e08], },
+	{}
+};
+MODULE_DEVICE_TABLE(of, ads131e08_of_match);
+
+static struct spi_driver ads131e08_driver = {
+	.driver = {
+		.name = "ads131e08",
+		.of_match_table = ads131e08_of_match,
+	},
+	.probe = ads131e08_probe,
+};
+module_spi_driver(ads131e08_driver);
+
+MODULE_AUTHOR("Tomislav Denis <tomislav.denis@avl.com>");
+MODULE_DESCRIPTION("Driver for ADS131E0x ADC family");
+MODULE_LICENSE("GPL v2");