2022-08-24 10:46:49

by Vincent Whitchurch

[permalink] [raw]
Subject: [PATCH 2/2] iio: adc: mcp320x: add triggered buffer support

From: Axel Jonsson <[email protected]>

Add support for triggered buffers. Just read the channels in a loop to
keep things simple.

Signed-off-by: Axel Jonsson <[email protected]>
Signed-off-by: Vincent Whitchurch <[email protected]>
---
drivers/iio/adc/Kconfig | 2 +
drivers/iio/adc/mcp320x.c | 130 ++++++++++++++++++++++++++++----------
2 files changed, 99 insertions(+), 33 deletions(-)

diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
index 48ace7412874..9f2628120885 100644
--- a/drivers/iio/adc/Kconfig
+++ b/drivers/iio/adc/Kconfig
@@ -696,6 +696,8 @@ config MAX9611
config MCP320X
tristate "Microchip Technology MCP3x01/02/04/08 and MCP3550/1/3"
depends on SPI
+ select IIO_BUFFER
+ select IIO_TRIGGERED_BUFFER
help
Say yes here to build support for Microchip Technology's
MCP3001, MCP3002, MCP3004, MCP3008, MCP3201, MCP3202, MCP3204,
diff --git a/drivers/iio/adc/mcp320x.c b/drivers/iio/adc/mcp320x.c
index 28398f34628a..9782cbd37728 100644
--- a/drivers/iio/adc/mcp320x.c
+++ b/drivers/iio/adc/mcp320x.c
@@ -43,6 +43,10 @@
#include <linux/module.h>
#include <linux/mod_devicetable.h>
#include <linux/iio/iio.h>
+#include <linux/interrupt.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/triggered_buffer.h>
+#include <linux/iio/trigger_consumer.h>
#include <linux/regulator/consumer.h>

enum {
@@ -231,37 +235,51 @@ static int mcp320x_read_raw(struct iio_dev *indio_dev,
return ret;
}

-#define MCP320X_VOLTAGE_CHANNEL(num) \
- { \
- .type = IIO_VOLTAGE, \
- .indexed = 1, \
- .channel = (num), \
- .address = (num), \
- .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
- .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) \
+#define MCP320X_VOLTAGE_CHANNEL(num) \
+ { \
+ .type = IIO_VOLTAGE, \
+ .indexed = 1, \
+ .channel = (num), \
+ .address = (num), \
+ .scan_index = (num), \
+ .scan_type = { \
+ .sign = 's', \
+ .realbits = 32, \
+ .storagebits = 32, \
+ .endianness = IIO_CPU, \
+ }, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) \
}

-#define MCP320X_VOLTAGE_CHANNEL_DIFF(chan1, chan2) \
- { \
- .type = IIO_VOLTAGE, \
- .indexed = 1, \
- .channel = (chan1), \
- .channel2 = (chan2), \
- .address = (chan1), \
- .differential = 1, \
- .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
- .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) \
+#define MCP320X_VOLTAGE_CHANNEL_DIFF(chan1, chan2, diffoff) \
+ { \
+ .type = IIO_VOLTAGE, \
+ .indexed = 1, \
+ .channel = (chan1), \
+ .channel2 = (chan2), \
+ .address = (chan1), \
+ .scan_index = (chan1 + diffoff), \
+ .scan_type = { \
+ .sign = 's', \
+ .realbits = 32, \
+ .storagebits = 32, \
+ .endianness = IIO_CPU, \
+ }, \
+ .differential = 1, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) \
}

static const struct iio_chan_spec mcp3201_channels[] = {
- MCP320X_VOLTAGE_CHANNEL_DIFF(0, 1),
+ MCP320X_VOLTAGE_CHANNEL_DIFF(0, 1, 1),
};

static const struct iio_chan_spec mcp3202_channels[] = {
MCP320X_VOLTAGE_CHANNEL(0),
MCP320X_VOLTAGE_CHANNEL(1),
- MCP320X_VOLTAGE_CHANNEL_DIFF(0, 1),
- MCP320X_VOLTAGE_CHANNEL_DIFF(1, 0),
+ MCP320X_VOLTAGE_CHANNEL_DIFF(0, 1, 2),
+ MCP320X_VOLTAGE_CHANNEL_DIFF(1, 0, 2),
};

static const struct iio_chan_spec mcp3204_channels[] = {
@@ -269,10 +287,10 @@ static const struct iio_chan_spec mcp3204_channels[] = {
MCP320X_VOLTAGE_CHANNEL(1),
MCP320X_VOLTAGE_CHANNEL(2),
MCP320X_VOLTAGE_CHANNEL(3),
- MCP320X_VOLTAGE_CHANNEL_DIFF(0, 1),
- MCP320X_VOLTAGE_CHANNEL_DIFF(1, 0),
- MCP320X_VOLTAGE_CHANNEL_DIFF(2, 3),
- MCP320X_VOLTAGE_CHANNEL_DIFF(3, 2),
+ MCP320X_VOLTAGE_CHANNEL_DIFF(0, 1, 4),
+ MCP320X_VOLTAGE_CHANNEL_DIFF(1, 0, 4),
+ MCP320X_VOLTAGE_CHANNEL_DIFF(2, 3, 4),
+ MCP320X_VOLTAGE_CHANNEL_DIFF(3, 2, 4),
};

static const struct iio_chan_spec mcp3208_channels[] = {
@@ -284,14 +302,14 @@ static const struct iio_chan_spec mcp3208_channels[] = {
MCP320X_VOLTAGE_CHANNEL(5),
MCP320X_VOLTAGE_CHANNEL(6),
MCP320X_VOLTAGE_CHANNEL(7),
- MCP320X_VOLTAGE_CHANNEL_DIFF(0, 1),
- MCP320X_VOLTAGE_CHANNEL_DIFF(1, 0),
- MCP320X_VOLTAGE_CHANNEL_DIFF(2, 3),
- MCP320X_VOLTAGE_CHANNEL_DIFF(3, 2),
- MCP320X_VOLTAGE_CHANNEL_DIFF(4, 5),
- MCP320X_VOLTAGE_CHANNEL_DIFF(5, 4),
- MCP320X_VOLTAGE_CHANNEL_DIFF(6, 7),
- MCP320X_VOLTAGE_CHANNEL_DIFF(7, 6),
+ MCP320X_VOLTAGE_CHANNEL_DIFF(0, 1, 8),
+ MCP320X_VOLTAGE_CHANNEL_DIFF(1, 0, 8),
+ MCP320X_VOLTAGE_CHANNEL_DIFF(2, 3, 8),
+ MCP320X_VOLTAGE_CHANNEL_DIFF(3, 2, 8),
+ MCP320X_VOLTAGE_CHANNEL_DIFF(4, 5, 8),
+ MCP320X_VOLTAGE_CHANNEL_DIFF(5, 4, 8),
+ MCP320X_VOLTAGE_CHANNEL_DIFF(6, 7, 8),
+ MCP320X_VOLTAGE_CHANNEL_DIFF(7, 6, 8),
};

static const struct iio_info mcp320x_info = {
@@ -371,6 +389,46 @@ static const struct mcp320x_chip_info mcp320x_chip_infos[] = {
},
};

+static irqreturn_t mcp320x_buffer_trigger_handler(int irq, void *pollfunc)
+{
+ struct iio_poll_func *pf = pollfunc;
+ struct iio_dev *indio_dev = pf->indio_dev;
+ struct mcp320x *adc = iio_priv(indio_dev);
+ s32 data[ARRAY_SIZE(mcp3208_channels)];
+ int device_index;
+ int i = 0;
+ int chan;
+
+ device_index = spi_get_device_id(adc->spi)->driver_data;
+
+ mutex_lock(&adc->lock);
+
+ for_each_set_bit(chan, indio_dev->active_scan_mask, indio_dev->masklength) {
+ const struct iio_chan_spec *spec = &indio_dev->channels[chan];
+ int ret, val;
+
+ ret = mcp320x_adc_conversion(adc, spec->address,
+ spec->differential, device_index,
+ &val);
+ if (ret < 0) {
+ dev_err_ratelimited(&adc->spi->dev, "Failed to read data: %d\n",
+ ret);
+ goto out;
+ }
+
+ data[i++] = val;
+ }
+
+ iio_push_to_buffers(indio_dev, data);
+
+out:
+ mutex_unlock(&adc->lock);
+
+ iio_trigger_notify_done(indio_dev->trig);
+
+ return IRQ_HANDLED;
+}
+
static void mcp320x_reg_disable(void *reg)
{
regulator_disable(reg);
@@ -456,6 +514,12 @@ static int mcp320x_probe(struct spi_device *spi)

mutex_init(&adc->lock);

+ ret = devm_iio_triggered_buffer_setup(&spi->dev, indio_dev, NULL,
+ mcp320x_buffer_trigger_handler,
+ NULL);
+ if (ret)
+ return ret;
+
return devm_iio_device_register(&spi->dev, indio_dev);
}

--
2.34.1


2022-08-25 20:25:25

by Andy Shevchenko

[permalink] [raw]
Subject: Re: [PATCH 2/2] iio: adc: mcp320x: add triggered buffer support

On Wed, Aug 24, 2022 at 1:46 PM Vincent Whitchurch
<[email protected]> wrote:
>
> From: Axel Jonsson <[email protected]>
>
> Add support for triggered buffers. Just read the channels in a loop to
> keep things simple.

...

> #include <linux/module.h>
> #include <linux/mod_devicetable.h>
> #include <linux/iio/iio.h>
> +#include <linux/interrupt.h>

Ordering?

But honestly, I prefer the linux/iio/* to be split in a separate group...

> +#include <linux/iio/buffer.h>
> +#include <linux/iio/triggered_buffer.h>
> +#include <linux/iio/trigger_consumer.h>
> #include <linux/regulator/consumer.h>
>

...and be put here.

...

> + device_index = spi_get_device_id(adc->spi)->driver_data;

Hmm... Wondering if this can be derived from channel number or alike.

--
With Best Regards,
Andy Shevchenko

2022-08-28 17:56:57

by Jonathan Cameron

[permalink] [raw]
Subject: Re: [PATCH 2/2] iio: adc: mcp320x: add triggered buffer support

On Thu, 25 Aug 2022 23:06:01 +0300
Andy Shevchenko <[email protected]> wrote:

> On Wed, Aug 24, 2022 at 1:46 PM Vincent Whitchurch
> <[email protected]> wrote:
> >
> > From: Axel Jonsson <[email protected]>
> >
> > Add support for triggered buffers. Just read the channels in a loop to
> > keep things simple.
>
> ...
>
> > #include <linux/module.h>
> > #include <linux/mod_devicetable.h>
> > #include <linux/iio/iio.h>
> > +#include <linux/interrupt.h>
>
> Ordering?
>
> But honestly, I prefer the linux/iio/* to be split in a separate group...
>
> > +#include <linux/iio/buffer.h>
> > +#include <linux/iio/triggered_buffer.h>
> > +#include <linux/iio/trigger_consumer.h>
> > #include <linux/regulator/consumer.h>
> >
>
> ...and be put here.
>
> ...
>
> > + device_index = spi_get_device_id(adc->spi)->driver_data;
>
> Hmm... Wondering if this can be derived from channel number or alike.
>
It is weirder than the name implies.
Seem device_index is actually the chip type from amongst those the driver
supports.

That wants cleaning up, particularly there is also a chip_info structure.
Various ways of doing it, but simplest is probably a set of callbacks
covering the different data extraction methods in mcp320x_adc_conversion.

Jonathan


2022-08-28 18:42:53

by Jonathan Cameron

[permalink] [raw]
Subject: Re: [PATCH 2/2] iio: adc: mcp320x: add triggered buffer support

On Wed, 24 Aug 2022 12:40:02 +0200
Vincent Whitchurch <[email protected]> wrote:

> From: Axel Jonsson <[email protected]>
>
> Add support for triggered buffers. Just read the channels in a loop to
> keep things simple.

Just curious, but what other options are there? A quick datasheet scroll
through didn't seem to suggest you can overlap setup of next read with
reading out the previous (common on SPI ADCs).

A few minor additional comments inline.

Thanks,

Jonathan


>
> Signed-off-by: Axel Jonsson <[email protected]>
> Signed-off-by: Vincent Whitchurch <[email protected]>
> ---
> drivers/iio/adc/Kconfig | 2 +
> drivers/iio/adc/mcp320x.c | 130 ++++++++++++++++++++++++++++----------
> 2 files changed, 99 insertions(+), 33 deletions(-)
>
> diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
> index 48ace7412874..9f2628120885 100644
> --- a/drivers/iio/adc/Kconfig
> +++ b/drivers/iio/adc/Kconfig
> @@ -696,6 +696,8 @@ config MAX9611
> config MCP320X
> tristate "Microchip Technology MCP3x01/02/04/08 and MCP3550/1/3"
> depends on SPI
> + select IIO_BUFFER
> + select IIO_TRIGGERED_BUFFER
> help
> Say yes here to build support for Microchip Technology's
> MCP3001, MCP3002, MCP3004, MCP3008, MCP3201, MCP3202, MCP3204,
> diff --git a/drivers/iio/adc/mcp320x.c b/drivers/iio/adc/mcp320x.c
> index 28398f34628a..9782cbd37728 100644
> --- a/drivers/iio/adc/mcp320x.c
> +++ b/drivers/iio/adc/mcp320x.c
> @@ -43,6 +43,10 @@
> #include <linux/module.h>
> #include <linux/mod_devicetable.h>
> #include <linux/iio/iio.h>
> +#include <linux/interrupt.h>
> +#include <linux/iio/buffer.h>
> +#include <linux/iio/triggered_buffer.h>
> +#include <linux/iio/trigger_consumer.h>
> #include <linux/regulator/consumer.h>
>
> enum {
> @@ -231,37 +235,51 @@ static int mcp320x_read_raw(struct iio_dev *indio_dev,
> return ret;
> }
>
> -#define MCP320X_VOLTAGE_CHANNEL(num) \
> - { \
> - .type = IIO_VOLTAGE, \
> - .indexed = 1, \
> - .channel = (num), \
> - .address = (num), \
> - .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
> - .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) \
> +#define MCP320X_VOLTAGE_CHANNEL(num) \
> + { \
> + .type = IIO_VOLTAGE, \
> + .indexed = 1, \
> + .channel = (num), \
> + .address = (num), \
> + .scan_index = (num), \
> + .scan_type = { \
> + .sign = 's', \
> + .realbits = 32, \
> + .storagebits = 32, \
> + .endianness = IIO_CPU, \
> + }, \
> + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
> + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) \
> }
>
> -#define MCP320X_VOLTAGE_CHANNEL_DIFF(chan1, chan2) \
> - { \
> - .type = IIO_VOLTAGE, \
> - .indexed = 1, \
> - .channel = (chan1), \
> - .channel2 = (chan2), \
> - .address = (chan1), \
> - .differential = 1, \
> - .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
> - .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) \
> +#define MCP320X_VOLTAGE_CHANNEL_DIFF(chan1, chan2, diffoff) \
> + { \
> + .type = IIO_VOLTAGE, \
> + .indexed = 1, \
> + .channel = (chan1), \
> + .channel2 = (chan2), \
> + .address = (chan1), \
> + .scan_index = (chan1 + diffoff), \

I wonder if it's just more readable to use scan_index as one of the parameters
of this macro. If it's the first one, then you'll
see
*VOLTAGE_CHANNEL(0),
*VOLTAGE_CHANNEL(1),
*VOLTAGE_CHANNEL_DIFF(2, 0, 1),
*VOLTAGE_CHANNEL_DIFF(3, 1, 0),
etc

If we can avoid reformatting this in the same patch that adds the new
stuff it will make it easier to see the changes.

If you want to reformat, add another patch to do that as a no-op change.


> + .scan_type = { \
> + .sign = 's', \
> + .realbits = 32, \
> + .storagebits = 32, \
> + .endianness = IIO_CPU, \
> + }, \
> + .differential = 1, \
> + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
> + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) \
> }
>
> static const struct iio_chan_spec mcp3201_channels[] = {
> - MCP320X_VOLTAGE_CHANNEL_DIFF(0, 1),
> + MCP320X_VOLTAGE_CHANNEL_DIFF(0, 1, 1),
> };
>
> static const struct iio_chan_spec mcp3202_channels[] = {
> MCP320X_VOLTAGE_CHANNEL(0),
> MCP320X_VOLTAGE_CHANNEL(1),
> - MCP320X_VOLTAGE_CHANNEL_DIFF(0, 1),
> - MCP320X_VOLTAGE_CHANNEL_DIFF(1, 0),
> + MCP320X_VOLTAGE_CHANNEL_DIFF(0, 1, 2),
> + MCP320X_VOLTAGE_CHANNEL_DIFF(1, 0, 2),
> };
>
> static const struct iio_chan_spec mcp3204_channels[] = {
> @@ -269,10 +287,10 @@ static const struct iio_chan_spec mcp3204_channels[] = {
> MCP320X_VOLTAGE_CHANNEL(1),
> MCP320X_VOLTAGE_CHANNEL(2),
> MCP320X_VOLTAGE_CHANNEL(3),
> - MCP320X_VOLTAGE_CHANNEL_DIFF(0, 1),
> - MCP320X_VOLTAGE_CHANNEL_DIFF(1, 0),
> - MCP320X_VOLTAGE_CHANNEL_DIFF(2, 3),
> - MCP320X_VOLTAGE_CHANNEL_DIFF(3, 2),
> + MCP320X_VOLTAGE_CHANNEL_DIFF(0, 1, 4),
> + MCP320X_VOLTAGE_CHANNEL_DIFF(1, 0, 4),
> + MCP320X_VOLTAGE_CHANNEL_DIFF(2, 3, 4),
> + MCP320X_VOLTAGE_CHANNEL_DIFF(3, 2, 4),
> };


2022-08-31 07:51:14

by Vincent Whitchurch

[permalink] [raw]
Subject: Re: [PATCH 2/2] iio: adc: mcp320x: add triggered buffer support

On Sun, Aug 28, 2022 at 07:24:30PM +0200, Jonathan Cameron wrote:
> On Wed, 24 Aug 2022 12:40:02 +0200
> Vincent Whitchurch <[email protected]> wrote:
> > Add support for triggered buffers. Just read the channels in a loop to
> > keep things simple.
>
> Just curious, but what other options are there? A quick datasheet scroll
> through didn't seem to suggest you can overlap setup of next read with
> reading out the previous (common on SPI ADCs).

You're right that the hardware doesn't support any special method to
read out multiple channels; I can mention that in the commit message.
But I think you could construct a spi_message which a bunch of
spi_transfers which toggle the CS appropriately between them to read out
multiple channels in one go? (Note that the variants have different
data formats, and many of the variants only have one channel.)

2022-09-04 17:21:38

by Jonathan Cameron

[permalink] [raw]
Subject: Re: [PATCH 2/2] iio: adc: mcp320x: add triggered buffer support

On Wed, 31 Aug 2022 09:47:50 +0200
Vincent Whitchurch <[email protected]> wrote:

> On Sun, Aug 28, 2022 at 07:24:30PM +0200, Jonathan Cameron wrote:
> > On Wed, 24 Aug 2022 12:40:02 +0200
> > Vincent Whitchurch <[email protected]> wrote:
> > > Add support for triggered buffers. Just read the channels in a loop to
> > > keep things simple.
> >
> > Just curious, but what other options are there? A quick datasheet scroll
> > through didn't seem to suggest you can overlap setup of next read with
> > reading out the previous (common on SPI ADCs).
>
> You're right that the hardware doesn't support any special method to
> read out multiple channels; I can mention that in the commit message.
> But I think you could construct a spi_message which a bunch of
> spi_transfers which toggle the CS appropriately between them to read out
> multiple channels in one go? (Note that the variants have different
> data formats, and many of the variants only have one channel.)

That might be a little more efficient on a suitably advanced
SPI controller, or where the overhead of jumping between drivers
is large, but in many cases it won't save that much.

Would need an experiment on a platform someone cares about to decide
if it is worthwhile.

Jonathan