RE: [PATCH] iio: impedance-analyzer: New driver for AD5933/4 Impedance Converter, Network Analyzer

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

 



Jonathan Cameron wrote on 2011-08-01:
> On 08/01/11 16:28, michael.hennerich@xxxxxxxxxx wrote:
>> From: Michael Hennerich <michael.hennerich@xxxxxxxxxx>
>>
>> The AD5933 is a high precision impedance converter system solution
>> that combines an on-board frequency generator with a 12-bit, 1 MSPS,
>> analog-to-digital converter (ADC). The frequency generator allows an
>> external complex impedance to be excited with a known frequency.
>>
>> The response signal from the impedance is sampled by the on-board ADC
>> and a discrete Fourier transform (DFT) is processed by an on-chip DSP
>> engine. The DFT algorithm returns a real (R) and imaginary (I)
>> data-word at each output frequency.
>>
>> Changes since V1:
>>
>> Apply list review feedback:
>> Consistently use poll_time_jiffies.
>> Use be|le cpu endian helpers where applicable.
>> Add various comments.
>>
> I think next kernel is intended to be 3.1 not 3.0.1?
Ups...
>
> Please do some more build tests... Missing semicolons on 414, 422.
> ad5933_default_pdata should be static. Data undefined at 209.
> All trivial stuff though so not to worry from review point of view.

Stupid kate build plug-in - that's the state before my initial build test.
I fixed all these. If you click on an error/warning in the build output it automatically
jumps to the line - which is great, however it opens up a new file instance and tab.
If you happen to fix these issues on one instance, and by accident also save the other one.
You basically revert everything you have done. This recently cost me a day of work.

Thanks for the detailed review!

> Will pull fixed version into iio-blue when you send it to Greg (below
> everything else).
>
>> Signed-off-by: Michael Hennerich <michael.hennerich@xxxxxxxxxx>
>> Reviewed-by: Jonathan Cameron <jic23@xxxxxxxxx> Acked-by: Jonathan
>> Cameron <jic23@xxxxxxxxx>
>>
>> ---
>>  .../sysfs-bus-iio-impedance-analyzer-ad5933        |   30 +
>>  drivers/staging/iio/Kconfig                        |    1 +
>>  drivers/staging/iio/Makefile                       |    1 +
>>  drivers/staging/iio/impedance-analyzer/Kconfig     |   16 +
>>  drivers/staging/iio/impedance-analyzer/Makefile    |    5 +
>>  drivers/staging/iio/impedance-analyzer/ad5933.c    |  818
>>  ++++++++++++++++++++ drivers/staging/iio/impedance-analyzer/ad5933.h
>>   |   28 + 7 files changed, 899 insertions(+), 0 deletions(-)  create
>>  mode
>> 100644
>> drivers/staging/iio/Documentation/sysfs-bus-iio-impedance-analyzer- ad5
>> 933  create mode 100644 drivers/staging/iio/impedance-
> analyzer/Kconfig
>>  create mode 100644 drivers/staging/iio/impedance-analyzer/Makefile
>>  create mode 100644 drivers/staging/iio/impedance-analyzer/ad5933.c
>>  create mode 100644 drivers/staging/iio/impedance-analyzer/ad5933.h
>> diff --git
>> a/drivers/staging/iio/Documentation/sysfs-bus-iio-impedance-analyzer -
>> a d5933
>> b/drivers/staging/iio/Documentation/sysfs-bus-iio-impedance-analyzer -
>> a d5933 new file mode 100644 index 0000000..798029a --- /dev/null +++
>> b/drivers/staging/iio/Documentation/sysfs-bus-iio-impedance- analyz +++
>> er-ad5933 @@ -0,0 +1,30 @@
>> +What:               /sys/bus/iio/devices/iio:deviceX/outY_freq_start Isn't next
>> kernel going to be 3.1? +KernelVersion:      3.0.1
>> +Contact:    linux-iio@xxxxxxxxxxxxxxx +Description: +               Frequency sweep
>> start frequency in Hz. +
>> +What:               /sys/bus/iio/devices/iio:deviceX/outY_freq_increment
>> +KernelVersion:      3.0.1 +Contact: linux-iio@xxxxxxxxxxxxxxx +Description:
>> +            Frequency increment in Hz (step size) between consecutive
>> +            frequency points along the sweep. +
>> +What:               /sys/bus/iio/devices/iio:deviceX/outY_freq_points
>> +KernelVersion:      3.0.1 +Contact: linux-iio@xxxxxxxxxxxxxxx +Description:
>> +            Number of frequency points (steps) in the frequency sweep. +            This
>> value, in conjunction with the outY_freq_start and the
>> +            outY_freq_increment, determines the frequency sweep range +             for the
>> sweep operation. +
>> +What:               /sys/bus/iio/devices/iio:deviceX/outY_settling_cycles
>> +KernelVersion:      3.0.1 +Contact: linux-iio@xxxxxxxxxxxxxxx +Description:
>> +            Number of output excitation cycles (settling time cycles) +             that
>> are allowed to pass through the unknown impedance, +         after each
>> frequency increment, and before the ADC is triggered +               to perform a
>> conversion sequence of the response signal. diff --git
>> a/drivers/staging/iio/Kconfig b/drivers/staging/iio/Kconfig index
>> d329635..7526567 100644 --- a/drivers/staging/iio/Kconfig +++
>> b/drivers/staging/iio/Kconfig @@ -62,6 +62,7 @@ source
>> "drivers/staging/iio/addac/Kconfig"
>>  source "drivers/staging/iio/dac/Kconfig" source
>>  "drivers/staging/iio/dds/Kconfig" source
>>  "drivers/staging/iio/gyro/Kconfig" +source
>>  "drivers/staging/iio/impedance-analyzer/Kconfig" source
>>  "drivers/staging/iio/imu/Kconfig" source
>>  "drivers/staging/iio/light/Kconfig" source
>>  "drivers/staging/iio/magnetometer/Kconfig"
>> diff --git a/drivers/staging/iio/Makefile
>> b/drivers/staging/iio/Makefile index bb5c95c..eaff649 100644
>> --- a/drivers/staging/iio/Makefile
>> +++ b/drivers/staging/iio/Makefile
>> @@ -16,6 +16,7 @@ obj-y += addac/
>>  obj-y += dac/ obj-y += dds/ obj-y += gyro/ +obj-y +=
>>  impedance-analyzer/ obj-y += imu/ obj-y += light/ obj-y +=
>>  magnetometer/
>> diff --git a/drivers/staging/iio/impedance-analyzer/Kconfig
>> b/drivers/staging/iio/impedance-analyzer/Kconfig new file mode 100644
>> index 0000000..9e91a9b --- /dev/null +++
>> b/drivers/staging/iio/impedance-analyzer/Kconfig @@ -0,0 +1,16 @@ +# +#
>> Impedance Converter, Network Analyzer drivers # comment "Network
>> +Analyzer, Impedance Converters" Analyzer->Analyzers (for consistency).
>> + +config AD5933 +   tristate "Analog Devices AD5933, AD5934 driver"
>> +    depends on I2C +        select IIO_RING_BUFFER +        select IIO_SW_RING +    help
>> +      Say yes here to build support for Analog Devices Impedance
>> Converter, +   Network Analyzer, AD5933/4, provides direct access via
>> sysfs. + +     To compile this driver as a module, choose M here: the +
>> module will be called ad5933. diff --git
>> a/drivers/staging/iio/impedance-analyzer/Makefile
>> b/drivers/staging/iio/impedance-analyzer/Makefile new file mode 100644
>> index 0000000..7604d78 --- /dev/null +++
>> b/drivers/staging/iio/impedance-analyzer/Makefile @@ -0,0 +1,5 @@ +# +#
>> Makefile for Impedance Converter, Network Analyzer drivers # +
>> +obj-$(CONFIG_AD5933) += ad5933.o diff --git
>> a/drivers/staging/iio/impedance-analyzer/ad5933.c
>> b/drivers/staging/iio/impedance-analyzer/ad5933.c new file mode 100644
>> index 0000000..0df5653 --- /dev/null +++
>> b/drivers/staging/iio/impedance-analyzer/ad5933.c @@ -0,0 +1,818 @@ +/*
>> + * AD5933 AD5934 Impedance Converter, Network Analyzer + * + *
>> Copyright 2011 Analog Devices Inc. + * + * Licensed under the GPL-2. +
>> */ + +#include <linux/interrupt.h> +#include <linux/device.h> +#include
>> <linux/kernel.h> +#include <linux/sysfs.h> +#include <linux/i2c.h>
>> +#include <linux/regulator/consumer.h> #include <linux/slab.h>
>> +#include <linux/types.h> #include <linux/err.h> #include
>> +<linux/delay.h> #include <asm/div64.h> + +#include "../iio.h"
>> +#include "../sysfs.h" +#include "../ring_generic.h" +#include
>> "../ring_sw.h" + +#include "ad5933.h" + +/* AD5933/AD5934 Registers */
>> +#define AD5933_REG_CONTROL_HB               0x80    /* R/W, 2 bytes */ +#define
>> AD5933_REG_CONTROL_LB                0x81    /* R/W, 2 bytes */ +#define
>> AD5933_REG_FREQ_START                0x82    /* R/W, 3 bytes */ +#define
>> AD5933_REG_FREQ_INC          0x85    /* R/W, 3 bytes */ +#define
>> AD5933_REG_INC_NUM           0x88    /* R/W, 2 bytes, 9 bit */ +#define
>> AD5933_REG_SETTLING_CYCLES   0x8A    /* R/W, 2 bytes */ +#define
>> AD5933_REG_STATUS            0x8F    /* R, 1 byte */ +#define
>> AD5933_REG_TEMP_DATA         0x92    /* R, 2 bytes*/ +#define
>> AD5933_REG_REAL_DATA         0x94    /* R, 2 bytes*/ +#define
>> AD5933_REG_IMAG_DATA         0x96    /* R, 2 bytes*/ + +/* AD5933_REG_CONTROL_HB
>> Bits */ +#define AD5933_CTRL_INIT_START_FREQ (0x1 << 4) +#define
>> AD5933_CTRL_START_SWEEP              (0x2 << 4) +#define AD5933_CTRL_INC_FREQ                (0x3
>> << 4) +#define AD5933_CTRL_REPEAT_FREQ               (0x4 << 4) +#define
>> AD5933_CTRL_MEASURE_TEMP     (0x9 << 4) +#define
>> AD5933_CTRL_POWER_DOWN               (0xA << 4) +#define AD5933_CTRL_STANDBY         (0xB
>> << 4) + +#define AD5933_CTRL_RANGE_2000mVpp  (0x0 << 1) +#define
>> AD5933_CTRL_RANGE_200mVpp    (0x1 << 1) +#define
>> AD5933_CTRL_RANGE_400mVpp    (0x2 << 1) +#define
>> AD5933_CTRL_RANGE_1000mVpp   (0x3 << 1) +#define
>> AD5933_CTRL_RANGE(x)         ((x) << 1) + +#define
>> AD5933_CTRL_PGA_GAIN_1               (0x1 << 0) +#define
>> AD5933_CTRL_PGA_GAIN_5               (0x0 << 0) + +/* AD5933_REG_CONTROL_LB Bits */
>> +#define AD5933_CTRL_RESET           (0x1 << 4) +#define
>> AD5933_CTRL_INT_SYSCLK               (0x0 << 3) +#define
>> AD5933_CTRL_EXT_SYSCLK               (0x1 << 3) + +/* AD5933_REG_STATUS Bits */
>> +#define AD5933_STAT_TEMP_VALID              (0x1 << 0) +#define
>> AD5933_STAT_DATA_VALID               (0x1 << 1) +#define
>> AD5933_STAT_SWEEP_DONE               (0x1 << 2) + +/* I2C Block Commands */ +#define
>> AD5933_I2C_BLOCK_WRITE               0xA0 +#define AD5933_I2C_BLOCK_READ             0xA1
>> +#define AD5933_I2C_ADDR_POINTER             0xB0 + +/* Device Specs */ +#define
>> AD5933_INT_OSC_FREQ_Hz               16776000 +#define
>> AD5933_MAX_OUTPUT_FREQ_Hz    100000 +#define AD5933_MAX_RETRIES              100 +
>> +#define AD5933_OUT_RANGE            1 +#define AD5933_OUT_RANGE_AVAIL               2
>> +#define AD5933_OUT_SETTLING_CYCLES  3 +#define AD5933_IN_PGA_GAIN           4
>> +#define AD5933_IN_PGA_GAIN_AVAIL    5 +#define AD5933_FREQ_POINTS           6 +
>> +#define AD5933_POLL_TIME_ms         10 +#define
>> AD5933_INIT_EXCITATION_TIME_ms       100 + +struct ad5933_state { +  struct
>> i2c_client           *client; +      struct regulator                *reg; + struct
>> ad5933_platform_data *pdata; +       struct delayed_work             work; + unsigned
>> long                 mclk_hz; +      unsigned char                   ctrl_hb; +      unsigned char                   ctrl_lb;
>> +    unsigned                        range_avail[4]; +       unsigned short                  vref_mv; +      unsigned
>> short                        settling_cycles; +      unsigned short                  freq_points;
>> +    unsigned                        freq_start; +   unsigned                        freq_inc; +     unsigned                        state;
>> +    unsigned                        poll_time_jiffies; +}; + +struct ad5933_platform_data
>> ad5933_default_pdata  = { +  .vref_mv = 3300, +}; + +static struct
>> iio_chan_spec ad5933_channels[] = { +        IIO_CHAN(IIO_TEMP, 0, 1, 1, NULL,
>> 0, 0, 0, +            0, AD5933_REG_TEMP_DATA, IIO_ST('s', 14, 16, 0), 0), + /*
>> Ring Channels */ +   IIO_CHAN(IIO_IN, 0, 1, 0, "real_raw", 0, 0, +            (1
>> << IIO_CHAN_INFO_SCALE_SEPARATE), +           AD5933_REG_REAL_DATA, 0,
>> IIO_ST('s', 16, 16, 0), 0), +        IIO_CHAN(IIO_IN, 0, 1, 0, "imag_raw", 0,
>> 0, +          (1 << IIO_CHAN_INFO_SCALE_SEPARATE), +          AD5933_REG_IMAG_DATA,
>> 1, IIO_ST('s', 16, 16, 0), 0), }; + +static int ad5933_i2c_write(struct
>> i2c_client *client, +                              u8 reg, u8 len, u8 *data) +{ +    int ret; +
>> +    while (len--) { +               ret = i2c_smbus_write_byte_data(client, reg++,
>> *data++); +          if (ret < 0) { +                        dev_err(&client->dev, "I2C write
>> error\n"); +                 return ret; +           } +     } +     return 0; +} + +static int
>> ad5933_i2c_read(struct i2c_client *client, +                       u8 reg, u8 len, u8
>> *data) +{ +  int ret; + +    while (len--) { +               ret =
>> i2c_smbus_read_byte_data(client, reg++); +           if (ret < 0) {
>> +                    dev_err(&client->dev, "I2C read error\n"); +                    return ret; +           }
>> +            *data++ = ret; +        } +     return 0; +} + +static int ad5933_cmd(struct
>> ad5933_state *st, unsigned char cmd) { +     unsigned char dat =
>> st->ctrl_hb | cmd; + +       return ad5933_i2c_write(st->client,
>> +                    AD5933_REG_CONTROL_HB, 1, &dat); } + +static int
>> ad5933_reset(struct ad5933_state *st) { +    unsigned char dat =
>> st->ctrl_lb | AD5933_CTRL_RESET; +   return ad5933_i2c_write(st->client,
>> +                    AD5933_REG_CONTROL_LB, 1, &dat); } + +static int
>> ad5933_wait_busy(struct ad5933_state *st, unsigned char +event) {
>> +    unsigned char val, timeout = AD5933_MAX_RETRIES; +      int ret; + +    while
>> (timeout--) { +              ret =  ad5933_i2c_read(st->client, AD5933_REG_STATUS,
>> 1, &val); +          if (ret < 0) +                  return ret; +           if (val & event)
>> +                    return val; +           cpu_relax(); +          mdelay(1); +    } + +   return -EAGAIN;
>> +} + +static int ad5933_set_freq(struct ad5933_state *st, +
>> unsigned reg, unsigned long freq) { +        unsigned long long freqreg;
>> +    union { +               u32 d32; +              u8 d8[4]; +     } dat; + +      freqreg = (u64) freq *
>> (u64) (1 << 27); +   do_div(freqreg, st->mclk_hz / 4); + +   switch (reg) {
>> +    case AD5933_REG_FREQ_START: +           st->freq_start = freq; +                break;
>> +    case AD5933_REG_FREQ_INC: +             st->freq_inc = freq; +          break;
>> +    default: +              return -EINVAL; +       } + +   data.d32 = cpu_to_be32(freqreg);
>> +    return ad5933_i2c_write(st->client, reg, 3, &dat.d8[1]); } + +static
>> int ad5933_setup(struct ad5933_state *st) { +        unsigned short dat; +   int
>> ret; + +     ret = ad5933_reset(st); +       if (ret < 0) +          return ret; + + ret
>> = ad5933_set_freq(st, AD5933_REG_FREQ_START, 10000); +       if (ret < 0)
>> +            return ret; + + ret = ad5933_set_freq(st, AD5933_REG_FREQ_INC, 200);
>> +    if (ret < 0) +          return ret; + + st->settling_cycles = 10; +     dat =
>> cpu_to_be16(st->settling_cycles); + +        ret =
>> ad5933_i2c_write(st->client, +                       AD5933_REG_SETTLING_CYCLES, 2, (u8
>> *)&dat); +   if (ret < 0) +          return ret; + + st->freq_points = 100; +        dat
>> = cpu_to_be16(st->freq_points); + +  return ad5933_i2c_write(st->client,
>> AD5933_REG_INC_NUM, 2, (u8 +*)&dat); } + +static void
>> ad5933_calc_out_ranges(struct ad5933_state *st) { +  int i; +        unsigned
>> normalized_3v3[4] = {1980, 198, 383, 970}; + +       for (i = 0; i < 4; i++)
>> +            st->range_avail[i] = normalized_3v3[i] * st->vref_mv / 3300; + +} +
>> +/* + * handles: AD5933_REG_FREQ_START and AD5933_REG_FREQ_INC  */ +
>> +static ssize_t ad5933_show_frequency(struct device *dev, +                                  struct
>> device_attribute *attr, +                                    char *buf) +{ + struct iio_dev *dev_info
>> = dev_get_drvdata(dev); +    struct ad5933_state *st = iio_priv(dev_info);
>> +    struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); +       int ret;
>> +    unsigned long long freqreg; +   union { +               u32 d32; +              u8 d8[4]; +     }
>> dat; + +     mutex_lock(&dev_info->mlock); + ret =
>> ad5933_i2c_read(st->client, this_attr->address, 3, &dat.d8[1]);
>> +    mutex_unlock(&dev_info->mlock); +       if (ret < 0) +          return ret; +
>> +    freqreg = be32_to_cpu(dat.d32) & 0xFFFFFF; + +  freqreg = (u64)
>> freqreg * (u64) (st->mclk_hz / 4); + do_div(freqreg, 1 << 27); +
>> +    return sprintf(buf, "%d\n", (int) freqreg); } + +static ssize_t
>> ad5933_store_frequency(struct device *dev, +                                  struct
>> device_attribute *attr, +                                     const char *buf, +                                      size_t len) +{
>> +    struct iio_dev *dev_info = dev_get_drvdata(dev); +      struct
>> ad5933_state *st = iio_priv(dev_info); +     struct iio_dev_attr *this_attr
>> = to_iio_dev_attr(attr); +   long val; +     int ret; + +    ret =
>> strict_strtoul(buf, 10, &val); +     if (ret) +              return ret; + + if (val >
>> AD5933_MAX_OUTPUT_FREQ_Hz) +         return -EINVAL; +
>> +    mutex_lock(&dev_info->mlock); + ret = ad5933_set_freq(st,
>> this_attr->address, val); +  mutex_unlock(&dev_info->mlock); + +     return
>> ret ? ret : len; +} + +static IIO_DEVICE_ATTR(out0_freq_start, S_IRUGO
>> | S_IWUSR, +                 ad5933_show_frequency, +                        ad5933_store_frequency,
>> +                    AD5933_REG_FREQ_START); + +static
>> IIO_DEVICE_ATTR(out0_freq_increment, S_IRUGO | S_IWUSR,
>> +                    ad5933_show_frequency, +                        ad5933_store_frequency,
>> +                    AD5933_REG_FREQ_INC); + +static ssize_t ad5933_show(struct device
>> *dev, +                                      struct device_attribute *attr, +                                        char *buf) +{ + struct
>> iio_dev *dev_info = dev_get_drvdata(dev); +  struct ad5933_state *st =
>> iio_priv(dev_info); +        struct iio_dev_attr *this_attr =
>> to_iio_dev_attr(attr); +     int ret = 0, len = 0; +
>> +    mutex_lock(&dev_info->mlock); + switch (this_attr->address) { + case
>> AD5933_OUT_RANGE: +          len = sprintf(buf, "%d\n", +
>> st->range_avail[(st->ctrl_hb >> 1) & 0x3]); +                break; +        case
>> AD5933_OUT_RANGE_AVAIL: +            len = sprintf(buf, "%d %d %d %d\n",
>> st->range_avail[0], +                              st->range_avail[3], st->range_avail[2],
>> +                          st->range_avail[1]); +            break; +        case
>> AD5933_OUT_SETTLING_CYCLES: +                len = sprintf(buf, "%d\n",
>> st->settling_cycles); +              break; +        case AD5933_IN_PGA_GAIN: +              len =
>> sprintf(buf, "%s\n", +                             (st->ctrl_hb & AD5933_CTRL_PGA_GAIN_1) ?
>> +                          "1" : "0.2"); +           break; +        case AD5933_IN_PGA_GAIN_AVAIL:
>> +            len = sprintf(buf, "1 0.2\n"); +                break; +        case AD5933_FREQ_POINTS:
>> +            len = sprintf(buf, "%d\n", st->freq_points); +          break; +        default:
>> +            ret = -EINVAL; +        } + +   mutex_unlock(&dev_info->mlock); +       return ret
>> ? ret : len; +} + +static ssize_t ad5933_store(struct device *dev,
>> +                                     struct device_attribute *attr, +                                        const char *buf, +
>> size_t len) +{ +     struct iio_dev *dev_info = dev_get_drvdata(dev);
>> +    struct ad5933_state *st = iio_priv(dev_info); + struct iio_dev_attr
>> *this_attr = to_iio_dev_attr(attr); +        long val; +     int i, ret = 0;
>> +    unsigned short dat; + + if (this_attr->address != AD5933_IN_PGA_GAIN)
>> { +          ret = strict_strtol(buf, 10, &val); +           if (ret) +                      return ret;
>> +    } + +   mutex_lock(&dev_info->mlock); + switch (this_attr->address) {
>> +    case AD5933_OUT_RANGE: +                for (i = 0; i < 4; i++) +                       if (val ==
>> st->range_avail[i]) { +                              st->ctrl_hb &= ~AD5933_CTRL_RANGE(0x3);
>> +                            st->ctrl_hb |= AD5933_CTRL_RANGE(i); +                          ret = ad5933_cmd(st, 0);
>> +                            break; +                        } +             ret = -EINVAL; +                break; +        case
>> AD5933_IN_PGA_GAIN: +                if (sysfs_streq(buf, "1")) { +                  st->ctrl_hb |=
>> AD5933_CTRL_PGA_GAIN_1; +            } else if (sysfs_streq(buf, "0.2")) {
>> +                    st->ctrl_hb &= ~AD5933_CTRL_PGA_GAIN_1; +               } else { +                      ret =
>> -EINVAL; +                   break; +                } +             ret = ad5933_cmd(st, 0); +              break; +        case
>> AD5933_OUT_SETTLING_CYCLES: +                val = clamp(val, 0L, 0x7FFL);
>> +            st->settling_cycles = val; + +          /* 2x, 4x handling, see datasheet */
>> +            if (val > 511) +                        val = (val >> 1) | (1 << 9); +          else if (val >
>> 1022) +                      val = (val >> 2) | (3 << 9); + +                dat = cpu_to_be16(val)
>> +            ret = ad5933_i2c_write(st->client, +                            AD5933_REG_SETTLING_CYCLES,
>> 2, (u8 *)&dat); +            break; +        case AD5933_FREQ_POINTS: +              val =
>> clamp(val, 0L, 511L); +              st->freq_points = val; + +              dat =
>> cpu_to_be16(val) +           ret = ad5933_i2c_write(st->client,
>> AD5933_REG_INC_NUM, 2, +                                    (u8 *)&dat); +           break; +        default:
>> +            ret = -EINVAL; +        } + +   mutex_unlock(&dev_info->mlock); +       return ret
>> ? ret : len; +} + +static IIO_DEVICE_ATTR(out0_scale, S_IRUGO |
>> S_IWUSR, +                   ad5933_show, +                  ad5933_store, +                 AD5933_OUT_RANGE); +
>> +static IIO_DEVICE_ATTR(out0_scale_available, S_IRUGO, +                     ad5933_show,
>> +                    NULL, +                 AD5933_OUT_RANGE_AVAIL); + +static
>> IIO_DEVICE_ATTR(in0_scale, S_IRUGO | S_IWUSR, +                      ad5933_show,
>> +                    ad5933_store, +                 AD5933_IN_PGA_GAIN); + +static
>> IIO_DEVICE_ATTR(in0_scale_available, S_IRUGO, +                      ad5933_show,
>> +                    NULL, +                 AD5933_IN_PGA_GAIN_AVAIL); + +static
>> IIO_DEVICE_ATTR(out0_freq_points, S_IRUGO | S_IWUSR, +                       ad5933_show,
>> +                    ad5933_store, +                 AD5933_FREQ_POINTS); + +static
>> IIO_DEVICE_ATTR(out0_settling_cycles, S_IRUGO | S_IWUSR,
>> +                    ad5933_show, +                  ad5933_store, +                 AD5933_OUT_SETTLING_CYCLES); +
>> +/* note: + * ideally we would handle the scale attributes via the
>> iio_info + * (read|write)_raw methods, however this part is a untypical
>> since +we + * don't create dedicated sysfs channel attributes for out0
>> and in0. + */ +static struct attribute *ad5933_attributes[] = {
>> +    &iio_dev_attr_out0_scale.dev_attr.attr,
>> +    &iio_dev_attr_out0_scale_available.dev_attr.attr,
>> +    &iio_dev_attr_out0_freq_start.dev_attr.attr,
>> +    &iio_dev_attr_out0_freq_increment.dev_attr.attr,
>> +    &iio_dev_attr_out0_freq_points.dev_attr.attr,
>> +    &iio_dev_attr_out0_settling_cycles.dev_attr.attr,
>> +    &iio_dev_attr_in0_scale.dev_attr.attr,
>> +    &iio_dev_attr_in0_scale_available.dev_attr.attr, +      NULL +}; + +static
>> const struct attribute_group ad5933_attribute_group = { +    .attrs =
>> ad5933_attributes, +}; + +static int ad5933_read_raw(struct iio_dev
>> *dev_info, +                    struct iio_chan_spec const *chan, +                     int *val,
>> +                       int *val2, +                    long m) +{ + struct ad5933_state *st =
>> iio_priv(dev_info); +        unsigned short dat; +   int ret = -EINVAL; +
>> +    mutex_lock(&dev_info->mlock); + switch (m) { +  case 0: +               if
>> (iio_ring_enabled(dev_info)) { +                     ret = -EBUSY; +                 goto out; +             }
>> +            ret = ad5933_cmd(st, AD5933_CTRL_MEASURE_TEMP); +               if (ret < 0)
>> +                    goto out; +             ret = ad5933_wait_busy(st, AD5933_STAT_TEMP_VALID);
>> +            if (ret < 0) +                  goto out; + +           ret = ad5933_i2c_read(st->client,
>> +                            AD5933_REG_TEMP_DATA, 2, +                              (u8 *)&dat); +          if (ret < 0)
>> +                    goto out; +             mutex_unlock(&dev_info->mlock); +               ret =
>> be16_to_cpu(dat); +          /* Temp in Milli degrees Celsius */ +           if (ret <
>> 8192) +                      *val = ret * 1000 / 32; +               else +                  *val = (ret - 16384) *
>> 1000 / 32; + +               return IIO_VAL_INT; +   } + +out:
>> +    mutex_unlock(&dev_info->mlock); +       return ret; +} + +static const
>> struct iio_info ad5933_info = { +    .read_raw = &ad5933_read_raw,
>> +    .attrs = &ad5933_attribute_group, +     .driver_module = THIS_MODULE, +};
>> + +static int ad5933_ring_preenable(struct iio_dev *indio_dev) {
>> +    struct ad5933_state *st = iio_priv(indio_dev); +        struct
>> iio_ring_buffer *ring = indio_dev->ring; +   size_t d_size; +        int ret; +
>> +    if (!ring->scan_count) +                return -EINVAL; + +     d_size =
>> ring->scan_count * +          ad5933_channels[1].scan_type.storagebits / 8; +
>> +    if (indio_dev->ring->access->set_bytes_per_datum)
>> +            indio_dev->ring->access->set_bytes_per_datum(indio_dev- ring,
>> +                                                         d_size); + +       ret = ad5933_reset(st); +       if (ret < 0)
>> +            return ret; + + ret = ad5933_cmd(st, AD5933_CTRL_STANDBY); +    if (ret
>> < 0) +               return ret; + + ret = ad5933_cmd(st,
>> AD5933_CTRL_INIT_START_FREQ); +      if (ret < 0) +          return ret; +
>> +    st->state = AD5933_CTRL_INIT_START_FREQ; + +    return 0; +} + +static
>> int ad5933_ring_postenable(struct iio_dev *indio_dev) { +    struct
>> ad5933_state *st = iio_priv(indio_dev); + +  /*
>> AD5933_CTRL_INIT_START_FREQ: +        * High Q complex circuits require a
>> long time to reach steady state. +    * To facilitate the measurement of
>> such impedances, this mode allows +   * the user full control of the
>> settling time requirement before +    * entering start frequency sweep
>> mode where the impedance measurement +        * takes place. In this mode the
>> impedance is excited with the +       * programmed start frequency
>> (ad5933_ring_preenable), +    * but no measurement takes place. +     */ +
>> +    schedule_delayed_work(&st->work, +
>> msecs_to_jiffies(AD5933_INIT_EXCITATION_TIME_ms)); + return 0; +} +
>> +static int ad5933_ring_postdisable(struct iio_dev *indio_dev) {
>> +    struct ad5933_state *st = iio_priv(indio_dev); +
>> +    cancel_delayed_work_sync(&st->work); +  return ad5933_cmd(st,
>> AD5933_CTRL_POWER_DOWN); } + +static const struct iio_ring_setup_ops
>> ad5933_ring_setup_ops = { +  .preenable = &ad5933_ring_preenable,
>> +    .postenable = &ad5933_ring_postenable, +        .postdisable =
>> &ad5933_ring_postdisable, }; + +static int
>> ad5933_register_ring_funcs_and_init(struct iio_dev +*indio_dev) {
>> +    indio_dev->ring = iio_sw_rb_allocate(indio_dev); +      if
>> (!indio_dev->ring) +         return -ENOMEM; + +     /* Effectively select the
>> ring buffer implementation */ +      indio_dev->ring->access =
>> &ring_sw_access_funcs; + +   /* Ring buffer functions - here trigger
>> setup related */ +   indio_dev->ring->setup_ops = &ad5933_ring_setup_ops;
>> + +  indio_dev->modes |= INDIO_RING_HARDWARE_BUFFER; + +     return 0; +} +
>> +static void ad5933_work(struct work_struct *work) { +       struct
>> ad5933_state *st = container_of(work, +              struct ad5933_state,
>> work.work); +        struct iio_dev *indio_dev =
>> i2c_get_clientdata(st->client); +    struct iio_ring_buffer *ring =
>> indio_dev->ring; +   signed short buf[2]; +  unsigned char status; +
>> +    mutex_lock(&indio_dev->mlock); +        if (st->state ==
>> AD5933_CTRL_INIT_START_FREQ) { +             /* start sweep */ +             ad5933_cmd(st,
>> AD5933_CTRL_START_SWEEP); +          st->state = AD5933_CTRL_START_SWEEP;
>> +            schedule_delayed_work(&st->work, st->poll_time_jiffies);
>> +            mutex_unlock(&indio_dev->mlock); +              return; +       } +
>> +    ad5933_i2c_read(st->client, AD5933_REG_STATUS, 1, &status); + + if
>> (status & AD5933_STAT_DATA_VALID) { +                ad5933_i2c_read(st->client,
>> +                            (ring->scan_mask & (1 << 0)) ? +                                AD5933_REG_REAL_DATA :
>> AD5933_REG_IMAG_DATA, +                              ring->scan_count * 2, (u8 *)buf); + +           if
>> (ring->scan_count == 2) { +                  buf[0] = be16_to_cpu(buf[0]); +                 buf[1]
>> = be16_to_cpu(buf[1]); +             } else { +                      buf[0] = be16_to_cpu(buf[0]);
>> +            } +             /* save datum to the ring */ +          ring->access->store_to(ring,
>> (u8 *)buf, iio_get_time_ns()); +     } else { +              /* no data available - try
>> again later */ +             schedule_delayed_work(&st->work,
>> st->poll_time_jiffies); +            mutex_unlock(&indio_dev->mlock); +              return;
>> +    } + +   if (status & AD5933_STAT_SWEEP_DONE) { +                /* last sample
>> received - power down do nothing until +              * the ring enable is toggled
>> */ +         ad5933_cmd(st, AD5933_CTRL_POWER_DOWN); +       } else { +              /* we just
>> received a valid datum, move on to the next */ +             ad5933_cmd(st,
>> AD5933_CTRL_INC_FREQ); +             schedule_delayed_work(&st->work,
>> st->poll_time_jiffies); +    } + +   mutex_unlock(&indio_dev->mlock); +} +
>> +static int __devinit ad5933_probe(struct i2c_client *client, +
>> const struct i2c_device_id *id) { +  int ret, regdone = 0, voltage_uv =
>> 0; + struct ad5933_platform_data *pdata = client->dev.platform_data;
>> +    struct ad5933_state *st; +      struct iio_dev *indio_dev =
>> iio_allocate_device(sizeof(*st)); +  if (indio_dev == NULL) +                return
>> -ENOMEM; + + st = iio_priv(indio_dev); +     i2c_set_clientdata(client,
>> indio_dev); +        st->client = client; + +        if (!pdata) +           st->pdata =
>> &ad5933_default_pdata; +     else +          st->pdata = pdata; + +  st->reg =
>> regulator_get(&client->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); +  } + +   if (voltage_uv)
>> +            st->vref_mv = voltage_uv / 1000; +      else +          st->vref_mv =
>> st->pdata->vref_mv; + +      if (st->pdata->ext_clk_Hz) { +          st->mclk_hz =
>> st->pdata->ext_clk_Hz; +             st->ctrl_lb = AD5933_CTRL_EXT_SYSCLK; + }
>> else { +             st->mclk_hz = AD5933_INT_OSC_FREQ_Hz; +         st->ctrl_lb =
>> AD5933_CTRL_INT_SYSCLK; +    } + +   ad5933_calc_out_ranges(st);
>> +    INIT_DELAYED_WORK(&st->work, ad5933_work); +    st->poll_time_jiffies =
>> msecs_to_jiffies(AD5933_POLL_TIME_ms); + +   indio_dev->dev.parent =
>> &client->dev; +      indio_dev->info = &ad5933_info; +       indio_dev->name =
>> id->name; +  indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels
>> = ad5933_channels; + indio_dev->num_channels = 1; /* only register
>> temp0_input */ + +   ret =
>> ad5933_register_ring_funcs_and_init(indio_dev); +    if (ret) +              goto
>> error_disable_reg; + +       ret = iio_device_register(indio_dev); + if (ret)
>> +            goto error_unreg_ring; +        regdone = 1; + +        /* skip temp0_input,
>> register in0_(real|imag)_raw */ +    ret =
>> iio_ring_buffer_register_ex(indio_dev->ring, 0, +
>> &ad5933_channels[1], +                                         2); + if (ret) +              goto error_unreg_ring; +
>> +    /* enable both REAL and IMAG channels by default */
>> +    iio_scan_mask_set(indio_dev->ring, 0);
>> +    iio_scan_mask_set(indio_dev->ring, 1); + +      ret = ad5933_setup(st);
>> +    if (ret) +              goto error_uninitialize_ring; + +       return 0; +
>> +error_uninitialize_ring:
>> +    iio_ring_buffer_unregister(indio_dev->ring); +error_unreg_ring:
>> +    iio_sw_rb_free(indio_dev->ring); +error_disable_reg: +  if
>> (!IS_ERR(st->reg)) +         regulator_disable(st->reg); +error_put_reg: +   if
>> (!IS_ERR(st->reg)) +         regulator_put(st->reg); + +     if (regdone)
>> +            iio_device_unregister(indio_dev); +     else
>> +            iio_free_device(indio_dev); + + return ret; +} + +static __devexit
>> int ad5933_remove(struct i2c_client *client) { +     struct iio_dev
>> *indio_dev = i2c_get_clientdata(client); +   struct ad5933_state *st =
>> iio_priv(indio_dev); + +     iio_ring_buffer_unregister(indio_dev->ring);
>> +    iio_sw_rb_free(indio_dev->ring); +      if (!IS_ERR(st->reg)) {
>> +            regulator_disable(st->reg); +           regulator_put(st->reg); +       }
>> +    iio_device_unregister(indio_dev); + +   return 0; +} + +static const
>> struct i2c_device_id ad5933_id[] = { +       { "ad5933", 0 }, +      { "ad5934", 0
>> }, + {} +}; + +MODULE_DEVICE_TABLE(i2c, ad5933_id); + +static struct
>> i2c_driver ad5933_driver = { +       .driver = { +           .name = "ad5933", +     },
>> +    .probe = ad5933_probe, +        .remove = __devexit_p(ad5933_remove),
>> +    .id_table = ad5933_id, +}; + +static __init int ad5933_init(void) {
>> +    return i2c_add_driver(&ad5933_driver); } module_init(ad5933_init); +
>> +static __exit void ad5933_exit(void) {
>> +    i2c_del_driver(&ad5933_driver); +} +module_exit(ad5933_exit); +
>> +MODULE_AUTHOR("Michael Hennerich +<hennerich@xxxxxxxxxxxxxxxxxxxx>");
>> +MODULE_DESCRIPTION("Analog Devices AD5933 Impedance Conv. Network
>> +Analyzer"); MODULE_LICENSE("GPL v2"); diff --git
>> a/drivers/staging/iio/impedance-analyzer/ad5933.h
>> b/drivers/staging/iio/impedance-analyzer/ad5933.h new file mode 100644
>> index 0000000..b140e42 --- /dev/null +++
>> b/drivers/staging/iio/impedance-analyzer/ad5933.h @@ -0,0 +1,28 @@ +/*
>> + * AD5933 AD5934 Impedance Converter, Network Analyzer + * + *
>> Copyright 2011 Analog Devices Inc. + * + * Licensed under the GPL-2. +
>> */ + +#ifndef IIO_ADC_AD5933_H_ +#define IIO_ADC_AD5933_H_ + +/* + *
>> TODO: struct ad5933_platform_data needs to go into +include/linux/iio
>> */ + +/** + * struct ad5933_platform_data - platform specific data + *
>> @ext_clk_Hz:         the external clock frequency in Hz, if not set + *                      the
>> driver uses the internal clock (16.776 MHz) + * @vref_mv:            the external
>> reference voltage in millivolt + */ + +struct ad5933_platform_data {
>> +    unsigned long                   ext_clk_Hz; +   unsigned short                  vref_mv; +}; + +#endif
>> /* IIO_ADC_AD5933_H_ */ -- 1.7.0.4
>>
>>
>>
>

Greetings,
Michael

--
Analog Devices GmbH      Wilhelm-Wagenfeld-Str. 6      80807 Muenchen
Sitz der Gesellschaft: Muenchen; Registergericht: Muenchen HRB 40368; Geschaeftsfuehrer:Dr.Carsten Suckrow, 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