2023-06-14 08:03:29

by Leonard Göhrs

[permalink] [raw]
Subject: [PATCH v1] iio: adc: add buffering support to the TI LMP92064 ADC driver

Enable buffered reading of samples from the LMP92064 ADC.
The main benefit of this change is being able to read out current and
voltage measurements in a single transfer, allowing instantaneous power
measurements.

Reads into the buffer can be triggered by any software triggers, e.g.
the iio-trig-hrtimer:

$ mkdir /sys/kernel/config/iio/triggers/hrtimer/my-trigger
$ cat /sys/bus/iio/devices/iio\:device3/name
lmp92064
$ iio_readdev -t my-trigger -b 16 iio:device3 | hexdump
WARNING: High-speed mode not enabled
0000000 0000 0176 0101 0001 5507 abd5 7645 1768
0000010 0000 016d 0101 0001 ee1e ac6b 7645 1768
...

Signed-off-by: Leonard Göhrs <[email protected]>
---
drivers/iio/adc/ti-lmp92064.c | 54 +++++++++++++++++++++++++++++++++++
1 file changed, 54 insertions(+)

diff --git a/drivers/iio/adc/ti-lmp92064.c b/drivers/iio/adc/ti-lmp92064.c
index c30ed824924f3..03765c4057dda 100644
--- a/drivers/iio/adc/ti-lmp92064.c
+++ b/drivers/iio/adc/ti-lmp92064.c
@@ -16,7 +16,10 @@
#include <linux/spi/spi.h>

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

#define TI_LMP92064_REG_CONFIG_A 0x0000
#define TI_LMP92064_REG_CONFIG_B 0x0001
@@ -91,6 +94,13 @@ static const struct iio_chan_spec lmp92064_adc_channels[] = {
.address = TI_LMP92064_CHAN_INC,
.info_mask_separate =
BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
+ .scan_index = 0,
+ .scan_type = {
+ .sign = 'u',
+ .realbits = 12,
+ .storagebits = 16,
+ .shift = 0,
+ },
.datasheet_name = "INC",
},
{
@@ -98,8 +108,16 @@ static const struct iio_chan_spec lmp92064_adc_channels[] = {
.address = TI_LMP92064_CHAN_INV,
.info_mask_separate =
BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
+ .scan_index = 1,
+ .scan_type = {
+ .sign = 'u',
+ .realbits = 12,
+ .storagebits = 16,
+ .shift = 0,
+ },
.datasheet_name = "INV",
},
+ IIO_CHAN_SOFT_TIMESTAMP(2),
};

static int lmp92064_read_meas(struct lmp92064_adc_priv *priv, u16 *res)
@@ -171,6 +189,37 @@ static int lmp92064_read_raw(struct iio_dev *indio_dev,
}
}

+static irqreturn_t lmp92064_trigger_handler(int irq, void *p)
+{
+ struct iio_poll_func *pf = p;
+ struct iio_dev *indio_dev = pf->indio_dev;
+ struct lmp92064_adc_priv *priv = iio_priv(indio_dev);
+ int i = 0, j, ret;
+ u16 raw[2];
+ u16 *data;
+
+ ret = lmp92064_read_meas(priv, raw);
+ if (ret < 0)
+ goto done;
+
+ data = kmalloc(indio_dev->scan_bytes, GFP_KERNEL);
+ if (!data)
+ goto done;
+
+ for_each_set_bit(j, indio_dev->active_scan_mask, indio_dev->masklength)
+ data[i++] = raw[j];
+
+ iio_push_to_buffers_with_timestamp(indio_dev, data,
+ iio_get_time_ns(indio_dev));
+
+ kfree(data);
+
+done:
+ iio_trigger_notify_done(indio_dev->trig);
+
+ return IRQ_HANDLED;
+}
+
static int lmp92064_reset(struct lmp92064_adc_priv *priv,
struct gpio_desc *gpio_reset)
{
@@ -302,6 +351,11 @@ static int lmp92064_adc_probe(struct spi_device *spi)
indio_dev->num_channels = ARRAY_SIZE(lmp92064_adc_channels);
indio_dev->info = &lmp92064_adc_info;

+ ret = devm_iio_triggered_buffer_setup(dev, indio_dev, NULL,
+ lmp92064_trigger_handler, NULL);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to setup buffered read\n");
+
return devm_iio_device_register(dev, indio_dev);
}


base-commit: 9561de3a55bed6bdd44a12820ba81ec416e705a7
--
2.39.2



2023-06-14 12:11:01

by Lars-Peter Clausen

[permalink] [raw]
Subject: Re: [PATCH v1] iio: adc: add buffering support to the TI LMP92064 ADC driver

On 6/14/23 00:55, Leonard Göhrs wrote:
> Enable buffered reading of samples from the LMP92064 ADC.
> The main benefit of this change is being able to read out current and
> voltage measurements in a single transfer, allowing instantaneous power
> measurements.
>
> Reads into the buffer can be triggered by any software triggers, e.g.
> the iio-trig-hrtimer:
>
> $ mkdir /sys/kernel/config/iio/triggers/hrtimer/my-trigger
> $ cat /sys/bus/iio/devices/iio\:device3/name
> lmp92064
> $ iio_readdev -t my-trigger -b 16 iio:device3 | hexdump
> WARNING: High-speed mode not enabled
> 0000000 0000 0176 0101 0001 5507 abd5 7645 1768
> 0000010 0000 016d 0101 0001 ee1e ac6b 7645 1768
> ...
>
> Signed-off-by: Leonard Göhrs <[email protected]>

Patch looks good. A small comment for an improvement, but not a most have.

> ---
> drivers/iio/adc/ti-lmp92064.c | 54 +++++++++++++++++++++++++++++++++++
> 1 file changed, 54 insertions(+)
>
> diff --git a/drivers/iio/adc/ti-lmp92064.c b/drivers/iio/adc/ti-lmp92064.c
> index c30ed824924f3..03765c4057dda 100644
> --- a/drivers/iio/adc/ti-lmp92064.c
> +++ b/drivers/iio/adc/ti-lmp92064.c
> @@ -16,7 +16,10 @@
> #include <linux/spi/spi.h>
>
> #include <linux/iio/iio.h>
> +#include <linux/iio/buffer.h>
> #include <linux/iio/driver.h>
> +#include <linux/iio/triggered_buffer.h>
> +#include <linux/iio/trigger_consumer.h>
>
> #define TI_LMP92064_REG_CONFIG_A 0x0000
> #define TI_LMP92064_REG_CONFIG_B 0x0001
> @@ -91,6 +94,13 @@ static const struct iio_chan_spec lmp92064_adc_channels[] = {
> .address = TI_LMP92064_CHAN_INC,
> .info_mask_separate =
> BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
> + .scan_index = 0,
> + .scan_type = {
> + .sign = 'u',
> + .realbits = 12,
> + .storagebits = 16,
> + .shift = 0,
> + },
> .datasheet_name = "INC",
> },
> {
> @@ -98,8 +108,16 @@ static const struct iio_chan_spec lmp92064_adc_channels[] = {
> .address = TI_LMP92064_CHAN_INV,
> .info_mask_separate =
> BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
> + .scan_index = 1,
> + .scan_type = {
> + .sign = 'u',
> + .realbits = 12,
> + .storagebits = 16,
> + .shift = 0,
> + },
> .datasheet_name = "INV",
> },
> + IIO_CHAN_SOFT_TIMESTAMP(2),
> };
>
> static int lmp92064_read_meas(struct lmp92064_adc_priv *priv, u16 *res)
> @@ -171,6 +189,37 @@ static int lmp92064_read_raw(struct iio_dev *indio_dev,
> }
> }
>
> +static irqreturn_t lmp92064_trigger_handler(int irq, void *p)
> +{
> + struct iio_poll_func *pf = p;
> + struct iio_dev *indio_dev = pf->indio_dev;
> + struct lmp92064_adc_priv *priv = iio_priv(indio_dev);
> + int i = 0, j, ret;
> + u16 raw[2];
> + u16 *data;
> +
> + ret = lmp92064_read_meas(priv, raw);
> + if (ret < 0)
> + goto done;
> +
> + data = kmalloc(indio_dev->scan_bytes, GFP_KERNEL);
> + if (!data)
> + goto done;
If you want to avoid allocating the buffer each time a sample-set is
captured you can register the `update_scan_mode` callback and allocate
the buffer in there. Or if you know the upper limit of your buffer size,
base on number of channels, and its small you can also use a one time
allocation directly embedded in the priv struct.
> +
> + for_each_set_bit(j, indio_dev->active_scan_mask, indio_dev->masklength)
> + data[i++] = raw[j];
> +
> + iio_push_to_buffers_with_timestamp(indio_dev, data,
> + iio_get_time_ns(indio_dev));
> +
> + kfree(data);
> +
> +done:
> + iio_trigger_notify_done(indio_dev->trig);
> +
> + return IRQ_HANDLED;
> +}
> +
]...\

2023-06-17 18:45:48

by Jonathan Cameron

[permalink] [raw]
Subject: Re: [PATCH v1] iio: adc: add buffering support to the TI LMP92064 ADC driver

On Wed, 14 Jun 2023 09:55:36 +0200
Leonard Göhrs <[email protected]> wrote:

> Enable buffered reading of samples from the LMP92064 ADC.
> The main benefit of this change is being able to read out current and
> voltage measurements in a single transfer, allowing instantaneous power
> measurements.
>
> Reads into the buffer can be triggered by any software triggers, e.g.
> the iio-trig-hrtimer:
>
> $ mkdir /sys/kernel/config/iio/triggers/hrtimer/my-trigger
> $ cat /sys/bus/iio/devices/iio\:device3/name
> lmp92064
> $ iio_readdev -t my-trigger -b 16 iio:device3 | hexdump
> WARNING: High-speed mode not enabled
> 0000000 0000 0176 0101 0001 5507 abd5 7645 1768
> 0000010 0000 016d 0101 0001 ee1e ac6b 7645 1768
> ...
>
> Signed-off-by: Leonard Göhrs <[email protected]>
> ---
> drivers/iio/adc/ti-lmp92064.c | 54 +++++++++++++++++++++++++++++++++++
> 1 file changed, 54 insertions(+)
>
> diff --git a/drivers/iio/adc/ti-lmp92064.c b/drivers/iio/adc/ti-lmp92064.c
> index c30ed824924f3..03765c4057dda 100644
> --- a/drivers/iio/adc/ti-lmp92064.c
> +++ b/drivers/iio/adc/ti-lmp92064.c
> @@ -16,7 +16,10 @@
> #include <linux/spi/spi.h>
>
> #include <linux/iio/iio.h>
> +#include <linux/iio/buffer.h>
> #include <linux/iio/driver.h>
> +#include <linux/iio/triggered_buffer.h>
> +#include <linux/iio/trigger_consumer.h>
>
> #define TI_LMP92064_REG_CONFIG_A 0x0000
> #define TI_LMP92064_REG_CONFIG_B 0x0001
> @@ -91,6 +94,13 @@ static const struct iio_chan_spec lmp92064_adc_channels[] = {
> .address = TI_LMP92064_CHAN_INC,
> .info_mask_separate =
> BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
> + .scan_index = 0,
> + .scan_type = {
> + .sign = 'u',
> + .realbits = 12,
> + .storagebits = 16,
> + .shift = 0,

As zero is the 'obvious' default for shift (do nothing case) and c will set it to 0 for
you anyway, no need to set it explicitly like this.

> + },
> .datasheet_name = "INC",
> },
> {
> @@ -98,8 +108,16 @@ static const struct iio_chan_spec lmp92064_adc_channels[] = {
> .address = TI_LMP92064_CHAN_INV,
> .info_mask_separate =
> BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
> + .scan_index = 1,
> + .scan_type = {
> + .sign = 'u',
> + .realbits = 12,
> + .storagebits = 16,
> + .shift = 0,

Same here. No need for this last line.

> + },
> .datasheet_name = "INV",
> },
> + IIO_CHAN_SOFT_TIMESTAMP(2),
> };
>
> static int lmp92064_read_meas(struct lmp92064_adc_priv *priv, u16 *res)
> @@ -171,6 +189,37 @@ static int lmp92064_read_raw(struct iio_dev *indio_dev,
> }
> }
>
> +static irqreturn_t lmp92064_trigger_handler(int irq, void *p)
> +{
> + struct iio_poll_func *pf = p;
> + struct iio_dev *indio_dev = pf->indio_dev;
> + struct lmp92064_adc_priv *priv = iio_priv(indio_dev);
> + int i = 0, j, ret;
> + u16 raw[2];
> + u16 *data;
> +
> + ret = lmp92064_read_meas(priv, raw);
> + if (ret < 0)
> + goto done;
> +
> + data = kmalloc(indio_dev->scan_bytes, GFP_KERNEL);

A driver shouldn't be directly accessing scan_bytes.
You will see iio.h that it is marked [INTERN] to show it should only be accessed
by the IIO core code.

The reasoning is closely related to Lars' point. This is almost always a small
structure with a known maximum size (across all possible channel configurations)
so allocate that space once not every scan (will be 16 bytes I think)


> + if (!data)
> + goto done;
> +
> + for_each_set_bit(j, indio_dev->active_scan_mask, indio_dev->masklength)
> + data[i++] = raw[j];
When a sensor 'always' reads the two channels, we can leave the necessary data
mangling up to the IIO core.

Provide available_scan_masks and then always push the lot every time.
Mostly people will want both channels anyway (as they paid for them ;)
so that core code that moves the data around will do nothing. In the cases
where they only want one channel it will handle the complexity for you.

> +
> + iio_push_to_buffers_with_timestamp(indio_dev, data,
> + iio_get_time_ns(indio_dev));
> +
> + kfree(data);
> +
> +done:
> + iio_trigger_notify_done(indio_dev->trig);
> +
> + return IRQ_HANDLED;
> +}
> +
> static int lmp92064_reset(struct lmp92064_adc_priv *priv,
> struct gpio_desc *gpio_reset)
> {
> @@ -302,6 +351,11 @@ static int lmp92064_adc_probe(struct spi_device *spi)
> indio_dev->num_channels = ARRAY_SIZE(lmp92064_adc_channels);
> indio_dev->info = &lmp92064_adc_info;
>
> + ret = devm_iio_triggered_buffer_setup(dev, indio_dev, NULL,
> + lmp92064_trigger_handler, NULL);
> + if (ret)
> + return dev_err_probe(dev, ret, "Failed to setup buffered read\n");
> +
> return devm_iio_device_register(dev, indio_dev);
> }
>
>
> base-commit: 9561de3a55bed6bdd44a12820ba81ec416e705a7


2023-06-22 07:11:59

by Leonard Göhrs

[permalink] [raw]
Subject: Re: [PATCH v1] iio: adc: add buffering support to the TI LMP92064 ADC driver



On 17.06.23 20:27, Jonathan Cameron wrote:
> On Wed, 14 Jun 2023 09:55:36 +0200
> Leonard Göhrs <[email protected]> wrote:
>
>> Enable buffered reading of samples from the LMP92064 ADC.
>> The main benefit of this change is being able to read out current and
>> voltage measurements in a single transfer, allowing instantaneous power
>> measurements.
>>
>> Reads into the buffer can be triggered by any software triggers, e.g.
>> the iio-trig-hrtimer:
>>
>> $ mkdir /sys/kernel/config/iio/triggers/hrtimer/my-trigger
>> $ cat /sys/bus/iio/devices/iio\:device3/name
>> lmp92064
>> $ iio_readdev -t my-trigger -b 16 iio:device3 | hexdump
>> WARNING: High-speed mode not enabled
>> 0000000 0000 0176 0101 0001 5507 abd5 7645 1768
>> 0000010 0000 016d 0101 0001 ee1e ac6b 7645 1768
>> ...
>>
>> Signed-off-by: Leonard Göhrs <[email protected]>
>> ---
>> drivers/iio/adc/ti-lmp92064.c | 54 +++++++++++++++++++++++++++++++++++
>> 1 file changed, 54 insertions(+)
>>
>> diff --git a/drivers/iio/adc/ti-lmp92064.c b/drivers/iio/adc/ti-lmp92064.c
>> index c30ed824924f3..03765c4057dda 100644
>> --- a/drivers/iio/adc/ti-lmp92064.c
>> +++ b/drivers/iio/adc/ti-lmp92064.c
>> @@ -16,7 +16,10 @@
>> #include <linux/spi/spi.h>
>>
>> #include <linux/iio/iio.h>
>> +#include <linux/iio/buffer.h>
>> #include <linux/iio/driver.h>
>> +#include <linux/iio/triggered_buffer.h>
>> +#include <linux/iio/trigger_consumer.h>
>>
>> #define TI_LMP92064_REG_CONFIG_A 0x0000
>> #define TI_LMP92064_REG_CONFIG_B 0x0001
>> @@ -91,6 +94,13 @@ static const struct iio_chan_spec lmp92064_adc_channels[] = {
>> .address = TI_LMP92064_CHAN_INC,
>> .info_mask_separate =
>> BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
>> + .scan_index = 0,
>> + .scan_type = {
>> + .sign = 'u',
>> + .realbits = 12,
>> + .storagebits = 16,
>> + .shift = 0,
>
> As zero is the 'obvious' default for shift (do nothing case) and c will set it to 0 for
> you anyway, no need to set it explicitly like this.
>
>> + },
>> .datasheet_name = "INC",
>> },
>> {
>> @@ -98,8 +108,16 @@ static const struct iio_chan_spec lmp92064_adc_channels[] = {
>> .address = TI_LMP92064_CHAN_INV,
>> .info_mask_separate =
>> BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
>> + .scan_index = 1,
>> + .scan_type = {
>> + .sign = 'u',
>> + .realbits = 12,
>> + .storagebits = 16,
>> + .shift = 0,
>
> Same here. No need for this last line.
>
>> + },
>> .datasheet_name = "INV",
>> },
>> + IIO_CHAN_SOFT_TIMESTAMP(2),
>> };
>>
>> static int lmp92064_read_meas(struct lmp92064_adc_priv *priv, u16 *res)
>> @@ -171,6 +189,37 @@ static int lmp92064_read_raw(struct iio_dev *indio_dev,
>> }
>> }
>>
>> +static irqreturn_t lmp92064_trigger_handler(int irq, void *p)
>> +{
>> + struct iio_poll_func *pf = p;
>> + struct iio_dev *indio_dev = pf->indio_dev;
>> + struct lmp92064_adc_priv *priv = iio_priv(indio_dev);
>> + int i = 0, j, ret;
>> + u16 raw[2];
>> + u16 *data;
>> +
>> + ret = lmp92064_read_meas(priv, raw);
>> + if (ret < 0)
>> + goto done;
>> +
>> + data = kmalloc(indio_dev->scan_bytes, GFP_KERNEL);
>
> A driver shouldn't be directly accessing scan_bytes.
> You will see iio.h that it is marked [INTERN] to show it should only be accessed
> by the IIO core code.
>
> The reasoning is closely related to Lars' point. This is almost always a small
> structure with a known maximum size (across all possible channel configurations)
> so allocate that space once not every scan (will be 16 bytes I think)

Sounds reasonable. I've more or less copied the trigger handler from
iio_simple_dummy_buffer.c assuming it contained the best practice when
it comes to buffer handling w.r.t. size and alignment (which I was not quite
sure about how to do right).

I've sent a V2 now that uses a stack allocated structure as buffer. This makes
the whole trigger handler a lot more concise.

>> + if (!data)
>> + goto done;
>> +
>> + for_each_set_bit(j, indio_dev->active_scan_mask, indio_dev->masklength)
>> + data[i++] = raw[j];
> When a sensor 'always' reads the two channels, we can leave the necessary data
> mangling up to the IIO core.
>
> Provide available_scan_masks and then always push the lot every time.
> Mostly people will want both channels anyway (as they paid for them ;)
> so that core code that moves the data around will do nothing. In the cases
> where they only want one channel it will handle the complexity for you.

Thanks for the tip! This simplifies the trigger handler even further.

>> +
>> + iio_push_to_buffers_with_timestamp(indio_dev, data,
>> + iio_get_time_ns(indio_dev));
>> +
>> + kfree(data);
>> +
>> +done:
>> + iio_trigger_notify_done(indio_dev->trig);
>> +
>> + return IRQ_HANDLED;
>> +}
>> +
>> static int lmp92064_reset(struct lmp92064_adc_priv *priv,
>> struct gpio_desc *gpio_reset)
>> {
>> @@ -302,6 +351,11 @@ static int lmp92064_adc_probe(struct spi_device *spi)
>> indio_dev->num_channels = ARRAY_SIZE(lmp92064_adc_channels);
>> indio_dev->info = &lmp92064_adc_info;
>>
>> + ret = devm_iio_triggered_buffer_setup(dev, indio_dev, NULL,
>> + lmp92064_trigger_handler, NULL);
>> + if (ret)
>> + return dev_err_probe(dev, ret, "Failed to setup buffered read\n");
>> +
>> return devm_iio_device_register(dev, indio_dev);
>> }
>>
>>
>> base-commit: 9561de3a55bed6bdd44a12820ba81ec416e705a7
>
>