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]

 



On 07/28/2011 04:03 PM, Jonathan Cameron wrote:
On 07/28/11 12:32, 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.

Mostly looks good. Couple of nitpicks / queries in line.  I'm particularly
bothered about the SCALE info elements in chan_spec that aren't matched
in the read_raw (or a write_raw if relevant).
in0_real_raw and in0_imag_raw only exist as scan elements.
They aren't present in indio_dev->channels. I therefore couldn't use
scale info elements from channel spec.

Jonathan
Signed-off-by: Michael Hennerich<michael.hennerich@xxxxxxxxxx>
---
  .../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    |  815 ++++++++++++++++++++
  drivers/staging/iio/impedance-analyzer/ad5933.h    |   28 +
  7 files changed, 896 insertions(+), 0 deletions(-)
  create mode 100644 drivers/staging/iio/Documentation/sysfs-bus-iio-impedance-analyzer-ad5933
  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-ad5933 b/drivers/staging/iio/Documentation/sysfs-bus-iio-impedance-analyzer-ad5933
new file mode 100644
index 0000000..798029a
--- /dev/null
+++ b/drivers/staging/iio/Documentation/sysfs-bus-iio-impedance-analyzer-ad5933
Maybe keep in mind to move these to a general impedance-analyzer file the
moment we get a second one. + we can add these to chan spec masks if we get a few
users.

@@ -0,0 +1,30 @@
+What:                /sys/bus/iio/devices/iio:deviceX/outY_freq_start
+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"
+
+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..d43161b
--- /dev/null
+++ b/drivers/staging/iio/impedance-analyzer/ad5933.c
@@ -0,0 +1,815 @@
+/*
+ * 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 Commands */
What are these?
Those are special I2C block commands, but they didn't work
as detailed in the datasheet. I therefore switched to
single byte transfers. I can remove these defines.

+#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),
+};
If those SCALE_SEPARATE elements aren't causing files to be created
and reads into the read_raw function then something nasty is going wrong...

     indio_dev->channels = ad5933_channels;
     indio_dev->num_channels = 1;

:-)


+
+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;
+     u8 dat[3];
+
+     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;
+     }
+
+     dat[0] = freqreg>>  16;
+     dat[1] = freqreg>>  8;
+     dat[2] = freqreg&  0xFF;
+
+     return ad5933_i2c_write(st->client, reg, 3, dat);
+}
+
+static int ad5933_setup(struct ad5933_state *st)
+{
+     u8 dat[2];
+     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[0] = st->settling_cycles>>  8;
+     dat[1] = st->settling_cycles;
+
+     ret = ad5933_i2c_write(st->client,
+                     AD5933_REG_SETTLING_CYCLES, 2, dat);
+     if (ret<  0)
+             return ret;
+
+     st->freq_points = 100;
+     dat[0] = st->freq_points>>  8;
+     dat[1] = st->freq_points;
+
+     return ad5933_i2c_write(st->client, AD5933_REG_INC_NUM, 2, 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;
+     u8 dat[3];
+
+     mutex_lock(&dev_info->mlock);
+     ret = ad5933_i2c_read(st->client, this_attr->address, 3, dat);
+     mutex_unlock(&dev_info->mlock);
+     if (ret<  0)
+             return ret;
+
+     freqreg = dat[0]<<  16 | dat[1]<<  8 | dat[2];
+
+     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 char dat[2];
+
+     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[0] = val>>  8;
+             dat[1] = val;
+
+             ret = ad5933_i2c_write(st->client,
+                             AD5933_REG_SETTLING_CYCLES, 2, dat);
+             break;
+     case AD5933_FREQ_POINTS:
+             val = clamp(val, 0L, 511L);
+             st->freq_points = val;
+
Looks like an endian conversion to me.  Can we do this any slicker?
Sure
+             dat[0] = val>>  8;
+             dat[1] = val;
+
+             ret = ad5933_i2c_write(st->client, AD5933_REG_INC_NUM, 2, 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);
+
+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,
Should be able to do the scale attributes via the chan_spec. Could also
add scale_available to that given it's pretty common.  The others
are one offs for now so probably best to leave them here.  Depends if you
have lots more of these drivers queued up!
+&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 char dat[2];
+     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,
+                             dat);
+             if (ret<  0)
+                     goto out;
+             mutex_unlock(&dev_info->mlock);
+
+             ret = dat[0]<<  8 | dat[1];
+             /* 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,
This will fall foul of the change to bitmap for these, but I'll fix that
up if this goes in first.

+                             (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);
Use st->poll_time_jiffies?  If not, please comment why not.
Good catch.
+             schedule_delayed_work(&st->work, msecs_to_jiffies(10));
+     }
+
+     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;
+
+     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;
+
+     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_ */



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