RE: [PATCH] staging: iio: adc: Enable driver support for ad7887 AD converter

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

 



Sorry my Outlook QuoteFix Macro often fails with patches...

>-----Original Message-----
>From: Jonathan Cameron [mailto:jic23@xxxxxxxxx]
>Sent: Friday, November 19, 2010 3:44 PM
>To: Hennerich, Michael
>Cc: linux-iio@xxxxxxxxxxxxxxx; Drivers; device-drivers-
>devel@xxxxxxxxxxxxxxxxxxxx
>Subject: Re: [PATCH] staging: iio: adc: Enable driver support for
>ad7887 AD converter
>
>On 11/18/10 16:22, michael.hennerich@xxxxxxxxxx wrote:
>> From: Michael Hennerich <michael.hennerich@xxxxxxxxxx>
>>
>> Enable support for AD7887:  SPI Micropower, 2-Channel, 125 kSPS,
>> 12-Bit ADC
>Nice driver. Few very minor questions / suggestions inline.
>>
>> Signed-off-by: Michael Hennerich <michael.hennerich@xxxxxxxxxx>
>> ---
>>  drivers/staging/iio/adc/Kconfig       |   14 ++
>>  drivers/staging/iio/adc/Makefile      |    4 +
>>  drivers/staging/iio/adc/ad7887.h      |   92 ++++++++++
>>  drivers/staging/iio/adc/ad7887_core.c |  303
>> +++++++++++++++++++++++++++++++++
>> drivers/staging/iio/adc/ad7887_ring.c |  273
>> +++++++++++++++++++++++++++++
>>  5 files changed, 686 insertions(+), 0 deletions(-)  create mode
>> 100644 drivers/staging/iio/adc/ad7887.h  create mode 100644
>> drivers/staging/iio/adc/ad7887_core.c
>>  create mode 100644 drivers/staging/iio/adc/ad7887_ring.c
>>
>> diff --git a/drivers/staging/iio/adc/Kconfig
>> b/drivers/staging/iio/adc/Kconfig index 9ca6565..86869cd 100644
>> --- a/drivers/staging/iio/adc/Kconfig
>> +++ b/drivers/staging/iio/adc/Kconfig
>> @@ -97,6 +97,20 @@ config AD7476
>>        To compile this driver as a module, choose M here: the
>>        module will be called ad7476.
>>
>> +config AD7887
>> +    tristate "Analog Devices AD7887 ADC driver"
>> +    depends on SPI
>> +    select IIO_RING_BUFFER
>> +    select IIO_SW_RING
>> +    select IIO_TRIGGER
>> +    help
>> +      Say yes here to build support for Analog Devices
>> +      AD7887 SPI analog to digital convertor (ADC).
>converter
>> +      If unsure, say N (but it's safe to say "Y").
>> +
>> +      To compile this driver as a module, choose M here: the
>> +      module will be called ad7887.
>> +
>>  config AD7745
>>      tristate "Analog Devices AD7745, AD7746 AD7747 capacitive sensor
>driver"
>>      depends on I2C
>> diff --git a/drivers/staging/iio/adc/Makefile
>> b/drivers/staging/iio/adc/Makefile
>> index a7dce6b..6f231a2 100644
>> --- a/drivers/staging/iio/adc/Makefile
>> +++ b/drivers/staging/iio/adc/Makefile
>> @@ -15,6 +15,10 @@ ad7476-y := ad7476_core.o
>>  ad7476-$(CONFIG_IIO_RING_BUFFER) += ad7476_ring.o
>>  obj-$(CONFIG_AD7476) += ad7476.o
>>
>> +ad7887-y := ad7887_core.o
>> +ad7887-$(CONFIG_IIO_RING_BUFFER) += ad7887_ring.o
>> +obj-$(CONFIG_AD7887) += ad7887.o
>> +
>>  obj-$(CONFIG_AD7150) += ad7150.o
>>  obj-$(CONFIG_AD7152) += ad7152.o
>>  obj-$(CONFIG_AD7291) += ad7291.o
>> diff --git a/drivers/staging/iio/adc/ad7887.h
>> b/drivers/staging/iio/adc/ad7887.h
>> new file mode 100644
>> index 0000000..43c5fa1
>> --- /dev/null
>> +++ b/drivers/staging/iio/adc/ad7887.h
>> @@ -0,0 +1,92 @@
>> +/*
>> + * AD7887 SPI ADC driver
>> + *
>> + * Copyright 2010 Analog Devices Inc.
>> + *
>> + * Licensed under the GPL-2 or later.
>> + */
>> +#ifndef IIO_ADC_AD7887_H_
>> +#define IIO_ADC_AD7887_H_
>> +
>> +#define AD7887_REF_DIS              (1 << 5) /* on-chip reference
>disable */
>> +#define AD7887_DUAL         (1 << 4) /* dual-channel mode */
>> +#define AD7887_CH_AIN1              (1 << 3) /* convert on channel 1,
>DUAL=1 */
>> +#define AD7887_CH_AIN0              (0 << 3) /* convert on channel 0,
>DUAL=0,1 */
>> +#define AD7887_PM_MODE1             (0)      /* CS based shutdown */
>> +#define AD7887_PM_MODE2             (1)      /* full on */
>> +#define AD7887_PM_MODE3             (2)      /* auto shutdown after
>conversion */
>> +#define AD7887_PM_MODE4             (3)      /* standby mode */
>> +
>> +enum ad7887_channels {
>> +    AD7887_CH0,
>> +    AD7887_CH0_CH1,
>> +    AD7887_CH1,
>> +};
>> +
>> +#define RES_MASK(bits)      ((1 << (bits)) - 1)
>It feels like this ought to be a standard macro, but I can't find it in
>any of the obvious headers...

I thought so too.
But I also can't find it - maybe add to one of the iio common headers?

>> +
>> +/*
>> + * TODO: struct ad7887_platform_data needs to go into
>> +include/linux/iio  */
>> +
>Please document this structure.  That en_dual in particular is not
>entirely obvious.

ok

>> +struct ad7887_platform_data {
>> +    u16                             vref_mv;
>> +    bool                            en_dual;
>> +    bool                            use_onchip_ref;
>> +};
>> +
>Ideally this struct could do with some docs as well.
>That res_shift is non obvious.

ok

>> +struct ad7887_chip_info {
>> +    u8                              bits;
>> +    u8                              storagebits;
>> +    u8                              res_shift;
>> +    char                            sign;
>> +    u16                             int_vref_mv;
>> +};
>> +
>> +struct ad7887_state {
>> +    struct iio_dev                  *indio_dev;
>> +    struct spi_device               *spi;
>> +    const struct ad7887_chip_info   *chip_info;
>> +    struct regulator                *reg;
>> +    struct work_struct              poll_work;
>> +    atomic_t                        protect_ring;
>> +    u16                             int_vref_mv;
>> +    bool                            en_dual;
>> +    struct spi_transfer             xfer[4];
>> +    struct spi_message              msg[3];
>> +    struct spi_message              *ring_msg;
>> +    unsigned char                   tx_cmd_buf[8];
>> +
>> +    /*
>> +     * DMA (thus cache coherency maintenance) requires the
>> +     * transfer buffers to live in their own cache lines.
>> +     */
>> +
>> +    unsigned char                   data[4] ____cacheline_aligned;
>> +};
>> +
>> +enum ad7887_supported_device_ids {
>> +    ID_AD7887
>> +};
>> +
>> +#ifdef CONFIG_IIO_RING_BUFFER
>> +int ad7887_scan_from_ring(struct ad7887_state *st, long mask); int
>> +ad7887_register_ring_funcs_and_init(struct iio_dev *indio_dev); void
>> +ad7887_ring_cleanup(struct iio_dev *indio_dev); #else /*
>> +CONFIG_IIO_RING_BUFFER */ static inline int
>> +ad7887_scan_from_ring(struct ad7887_state *st, long mask) {
>> +    return 0;
>> +}
>> +
>> +static inline int
>> +ad7887_register_ring_funcs_and_init(struct iio_dev *indio_dev) {
>> +    return 0;
>> +}
>> +
>> +static inline void ad7887_ring_cleanup(struct iio_dev *indio_dev) {
>> +} #endif /* CONFIG_IIO_RING_BUFFER */ #endif /* IIO_ADC_AD7887_H_ */
>> diff --git a/drivers/staging/iio/adc/ad7887_core.c
>> b/drivers/staging/iio/adc/ad7887_core.c
>> new file mode 100644
>> index 0000000..aee70cd
>> --- /dev/null
>> +++ b/drivers/staging/iio/adc/ad7887_core.c
>> @@ -0,0 +1,303 @@
>> +/*
>> + * AD7887 SPI ADC driver
>> + *
>> + * Copyright 2010 Analog Devices Inc.
>> + *
>> + * Licensed under the GPL-2 or later.
>> + */
>> +
>> +#include <linux/interrupt.h>
>> +#include <linux/workqueue.h>
>> +#include <linux/device.h>
>> +#include <linux/kernel.h>
>> +#include <linux/slab.h>
>> +#include <linux/sysfs.h>
>> +#include <linux/list.h>
>> +#include <linux/spi/spi.h>
>> +#include <linux/regulator/consumer.h> #include <linux/err.h>
>> +
>> +#include "../iio.h"
>> +#include "../sysfs.h"
>> +#include "../ring_generic.h"
>> +#include "adc.h"
>> +
>> +#include "ad7887.h"
>> +
>> +static int ad7887_scan_direct(struct ad7887_state *st, unsigned ch) {
>> +    int ret = spi_sync(st->spi, &st->msg[ch]);
>> +    if (ret)
>> +            return ret;
>> +
>Use one of the endian macros for this perhaps?

Can do but need to make data 16-bit aligned.

>> +    return (st->data[(ch * 2)] << 8) | st->data[(ch * 2) + 1]; }
>> +
>> +static ssize_t ad7887_scan(struct device *dev,
>> +                        struct device_attribute *attr,
>> +                        char *buf)
>> +{
>> +    struct iio_dev *dev_info = dev_get_drvdata(dev);
>> +    struct ad7887_state *st = dev_info->dev_data;
>> +    struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
>> +    int ret;
>> +
>> +    mutex_lock(&dev_info->mlock);
>> +    if (iio_ring_enabled(dev_info))
>> +            ret = ad7887_scan_from_ring(st, 1 << this_attr->address);
>> +    else
>> +            ret = ad7887_scan_direct(st, this_attr->address);
>> +    mutex_unlock(&dev_info->mlock);
>> +
>> +    if (ret < 0)
>> +            return ret;
>> +
>> +    return sprintf(buf, "%d\n", (ret >> st->chip_info->res_shift) &
>> +                   RES_MASK(st->chip_info->bits)); } static
>> +IIO_DEV_ATTR_IN_RAW(0, ad7887_scan, 0); static
>> +IIO_DEV_ATTR_IN_RAW(1, ad7887_scan, 1);
>> +
>> +static ssize_t ad7887_show_scale(struct device *dev,
>> +                            struct device_attribute *attr,
>> +                            char *buf)
>> +{
>> +    /* Driver currently only support internal vref */
>> +    struct iio_dev *dev_info = dev_get_drvdata(dev);
>> +    struct ad7887_state *st = iio_dev_get_devdata(dev_info);
>> +    /* Corresponds to Vref / 2^(bits) */
>> +    unsigned int scale_uv = (st->int_vref_mv * 1000) >>
>> +st->chip_info->bits;
>> +
>> +    return sprintf(buf, "%d.%d\n", scale_uv / 1000, scale_uv % 1000);
>}
>> +static IIO_DEVICE_ATTR(in_scale, S_IRUGO, ad7887_show_scale, NULL,
>> +0);
>> +
>> +static ssize_t ad7887_show_name(struct device *dev,
>> +                             struct device_attribute *attr,
>> +                             char *buf)
>> +{
>> +    struct iio_dev *dev_info = dev_get_drvdata(dev);
>> +    struct ad7887_state *st = iio_dev_get_devdata(dev_info);
>> +
>> +    return sprintf(buf, "%s\n", spi_get_device_id(st->spi)->name);
>> +}
>> +static IIO_DEVICE_ATTR(name, S_IRUGO, ad7887_show_name, NULL, 0);
>> +
>> +static struct attribute *ad7887_attributes[] = {
>> +    &iio_dev_attr_in0_raw.dev_attr.attr,
>> +    &iio_dev_attr_in1_raw.dev_attr.attr,
>> +    &iio_dev_attr_in_scale.dev_attr.attr,
>> +    &iio_dev_attr_name.dev_attr.attr,
>> +    NULL,
>> +};
>> +
>
>I learn something new every day. Didn't know you could do this. Might
>be a sensible method to clean up a number of other drivers... Will look
>into it futher at some point.

Yeah is_visible can help a lot. Actually I think this facility is pretty new.

>> +static mode_t ad7887_attr_is_visible(struct kobject *kobj,
>> +                                 struct attribute *attr, int n) {
>> +    struct device *dev = container_of(kobj, struct device, kobj);
>> +    struct iio_dev *dev_info = dev_get_drvdata(dev);
>> +    struct ad7887_state *st = iio_dev_get_devdata(dev_info);
>> +
>> +    mode_t mode = attr->mode;
>> +
>> +    if (attr == &iio_dev_attr_in1_raw.dev_attr.attr)
>> +            if (!st->en_dual)
>combine the two if statements

ok

>> +                    mode = 0;
>> +
>> +    return mode;
>> +}
>> +
>> +static const struct attribute_group ad7887_attribute_group = {
>> +    .attrs = ad7887_attributes,
>> +    .is_visible = ad7887_attr_is_visible, };
>> +
>
>So is there a plan to add more parts to this driver?  Usually one
>doesn't put this code in until at's needed, but if more are following
>shortly then it's fine with me. Perhaps add something to the commit
>message to indicate that this is going to be needed by future device
>support?

Plan is to add more drivers - indeed I already have added one.
But I need to wait for test hardware to arrive.

>
>> +static const struct ad7887_chip_info ad7887_chip_info_tbl[] = {
>> +    [ID_AD7887] = {
>> +            .bits = 12,
>> +            .storagebits = 16,
>> +            .res_shift = 0,
>> +            .sign = IIO_SCAN_EL_TYPE_UNSIGNED,
>> +            .int_vref_mv = 2500,
>> +    },
>> +};
>> +
>> +static int __devinit ad7887_probe(struct spi_device *spi) {
>> +    struct ad7887_platform_data *pdata = spi->dev.platform_data;
>> +    struct ad7887_state *st;
>> +    int ret, voltage_uv = 0;
>> +
>> +    st = kzalloc(sizeof(*st), GFP_KERNEL);
>> +    if (st == NULL) {
>> +            ret = -ENOMEM;
>> +            goto error_ret;
>> +    }
>> +
>> +    st->reg = regulator_get(&spi->dev, "vcc");
>> +    if (!IS_ERR(st->reg)) {
>> +            ret = regulator_enable(st->reg);
>> +            if (ret)
>> +                    goto error_put_reg;
>> +
>> +            voltage_uv = regulator_get_voltage(st->reg);
>Technically you might want to register for the voltage change
>notification from the regulator.  Some other driver might request a
>different voltage and I don't think you have prevented it from changing.

I'm not quite sure what you mean. I don't think someone wants to change this voltage on the fly.

>> +    }
>> +
>> +    st->chip_info =
>> +            &ad7887_chip_info_tbl[spi_get_device_id(spi)->driver_data];
>> +
>> +    spi_set_drvdata(spi, st);
>> +
>> +    atomic_set(&st->protect_ring, 0);
>> +    st->spi = spi;
>> +
>> +    st->indio_dev = iio_allocate_device();
>> +    if (st->indio_dev == NULL) {
>> +            ret = -ENOMEM;
>> +            goto error_disable_reg;
>> +    }
>> +
>> +    /* Estabilish that the iio_dev is a child of the i2c device */
>> +    st->indio_dev->dev.parent = &spi->dev;
>> +    st->indio_dev->attrs = &ad7887_attribute_group;
>> +    st->indio_dev->dev_data = (void *)(st);
>> +    st->indio_dev->driver_module = THIS_MODULE;
>> +    st->indio_dev->modes = INDIO_DIRECT_MODE;
>> +
>> +    /* Setup default message */
>> +
>> +    st->tx_cmd_buf[0] = AD7887_CH_AIN0 | AD7887_PM_MODE4 |
>> +                        ((pdata && pdata->use_onchip_ref) ?
>> +                        0 : AD7887_REF_DIS);
>> +
>> +    st->xfer[0].rx_buf = &st->data[0];
>> +    st->xfer[0].tx_buf = &st->tx_cmd_buf[0],
>> +    st->xfer[0].len = 2,
>> +
>> +    spi_message_init(&st->msg[AD7887_CH0]);
>> +    spi_message_add_tail(&st->xfer[0], &st->msg[AD7887_CH0]);
>> +
>> +    if (pdata && pdata->en_dual) {
>> +            st->tx_cmd_buf[0] |= AD7887_DUAL | AD7887_REF_DIS;
>> +
>> +            st->tx_cmd_buf[2] = AD7887_CH_AIN1 | AD7887_DUAL |
>> +                                AD7887_REF_DIS | AD7887_PM_MODE4;
>> +            st->tx_cmd_buf[4] = AD7887_CH_AIN0 | AD7887_DUAL |
>> +                                AD7887_REF_DIS | AD7887_PM_MODE4;
>> +            st->tx_cmd_buf[6] = AD7887_CH_AIN1 | AD7887_DUAL |
>> +                                AD7887_REF_DIS | AD7887_PM_MODE4;
>> +
>> +            st->xfer[1].rx_buf = &st->data[0];
>> +            st->xfer[1].tx_buf = &st->tx_cmd_buf[2],
>> +            st->xfer[1].len = 2,
>> +
>> +            st->xfer[2].rx_buf = &st->data[2];
>> +            st->xfer[2].tx_buf = &st->tx_cmd_buf[4],
>> +            st->xfer[2].len = 2,
>> +
>> +            spi_message_init(&st->msg[AD7887_CH0_CH1]);
>> +            spi_message_add_tail(&st->xfer[1], &st-
>>msg[AD7887_CH0_CH1]);
>> +            spi_message_add_tail(&st->xfer[2], &st-
>>msg[AD7887_CH0_CH1]);
>> +
>> +            st->xfer[3].rx_buf = &st->data[0];
>> +            st->xfer[3].tx_buf = &st->tx_cmd_buf[6],
>> +            st->xfer[3].len = 2,
>> +
>> +            spi_message_init(&st->msg[AD7887_CH1]);
>> +            spi_message_add_tail(&st->xfer[3], &st->msg[AD7887_CH1]);
>> +
>> +            st->en_dual = true;
>> +
>> +            if (pdata && pdata->vref_mv)
>> +                    st->int_vref_mv = pdata->vref_mv;
>> +            else if (voltage_uv)
>> +                    st->int_vref_mv = voltage_uv / 1000;
>> +            else
>> +                    dev_warn(&spi->dev, "reference voltage
>unspecified\n");
>> +
>> +    } else {
>> +            if (pdata && pdata->vref_mv)
>> +                    st->int_vref_mv = pdata->vref_mv;
>> +            else if (pdata && pdata->use_onchip_ref)
>> +                    st->int_vref_mv = st->chip_info->int_vref_mv;
>> +            else
>> +                    dev_warn(&spi->dev, "reference voltage
>unspecified\n");
>> +    }
>> +
>> +
>> +    ret = ad7887_register_ring_funcs_and_init(st->indio_dev);
>> +    if (ret)
>> +            goto error_free_device;
>> +
>> +    ret = iio_device_register(st->indio_dev);
>> +    if (ret)
>> +            goto error_free_device;
>> +
>> +    ret = iio_ring_buffer_register(st->indio_dev->ring, 0);
>> +    if (ret)
>> +            goto error_cleanup_ring;
>> +    return 0;
>> +
>> +error_cleanup_ring:
>> +    ad7887_ring_cleanup(st->indio_dev);
>> +    iio_device_unregister(st->indio_dev);
>> +error_free_device:
>> +    iio_free_device(st->indio_dev);
>> +error_disable_reg:
>> +    if (!IS_ERR(st->reg))
>> +            regulator_disable(st->reg);
>> +error_put_reg:
>> +    if (!IS_ERR(st->reg))
>> +            regulator_put(st->reg);
>> +    kfree(st);
>> +error_ret:
>> +    return ret;
>> +}
>> +
>> +static int ad7887_remove(struct spi_device *spi) {
>> +    struct ad7887_state *st = spi_get_drvdata(spi);
>> +    struct iio_dev *indio_dev = st->indio_dev;
>> +    iio_ring_buffer_unregister(indio_dev->ring);
>> +    ad7887_ring_cleanup(indio_dev);
>> +    iio_device_unregister(indio_dev);
>> +    if (!IS_ERR(st->reg)) {
>> +            regulator_disable(st->reg);
>> +            regulator_put(st->reg);
>> +    }
>> +    kfree(st);
>> +    return 0;
>> +}
>> +
>> +static const struct spi_device_id ad7887_id[] = {
>> +    {"ad7887", ID_AD7887},
>> +    {}
>> +};
>> +
>> +static struct spi_driver ad7887_driver = {
>> +    .driver = {
>> +            .name   = "ad7887",
>> +            .bus    = &spi_bus_type,
>> +            .owner  = THIS_MODULE,
>> +    },
>> +    .probe          = ad7887_probe,
>> +    .remove         = __devexit_p(ad7887_remove),
>> +    .id_table       = ad7887_id,
>> +};
>> +
>> +static int __init ad7887_init(void)
>> +{
>> +    return spi_register_driver(&ad7887_driver);
>> +}
>> +module_init(ad7887_init);
>> +
>> +static void __exit ad7887_exit(void) {
>> +    spi_unregister_driver(&ad7887_driver);
>> +}
>> +module_exit(ad7887_exit);
>> +
>> +MODULE_AUTHOR("Michael Hennerich <hennerich@xxxxxxxxxxxxxxxxxxxx>");
>> +MODULE_DESCRIPTION("Analog Devices AD7887 ADC"); MODULE_LICENSE("GPL
>> +v2"); MODULE_ALIAS("spi:ad7887");
>> diff --git a/drivers/staging/iio/adc/ad7887_ring.c
>> b/drivers/staging/iio/adc/ad7887_ring.c
>> new file mode 100644
>> index 0000000..a207d07
>> --- /dev/null
>> +++ b/drivers/staging/iio/adc/ad7887_ring.c
>> @@ -0,0 +1,273 @@
>> +/*
>> + * Copyright 2010 Analog Devices Inc.
>> + * Copyright (C) 2008 Jonathan Cameron
>> + *
>> + * Licensed under the GPL-2 or later.
>> + *
>> + * ad7887_ring.c
>> + */
>> +
>> +#include <linux/interrupt.h>
>> +#include <linux/gpio.h>
>> +#include <linux/workqueue.h>
>> +#include <linux/device.h>
>> +#include <linux/kernel.h>
>> +#include <linux/slab.h>
>> +#include <linux/sysfs.h>
>> +#include <linux/list.h>
>> +#include <linux/spi/spi.h>
>> +
>> +#include "../iio.h"
>> +#include "../ring_generic.h"
>> +#include "../ring_sw.h"
>> +#include "../trigger.h"
>> +#include "../sysfs.h"
>> +
>> +#include "ad7887.h"
>> +
>> +static IIO_SCAN_EL_C(in0, 0, 0, NULL); static IIO_SCAN_EL_C(in1, 1,
>> +0, NULL);
>> +
>> +static ssize_t ad7887_show_type(struct device *dev,
>> +                            struct device_attribute *attr,
>> +                            char *buf)
>> +{
>> +    struct iio_ring_buffer *ring = dev_get_drvdata(dev);
>> +    struct iio_dev *indio_dev = ring->indio_dev;
>> +    struct ad7887_state *st = indio_dev->dev_data;
>> +
>> +    return sprintf(buf, "%c%d/%d>>%d\n", st->chip_info->sign,
>> +                   st->chip_info->bits, st->chip_info->storagebits,
>> +                   st->chip_info->res_shift);
>> +}
>> +static IIO_DEVICE_ATTR(in_type, S_IRUGO, ad7887_show_type, NULL, 0);
>> +
>> +static struct attribute *ad7887_scan_el_attrs[] = {
>> +    &iio_scan_el_in0.dev_attr.attr,
>> +    &iio_const_attr_in0_index.dev_attr.attr,
>> +    &iio_scan_el_in1.dev_attr.attr,
>> +    &iio_const_attr_in1_index.dev_attr.attr,
>> +    &iio_dev_attr_in_type.dev_attr.attr,
>> +    NULL,
>> +};
>> +
>> +static mode_t ad7887_scan_el_attr_is_visible(struct kobject *kobj,
>> +                                 struct attribute *attr, int n) {
>> +    struct device *dev = container_of(kobj, struct device, kobj);
>> +    struct iio_ring_buffer *ring = dev_get_drvdata(dev);
>> +    struct iio_dev *indio_dev = ring->indio_dev;
>> +    struct ad7887_state *st = indio_dev->dev_data;
>> +
>> +    mode_t mode = attr->mode;
>> +
>> +    if ((attr == &iio_scan_el_in1.dev_attr.attr) ||
>> +            (attr == &iio_const_attr_in1_index.dev_attr.attr))
>> +            if (!st->en_dual)
>> +                    mode = 0;
>> +
>> +    return mode;
>> +}
>> +
>> +static struct attribute_group ad7887_scan_el_group = {
>> +    .name = "scan_elements",
>> +    .attrs = ad7887_scan_el_attrs,
>> +    .is_visible = ad7887_scan_el_attr_is_visible, };
>> +
>> +int ad7887_scan_from_ring(struct ad7887_state *st, long mask) {
>> +    struct iio_ring_buffer *ring = st->indio_dev->ring;
>> +    int count = 0, ret;
>> +    u16 *ring_data;
>> +
>> +    if (!(ring->scan_mask & mask)) {
>> +            ret = -EBUSY;
>> +            goto error_ret;
>> +    }
>> +
>> +    ring_data = kmalloc(ring->access.get_bytes_per_datum(ring),
>GFP_KERNEL);
>> +    if (ring_data == NULL) {
>> +            ret = -ENOMEM;
>> +            goto error_ret;
>> +    }
>> +    ret = ring->access.read_last(ring, (u8 *) ring_data);
>> +    if (ret)
>> +            goto error_free_ring_data;
>> +
>> +    /* for single channel scan the result is stored with zero offset
>*/
>This does seem somewhat over complex given the device only has two
>channels...

Ok - will simplify a bit.


>> +    if (hweight_long(ring->scan_mask) > 1) {
>> +            /* Need a count of channels prior to this one */
>> +            mask >>= 1;
>> +            while (mask) {
>> +                    if (mask & ring->scan_mask)
>> +                            count++;
>> +                    mask >>= 1;
>> +            }
>> +    }
>> +
>> +    ret = be16_to_cpu(ring_data[count]);
>> +
>> +error_free_ring_data:
>> +    kfree(ring_data);
>> +error_ret:
>> +    return ret;
>> +}
>> +
>> +/**
>> + * ad7887_ring_preenable() setup the parameters of the ring before
>> +enabling
>> + *
>> + * The complex nature of the setting of the nuber of bytes per datum
>> +is due
>> + * to this driver currently ensuring that the timestamp is stored at
>> +an 8
>> + * byte boundary.
>> + **/
>> +static int ad7887_ring_preenable(struct iio_dev *indio_dev) {
>> +    struct ad7887_state *st = indio_dev->dev_data;
>> +    struct iio_ring_buffer *ring = indio_dev->ring;
>> +    size_t d_size;
>> +
>> +    if (indio_dev->ring->access.set_bytes_per_datum) {
>> +            d_size = st->chip_info->storagebits / 8 + sizeof(s64);
>> +            if (d_size % 8)
>> +                    d_size += 8 - (d_size % 8);
>> +            indio_dev->ring->access.set_bytes_per_datum(indio_dev->ring,
>> +                                                        d_size);
>> +    }
>> +
>> +    switch (ring->scan_mask) {
>> +    case 1:
>> +            st->ring_msg = &st->msg[AD7887_CH0];
>> +            break;
>> +    case 2:
>To clarify for code readers what is happening here, can you make this:
>case (1 << 1):

ok
>> +            st->ring_msg = &st->msg[AD7887_CH1];
>> +            /* Dummy read: push CH1 setting down to hardware */
>> +            spi_sync(st->spi, st->ring_msg);
>> +            break;
>As a clarification here, can you make this case (1 << 1) | 1:

ok
>> +    case 3:
>> +            st->ring_msg = &st->msg[AD7887_CH0_CH1];
>> +            break;
>> +    }
>> +
>> +    return 0;
>> +}
>> +
>> +static int ad7887_ring_postdisable(struct iio_dev *indio_dev) {
>> +    struct ad7887_state *st = indio_dev->dev_data;
>> +
>> +    /* dummy read: restore default CH0 settin */
>> +    return spi_sync(st->spi, &st->msg[AD7887_CH0]); }
>> +
>> +/**
>> + * ad7887_poll_func_th() th of trigger launched polling to ring
>> +buffer
>> + *
>> + * As sampling only occurs on i2c comms occuring, leave timestamping
>> +until
>> + * then.  Some triggers will generate their own time stamp.
>> +Currently
>> + * there is no way of notifying them when no one cares.
>> + **/
>> +static void ad7887_poll_func_th(struct iio_dev *indio_dev, s64 time)
>> +{
>> +    struct ad7887_state *st = indio_dev->dev_data;
>> +
>> +    schedule_work(&st->poll_work);
>> +    return;
>> +}
>> +/**
>> + * ad7887_poll_bh_to_ring() bh of trigger launched polling to ring
>buffer
>> + * @work_s: the work struct through which this was scheduled
>> + *
>> + * Currently there is no option in this driver to disable the saving
>> +of
>> + * timestamps within the ring.
>> + * I think the one copy of this at a time was to avoid problems if
>> +the
>> + * trigger was set far too high and the reads then locked up the
>computer.
>> + **/
>Good to see my random comments turning up in lots of drivers ;)  I
>really ought to clear some of these out...
>
>> +static void ad7887_poll_bh_to_ring(struct work_struct *work_s) {
>> +    struct ad7887_state *st = container_of(work_s, struct
>ad7887_state,
>> +                                              poll_work);
>> +    struct iio_dev *indio_dev = st->indio_dev;
>> +    struct iio_sw_ring_buffer *sw_ring = iio_to_sw_ring(indio_dev-
>>ring);
>> +    struct iio_ring_buffer *ring = indio_dev->ring;
>> +    s64 time_ns;
>> +    __u8 *buf;
>> +    int b_sent;
>> +    size_t d_size;
>> +
>> +    unsigned int bytes = ring->scan_count * st->chip_info->storagebits
>/
>> +8;
>> +
>> +    /* Ensure the timestamp is 8 byte aligned */
>> +    d_size = bytes + sizeof(s64);
>> +    if (d_size % sizeof(s64))
>Spacing looks dubious round that minus sign. Would have thought
>checkpatch would have moaned about that.

Actually checkpatch complained about having the spacing.
I removed it to make checkpatch happy.
Must be a bug in checkpatch, I'll revert.


>> +            d_size += sizeof(s64)-(d_size % sizeof(s64));
>> +
>> +    /* Ensure only one copy of this function running at a time */
>> +    if (atomic_inc_return(&st->protect_ring) > 1)
>> +            return;
>> +
>> +    buf = kzalloc(d_size, GFP_KERNEL);
>> +    if (buf == NULL)
>> +            return;
>> +
>> +    b_sent = spi_sync(st->spi, st->ring_msg);
>> +    if (b_sent)
>> +            goto done;
>> +
>> +    time_ns = iio_get_time_ns();
>> +
>> +    memcpy(buf, st->data, bytes);
>> +    memcpy(buf + d_size - sizeof(s64), &time_ns, sizeof(time_ns));
>> +
>> +    indio_dev->ring->access.store_to(&sw_ring->buf, buf, time_ns);
>> +done:
>> +    kfree(buf);
>> +    atomic_dec(&st->protect_ring);
>> +}
>> +
>> +int ad7887_register_ring_funcs_and_init(struct iio_dev *indio_dev) {
>> +    struct ad7887_state *st = indio_dev->dev_data;
>> +    int ret = 0;
>Don't think this ret needs to be assigned. Can't see any path to the
>end where it isn't set first anyway.
Right

>> +
>> +    indio_dev->ring = iio_sw_rb_allocate(indio_dev);
>> +    if (!indio_dev->ring) {
>> +            ret = -ENOMEM;
>> +            goto error_ret;
>> +    }
>> +    /* Effectively select the ring buffer implementation */
>> +    iio_ring_sw_register_funcs(&indio_dev->ring->access);
>> +    ret = iio_alloc_pollfunc(indio_dev, NULL, &ad7887_poll_func_th);
>> +    if (ret)
>> +            goto error_deallocate_sw_rb;
>> +
>> +    /* Ring buffer functions - here trigger setup related */
>> +
>> +    indio_dev->ring->preenable = &ad7887_ring_preenable;
>> +    indio_dev->ring->postenable = &iio_triggered_ring_postenable;
>> +    indio_dev->ring->predisable = &iio_triggered_ring_predisable;
>> +    indio_dev->ring->postdisable = &ad7887_ring_postdisable;
>> +    indio_dev->ring->scan_el_attrs = &ad7887_scan_el_group;
>> +
>> +    INIT_WORK(&st->poll_work, &ad7887_poll_bh_to_ring);
>> +
>> +    /* Flag that polled ring buffering is possible */
>> +    indio_dev->modes |= INDIO_RING_TRIGGERED;
>> +    return 0;
>> +error_deallocate_sw_rb:
>> +    iio_sw_rb_free(indio_dev->ring);
>> +error_ret:
>> +    return ret;
>> +}
>> +
>
>Not a comment on this driver specifically, but this function is exactly
>replicated across a number of drivers. Guess ripping it out to a helper
>function is one for the todo list!

Great idea

>> +void ad7887_ring_cleanup(struct iio_dev *indio_dev) {
>> +    /* ensure that the trigger has been detached */
>> +    if (indio_dev->trig) {
>> +            iio_put_trigger(indio_dev->trig);
>> +            iio_trigger_dettach_poll_func(indio_dev->trig,
>> +                                          indio_dev->pollfunc);
>> +    }
>> +    kfree(indio_dev->pollfunc);
>> +    iio_sw_rb_free(indio_dev->ring);
>> +}

Thanks for reviewing!

-Michael
--
To unsubscribe from this list: send the line "unsubscribe linux-iio" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html


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

  Powered by Linux