RE: [PATCH] staging: iio: adc: Enable driver support for ad799x AD converters

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

 



Jonathan Cameron wrote on 2010-10-01:
> Hi Michael,
>
> I was pretty much happy with this last time round. Couple of comments
> inline. The new line missing in the Makefile is probably the only one
> that I'd advocate doing pre merge.
>
> The precision attrs have gone away to be replaced by the more
> informative type attributes in the scan_elements directory.  This can
> trivially be done as an additional patch post merge.
>
> There are some subtle changes to the event naming (post discussion on
> lkml) but I'll just do this driver alongside the others in the patch
> set implementing those changes. This is the first driver providing
> hyst so that attribute will also need documenting. Again that should
> happen post merge.
>
> I think we can also take advantage of some of the helper functions
> that are now in place for buffer registration (came out of Barry's
> work), but that can definitely happen at a later date.
>
> So in summary I'm happy to see this merge right now.  The sooner the
> better as far as I'm concerned

Hi Jonathan,

We have a few more iio drivers in our repository. Over the next couple
of weeks I'm trying to get them out.

> because I would like it to go in before
> the event clean up series currently sat in my tree. That way I can
> update this one at the same time.

Oops - this patch was already against your tree:
http://git.kernel.org/?p=linux/kernel/git/jic23/iio_temp.git

Need to check if it works on Greg's staging.
...It's time to get out of staging

> Acked-by: Jonathan Cameron <jic23@xxxxxxxxx>
>
> Please send on to Greg KH <greg@xxxxxxxxx> for him to merge.
>
> Thanks,
>
> Jonathan
>
>> Driver for ad7991, ad7995, ad7999, ad7992, ad7993, ad7994, ad7997
>> and
>> ad7998 multichannel ADC.
>>
>> Signed-off-by: Michael Hennerich <michael.hennerich@xxxxxxxxxx>
>> ---
>>  drivers/staging/iio/adc/Kconfig       |   20 +
>>  drivers/staging/iio/adc/Makefile      |    4 +
>>  drivers/staging/iio/adc/ad799x.h      |  157 ++++++
>>  drivers/staging/iio/adc/ad799x_core.c |  918
>> +++++++++++++++++++++++++++++++++
>> drivers/staging/iio/adc/ad799x_ring.c |  246 +++++++++
>>  5 files changed, 1345 insertions(+), 0 deletions(-)  create mode
>> 100644 drivers/staging/iio/adc/ad799x.h  create mode 100644
>> drivers/staging/iio/adc/ad799x_core.c
>>  create mode 100644 drivers/staging/iio/adc/ad799x_ring.c
>> diff --git a/drivers/staging/iio/adc/Kconfig
>> b/drivers/staging/iio/adc/Kconfig index 0835fbc..3ea4da8 100644
>> --- a/drivers/staging/iio/adc/Kconfig
>> +++ b/drivers/staging/iio/adc/Kconfig
>> @@ -26,3 +26,23 @@ config MAX1363_RING_BUFFER
>>      help
>>        Say yes here to include ring buffer support in the MAX1363
>>        ADC driver.
>> +
>> +config AD799X
>> +    tristate "Analog Devices AD799x ADC driver"
> I'll express my usual anti comment on driver names with wild cards,
> but as there are hardly any numbers left in the range it is probably
> fine here.
>> +    depends on I2C
>> +    select IIO_TRIGGER if IIO_RING_BUFFER
>> +    select AD799X_RING_BUFFER
>> +    help
>> +      Say yes here to build support for Analog Devices:
>> +      ad7991, ad7995, ad7999, ad7992, ad7993, ad7994, ad7997, ad7998
>> +      i2c analog to digital convertors (ADC). Provides direct access
>> +      via sysfs.
>> +
>> +config AD799X_RING_BUFFER
>> +    bool "Analog Devices AD799x: use ring buffer"
>> +    depends on AD799X
>> +    select IIO_RING_BUFFER
>> +    select IIO_SW_RING
>> +    help
>> +      Say yes here to include ring buffer support in the AD799X
>> +      ADC driver.
>> diff --git a/drivers/staging/iio/adc/Makefile
>> b/drivers/staging/iio/adc/Makefile
>> index 688510f..f4aa63a 100644
>> --- a/drivers/staging/iio/adc/Makefile
>> +++ b/drivers/staging/iio/adc/Makefile
>> @@ -6,3 +6,7 @@ max1363-y := max1363_core.o  max1363-y +=
>> max1363_ring.o
>>
>>  obj-$(CONFIG_MAX1363) += max1363.o
>> + +ad799x-y := ad799x_core.o +ad799x-$(CONFIG_AD799X_RING_BUFFER) +=
>> ad799x_ring.o +obj-$(CONFIG_AD799X) += ad799x.o Please add a new line
>> after this. \ No newline at end of file diff --git
>> a/drivers/staging/iio/adc/ad799x.h b/drivers/staging/iio/adc/ad799x.h
>> new file mode 100644 index 0000000..73f3ec5 --- /dev/null +++
>> b/drivers/staging/iio/adc/ad799x.h @@ -0,0 +1,157 @@ +/* + * Copyright
>> (C) 2010 Michael Hennerich, Analog Devices Inc. + * Copyright (C)
>> 2008-2010 Jonathan Cameron + * + * This program is free software; you
>> can redistribute it and/or +modify + * it under the terms of the GNU
>> General Public License version 2 +as + * published by the Free Software
>> Foundation. + * + * ad799x.h + */ + +#ifndef _AD799X_H_ +#define
>> _AD799X_H_ + +#define AD799X_CHANNEL_SHIFT                   4 + +/* + * AD7991, AD7995
>> and AD7999 defines  */ + +#define AD7991_REF_SEL                             0x08 +#define
>> AD7991_FLTR                          0x04 +#define AD7991_BIT_TRIAL_DELAY                    0x02 +#define
>> AD7991_SAMPLE_DELAY                  0x01 + +/* + * AD7992, AD7993, AD7994, AD7997 and
>> AD7998 defines  */ + +#define AD7998_FLTR                            0x08 +#define
>> AD7998_ALERT_EN                              0x04 +#define AD7998_BUSY_ALERT                 0x02 +#define
>> AD7998_BUSY_ALERT_POL                        0x01 + +#define AD7998_CONV_RES_REG                     0x0
>> +#define AD7998_ALERT_STAT_REG                       0x1 +#define AD7998_CONF_REG                            0x2
>> +#define AD7998_CYCLE_TMR_REG                        0x3 +#define
>> AD7998_DATALOW_CH1_REG                       0x4 +#define AD7998_DATAHIGH_CH1_REG                    0x5
>> +#define AD7998_HYST_CH1_REG                 0x6 +#define
>> AD7998_DATALOW_CH2_REG                       0x7 +#define AD7998_DATAHIGH_CH2_REG                    0x8
>> +#define AD7998_HYST_CH2_REG                 0x9 +#define
>> AD7998_DATALOW_CH3_REG                       0xA +#define AD7998_DATAHIGH_CH3_REG                    0xB
>> +#define AD7998_HYST_CH3_REG                 0xC +#define
>> AD7998_DATALOW_CH4_REG                       0xD +#define AD7998_DATAHIGH_CH4_REG                    0xE
>> +#define AD7998_HYST_CH4_REG                 0xF + +#define AD7998_CYC_MASK                          0x7
>> +#define AD7998_CYC_DIS                              0x0 +#define AD7998_CYC_TCONF_32                        0x1
>> +#define AD7998_CYC_TCONF_64                 0x2 +#define AD7998_CYC_TCONF_128                       0x3
>> +#define AD7998_CYC_TCONF_256                        0x4 +#define AD7998_CYC_TCONF_512                       0x5
>> +#define AD7998_CYC_TCONF_1024                       0x6 +#define
>> AD7998_CYC_TCONF_2048                        0x7 + +#define AD7998_ALERT_STAT_CLEAR                  0xFF +
>> +/* + * AD7997 and AD7997 defines + */ + +#define
>> AD7997_8_READ_SINGLE                 0x80 +#define AD7997_8_READ_SEQUENCE                    0x70 +
>> +enum { +    ad7991, +       ad7995, +       ad7999, +       ad7992, +       ad7993, +       ad7994,
>> +    ad7997, +       ad7998 +}; + +struct ad799x_state; + +/** + * struct
>> ad799x_chip_info - chip specifc information + * @num_inputs:         number of
>> physical inputs on chip + * @bits:           accuracy of the adc in bits + *
>> @int_vref_mv:        the internal reference voltage + * @monitor_mode:       whether
>> the chip supports monitor interrupts + * @default_config:    device
>> default configuration + * @dev_attrs:                pointer to the device attribute
>> group + * @scan_attrs:               pointer to the scan element attribute group + *
>> @event_attrs:        pointer to the monitor event attribute group + *
>> @ad799x_set_scan_mode: function pointer to the device specific +mode
>> function + + */ +struct ad799x_chip_info { + u8                              num_inputs;
>> +    u8                              bits; + u16                             int_vref_mv; +  bool                            monitor_mode;
>> +    u16                             default_config; +       struct attribute_group          *dev_attrs; +   struct
>> attribute_group              *scan_attrs; +  struct attribute_group          *event_attrs;
>> +    int (*ad799x_set_scan_mode)     (struct ad799x_state *st, +                                     unsigned
>> mask); +}; + +struct ad799x_state { +        struct iio_dev                  *indio_dev;
>> +    struct i2c_client               *client; +      const struct
>> ad799x_chip_info     *chip_info; +   struct work_struct              poll_work; +    struct
>> work_struct          work_thresh; +  atomic_t                        protect_ring; + struct
>> iio_trigger          *trig; +        struct regulator                *reg; + s64                             last_timestamp;
>> +    u16                             int_vref_mv; +  unsigned                        id; +   char                            *name;
>> +    u16                             config; +}; + +/* + * TODO: struct ad799x_platform_data needs
>> to go into +inlude/linux/iio Typo in comment. + */ + +struct
>> ad799x_platform_data { +     u16                             vref_mv; +}; + +int
>> ad799x_set_scan_mode(struct ad799x_state *st, unsigned mask); + +#ifdef
>> CONFIG_AD799X_RING_BUFFER +int ad799x_single_channel_from_ring(struct
>> ad799x_state *st, long +mask); int
>> ad799x_register_ring_funcs_and_init(struct iio_dev +*indio_dev); void
>> ad799x_ring_cleanup(struct iio_dev *indio_dev); +#else /*
>> CONFIG_AD799X_RING_BUFFER */ int
>> +ad799x_single_channel_from_ring(struct ad799x_state *st, long mask) {
>> +    return -EINVAL; +} + + +static inline int
>> +ad799x_register_ring_funcs_and_init(struct iio_dev *indio_dev) {
>> +    return 0; +} + +static inline void ad799x_ring_cleanup(struct iio_dev
>> *indio_dev) { } +#endif /* CONFIG_AD799X_RING_BUFFER */ #endif /*
>> _AD799X_H_ */ diff --git a/drivers/staging/iio/adc/ad799x_core.c
>> b/drivers/staging/iio/adc/ad799x_core.c new file mode 100644 index
>> 0000000..772c664 --- /dev/null +++
>> b/drivers/staging/iio/adc/ad799x_core.c @@ -0,0 +1,918 @@ +/* + *
>> iio/adc/ad799x.c + * Copyright (C) 2010 Michael Hennerich, Analog
>> Devices Inc. + * + * based on iio/adc/max1363 + * Copyright (C)
>> 2008-2010 Jonathan Cameron + * + * based on
>> linux/drivers/i2c/chips/max123x + * Copyright (C) 2002-2004 Stefan
>> Eletzhofer + * + * based on linux/drivers/acron/char/pcf8583.c + *
>> Copyright (C) 2000 Russell King + * + * This program is free software;
>> you can redistribute it and/or +modify + * it under the terms of the
>> GNU General Public License version 2 +as + * published by the Free
>> Software Foundation. + * + * ad799x.c + * + * Support for ad7991,
>> ad7995, ad7999, ad7992, ad7993, ad7994, +ad7997, + * ad7998 and similar
>> chips. + * + */ + +#include <linux/interrupt.h> +#include
>> <linux/workqueue.h> +#include <linux/device.h> +#include
>> <linux/kernel.h> +#include <linux/sysfs.h> +#include <linux/list.h>
>> +#include <linux/i2c.h> +#include <linux/regulator/consumer.h> #include
>> <linux/slab.h> +#include <linux/types.h> #include <linux/err.h> +
>> +#include "../iio.h" +#include "../sysfs.h" + +#include
>> "../ring_generic.h" +#include "adc.h" +#include "ad799x.h" + +/* + *
>> ad799x register access by I2C + */ +static int ad799x_i2c_read16(struct
>> ad799x_state *st, u8 reg, u16 +*data) { +    struct i2c_client *client =
>> st->client; +        int ret = 0; + +        ret = i2c_smbus_read_word_data(client,
>> reg); +      if (ret < 0) { +                dev_err(&client->dev, "I2C read error\n");
>> +            return ret; +   } + +   *data = swab16((u16)ret); + +   return 0; +} +
>> +static int ad799x_i2c_read8(struct ad799x_state *st, u8 reg, u8
>> +*data) { +  struct i2c_client *client = st->client; +       int ret = 0; +
>> +    ret = i2c_smbus_read_word_data(client, reg); +  if (ret < 0) {
>> +            dev_err(&client->dev, "I2C read error\n"); +            return ret; +   } +
>> +    *data = ret; + +        return 0; +} + +static int ad799x_i2c_write16(struct
>> ad799x_state *st, u8 reg, u16 +data) { +     struct i2c_client *client =
>> st->client; +        int ret = 0; + +        ret = i2c_smbus_write_word_data(client,
>> reg, swab16(data)); +        if (ret < 0) +          dev_err(&client->dev, "I2C write
>> error\n"); + +       return ret; +} + +static int ad799x_i2c_write8(struct
>> ad799x_state *st, u8 reg, u8 +data) { +      struct i2c_client *client =
>> st->client; +        int ret = 0; + +        ret = i2c_smbus_write_byte_data(client,
>> reg, data); +        if (ret < 0) +          dev_err(&client->dev, "I2C write
>> error\n"); + +       return ret; +} + +static int
>> ad799x_scan_el_set_state(struct iio_scan_el *scan_el, +
>> struct iio_dev *indio_dev, +                                bool state) +{ + struct
>> ad799x_state *st = indio_dev->dev_data; +    return
>> ad799x_set_scan_mode(st, st->indio_dev->ring->scan_mask); } + +/* Here
>> we claim all are 16 bits. This currently does no harm and +saves + * us
>> a lot of scan element listings */ + +#define
>> AD799X_SCAN_EL(number)                                               \ +     IIO_SCAN_EL_C(in##number, number, 0,
>> ad799x_scan_el_set_state); + +static AD799X_SCAN_EL(0); +static
>> AD799X_SCAN_EL(1); +static AD799X_SCAN_EL(2); +static
>> AD799X_SCAN_EL(3); +static AD799X_SCAN_EL(4); +static
>> AD799X_SCAN_EL(5); +static AD799X_SCAN_EL(6); +static
>> AD799X_SCAN_EL(7); + +static ssize_t ad799x_show_precision(struct
>> device *dev, +                               struct device_attribute *attr, +                                char *buf) +{
>> +    struct iio_dev *dev_info = dev_get_drvdata(dev); +      struct
>> ad799x_state *st = iio_dev_get_devdata(dev_info); +  return sprintf(buf,
>> "%d\n", st->chip_info->bits); } + +static IIO_DEVICE_ATTR(in_precision,
>> S_IRUGO, ad799x_show_precision, +                   NULL, 0); + +static int
>> ad7991_5_9_set_scan_mode(struct ad799x_state *st, unsigned +mask) {
>> +    return i2c_smbus_write_byte(st->client, +               st->config | (mask <<
>> AD799X_CHANNEL_SHIFT)); } + +static int ad7992_3_4_set_scan_mode(struct
>> ad799x_state *st, unsigned +mask) { +        return ad799x_i2c_write8(st,
>> AD7998_CONF_REG, +           st->config | (mask << AD799X_CHANNEL_SHIFT)); } +
>> +static int ad7997_8_set_scan_mode(struct ad799x_state *st, unsigned
>> +mask) { +   return ad799x_i2c_write16(st, AD7998_CONF_REG, +                st->config
>> | (mask << AD799X_CHANNEL_SHIFT)); } + +int ad799x_set_scan_mode(struct
>> ad799x_state *st, unsigned mask) { + int ret; + +    if
>> (st->chip_info->ad799x_set_scan_mode != NULL) { +            ret =
>> st->chip_info->ad799x_set_scan_mode(st, mask); +             return (ret > 0) ? 0
>> : ret; +     } + +   return 0; +} + +static ssize_t
>> ad799x_read_single_channel(struct device *dev, +                                struct
>> device_attribute *attr, +                               char *buf) +{ +      struct iio_dev
>> *dev_info = dev_get_drvdata(dev); +  struct ad799x_state *st =
>> iio_dev_get_devdata(dev_info); +     struct iio_dev_attr *this_attr =
>> to_iio_dev_attr(attr); +     int ret = 0, len = 0; + u32 data ; +    u16
>> rxbuf[1]; +  u8 cmd; +       long mask; + +  mutex_lock(&dev_info->mlock);
>> +    mask = 1 << this_attr->address; +       /* If ring buffer capture is
>> occuring, query the buffer */ +      if (iio_ring_enabled(dev_info)) {
>> +            data = ad799x_single_channel_from_ring(st, mask); +             if (data < 0) {
>> +                    ret = data; +                   goto error_ret; +               } +     } else { +              switch (st->id)
>> { +          case ad7991: +          case ad7995: +          case ad7999: +                  cmd = st->config
>> | (mask << AD799X_CHANNEL_SHIFT); +                  break; +                case ad7992: +          case
>> ad7993: +            case ad7994: +                  cmd = mask << AD799X_CHANNEL_SHIFT;
>> +                    break; +                case ad7997: +          case ad7998: +                  cmd =
>> (this_attr->address << +                             AD799X_CHANNEL_SHIFT) |
>> AD7997_8_READ_SINGLE; +                      break; +                default: +                      cmd = 0; + +            } +             ret
>> = ad799x_i2c_read16(st, cmd, rxbuf); +               if (ret < 0) +                  goto
>> error_ret; + +               data = rxbuf[0] & 0xFFF; +      } + +   /* Pretty print the
>> result */ +  len = sprintf(buf, "%u\n", data); + +error_ret:
>> +    mutex_unlock(&dev_info->mlock); +       return ret ? ret : len; +} +
>> +static ssize_t ad799x_read_frequency(struct device *dev, +                                  struct
>> device_attribute *attr, +                                    char *buf) +{ + struct iio_dev *dev_info
>> = dev_get_drvdata(dev); +    struct ad799x_state *st =
>> iio_dev_get_devdata(dev_info); + +   int ret, len = 0; +     u8 val; +       ret =
>> ad799x_i2c_read8(st, AD7998_CYCLE_TMR_REG, &val); +  if (ret) +              return
>> ret; + +     val &= AD7998_CYC_MASK; + +     switch (val) { +        case
>> AD7998_CYC_DIS: +            len = sprintf(buf, "0\n"); +            break; +        case
>> AD7998_CYC_TCONF_32: +               len = sprintf(buf, "15625\n"); +                break; +        case
>> AD7998_CYC_TCONF_64: +               len = sprintf(buf, "7812\n"); +         break; +        case
>> AD7998_CYC_TCONF_128: +              len = sprintf(buf, "3906\n"); +         break; +        case
>> AD7998_CYC_TCONF_256: +              len = sprintf(buf, "1953\n"); +         break; +        case
>> AD7998_CYC_TCONF_512: +              len = sprintf(buf, "976\n"); +          break; +        case
>> AD7998_CYC_TCONF_1024: +             len = sprintf(buf, "488\n"); +          break; +        case
>> AD7998_CYC_TCONF_2048: +             len = sprintf(buf, "244\n"); +          break; +        }
>> +    return len; +} + +static ssize_t ad799x_write_frequency(struct device
>> *dev, +                                       struct device_attribute *attr, +                                        const char *buf,
>> +                                     size_t len) +{ +       struct iio_dev *dev_info =
>> dev_get_drvdata(dev); +      struct ad799x_state *st =
>> iio_dev_get_devdata(dev_info); + +   long val; +     int ret; +      u8 t; + +       ret
>> = strict_strtol(buf, 10, &val); +    if (ret) +              return ret; +
>> +    mutex_lock(&dev_info->mlock); + ret = ad799x_i2c_read8(st,
>> AD7998_CYCLE_TMR_REG, &t); + if (ret) +              goto error_ret_mutex; + /*
>> Wipe the bits clean */ +     t &= ~AD7998_CYC_MASK; + +      switch (val) {
>> +    case 15625: +           t |= AD7998_CYC_TCONF_32; +             break; +        case 7812: +            t
>> |= AD7998_CYC_TCONF_64; +            break; +        case 3906: +            t |=
>> AD7998_CYC_TCONF_128; +              break; +        case 1953: +            t |=
>> AD7998_CYC_TCONF_256; +              break; +        case 976: +             t |=
>> AD7998_CYC_TCONF_512; +              break; +        case 488: +             t |=
>> AD7998_CYC_TCONF_1024; +             break; +        case 244: +             t |=
>> AD7998_CYC_TCONF_2048; +             break; +        case  0: +              t |= AD7998_CYC_DIS;
>> +            break; +        default: +              ret = -EINVAL; +                goto error_ret_mutex; + } +
>> +    ret = ad799x_i2c_write8(st, AD7998_CYCLE_TMR_REG, t); +
>> +error_ret_mutex: +  mutex_unlock(&dev_info->mlock); + +     return ret ?
>> ret : len; +} + + +static ssize_t ad799x_read_channel_config(struct
>> device *dev, +                                       struct device_attribute *attr, +                                        char *buf) +{
>> +    struct iio_dev *dev_info = dev_get_drvdata(dev); +      struct
>> ad799x_state *st = iio_dev_get_devdata(dev_info); +  struct
>> iio_event_attr *this_attr = to_iio_event_attr(attr); + +     int ret; +      u16
>> val; +       ret = ad799x_i2c_read16(st, this_attr->mask, &val); +   if (ret)
>> +            return ret; + + return sprintf(buf, "%d\n", val); +} + +static
>> ssize_t ad799x_write_channel_config(struct device *dev, +                                     struct
>> device_attribute *attr, +                                     const char *buf, +                                      size_t len) +{
>> +    struct iio_dev *dev_info = dev_get_drvdata(dev); +      struct
>> ad799x_state *st = iio_dev_get_devdata(dev_info); +  struct
>> iio_event_attr *this_attr = to_iio_event_attr(attr); + +     long val;
>> +    int ret; + +    ret = strict_strtol(buf, 10, &val); +   if (ret) +              return
>> ret; + +     mutex_lock(&dev_info->mlock); + ret = ad799x_i2c_write16(st,
>> this_attr->mask, val); +     mutex_unlock(&dev_info->mlock); + +     return ret
>> ? ret : len; +} + +static void ad799x_interrupt_bh(struct work_struct
>> *work_s) { + struct ad799x_state *st = container_of(work_s, +                struct
>> ad799x_state, work_thresh); +        u8 status; +    int i; + +      if
>> (ad799x_i2c_read8(st, AD7998_ALERT_STAT_REG, &status)) +             goto err_out;
>> + +  if (!status) +          goto err_out; + +       ad799x_i2c_write8(st,
>> AD7998_ALERT_STAT_REG, +AD7998_ALERT_STAT_CLEAR); + +        for (i = 0; i <
>> 8; i++) { +          if (status & (1 << i)) +                        iio_push_event(st->indio_dev,
>> 0, +                         i & 0x1 ? +                             IIO_EVENT_CODE_IN_HIGH_THRESH(i >> 1) :
>> +                            IIO_EVENT_CODE_IN_LOW_THRESH(i >> 1), +                         st->last_timestamp);
>> +    } + +err_out: + enable_irq(st->client->irq); +} + +static int
>> ad799x_interrupt(struct iio_dev *dev_info, +         int index, +            s64
>> timestamp, +         int no_test) +{ +       struct ad799x_state *st =
>> dev_info->dev_data; + +      st->last_timestamp = timestamp;
>> +    schedule_work(&st->work_thresh); +      return 0; +} +
>> +IIO_EVENT_SH(ad799x, &ad799x_interrupt); + +/* Direct read attribtues
>> */ +static IIO_DEV_ATTR_IN_RAW(0, ad799x_read_single_channel, 0);
>> +static IIO_DEV_ATTR_IN_RAW(1, ad799x_read_single_channel, 1); +static
>> IIO_DEV_ATTR_IN_RAW(2, ad799x_read_single_channel, 2); +static
>> IIO_DEV_ATTR_IN_RAW(3, ad799x_read_single_channel, 3); +static
>> IIO_DEV_ATTR_IN_RAW(4, ad799x_read_single_channel, 4); +static
>> IIO_DEV_ATTR_IN_RAW(5, ad799x_read_single_channel, 5); +static
>> IIO_DEV_ATTR_IN_RAW(6, ad799x_read_single_channel, 6); +static
>> IIO_DEV_ATTR_IN_RAW(7, ad799x_read_single_channel, 7); + +static
>> ssize_t ad799x_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 ad799x_state *st =
>> iio_dev_get_devdata(dev_info); +     /* Corresponds to Vref / 2^(bits) */ +
>> +    if ((1 << (st->chip_info->bits + 1)) +      > st->int_vref_mv)
>> +            return sprintf(buf, "0.5\n"); + else +          return sprintf(buf, "%d\n",
>> +                    st->int_vref_mv >> st->chip_info->bits); } + +static
>> IIO_DEVICE_ATTR(in_scale, S_IRUGO, ad799x_show_scale, NULL, +0); +
>> +static ssize_t ad799x_show_name(struct device *dev, +                                struct
>> device_attribute *attr, +                             char *buf) +{ +        struct iio_dev *dev_info
>> = dev_get_drvdata(dev); +    struct ad799x_state *st =
>> iio_dev_get_devdata(dev_info); +     return sprintf(buf, "%s\n",
>> st->client->name); } + +static IIO_DEVICE_ATTR(name, S_IRUGO,
>> ad799x_show_name, NULL, 0); + +static struct attribute
>> *ad7991_5_9_3_4_device_attrs[] = {
>> +    &iio_dev_attr_in0_raw.dev_attr.attr,
>> +    &iio_dev_attr_in1_raw.dev_attr.attr,
>> +    &iio_dev_attr_in2_raw.dev_attr.attr,
>> +    &iio_dev_attr_in3_raw.dev_attr.attr,
>> +    &iio_dev_attr_name.dev_attr.attr,
>> +    &iio_dev_attr_in_scale.dev_attr.attr, + NULL +}; + +static struct
>> attribute_group ad7991_5_9_3_4_dev_attr_group = { +  .attrs =
>> ad7991_5_9_3_4_device_attrs, }; + +static struct attribute
>> *ad7991_5_9_3_4_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_scan_el_in2.dev_attr.attr,
>> +    &iio_const_attr_in2_index.dev_attr.attr,
>> +    &iio_scan_el_in3.dev_attr.attr,
>> +    &iio_const_attr_in3_index.dev_attr.attr,
> The precision attribute has been replaced by the _type attributes in the
> scan_elements directory as they give all the information in one consice
> form.  Lets do that change as an additional patch post merge though.
>
>> +    &iio_dev_attr_in_precision.dev_attr.attr,
>> +    NULL,
>> +};
>> +
>> +static struct attribute_group ad7991_5_9_3_4_scan_el_group = {
>> +    .name = "scan_elements",
>> +    .attrs = ad7991_5_9_3_4_scan_el_attrs, };
>> +
>> +static struct attribute *ad7992_device_attrs[] = {
>> +    &iio_dev_attr_in0_raw.dev_attr.attr,
>> +    &iio_dev_attr_in1_raw.dev_attr.attr,
>> +    &iio_dev_attr_name.dev_attr.attr,
>> +    &iio_dev_attr_in_scale.dev_attr.attr,
>> +    NULL
>> +};
>> +
>> +static struct attribute_group ad7992_dev_attr_group = {
>> +    .attrs = ad7992_device_attrs,
>> +};
>> +
>> +static struct attribute *ad7992_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_precision.dev_attr.attr,
>> +    NULL,
>> +};
>> +
>> +static struct attribute_group ad7992_scan_el_group = {
>> +    .name = "scan_elements",
>> +    .attrs = ad7992_scan_el_attrs,
>> +};
>> +
>> +static struct attribute *ad7997_8_device_attrs[] = {
>> +    &iio_dev_attr_in0_raw.dev_attr.attr,
>> +    &iio_dev_attr_in1_raw.dev_attr.attr,
>> +    &iio_dev_attr_in2_raw.dev_attr.attr,
>> +    &iio_dev_attr_in3_raw.dev_attr.attr,
>> +    &iio_dev_attr_in4_raw.dev_attr.attr,
>> +    &iio_dev_attr_in5_raw.dev_attr.attr,
>> +    &iio_dev_attr_in6_raw.dev_attr.attr,
>> +    &iio_dev_attr_in7_raw.dev_attr.attr,
>> +    &iio_dev_attr_name.dev_attr.attr,
>> +    &iio_dev_attr_in_scale.dev_attr.attr,
>> +    NULL
>> +};
>> +
>> +static struct attribute_group ad7997_8_dev_attr_group = {
>> +    .attrs = ad7997_8_device_attrs,
>> +};
>> +
>> +static struct attribute *ad7997_8_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_scan_el_in2.dev_attr.attr,
>> +    &iio_const_attr_in2_index.dev_attr.attr,
>> +    &iio_scan_el_in3.dev_attr.attr,
>> +    &iio_const_attr_in3_index.dev_attr.attr,
>> +    &iio_scan_el_in4.dev_attr.attr,
>> +    &iio_const_attr_in4_index.dev_attr.attr,
>> +    &iio_scan_el_in5.dev_attr.attr,
>> +    &iio_const_attr_in5_index.dev_attr.attr,
>> +    &iio_scan_el_in6.dev_attr.attr,
>> +    &iio_const_attr_in6_index.dev_attr.attr,
>> +    &iio_scan_el_in7.dev_attr.attr,
>> +    &iio_const_attr_in7_index.dev_attr.attr,
>> +    &iio_dev_attr_in_precision.dev_attr.attr,
>> +    NULL,
>> +};
>> +
>> +static struct attribute_group ad7997_8_scan_el_group = {
>> +    .name = "scan_elements",
>> +    .attrs = ad7997_8_scan_el_attrs,
>> +};
>> +
>> +IIO_EVENT_ATTR_SH(in0_thresh_low_value,
>> +              iio_event_ad799x,
>> +              ad799x_read_channel_config,
>> +              ad799x_write_channel_config,
>> +              AD7998_DATALOW_CH1_REG);
>> +
>> +IIO_EVENT_ATTR_SH(in0_thresh_high_value,
>> +              iio_event_ad799x,
>> +              ad799x_read_channel_config,
>> +              ad799x_write_channel_config,
>> +              AD7998_DATAHIGH_CH1_REG);
>> +
>> +IIO_EVENT_ATTR_SH(in0_thresh_both_hyst_raw,
>> +              iio_event_ad799x,
>> +              ad799x_read_channel_config,
>> +              ad799x_write_channel_config,
>> +              AD7998_HYST_CH1_REG);
>> +
>> +IIO_EVENT_ATTR_SH(in1_thresh_low_value,
>> +              iio_event_ad799x,
>> +              ad799x_read_channel_config,
>> +              ad799x_write_channel_config,
>> +              AD7998_DATALOW_CH2_REG);
>> +
>> +IIO_EVENT_ATTR_SH(in1_thresh_high_value,
>> +              iio_event_ad799x,
>> +              ad799x_read_channel_config,
>> +              ad799x_write_channel_config,
>> +              AD7998_DATAHIGH_CH2_REG);
>> +
>> +IIO_EVENT_ATTR_SH(in1_thresh_both_hyst_raw,
>> +              iio_event_ad799x,
>> +              ad799x_read_channel_config,
>> +              ad799x_write_channel_config,
>> +              AD7998_HYST_CH2_REG);
>> +
>> +IIO_EVENT_ATTR_SH(in2_thresh_low_value,
>> +              iio_event_ad799x,
>> +              ad799x_read_channel_config,
>> +              ad799x_write_channel_config,
>> +              AD7998_DATALOW_CH3_REG);
>> +
>> +IIO_EVENT_ATTR_SH(in2_thresh_high_value,
>> +              iio_event_ad799x,
>> +              ad799x_read_channel_config,
>> +              ad799x_write_channel_config,
>> +              AD7998_DATAHIGH_CH3_REG);
>> +
>> +IIO_EVENT_ATTR_SH(in2_thresh_both_hyst_raw,
>> +              iio_event_ad799x,
>> +              ad799x_read_channel_config,
>> +              ad799x_write_channel_config,
>> +              AD7998_HYST_CH3_REG);
>> +
>> +IIO_EVENT_ATTR_SH(in3_thresh_low_value,
>> +              iio_event_ad799x,
>> +              ad799x_read_channel_config,
>> +              ad799x_write_channel_config,
>> +              AD7998_DATALOW_CH4_REG);
>> +
>> +IIO_EVENT_ATTR_SH(in3_thresh_high_value,
>> +              iio_event_ad799x,
>> +              ad799x_read_channel_config,
>> +              ad799x_write_channel_config,
>> +              AD7998_DATAHIGH_CH4_REG);
>> +
>> +IIO_EVENT_ATTR_SH(in3_thresh_both_hyst_raw,
>> +              iio_event_ad799x,
>> +              ad799x_read_channel_config,
>> +              ad799x_write_channel_config,
>> +              AD7998_HYST_CH4_REG);
>> +
>> +static IIO_DEV_ATTR_SAMP_FREQ(S_IWUSR | S_IRUGO,
>> +                          ad799x_read_frequency,
>> +                          ad799x_write_frequency);
>> +static IIO_CONST_ATTR_SAMP_FREQ_AVAIL("15625 7812 3906 1953 976 488
>> +244 0");
>> +
>> +static struct attribute *ad7993_4_7_8_event_attributes[] = {
>> +    &iio_event_attr_in0_thresh_low_value.dev_attr.attr,
>> +    &iio_event_attr_in0_thresh_high_value.dev_attr.attr,
>> +    &iio_event_attr_in0_thresh_both_hyst_raw.dev_attr.attr,
>> +    &iio_event_attr_in1_thresh_low_value.dev_attr.attr,
>> +    &iio_event_attr_in1_thresh_high_value.dev_attr.attr,
>> +    &iio_event_attr_in1_thresh_both_hyst_raw.dev_attr.attr,
>> +    &iio_event_attr_in2_thresh_low_value.dev_attr.attr,
>> +    &iio_event_attr_in2_thresh_high_value.dev_attr.attr,
>> +    &iio_event_attr_in2_thresh_both_hyst_raw.dev_attr.attr,
>> +    &iio_event_attr_in3_thresh_low_value.dev_attr.attr,
>> +    &iio_event_attr_in3_thresh_high_value.dev_attr.attr,
>> +    &iio_event_attr_in3_thresh_both_hyst_raw.dev_attr.attr,
>> +    &iio_dev_attr_sampling_frequency.dev_attr.attr,
>> +    &iio_const_attr_sampling_frequency_available.dev_attr.attr,
>> +    NULL,
>> +};
>> +
>> +static struct attribute_group ad7993_4_7_8_event_attrs_group = {
>> +    .attrs = ad7993_4_7_8_event_attributes, };
>> +
>> +static struct attribute *ad7992_event_attributes[] = {
>> +    &iio_event_attr_in0_thresh_low_value.dev_attr.attr,
>> +    &iio_event_attr_in0_thresh_high_value.dev_attr.attr,
>> +    &iio_event_attr_in0_thresh_both_hyst_raw.dev_attr.attr,
>> +    &iio_event_attr_in1_thresh_low_value.dev_attr.attr,
>> +    &iio_event_attr_in1_thresh_high_value.dev_attr.attr,
>> +    &iio_event_attr_in1_thresh_both_hyst_raw.dev_attr.attr,
>> +    &iio_dev_attr_sampling_frequency.dev_attr.attr,
>> +    &iio_const_attr_sampling_frequency_available.dev_attr.attr,
>> +    NULL,
>> +};
>> +
>> +static struct attribute_group ad7992_event_attrs_group = {
>> +    .attrs = ad7992_event_attributes,
>> +};
>> +
>> +static const struct ad799x_chip_info ad799x_chip_info_tbl[] = {
>> +    [ad7991] = {
>> +            .num_inputs = 4,
>> +            .bits = 12,
>> +            .int_vref_mv = 4096,
>> +            .dev_attrs = &ad7991_5_9_3_4_dev_attr_group,
>> +            .scan_attrs = &ad7991_5_9_3_4_scan_el_group,
>> +            .ad799x_set_scan_mode = ad7991_5_9_set_scan_mode,
>> +    },
>> +    [ad7995] = {
>> +            .num_inputs = 4,
>> +            .bits = 10,
>> +            .int_vref_mv = 1024,
>> +            .dev_attrs = &ad7991_5_9_3_4_dev_attr_group,
>> +            .scan_attrs = &ad7991_5_9_3_4_scan_el_group,
>> +            .ad799x_set_scan_mode = ad7991_5_9_set_scan_mode,
>> +    },
>> +    [ad7999] = {
>> +            .num_inputs = 4,
>> +            .bits = 10,
>> +            .int_vref_mv = 1024,
>> +            .dev_attrs = &ad7991_5_9_3_4_dev_attr_group,
>> +            .scan_attrs = &ad7991_5_9_3_4_scan_el_group,
>> +            .ad799x_set_scan_mode = ad7991_5_9_set_scan_mode,
>> +    },
>> +    [ad7992] = {
>> +            .num_inputs = 2,
>> +            .bits = 12,
>> +            .int_vref_mv = 4096,
>> +            .monitor_mode = true,
>> +            .default_config = AD7998_ALERT_EN,
>> +            .dev_attrs = &ad7992_dev_attr_group,
>> +            .scan_attrs = &ad7992_scan_el_group,
>> +            .event_attrs = &ad7992_event_attrs_group,
>> +            .ad799x_set_scan_mode = ad7992_3_4_set_scan_mode,
>> +    },
>> +    [ad7993] = {
>> +            .num_inputs = 4,
>> +            .bits = 10,
>> +            .int_vref_mv = 1024,
>> +            .monitor_mode = true,
>> +            .default_config = AD7998_ALERT_EN,
>> +            .dev_attrs = &ad7991_5_9_3_4_dev_attr_group,
>> +            .scan_attrs = &ad7991_5_9_3_4_scan_el_group,
>> +            .event_attrs = &ad7993_4_7_8_event_attrs_group,
>> +            .ad799x_set_scan_mode = ad7992_3_4_set_scan_mode,
>> +    },
>> +    [ad7994] = {
>> +            .num_inputs = 4,
>> +            .bits = 12,
>> +            .int_vref_mv = 4096,
>> +            .monitor_mode = true,
>> +            .default_config = AD7998_ALERT_EN,
>> +            .dev_attrs = &ad7991_5_9_3_4_dev_attr_group,
>> +            .scan_attrs = &ad7991_5_9_3_4_scan_el_group,
>> +            .event_attrs = &ad7993_4_7_8_event_attrs_group,
>> +            .ad799x_set_scan_mode = ad7992_3_4_set_scan_mode,
>> +    },
>> +    [ad7997] = {
>> +            .num_inputs = 8,
>> +            .bits = 10,
>> +            .int_vref_mv = 1024,
>> +            .monitor_mode = true,
>> +            .default_config = AD7998_ALERT_EN,
>> +            .dev_attrs = &ad7997_8_dev_attr_group,
>> +            .scan_attrs = &ad7997_8_scan_el_group,
>> +            .event_attrs = &ad7993_4_7_8_event_attrs_group,
>> +            .ad799x_set_scan_mode = ad7997_8_set_scan_mode,
>> +    },
>> +    [ad7998] = {
>> +            .num_inputs = 8,
>> +            .bits = 12,
>> +            .int_vref_mv = 4096,
>> +            .monitor_mode = true,
>> +            .default_config = AD7998_ALERT_EN,
>> +            .dev_attrs = &ad7997_8_dev_attr_group,
>> +            .scan_attrs = &ad7997_8_scan_el_group,
>> +            .event_attrs = &ad7993_4_7_8_event_attrs_group,
>> +            .ad799x_set_scan_mode = ad7997_8_set_scan_mode,
>> +    },
>> +};
>> +
>> +static int __devinit ad799x_probe(struct i2c_client *client,
>> +                               const struct i2c_device_id *id) {
>> +    int ret, regdone = 0;
>> +    struct ad799x_platform_data *pdata = client->dev.platform_data;
>> +    struct ad799x_state *st = kzalloc(sizeof(*st), GFP_KERNEL);
>> +    if (st == NULL) {
>> +            ret = -ENOMEM;
>> +            goto error_ret;
>> +    }
>> +
>> +    /* this is only used for device removal purposes */
>> +    i2c_set_clientdata(client, st);
>> +
>> +    atomic_set(&st->protect_ring, 0);
>> +    st->id = id->driver_data;
>> +    st->chip_info = &ad799x_chip_info_tbl[st->id];
>> +    st->config = st->chip_info->default_config;
>> +
>> +    /* TODO: Add pdata options for filtering and bit delay */
> We also need to add userspace attributes to cover filtering for these
> devices.  However, that is definitely a discussion for another day!
>> + +  if (pdata) +            st->int_vref_mv = pdata->vref_mv; +     else
>> +            st->int_vref_mv = st->chip_info->int_vref_mv; + +       st->reg =
>> regulator_get(&client->dev, "vcc"); +        if (!IS_ERR(st->reg)) { +               ret =
>> regulator_enable(st->reg); +         if (ret) +                      goto error_put_reg; +   }
>> +    st->client = client; + +        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 = &client->dev; +     st->indio_dev->attrs =
>> st->chip_info->dev_attrs; +  st->indio_dev->event_attrs =
>> st->chip_info->event_attrs; + +      st->indio_dev->dev_data = (void *)(st);
>> +    st->indio_dev->driver_module = THIS_MODULE; +   st->indio_dev->modes =
>> INDIO_DIRECT_MODE; + st->indio_dev->num_interrupt_lines = 1; + +     ret =
>> ad799x_set_scan_mode(st, 0); +       if (ret) +              goto error_free_device; +
>> +    ret = ad799x_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_cleanup_ring; +        regdone = 1; + +        ret =
>> iio_ring_buffer_register(st->indio_dev->ring, 0); +  if (ret) +              goto
>> error_cleanup_ring; + +      if (client->irq > 0 &&
>> st->chip_info->monitor_mode) { +             INIT_WORK(&st->work_thresh,
>> ad799x_interrupt_bh); + +            ret =
>> iio_register_interrupt_line(client->irq, +                           st->indio_dev, +                                0,
>> +                            IRQF_TRIGGER_FALLING, +                         client->name); +                if (ret) +                      goto
>> error_cleanup_ring; + +              /* +             * The event handler list element refer
>> to iio_event_ad799x. +                * All event attributes bind to the same event
>> handler. +            * So, only register event handler once. +               */
>> +            iio_add_event_to_list(&iio_event_ad799x,
>> +                            &st->indio_dev->interrupts[0]->ev_list); +      } + +   return 0;
>> +error_cleanup_ring: +       ad799x_ring_cleanup(st->indio_dev);
>> +error_free_device: +        if (!regdone) +         iio_free_device(st->indio_dev);
>> +    else +          iio_device_unregister(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 __devexit int
>> ad799x_remove(struct i2c_client *client) { + struct ad799x_state *st =
>> i2c_get_clientdata(client); +        struct iio_dev *indio_dev =
>> st->indio_dev; + +   if (client->irq > 0 && st->chip_info->monitor_mode)
>> +            iio_unregister_interrupt_line(indio_dev, 0); +
>> +    iio_ring_buffer_unregister(indio_dev->ring);
>> +    ad799x_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 i2c_device_id ad799x_id[] = { + { "ad7991", ad7991 }, + {
>> "ad7995", ad7995 }, +        { "ad7999", ad7999 }, + { "ad7992", ad7992 }, + {
>> "ad7993", ad7993 }, +        { "ad7994", ad7994 }, + { "ad7997", ad7997 }, + {
>> "ad7998", ad7998 }, +        {} +}; + +MODULE_DEVICE_TABLE(i2c, ad799x_id); +
>> +static struct i2c_driver ad799x_driver = { +        .driver = { +           .name =
>> "ad799x", +  }, +    .probe = ad799x_probe, +        .remove =
>> __devexit_p(ad799x_remove), +        .id_table = ad799x_id, +}; + +static
>> __init int ad799x_init(void) { +     return i2c_add_driver(&ad799x_driver);
>> } + +static __exit void ad799x_exit(void) {
>> +    i2c_del_driver(&ad799x_driver); +} + +MODULE_AUTHOR("Michael
>> Hennerich +<hennerich@xxxxxxxxxxxxxxxxxxxx>");
>> +MODULE_DESCRIPTION("Analog Devices AD799x ADC"); +MODULE_LICENSE("GPL
>> v2"); MODULE_ALIAS("i2c:ad799x"); + +module_init(ad799x_init);
>> +module_exit(ad799x_exit); diff --git
>> a/drivers/staging/iio/adc/ad799x_ring.c
>> b/drivers/staging/iio/adc/ad799x_ring.c new file mode 100644 index
>> 0000000..0f2041a --- /dev/null +++
>> b/drivers/staging/iio/adc/ad799x_ring.c @@ -0,0 +1,246 @@ +/* + *
>> Copyright (C) 2010 Michael Hennerich, Analog Devices Inc. + * Copyright
>> (C) 2008-2010 Jonathan Cameron + * + * This program is free software;
>> you can redistribute it and/or +modify + * it under the terms of the
>> GNU General Public License version 2 +as + * published by the Free
>> Software Foundation. + * + * ad799x_ring.c + */ + +#include
>> <linux/interrupt.h> +#include <linux/workqueue.h> +#include
>> <linux/device.h> +#include <linux/slab.h> +#include <linux/kernel.h>
>> +#include <linux/sysfs.h> +#include <linux/list.h> +#include
>> <linux/i2c.h> +#include <linux/bitops.h> + +#include "../iio.h"
>> +#include "../ring_generic.h" +#include "../ring_sw.h" +#include
>> "../trigger.h" +#include "../sysfs.h" + +#include "ad799x.h" + +int
>> ad799x_single_channel_from_ring(struct ad799x_state *st, long +mask) {
>> +    unsigned long numvals; +        int count = 0, ret; +   u16 *ring_data; +       if
>> (!(st->indio_dev->ring->scan_mask & mask)) { +               ret = -EBUSY; +         goto
>> error_ret; + } +     numvals = st->indio_dev->ring->scan_count; +
>> +    ring_data = kmalloc(numvals*2, GFP_KERNEL); +   if (ring_data == NULL)
>> { +          ret = -ENOMEM; +                goto error_ret; +       } +     ret =
>> st->indio_dev->ring->access.read_last(st->indio_dev->ring, +                                         (u8
>> *) ring_data); +     if (ret) +              goto error_free_ring_data; +    /* Need a
>> count of channels prior to this one */ +     mask >>= 1; +   while (mask) {
>> +            if (mask & st->indio_dev->ring->scan_mask) +                    count++; +              mask >>=
>> 1; + } + +   ret = be16_to_cpu(ring_data[count]) & 0xFFF; +
>> +error_free_ring_data: +     kfree(ring_data); +error_ret: + return ret; +}
>> + +/** + * ad799x_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
>> ad799x_ring_preenable(struct iio_dev *indio_dev) { + struct
>> ad799x_state *st = indio_dev->dev_data; +    size_t d_size; +        unsigned
>> long numvals; + +    /* +     * Need to figure out the current mode based
>> upon the requested +  * scan mask in iio_dev +        */ + + if (st->id ==
>> ad7997 || st->id == ad7998) +                ad799x_set_scan_mode(st,
>> st->indio_dev->ring->scan_mask); + + numvals =
>> st->indio_dev->ring->scan_count; + + if
>> (indio_dev->ring->access.set_bytes_per_datum) { +            d_size = numvals*2 +
>> 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); +  } + +   return 0; +} + +/** + *
>> ad799x_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 ad799x_poll_func_th(struct iio_dev
>> *indio_dev, s64 +time) { +   struct ad799x_state *st =
>> indio_dev->dev_data; + +     schedule_work(&st->poll_work); + +      return; +}
>> +/** + * ad799x_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. + **/
>> +static void ad799x_poll_bh_to_ring(struct work_struct *work_s) {
>> +    struct ad799x_state *st = container_of(work_s, struct ad799x_state,
>> +                                              poll_work); + struct iio_dev *indio_dev = st->indio_dev;
>> +    struct iio_sw_ring_buffer *ring = iio_to_sw_ring(indio_dev- ring);
>> +    s64 time_ns; +  __u8 *rxbuf; +  int b_sent; +   size_t d_size; +        u8 cmd;
>> + +  unsigned long numvals = st->indio_dev->ring->scan_count; + +    /*
>> Ensure the timestamp is 8 byte aligned */ +  d_size = numvals*2 +
>> sizeof(s64); + +     if (d_size % sizeof(s64)) +             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; + +     /* Monitor mode prevents reading. Whilst not currently
>> implemented +         * might as well have this test in here in the meantime
>> as it does +  * no harm. +    */ +   if (numvals == 0) +             return; + +     rxbuf
>> = kmalloc(d_size,    GFP_KERNEL); +  if (rxbuf == NULL) +            return; +
>> +    switch (st->id) { +     case ad7991: +  case ad7995: +  case ad7999: +          cmd
>> = st->config | (st->indio_dev->ring->scan_mask <<
>> +                    AD799X_CHANNEL_SHIFT); +                break; +        case ad7992: +  case ad7993:
>> +    case ad7994: +          cmd = (st->indio_dev->ring->scan_mask <<
>> +                    AD799X_CHANNEL_SHIFT) | AD7998_CONV_RES_REG; +          break; +        case
>> ad7997: +    case ad7998: +          cmd = AD7997_8_READ_SEQUENCE |
>> AD7998_CONV_RES_REG; +               break; +        default: +              cmd = 0; +      } + +   b_sent =
>> i2c_smbus_read_i2c_block_data(st->client, +                  cmd, numvals*2, rxbuf);
>> +    if (b_sent < 0) +               goto done; + +  time_ns = iio_get_time_ns(); +
>> +    memcpy(rxbuf + d_size - sizeof(s64), &time_ns, sizeof(time_ns)); +
>> +    indio_dev->ring->access.store_to(&ring->buf, rxbuf, time_ns); +done:
>> +    kfree(rxbuf); + atomic_dec(&st->protect_ring); +} + + +int
>> ad799x_register_ring_funcs_and_init(struct iio_dev *indio_dev) {
>> +    struct ad799x_state *st = indio_dev->dev_data; +        int ret = 0; +
>> +    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(&st->indio_dev->ring->access);
>> +    indio_dev->pollfunc = kzalloc(sizeof(*indio_dev->pollfunc),
>> GFP_KERNEL); +       if (indio_dev->pollfunc == NULL) { +            ret = -ENOMEM;
>> +            goto error_deallocate_sw_rb; +  } +     /* Configure the polling function
>> called on trigger interrupts */ +    indio_dev->pollfunc->poll_func_main =
>> &ad799x_poll_func_th; +      indio_dev->pollfunc->private_data = indio_dev;
>> + +  /* Ring buffer functions - here trigger setup related */ +
>> +    indio_dev->ring->preenable = &ad799x_ring_preenable;
>> +    indio_dev->ring->postenable = &iio_triggered_ring_postenable;
>> +    indio_dev->ring->predisable = &iio_triggered_ring_predisable; +
>> +    INIT_WORK(&st->poll_work, &ad799x_poll_bh_to_ring); +
>> +    indio_dev->ring->scan_el_attrs = st->chip_info->scan_attrs; + + /*
>> 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; +} + +void
>> ad799x_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); +}

Greetings,
Michael

Analog Devices GmbH      Wilhelm-Wagenfeld-Str. 6      80807 Muenchen
Sitz der Gesellschaft Muenchen, Registergericht Muenchen HRB 4036 Geschaeftsfuehrer Thomas Wessel, William A. Martin, Margaret Seif

--
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