All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH] iio: impedance-analyzer: New driver for AD5933/4 Impedance Converter, Network Analyzer
@ 2011-07-28 11:32 michael.hennerich
  2011-07-28 14:03 ` Jonathan Cameron
  0 siblings, 1 reply; 14+ messages in thread
From: michael.hennerich @ 2011-07-28 11:32 UTC (permalink / raw)
  To: jic23; +Cc: linux-iio, device-drivers-devel, drivers, Michael Hennerich

From: Michael Hennerich <michael.hennerich@analog.com>

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.

Signed-off-by: Michael Hennerich <michael.hennerich@analog.com>
---
 .../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
@@ -0,0 +1,30 @@
+What:		/sys/bus/iio/devices/iio:deviceX/outY_freq_start
+KernelVersion:	3.0.1
+Contact:	linux-iio@vger.kernel.org
+Description:
+		Frequency sweep start frequency in Hz.
+
+What:		/sys/bus/iio/devices/iio:deviceX/outY_freq_increment
+KernelVersion:	3.0.1
+Contact:	linux-iio@vger.kernel.org
+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@vger.kernel.org
+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@vger.kernel.org
+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 */
+#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;
+	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;
+
+		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,
+	&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,
+				(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, 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@blackfin.uclinux.org>");
+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

^ permalink raw reply related	[flat|nested] 14+ messages in thread

* Re: [PATCH] iio: impedance-analyzer: New driver for AD5933/4 Impedance Converter, Network Analyzer
  2011-07-28 11:32 [PATCH] iio: impedance-analyzer: New driver for AD5933/4 Impedance Converter, Network Analyzer michael.hennerich
@ 2011-07-28 14:03 ` Jonathan Cameron
  2011-07-28 15:19   ` Michael Hennerich
  0 siblings, 1 reply; 14+ messages in thread
From: Jonathan Cameron @ 2011-07-28 14:03 UTC (permalink / raw)
  To: michael.hennerich; +Cc: linux-iio, device-drivers-devel, drivers

On 07/28/11 12:32, michael.hennerich@analog.com wrote:
> From: Michael Hennerich <michael.hennerich@analog.com>
> 
> 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).

Jonathan
> Signed-off-by: Michael Hennerich <michael.hennerich@analog.com>
> ---
>  .../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@vger.kernel.org
> +Description:
> +		Frequency sweep start frequency in Hz.
> +
> +What:		/sys/bus/iio/devices/iio:deviceX/outY_freq_increment
> +KernelVersion:	3.0.1
> +Contact:	linux-iio@vger.kernel.org
> +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@vger.kernel.org
> +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@vger.kernel.org
> +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?
> +#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...

> +
> +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?
> +		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.
> +		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@blackfin.uclinux.org>");
> +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_ */


^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH] iio: impedance-analyzer: New driver for AD5933/4 Impedance Converter, Network Analyzer
  2011-07-28 14:03 ` Jonathan Cameron
@ 2011-07-28 15:19   ` Michael Hennerich
  2011-07-28 15:54     ` Jonathan Cameron
  0 siblings, 1 reply; 14+ messages in thread
From: Michael Hennerich @ 2011-07-28 15:19 UTC (permalink / raw)
  To: Jonathan Cameron; +Cc: linux-iio, device-drivers-devel, Drivers

On 07/28/2011 04:03 PM, Jonathan Cameron wrote:
> On 07/28/11 12:32, michael.hennerich@analog.com wrote:
>> From: Michael Hennerich<michael.hennerich@analog.com>
>>
>> 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@analog.com>
>> ---
>>   .../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@vger.kernel.org
>> +Description:
>> +             Frequency sweep start frequency in Hz.
>> +
>> +What:                /sys/bus/iio/devices/iio:deviceX/outY_freq_increment
>> +KernelVersion:       3.0.1
>> +Contact:     linux-iio@vger.kernel.org
>> +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@vger.kernel.org
>> +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@vger.kernel.org
>> +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@blackfin.uclinux.org>");
>> +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

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH] iio: impedance-analyzer: New driver for AD5933/4 Impedance Converter, Network Analyzer
  2011-07-28 15:19   ` Michael Hennerich
@ 2011-07-28 15:54     ` Jonathan Cameron
  2011-08-01  8:56       ` Michael Hennerich
  2011-08-01 11:23       ` Michael Hennerich
  0 siblings, 2 replies; 14+ messages in thread
From: Jonathan Cameron @ 2011-07-28 15:54 UTC (permalink / raw)
  To: michael.hennerich; +Cc: linux-iio, device-drivers-devel, Drivers

On 07/28/11 16:19, Michael Hennerich wrote:
> On 07/28/2011 04:03 PM, Jonathan Cameron wrote:
>> On 07/28/11 12:32, michael.hennerich@analog.com wrote:
>>> From: Michael Hennerich<michael.hennerich@analog.com>
>>>
>>> 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.

Ah. I hadn't noticed that (obviously).  Why is this the case?  Does it just
not make sense to read them directly?  If so, set their channel number to -1
and they won't appear.  Hmm.  Perhaps we need to make that test a little
more refined to make this work.  iio_device_add_channel_sysfs drops the channel
immediately if it's number is negative.   Maybe move the magic value into channel2?

That way the only uggliness will come if we have a differential channel that only exists
for scans.  Or use a negative index (rather than just -1).  That's probably the cleanest
option, but will require a few abs() insertions in the code and some testing 
to make sure we got them all.

Something like:

diff --git a/drivers/staging/iio/industrialio-core.c b/drivers/staging/iio/industrialio-core.c
index b49db92..aeeb5e6 100644
--- a/drivers/staging/iio/industrialio-core.c
+++ b/drivers/staging/iio/industrialio-core.c
@@ -480,7 +480,7 @@ int __iio_device_attr_init(struct device_attribute *dev_attr,
                        = kasprintf(GFP_KERNEL, "%s_%s%d_%s",
                                    iio_direction[chan->output],
                                    iio_chan_type_name_spec_shared[chan->type],
-                                   chan->channel,
+                                   (int)abs(chan->channel),
                                    full_postfix);
 
        if (name_format == NULL) {
@@ -489,7 +489,7 @@ int __iio_device_attr_init(struct device_attribute *dev_attr,
        }
        dev_attr->attr.name = kasprintf(GFP_KERNEL,
                                        name_format,
-                                       chan->channel,
+                                       (int)abs(chan->channel),
                                        chan->channel2);
        if (dev_attr->attr.name == NULL) {
                ret = -ENOMEM;
@@ -585,27 +585,27 @@ static int iio_device_add_channel_sysfs(struct iio_dev *dev_info,
 {
        int ret, i;
 
-       if (chan->channel < 0)
-               return 0;
-       if (chan->processed_val)
-               ret = __iio_add_chan_devattr("input", NULL, chan,
-                                            &iio_read_channel_info,
-                                            NULL,
-                                            0,
-                                            0,
-                                            &dev_info->dev,
-                                            &dev_info->channel_attr_list);
-       else
-               ret = __iio_add_chan_devattr("raw", NULL, chan,
-                                            &iio_read_channel_info,
-                                            (chan->output ?
-                                            &iio_write_channel_info : NULL),
-                                            0,
-                                            0,
-                                            &dev_info->dev,
-                                            &dev_info->channel_attr_list);
-       if (ret)
-               goto error_ret;
+       if (chan->channel >= 0) {
+               if (chan->processed_val)
+                       ret = __iio_add_chan_devattr("input", NULL, chan,
+                                                    &iio_read_channel_info,
+                                                    NULL,
+                                                    0,
+                                                    0,
+                                                    &dev_info->dev,
+                                                    &dev_info->channel_attr_list);
+               else
+                       ret = __iio_add_chan_devattr("raw", NULL, chan,
+                                                    &iio_read_channel_info,
+                                                    (chan->output ?
+                                                     &iio_write_channel_info : NULL),
+                                                    0,
+                                                    0,
+                                                    &dev_info->dev,
+                                                    &dev_info->channel_attr_list);
+               if (ret)
+                       goto error_ret;
+       }
 
        for_each_set_bit(i, &chan->info_mask, sizeof(long)*8) {
                ret = __iio_add_chan_devattr(iio_chan_info_postfix[i/2],

> 
>> Jonathan
>>> Signed-off-by: Michael Hennerich<michael.hennerich@analog.com>
>>> ---
>>>   .../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@vger.kernel.org
>>> +Description:
>>> +             Frequency sweep start frequency in Hz.
>>> +
>>> +What:                /sys/bus/iio/devices/iio:deviceX/outY_freq_increment
>>> +KernelVersion:       3.0.1
>>> +Contact:     linux-iio@vger.kernel.org
>>> +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@vger.kernel.org
>>> +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@vger.kernel.org
>>> +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.
Cool.  Or leave them and state they don't work. Might save
someone else some time in the future ;)
> 
>>> +#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;
> 
> :-)
Nasty.  Please add a comment here so people don't have to notice that!
> 
> 
>>> +
>>> +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@blackfin.uclinux.org>");
>>> +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_ */
>>
> 
> 


^ permalink raw reply related	[flat|nested] 14+ messages in thread

* Re: [PATCH] iio: impedance-analyzer: New driver for AD5933/4 Impedance Converter, Network Analyzer
  2011-07-28 15:54     ` Jonathan Cameron
@ 2011-08-01  8:56       ` Michael Hennerich
  2011-08-01  9:14         ` Jonathan Cameron
  2011-08-01 11:23       ` Michael Hennerich
  1 sibling, 1 reply; 14+ messages in thread
From: Michael Hennerich @ 2011-08-01  8:56 UTC (permalink / raw)
  To: Jonathan Cameron; +Cc: linux-iio, device-drivers-devel, Drivers

On 07/28/2011 05:54 PM, Jonathan Cameron wrote:
> On 07/28/11 16:19, Michael Hennerich wrote:
>> On 07/28/2011 04:03 PM, Jonathan Cameron wrote:
>>> On 07/28/11 12:32, michael.hennerich@analog.com wrote:
>>>> From: Michael Hennerich<michael.hennerich@analog.com>
>>>>
>>>> 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.
> Ah. I hadn't noticed that (obviously).  Why is this the case?  Does it just
> not make sense to read them directly?
Hi Jonathan,

It typically doesn't make sense. One use case I could think of is the
single-point gain factor calculation/calibration.
we could add in0_(real|imag) however it still doesn't help us for the 
out0_scale.

>    If so, set their channel number to -1
> and they won't appear.  Hmm.  Perhaps we need to make that test a little
> more refined to make this work.  iio_device_add_channel_sysfs drops the channel
> immediately if it's number is negative.   Maybe move the magic value into channel2?
>
> That way the only uggliness will come if we have a differential channel that only exists
> for scans.  Or use a negative index (rather than just -1).  That's probably the cleanest
> option, but will require a few abs() insertions in the code and some testing
> to make sure we got them all.
>
This should do the trick - I'll have a try.
Regarding adding _available to channel spec.
We probably need to do it for each and every item listed in 
iio_chan_info_enum.
The question is do we want to return a string?

> Something like:
>
> diff --git a/drivers/staging/iio/industrialio-core.c b/drivers/staging/iio/industrialio-core.c
> index b49db92..aeeb5e6 100644
> --- a/drivers/staging/iio/industrialio-core.c
> +++ b/drivers/staging/iio/industrialio-core.c
> @@ -480,7 +480,7 @@ int __iio_device_attr_init(struct device_attribute *dev_attr,
>                          = kasprintf(GFP_KERNEL, "%s_%s%d_%s",
>                                      iio_direction[chan->output],
>                                      iio_chan_type_name_spec_shared[chan->type],
> -                                   chan->channel,
> +                                   (int)abs(chan->channel),
>                                      full_postfix);
>
>          if (name_format == NULL) {
> @@ -489,7 +489,7 @@ int __iio_device_attr_init(struct device_attribute *dev_attr,
>          }
>          dev_attr->attr.name = kasprintf(GFP_KERNEL,
>                                          name_format,
> -                                       chan->channel,
> +                                       (int)abs(chan->channel),
>                                          chan->channel2);
>          if (dev_attr->attr.name == NULL) {
>                  ret = -ENOMEM;
> @@ -585,27 +585,27 @@ static int iio_device_add_channel_sysfs(struct iio_dev *dev_info,
>   {
>          int ret, i;
>
> -       if (chan->channel<  0)
> -               return 0;
> -       if (chan->processed_val)
> -               ret = __iio_add_chan_devattr("input", NULL, chan,
> -&iio_read_channel_info,
> -                                            NULL,
> -                                            0,
> -                                            0,
> -&dev_info->dev,
> -&dev_info->channel_attr_list);
> -       else
> -               ret = __iio_add_chan_devattr("raw", NULL, chan,
> -&iio_read_channel_info,
> -                                            (chan->output ?
> -&iio_write_channel_info : NULL),
> -                                            0,
> -                                            0,
> -&dev_info->dev,
> -&dev_info->channel_attr_list);
> -       if (ret)
> -               goto error_ret;
> +       if (chan->channel>= 0) {
> +               if (chan->processed_val)
> +                       ret = __iio_add_chan_devattr("input", NULL, chan,
> +&iio_read_channel_info,
> +                                                    NULL,
> +                                                    0,
> +                                                    0,
> +&dev_info->dev,
> +&dev_info->channel_attr_list);
> +               else
> +                       ret = __iio_add_chan_devattr("raw", NULL, chan,
> +&iio_read_channel_info,
> +                                                    (chan->output ?
> +&iio_write_channel_info : NULL),
> +                                                    0,
> +                                                    0,
> +&dev_info->dev,
> +&dev_info->channel_attr_list);
> +               if (ret)
> +                       goto error_ret;
> +       }
>
>          for_each_set_bit(i,&chan->info_mask, sizeof(long)*8) {
>                  ret = __iio_add_chan_devattr(iio_chan_info_postfix[i/2],
>
>>> Jonathan
>>>> Signed-off-by: Michael Hennerich<michael.hennerich@analog.com>
>>>> ---
>>>>    .../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@vger.kernel.org
>>>> +Description:
>>>> +             Frequency sweep start frequency in Hz.
>>>> +
>>>> +What:                /sys/bus/iio/devices/iio:deviceX/outY_freq_increment
>>>> +KernelVersion:       3.0.1
>>>> +Contact:     linux-iio@vger.kernel.org
>>>> +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@vger.kernel.org
>>>> +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@vger.kernel.org
>>>> +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.
> Cool.  Or leave them and state they don't work. Might save
> someone else some time in the future ;)
>>>> +#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;
>>
>> :-)
> Nasty.  Please add a comment here so people don't have to notice that!
>>
>>>> +
>>>> +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@blackfin.uclinux.org>");
>>>> +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

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH] iio: impedance-analyzer: New driver for AD5933/4 Impedance Converter, Network Analyzer
  2011-08-01  8:56       ` Michael Hennerich
@ 2011-08-01  9:14         ` Jonathan Cameron
  2011-08-01  9:48           ` Michael Hennerich
  0 siblings, 1 reply; 14+ messages in thread
From: Jonathan Cameron @ 2011-08-01  9:14 UTC (permalink / raw)
  To: michael.hennerich; +Cc: linux-iio, device-drivers-devel, Drivers

On 08/01/11 09:56, Michael Hennerich wrote:
> On 07/28/2011 05:54 PM, Jonathan Cameron wrote:
>> On 07/28/11 16:19, Michael Hennerich wrote:
>>> On 07/28/2011 04:03 PM, Jonathan Cameron wrote:
>>>> On 07/28/11 12:32, michael.hennerich@analog.com wrote:
>>>>> From: Michael Hennerich<michael.hennerich@analog.com>
>>>>>
>>>>> 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.
>> Ah. I hadn't noticed that (obviously).  Why is this the case?  Does it just
>> not make sense to read them directly?
> Hi Jonathan,
> 
> It typically doesn't make sense. One use case I could think of is the
> single-point gain factor calculation/calibration.
> we could add in0_(real|imag) however it still doesn't help us for the out0_scale.
> 
>>    If so, set their channel number to -1
>> and they won't appear.  Hmm.  Perhaps we need to make that test a little
>> more refined to make this work.  iio_device_add_channel_sysfs drops the channel
>> immediately if it's number is negative.   Maybe move the magic value into channel2?
>>
>> That way the only uggliness will come if we have a differential channel that only exists
>> for scans.  Or use a negative index (rather than just -1).  That's probably the cleanest
>> option, but will require a few abs() insertions in the code and some testing
>> to make sure we got them all.
>>
> This should do the trick - I'll have a try.
> Regarding adding _available to channel spec.
> We probably need to do it for each and every item listed in iio_chan_info_enum.
> The question is do we want to return a string?
String is rather ugly and kind of defeats all our attempts to avoid the drivers dealing with
strings at all.  Perhaps value pairs (and hence IIO_INT_PLUS_X type). The core can then format
the up appropriately.  Perhaps do this via a second mask (as it clearly aligns with the first existing
enum elements)? Bloat the channel struct a little, but not too badly.

Shall we leave this for another day and just poke them in separately as you have done here
for now? I'll put together a prototype of how to do this and see how difficult it actually is.

Jonathan

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH] iio: impedance-analyzer: New driver for AD5933/4 Impedance Converter, Network Analyzer
  2011-08-01  9:14         ` Jonathan Cameron
@ 2011-08-01  9:48           ` Michael Hennerich
  0 siblings, 0 replies; 14+ messages in thread
From: Michael Hennerich @ 2011-08-01  9:48 UTC (permalink / raw)
  To: Jonathan Cameron; +Cc: linux-iio, device-drivers-devel, Drivers

On 08/01/2011 11:14 AM, Jonathan Cameron wrote:
> On 08/01/11 09:56, Michael Hennerich wrote:
>> On 07/28/2011 05:54 PM, Jonathan Cameron wrote:
>>> On 07/28/11 16:19, Michael Hennerich wrote:
>>>> On 07/28/2011 04:03 PM, Jonathan Cameron wrote:
>>>>> On 07/28/11 12:32, michael.hennerich@analog.com wrote:
>>>>>> From: Michael Hennerich<michael.hennerich@analog.com>
>>>>>>
>>>>>> 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.
>>> Ah. I hadn't noticed that (obviously).  Why is this the case?  Does it just
>>> not make sense to read them directly?
>> Hi Jonathan,
>>
>> It typically doesn't make sense. One use case I could think of is the
>> single-point gain factor calculation/calibration.
>> we could add in0_(real|imag) however it still doesn't help us for the out0_scale.
>>
>>>     If so, set their channel number to -1
>>> and they won't appear.  Hmm.  Perhaps we need to make that test a little
>>> more refined to make this work.  iio_device_add_channel_sysfs drops the channel
>>> immediately if it's number is negative.   Maybe move the magic value into channel2?
>>>
>>> That way the only uggliness will come if we have a differential channel that only exists
>>> for scans.  Or use a negative index (rather than just -1).  That's probably the cleanest
>>> option, but will require a few abs() insertions in the code and some testing
>>> to make sure we got them all.
>>>
>> This should do the trick - I'll have a try.
>> Regarding adding _available to channel spec.
>> We probably need to do it for each and every item listed in iio_chan_info_enum.
>> The question is do we want to return a string?
> String is rather ugly and kind of defeats all our attempts to avoid the drivers dealing with
> strings at all.

Yeah - that is what I thought as well.
>    Perhaps value pairs (and hence IIO_INT_PLUS_X type). The core can then format
> the up appropriately.  Perhaps do this via a second mask (as it clearly aligns with the first existing
> enum elements)? Bloat the channel struct a little, but not too badly.
>
> Shall we leave this for another day and just poke them in separately as you have done here
> for now?
ok
> I'll put together a prototype of how to do this and see how difficult it actually is.
>
> Jonathan
>


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

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH] iio: impedance-analyzer: New driver for AD5933/4 Impedance Converter, Network Analyzer
  2011-07-28 15:54     ` Jonathan Cameron
  2011-08-01  8:56       ` Michael Hennerich
@ 2011-08-01 11:23       ` Michael Hennerich
  2011-08-01 11:42         ` Jonathan Cameron
  1 sibling, 1 reply; 14+ messages in thread
From: Michael Hennerich @ 2011-08-01 11:23 UTC (permalink / raw)
  To: Jonathan Cameron; +Cc: linux-iio, device-drivers-devel, Drivers

On 07/28/2011 05:54 PM, Jonathan Cameron wrote:
> On 07/28/11 16:19, Michael Hennerich wrote:
>> On 07/28/2011 04:03 PM, Jonathan Cameron wrote:
>>> On 07/28/11 12:32, michael.hennerich@analog.com wrote:
>>>> From: Michael Hennerich<michael.hennerich@analog.com>
>>>>
>>>> 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.
> Ah. I hadn't noticed that (obviously).  Why is this the case?  Does it just
> not make sense to read them directly?  If so, set their channel number to -1
> and they won't appear.  Hmm.  Perhaps we need to make that test a little
> more refined to make this work.  iio_device_add_channel_sysfs drops the channel
> immediately if it's number is negative.   Maybe move the magic value into channel2?
>
> That way the only uggliness will come if we have a differential channel that only exists
> for scans.  Or use a negative index (rather than just -1).  That's probably the cleanest
> option, but will require a few abs() insertions in the code and some testing
> to make sure we got them all.
>

Well this works well for channels abs(chan->channel) > 0.
But how about the case where I need no channel sysfs but the scale 
attributes of channel# 0?
Unfortunately there is no -0 :-)

Since the AD5933 is a corner case - I suggest we leave it as is - and I 
add some comments to the source... ?


> Something like:
>
> diff --git a/drivers/staging/iio/industrialio-core.c b/drivers/staging/iio/industrialio-core.c
> index b49db92..aeeb5e6 100644
> --- a/drivers/staging/iio/industrialio-core.c
> +++ b/drivers/staging/iio/industrialio-core.c
> @@ -480,7 +480,7 @@ int __iio_device_attr_init(struct device_attribute *dev_attr,
>                          = kasprintf(GFP_KERNEL, "%s_%s%d_%s",
>                                      iio_direction[chan->output],
>                                      iio_chan_type_name_spec_shared[chan->type],
> -                                   chan->channel,
> +                                   (int)abs(chan->channel),
>                                      full_postfix);
>
>          if (name_format == NULL) {
> @@ -489,7 +489,7 @@ int __iio_device_attr_init(struct device_attribute *dev_attr,
>          }
>          dev_attr->attr.name = kasprintf(GFP_KERNEL,
>                                          name_format,
> -                                       chan->channel,
> +                                       (int)abs(chan->channel),
>                                          chan->channel2);
>          if (dev_attr->attr.name == NULL) {
>                  ret = -ENOMEM;
> @@ -585,27 +585,27 @@ static int iio_device_add_channel_sysfs(struct iio_dev *dev_info,
>   {
>          int ret, i;
>
> -       if (chan->channel<  0)
> -               return 0;
> -       if (chan->processed_val)
> -               ret = __iio_add_chan_devattr("input", NULL, chan,
> -&iio_read_channel_info,
> -                                            NULL,
> -                                            0,
> -                                            0,
> -&dev_info->dev,
> -&dev_info->channel_attr_list);
> -       else
> -               ret = __iio_add_chan_devattr("raw", NULL, chan,
> -&iio_read_channel_info,
> -                                            (chan->output ?
> -&iio_write_channel_info : NULL),
> -                                            0,
> -                                            0,
> -&dev_info->dev,
> -&dev_info->channel_attr_list);
> -       if (ret)
> -               goto error_ret;
> +       if (chan->channel>= 0) {
> +               if (chan->processed_val)
> +                       ret = __iio_add_chan_devattr("input", NULL, chan,
> +&iio_read_channel_info,
> +                                                    NULL,
> +                                                    0,
> +                                                    0,
> +&dev_info->dev,
> +&dev_info->channel_attr_list);
> +               else
> +                       ret = __iio_add_chan_devattr("raw", NULL, chan,
> +&iio_read_channel_info,
> +                                                    (chan->output ?
> +&iio_write_channel_info : NULL),
> +                                                    0,
> +                                                    0,
> +&dev_info->dev,
> +&dev_info->channel_attr_list);
> +               if (ret)
> +                       goto error_ret;
> +       }
>
>          for_each_set_bit(i,&chan->info_mask, sizeof(long)*8) {
>                  ret = __iio_add_chan_devattr(iio_chan_info_postfix[i/2],
>
>


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

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH] iio: impedance-analyzer: New driver for AD5933/4 Impedance Converter, Network Analyzer
  2011-08-01 11:23       ` Michael Hennerich
@ 2011-08-01 11:42         ` Jonathan Cameron
  0 siblings, 0 replies; 14+ messages in thread
From: Jonathan Cameron @ 2011-08-01 11:42 UTC (permalink / raw)
  To: michael.hennerich; +Cc: linux-iio, device-drivers-devel, Drivers

On 08/01/11 12:23, Michael Hennerich wrote:
> On 07/28/2011 05:54 PM, Jonathan Cameron wrote:
>> On 07/28/11 16:19, Michael Hennerich wrote:
>>> On 07/28/2011 04:03 PM, Jonathan Cameron wrote:
>>>> On 07/28/11 12:32, michael.hennerich@analog.com wrote:
>>>>> From: Michael Hennerich<michael.hennerich@analog.com>
>>>>>
>>>>> 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.
>> Ah. I hadn't noticed that (obviously).  Why is this the case?  Does it just
>> not make sense to read them directly?  If so, set their channel number to -1
>> and they won't appear.  Hmm.  Perhaps we need to make that test a little
>> more refined to make this work.  iio_device_add_channel_sysfs drops the channel
>> immediately if it's number is negative.   Maybe move the magic value into channel2?
>>
>> That way the only uggliness will come if we have a differential channel that only exists
>> for scans.  Or use a negative index (rather than just -1).  That's probably the cleanest
>> option, but will require a few abs() insertions in the code and some testing
>> to make sure we got them all.
>>
> 
> Well this works well for channels abs(chan->channel) > 0.
> But how about the case where I need no channel sysfs but the scale attributes of channel# 0?
> Unfortunately there is no -0 :-)
oops. Good point. I'll drop this for now.  Could enforce that any such channel isn't given index
zero, but that would be clunky.
> 
> Since the AD5933 is a corner case - I suggest we leave it as is - and I add some comments to the source... ?
Good plan. We'll rethink this if it turns up again.
> 
> 
>> Something like:
>>
>> diff --git a/drivers/staging/iio/industrialio-core.c b/drivers/staging/iio/industrialio-core.c
>> index b49db92..aeeb5e6 100644
>> --- a/drivers/staging/iio/industrialio-core.c
>> +++ b/drivers/staging/iio/industrialio-core.c
>> @@ -480,7 +480,7 @@ int __iio_device_attr_init(struct device_attribute *dev_attr,
>>                          = kasprintf(GFP_KERNEL, "%s_%s%d_%s",
>>                                      iio_direction[chan->output],
>>                                      iio_chan_type_name_spec_shared[chan->type],
>> -                                   chan->channel,
>> +                                   (int)abs(chan->channel),
>>                                      full_postfix);
>>
>>          if (name_format == NULL) {
>> @@ -489,7 +489,7 @@ int __iio_device_attr_init(struct device_attribute *dev_attr,
>>          }
>>          dev_attr->attr.name = kasprintf(GFP_KERNEL,
>>                                          name_format,
>> -                                       chan->channel,
>> +                                       (int)abs(chan->channel),
>>                                          chan->channel2);
>>          if (dev_attr->attr.name == NULL) {
>>                  ret = -ENOMEM;
>> @@ -585,27 +585,27 @@ static int iio_device_add_channel_sysfs(struct iio_dev *dev_info,
>>   {
>>          int ret, i;
>>
>> -       if (chan->channel<  0)
>> -               return 0;
>> -       if (chan->processed_val)
>> -               ret = __iio_add_chan_devattr("input", NULL, chan,
>> -&iio_read_channel_info,
>> -                                            NULL,
>> -                                            0,
>> -                                            0,
>> -&dev_info->dev,
>> -&dev_info->channel_attr_list);
>> -       else
>> -               ret = __iio_add_chan_devattr("raw", NULL, chan,
>> -&iio_read_channel_info,
>> -                                            (chan->output ?
>> -&iio_write_channel_info : NULL),
>> -                                            0,
>> -                                            0,
>> -&dev_info->dev,
>> -&dev_info->channel_attr_list);
>> -       if (ret)
>> -               goto error_ret;
>> +       if (chan->channel>= 0) {
>> +               if (chan->processed_val)
>> +                       ret = __iio_add_chan_devattr("input", NULL, chan,
>> +&iio_read_channel_info,
>> +                                                    NULL,
>> +                                                    0,
>> +                                                    0,
>> +&dev_info->dev,
>> +&dev_info->channel_attr_list);
>> +               else
>> +                       ret = __iio_add_chan_devattr("raw", NULL, chan,
>> +&iio_read_channel_info,
>> +                                                    (chan->output ?
>> +&iio_write_channel_info : NULL),
>> +                                                    0,
>> +                                                    0,
>> +&dev_info->dev,
>> +&dev_info->channel_attr_list);
>> +               if (ret)
>> +                       goto error_ret;
>> +       }
>>
>>          for_each_set_bit(i,&chan->info_mask, sizeof(long)*8) {
>>                  ret = __iio_add_chan_devattr(iio_chan_info_postfix[i/2],
>>
>>
> 
> 


^ permalink raw reply	[flat|nested] 14+ messages in thread

* [PATCH] iio: impedance-analyzer: New driver for AD5933/4 Impedance Converter, Network Analyzer
@ 2011-08-02  6:48 michael.hennerich
  0 siblings, 0 replies; 14+ messages in thread
From: michael.hennerich @ 2011-08-02  6:48 UTC (permalink / raw)
  To: greg; +Cc: linux-iio, device-drivers-devel, drivers, jic23, Michael Hennerich

From: Michael Hennerich <michael.hennerich@analog.com>

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.

Changes since V2:

Fix KernelVersion tag in Documentation.
Declare ad5933_default_pdata static.
Fix typos.

Signed-off-by: Michael Hennerich <michael.hennerich@analog.com>
Acked-by: Jonathan Cameron <jic23@cam.ac.uk>
---
 .../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-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..79c7e88
--- /dev/null
+++ b/drivers/staging/iio/Documentation/sysfs-bus-iio-impedance-analyzer-ad5933
@@ -0,0 +1,30 @@
+What:		/sys/bus/iio/devices/iio:deviceX/outY_freq_start
+KernelVersion:	3.1.0
+Contact:	linux-iio@vger.kernel.org
+Description:
+		Frequency sweep start frequency in Hz.
+
+What:		/sys/bus/iio/devices/iio:deviceX/outY_freq_increment
+KernelVersion:	3.1.0
+Contact:	linux-iio@vger.kernel.org
+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.1.0
+Contact:	linux-iio@vger.kernel.org
+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.1.0
+Contact:	linux-iio@vger.kernel.org
+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..acc0e69
--- /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;
+};
+
+static 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;
+	}
+
+	dat.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@blackfin.uclinux.org>");
+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

^ permalink raw reply related	[flat|nested] 14+ messages in thread

* RE: [PATCH] iio: impedance-analyzer: New driver for AD5933/4 Impedance Converter, Network Analyzer
  2011-08-01 16:23 ` Jonathan Cameron
  2011-08-01 16:38   ` Jonathan Cameron
@ 2011-08-01 19:03   ` Hennerich, Michael
  1 sibling, 0 replies; 14+ messages in thread
From: Hennerich, Michael @ 2011-08-01 19:03 UTC (permalink / raw)
  To: Jonathan Cameron; +Cc: linux-iio, device-drivers-devel, Drivers

Jonathan Cameron wrote on 2011-08-01:
> On 08/01/11 16:28, michael.hennerich@analog.com wrote:
>> From: Michael Hennerich <michael.hennerich@analog.com>
>>
>> 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 sav=
e 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@analog.com>
>> Reviewed-by: Jonathan Cameron <jic23@cam.ac.uk> Acked-by: Jonathan
>> Cameron <jic23@cam.ac.uk>
>>
>> ---
>>  .../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 Is=
n't next
>> kernel going to be 3.1? +KernelVersion:      3.0.1
>> +Contact:    linux-iio@vger.kernel.org +Description: +               Fre=
quency sweep
>> start frequency in Hz. +
>> +What:               /sys/bus/iio/devices/iio:deviceX/outY_freq_incremen=
t
>> +KernelVersion:      3.0.1 +Contact: linux-iio@vger.kernel.org +Descript=
ion:
>> +            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@vger.kernel.org +Descript=
ion:
>> +            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_cycl=
es
>> +KernelVersion:      3.0.1 +Contact: linux-iio@vger.kernel.org +Descript=
ion:
>> +            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 +=3D addac/
>>  obj-y +=3D dac/ obj-y +=3D dds/ obj-y +=3D gyro/ +obj-y +=3D
>>  impedance-analyzer/ obj-y +=3D imu/ obj-y +=3D light/ obj-y +=3D
>>  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) +=3D 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_CO=
NTROL_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_STA=
NDBY         (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 { +  str=
uct
>> i2c_client           *client; +      struct regulator                *re=
g; + struct
>> ad5933_platform_data *pdata; +       struct delayed_work             wor=
k; + unsigned
>> long                 mclk_hz; +      unsigned char                   ctr=
l_hb; +      unsigned char                   ctrl_lb;
>> +    unsigned                        range_avail[4]; +       unsigned sh=
ort                  vref_mv; +      unsigned
>> short                        settling_cycles; +      unsigned short     =
             freq_points;
>> +    unsigned                        freq_start; +   unsigned           =
             freq_inc; +     unsigned                        state;
>> +    unsigned                        poll_time_jiffies; +}; + +struct ad=
5933_platform_data
>> ad5933_default_pdata  =3D { +  .vref_mv =3D 3300, +}; + +static struct
>> iio_chan_spec ad5933_channels[] =3D { +        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 *d=
ata) +{ +    int ret; +
>> +    while (len--) { +               ret =3D i2c_smbus_write_byte_data(c=
lient, reg++,
>> *data++); +          if (ret < 0) { +                        dev_err(&cl=
ient->dev, "I2C write
>> error\n"); +                 return ret; +           } +     } +     ret=
urn 0; +} + +static int
>> ad5933_i2c_read(struct i2c_client *client, +                       u8 re=
g, u8 len, u8
>> *data) +{ +  int ret; + +    while (len--) { +               ret =3D
>> i2c_smbus_read_byte_data(client, reg++); +           if (ret < 0) {
>> +                    dev_err(&client->dev, "I2C read error\n"); +       =
             return ret; +           }
>> +            *data++ =3D ret; +        } +     return 0; +} + +static in=
t ad5933_cmd(struct
>> ad5933_state *st, unsigned char cmd) { +     unsigned char dat =3D
>> 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 =3D
>> 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 =3D AD5933_MAX_RETRIES; +      int ret; =
+ +    while
>> (timeout--) { +              ret =3D  ad5933_i2c_read(st->client, AD5933=
_REG_STATUS,
>> 1, &val); +          if (ret < 0) +                  return ret; +      =
     if (val & event)
>> +                    return val; +           cpu_relax(); +          mde=
lay(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]; +     } d=
at; + +      freqreg =3D (u64) freq *
>> (u64) (1 << 27); +   do_div(freqreg, st->mclk_hz / 4); + +   switch (reg=
) {
>> +    case AD5933_REG_FREQ_START: +           st->freq_start =3D freq; + =
               break;
>> +    case AD5933_REG_FREQ_INC: +             st->freq_inc =3D freq; +   =
       break;
>> +    default: +              return -EINVAL; +       } + +   data.d32 =
=3D cpu_to_be32(freqreg);
>> +    return ad5933_i2c_write(st->client, reg, 3, &dat.d8[1]); } + +stati=
c
>> int ad5933_setup(struct ad5933_state *st) { +        unsigned short dat;=
 +   int
>> ret; + +     ret =3D ad5933_reset(st); +       if (ret < 0) +          r=
eturn ret; + + ret
>> =3D ad5933_set_freq(st, AD5933_REG_FREQ_START, 10000); +       if (ret <=
 0)
>> +            return ret; + + ret =3D ad5933_set_freq(st, AD5933_REG_FREQ=
_INC, 200);
>> +    if (ret < 0) +          return ret; + + st->settling_cycles =3D 10;=
 +     dat =3D
>> cpu_to_be16(st->settling_cycles); + +        ret =3D
>> ad5933_i2c_write(st->client, +                       AD5933_REG_SETTLING=
_CYCLES, 2, (u8
>> *)&dat); +   if (ret < 0) +          return ret; + + st->freq_points =3D=
 100; +        dat
>> =3D cpu_to_be16(st->freq_points); + +  return ad5933_i2c_write(st->clien=
t,
>> AD5933_REG_INC_NUM, 2, (u8 +*)&dat); } + +static void
>> ad5933_calc_out_ranges(struct ad5933_state *st) { +  int i; +        uns=
igned
>> normalized_3v3[4] =3D {1980, 198, 383, 970}; + +       for (i =3D 0; i <=
 4; i++)
>> +            st->range_avail[i] =3D normalized_3v3[i] * st->vref_mv / 33=
00; + +} +
>> +/* + * 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
>> =3D dev_get_drvdata(dev); +    struct ad5933_state *st =3D iio_priv(dev_=
info);
>> +    struct iio_dev_attr *this_attr =3D to_iio_dev_attr(attr); +       i=
nt ret;
>> +    unsigned long long freqreg; +   union { +               u32 d32; + =
             u8 d8[4]; +     }
>> dat; + +     mutex_lock(&dev_info->mlock); + ret =3D
>> ad5933_i2c_read(st->client, this_attr->address, 3, &dat.d8[1]);
>> +    mutex_unlock(&dev_info->mlock); +       if (ret < 0) +          ret=
urn ret; +
>> +    freqreg =3D be32_to_cpu(dat.d32) & 0xFFFFFF; + +  freqreg =3D (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 =3D dev_get_drvdata(dev); +      struct
>> ad5933_state *st =3D iio_priv(dev_info); +     struct iio_dev_attr *this=
_attr
>> =3D to_iio_dev_attr(attr); +   long val; +     int ret; + +    ret =3D
>> strict_strtoul(buf, 10, &val); +     if (ret) +              return ret;=
 + + if (val >
>> AD5933_MAX_OUTPUT_FREQ_Hz) +         return -EINVAL; +
>> +    mutex_lock(&dev_info->mlock); + ret =3D ad5933_set_freq(st,
>> this_attr->address, val); +  mutex_unlock(&dev_info->mlock); + +     ret=
urn
>> 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, +                        ad5=
933_store_frequency,
>> +                    AD5933_REG_FREQ_INC); + +static ssize_t ad5933_show=
(struct device
>> *dev, +                                      struct device_attribute *at=
tr, +                                        char *buf) +{ + struct
>> iio_dev *dev_info =3D dev_get_drvdata(dev); +  struct ad5933_state *st =
=3D
>> iio_priv(dev_info); +        struct iio_dev_attr *this_attr =3D
>> to_iio_dev_attr(attr); +     int ret =3D 0, len =3D 0; +
>> +    mutex_lock(&dev_info->mlock); + switch (this_attr->address) { + cas=
e
>> AD5933_OUT_RANGE: +          len =3D sprintf(buf, "%d\n", +
>> st->range_avail[(st->ctrl_hb >> 1) & 0x3]); +                break; +   =
     case
>> AD5933_OUT_RANGE_AVAIL: +            len =3D sprintf(buf, "%d %d %d %d\n=
",
>> st->range_avail[0], +                              st->range_avail[3], s=
t->range_avail[2],
>> +                          st->range_avail[1]); +            break; +   =
     case
>> AD5933_OUT_SETTLING_CYCLES: +                len =3D sprintf(buf, "%d\n"=
,
>> st->settling_cycles); +              break; +        case AD5933_IN_PGA_=
GAIN: +              len =3D
>> sprintf(buf, "%s\n", +                             (st->ctrl_hb & AD5933=
_CTRL_PGA_GAIN_1) ?
>> +                          "1" : "0.2"); +           break; +        cas=
e AD5933_IN_PGA_GAIN_AVAIL:
>> +            len =3D sprintf(buf, "1 0.2\n"); +                break; + =
       case AD5933_FREQ_POINTS:
>> +            len =3D sprintf(buf, "%d\n", st->freq_points); +          b=
reak; +        default:
>> +            ret =3D -EINVAL; +        } + +   mutex_unlock(&dev_info->m=
lock); +       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 =3D dev_get_drvdata(dev);
>> +    struct ad5933_state *st =3D iio_priv(dev_info); + struct iio_dev_at=
tr
>> *this_attr =3D to_iio_dev_attr(attr); +        long val; +     int i, re=
t =3D 0;
>> +    unsigned short dat; + + if (this_attr->address !=3D AD5933_IN_PGA_G=
AIN)
>> { +          ret =3D strict_strtol(buf, 10, &val); +           if (ret) =
+                      return ret;
>> +    } + +   mutex_lock(&dev_info->mlock); + switch (this_attr->address)=
 {
>> +    case AD5933_OUT_RANGE: +                for (i =3D 0; i < 4; i++) +=
                       if (val =3D=3D
>> st->range_avail[i]) { +                              st->ctrl_hb &=3D ~A=
D5933_CTRL_RANGE(0x3);
>> +                            st->ctrl_hb |=3D AD5933_CTRL_RANGE(i); +   =
                       ret =3D ad5933_cmd(st, 0);
>> +                            break; +                        } +        =
     ret =3D -EINVAL; +                break; +        case
>> AD5933_IN_PGA_GAIN: +                if (sysfs_streq(buf, "1")) { +     =
             st->ctrl_hb |=3D
>> AD5933_CTRL_PGA_GAIN_1; +            } else if (sysfs_streq(buf, "0.2"))=
 {
>> +                    st->ctrl_hb &=3D ~AD5933_CTRL_PGA_GAIN_1; +        =
       } else { +                      ret =3D
>> -EINVAL; +                   break; +                } +             ret=
 =3D ad5933_cmd(st, 0); +              break; +        case
>> AD5933_OUT_SETTLING_CYCLES: +                val =3D clamp(val, 0L, 0x7F=
FL);
>> +            st->settling_cycles =3D val; + +          /* 2x, 4x handlin=
g, see datasheet */
>> +            if (val > 511) +                        val =3D (val >> 1) =
| (1 << 9); +          else if (val >
>> 1022) +                      val =3D (val >> 2) | (3 << 9); + +         =
       dat =3D cpu_to_be16(val)
>> +            ret =3D ad5933_i2c_write(st->client, +                     =
       AD5933_REG_SETTLING_CYCLES,
>> 2, (u8 *)&dat); +            break; +        case AD5933_FREQ_POINTS: + =
             val =3D
>> clamp(val, 0L, 511L); +              st->freq_points =3D val; + +       =
       dat =3D
>> cpu_to_be16(val) +           ret =3D ad5933_i2c_write(st->client,
>> AD5933_REG_INC_NUM, 2, +                                    (u8 *)&dat);=
 +           break; +        default:
>> +            ret =3D -EINVAL; +        } + +   mutex_unlock(&dev_info->m=
lock); +       return ret
>> ? ret : len; +} + +static IIO_DEVICE_ATTR(out0_scale, S_IRUGO |
>> S_IWUSR, +                   ad5933_show, +                  ad5933_stor=
e, +                 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, +                      ad5=
933_show,
>> +                    ad5933_store, +                 AD5933_IN_PGA_GAIN)=
; + +static
>> IIO_DEVICE_ATTR(in0_scale_available, S_IRUGO, +                      ad5=
933_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[] =3D {
>> +    &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 =3D { +    .attrs =
=3D
>> ad5933_attributes, +}; + +static int ad5933_read_raw(struct iio_dev
>> *dev_info, +                    struct iio_chan_spec const *chan, +     =
                int *val,
>> +                       int *val2, +                    long m) +{ + str=
uct ad5933_state *st =3D
>> iio_priv(dev_info); +        unsigned short dat; +   int ret =3D -EINVAL=
; +
>> +    mutex_lock(&dev_info->mlock); + switch (m) { +  case 0: +          =
     if
>> (iio_ring_enabled(dev_info)) { +                     ret =3D -EBUSY; +  =
               goto out; +             }
>> +            ret =3D ad5933_cmd(st, AD5933_CTRL_MEASURE_TEMP); +        =
       if (ret < 0)
>> +                    goto out; +             ret =3D ad5933_wait_busy(st=
, AD5933_STAT_TEMP_VALID);
>> +            if (ret < 0) +                  goto out; + +           ret=
 =3D ad5933_i2c_read(st->client,
>> +                            AD5933_REG_TEMP_DATA, 2, +                 =
             (u8 *)&dat); +          if (ret < 0)
>> +                    goto out; +             mutex_unlock(&dev_info->mlo=
ck); +               ret =3D
>> be16_to_cpu(dat); +          /* Temp in Milli degrees Celsius */ +      =
     if (ret <
>> 8192) +                      *val =3D ret * 1000 / 32; +               e=
lse +                  *val =3D (ret - 16384) *
>> 1000 / 32; + +               return IIO_VAL_INT; +   } + +out:
>> +    mutex_unlock(&dev_info->mlock); +       return ret; +} + +static co=
nst
>> struct iio_info ad5933_info =3D { +    .read_raw =3D &ad5933_read_raw,
>> +    .attrs =3D &ad5933_attribute_group, +     .driver_module =3D THIS_M=
ODULE, +};
>> + +static int ad5933_ring_preenable(struct iio_dev *indio_dev) {
>> +    struct ad5933_state *st =3D iio_priv(indio_dev); +        struct
>> iio_ring_buffer *ring =3D indio_dev->ring; +   size_t d_size; +        i=
nt ret; +
>> +    if (!ring->scan_count) +                return -EINVAL; + +     d_s=
ize =3D
>> 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- rin=
g,
>> +                                                         d_size); + +  =
     ret =3D ad5933_reset(st); +       if (ret < 0)
>> +            return ret; + + ret =3D ad5933_cmd(st, AD5933_CTRL_STANDBY)=
; +    if (ret
>> < 0) +               return ret; + + ret =3D ad5933_cmd(st,
>> AD5933_CTRL_INIT_START_FREQ); +      if (ret < 0) +          return ret;=
 +
>> +    st->state =3D AD5933_CTRL_INIT_START_FREQ; + +    return 0; +} + +s=
tatic
>> int ad5933_ring_postenable(struct iio_dev *indio_dev) { +    struct
>> ad5933_state *st =3D 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 mod=
e 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 =3D 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 =3D { +  .preenable =3D &ad5933_ring_preenable,
>> +    .postenable =3D &ad5933_ring_postenable, +        .postdisable =3D
>> &ad5933_ring_postdisable, }; + +static int
>> ad5933_register_ring_funcs_and_init(struct iio_dev +*indio_dev) {
>> +    indio_dev->ring =3D iio_sw_rb_allocate(indio_dev); +      if
>> (!indio_dev->ring) +         return -ENOMEM; + +     /* Effectively sele=
ct the
>> ring buffer implementation */ +      indio_dev->ring->access =3D
>> &ring_sw_access_funcs; + +   /* Ring buffer functions - here trigger
>> setup related */ +   indio_dev->ring->setup_ops =3D &ad5933_ring_setup_o=
ps;
>> + +  indio_dev->modes |=3D INDIO_RING_HARDWARE_BUFFER; + +     return 0;=
 +} +
>> +static void ad5933_work(struct work_struct *work) { +       struct
>> ad5933_state *st =3D container_of(work, +              struct ad5933_sta=
te,
>> work.work); +        struct iio_dev *indio_dev =3D
>> i2c_get_clientdata(st->client); +    struct iio_ring_buffer *ring =3D
>> indio_dev->ring; +   signed short buf[2]; +  unsigned char status; +
>> +    mutex_lock(&indio_dev->mlock); +        if (st->state =3D=3D
>> AD5933_CTRL_INIT_START_FREQ) { +             /* start sweep */ +        =
     ad5933_cmd(st,
>> AD5933_CTRL_START_SWEEP); +          st->state =3D AD5933_CTRL_START_SWE=
EP;
>> +            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 =3D=3D 2) { +                  buf[0] =3D be16_to_cpu(=
buf[0]); +                 buf[1]
>> =3D be16_to_cpu(buf[1]); +             } else { +                      b=
uf[0] =3D be16_to_cpu(buf[0]);
>> +            } +             /* save datum to the ring */ +          rin=
g->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 =3D 0, voltage_uv =
=3D
>> 0; + struct ad5933_platform_data *pdata =3D client->dev.platform_data;
>> +    struct ad5933_state *st; +      struct iio_dev *indio_dev =3D
>> iio_allocate_device(sizeof(*st)); +  if (indio_dev =3D=3D NULL) +       =
         return
>> -ENOMEM; + + st =3D iio_priv(indio_dev); +     i2c_set_clientdata(client=
,
>> indio_dev); +        st->client =3D client; + +        if (!pdata) +    =
       st->pdata =3D
>> &ad5933_default_pdata; +     else +          st->pdata =3D pdata; + +  s=
t->reg =3D
>> regulator_get(&client->dev, "vcc"); +        if (!IS_ERR(st->reg)) { +  =
             ret =3D
>> regulator_enable(st->reg); +         if (ret) +                      got=
o error_put_reg;
>> +            voltage_uv =3D regulator_get_voltage(st->reg); +  } + +   i=
f (voltage_uv)
>> +            st->vref_mv =3D voltage_uv / 1000; +      else +          s=
t->vref_mv =3D
>> st->pdata->vref_mv; + +      if (st->pdata->ext_clk_Hz) { +          st-=
>mclk_hz =3D
>> st->pdata->ext_clk_Hz; +             st->ctrl_lb =3D AD5933_CTRL_EXT_SYS=
CLK; + }
>> else { +             st->mclk_hz =3D AD5933_INT_OSC_FREQ_Hz; +         s=
t->ctrl_lb =3D
>> AD5933_CTRL_INT_SYSCLK; +    } + +   ad5933_calc_out_ranges(st);
>> +    INIT_DELAYED_WORK(&st->work, ad5933_work); +    st->poll_time_jiffi=
es =3D
>> msecs_to_jiffies(AD5933_POLL_TIME_ms); + +   indio_dev->dev.parent =3D
>> &client->dev; +      indio_dev->info =3D &ad5933_info; +       indio_dev=
->name =3D
>> id->name; +  indio_dev->modes =3D INDIO_DIRECT_MODE; + indio_dev->channe=
ls
>> =3D ad5933_channels; + indio_dev->num_channels =3D 1; /* only register
>> temp0_input */ + +   ret =3D
>> ad5933_register_ring_funcs_and_init(indio_dev); +    if (ret) +         =
     goto
>> error_disable_reg; + +       ret =3D iio_device_register(indio_dev); + i=
f (ret)
>> +            goto error_unreg_ring; +        regdone =3D 1; + +        /=
* skip temp0_input,
>> register in0_(real|imag)_raw */ +    ret =3D
>> 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 =3D ad5933_setu=
p(st);
>> +    if (ret) +              goto error_uninitialize_ring; + +       ret=
urn 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 =3D i2c_get_clientdata(client); +   struct ad5933_state *st =
=3D
>> 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->r=
eg); +       }
>> +    iio_device_unregister(indio_dev); + +   return 0; +} + +static cons=
t
>> struct i2c_device_id ad5933_id[] =3D { +       { "ad5933", 0 }, +      {=
 "ad5934", 0
>> }, + {} +}; + +MODULE_DEVICE_TABLE(i2c, ad5933_id); + +static struct
>> i2c_driver ad5933_driver =3D { +       .driver =3D { +           .name =
=3D "ad5933", +     },
>> +    .probe =3D ad5933_probe, +        .remove =3D __devexit_p(ad5933_re=
move),
>> +    .id_table =3D 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@blackfin.uclinux.org>");
>> +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; Gesch=
aeftsfuehrer:Dr.Carsten Suckrow, Thomas Wessel, William A. Martin, Margaret=
 Seif

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH] iio: impedance-analyzer: New driver for AD5933/4 Impedance Converter, Network Analyzer
  2011-08-01 16:23 ` Jonathan Cameron
@ 2011-08-01 16:38   ` Jonathan Cameron
  2011-08-01 19:03   ` Hennerich, Michael
  1 sibling, 0 replies; 14+ messages in thread
From: Jonathan Cameron @ 2011-08-01 16:38 UTC (permalink / raw)
  To: Jonathan Cameron
  Cc: michael.hennerich, linux-iio, device-drivers-devel, drivers

On 08/01/11 17:23, Jonathan Cameron wrote:
> On 08/01/11 16:28, michael.hennerich@analog.com wrote:
>> From: Michael Hennerich <michael.hennerich@analog.com>
>>
>> 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?  
> 
> 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.
> 
> Will pull fixed version into iio-blue when you send it to Greg
> (below everything else).
I've pushed out an updated version of iio-blue.git with this driver
near the bottom (post the above mentioned trivial fixes).  I'll fixup
anything else when Greg has merged this driver into staging-next.

Also in the new version of iio-blue are some static checker found bug
fixes in various drivers.  Most of them are my errors and obvious.

I'll post them formally at some point.

Jonathan

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH] iio: impedance-analyzer: New driver for AD5933/4 Impedance Converter, Network Analyzer
  2011-08-01 15:28 michael.hennerich
@ 2011-08-01 16:23 ` Jonathan Cameron
  2011-08-01 16:38   ` Jonathan Cameron
  2011-08-01 19:03   ` Hennerich, Michael
  0 siblings, 2 replies; 14+ messages in thread
From: Jonathan Cameron @ 2011-08-01 16:23 UTC (permalink / raw)
  To: michael.hennerich; +Cc: linux-iio, device-drivers-devel, drivers

On 08/01/11 16:28, michael.hennerich@analog.com wrote:
> From: Michael Hennerich <michael.hennerich@analog.com>
> 
> 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?  

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.

Will pull fixed version into iio-blue when you send it to Greg
(below everything else).

> Signed-off-by: Michael Hennerich <michael.hennerich@analog.com>
> Reviewed-by: Jonathan Cameron <jic23@cam.ac.uk>
Acked-by: Jonathan Cameron <jic23@cam.ac.uk>
> 
> ---
>  .../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-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
> @@ -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@vger.kernel.org
> +Description:
> +		Frequency sweep start frequency in Hz.
> +
> +What:		/sys/bus/iio/devices/iio:deviceX/outY_freq_increment
> +KernelVersion:	3.0.1
> +Contact:	linux-iio@vger.kernel.org
> +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@vger.kernel.org
> +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@vger.kernel.org
> +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@blackfin.uclinux.org>");
> +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
> 
> 
> 


^ permalink raw reply	[flat|nested] 14+ messages in thread

* [PATCH] iio: impedance-analyzer: New driver for AD5933/4 Impedance Converter, Network Analyzer
@ 2011-08-01 15:28 michael.hennerich
  2011-08-01 16:23 ` Jonathan Cameron
  0 siblings, 1 reply; 14+ messages in thread
From: michael.hennerich @ 2011-08-01 15:28 UTC (permalink / raw)
  To: jic23; +Cc: linux-iio, device-drivers-devel, drivers, Michael Hennerich

From: Michael Hennerich <michael.hennerich@analog.com>

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.

Signed-off-by: Michael Hennerich <michael.hennerich@analog.com>
Reviewed-by: Jonathan Cameron <jic23@cam.ac.uk>

---
 .../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-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
@@ -0,0 +1,30 @@
+What:		/sys/bus/iio/devices/iio:deviceX/outY_freq_start
+KernelVersion:	3.0.1
+Contact:	linux-iio@vger.kernel.org
+Description:
+		Frequency sweep start frequency in Hz.
+
+What:		/sys/bus/iio/devices/iio:deviceX/outY_freq_increment
+KernelVersion:	3.0.1
+Contact:	linux-iio@vger.kernel.org
+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@vger.kernel.org
+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@vger.kernel.org
+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..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@blackfin.uclinux.org>");
+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

^ permalink raw reply related	[flat|nested] 14+ messages in thread

end of thread, other threads:[~2011-08-02  6:48 UTC | newest]

Thread overview: 14+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2011-07-28 11:32 [PATCH] iio: impedance-analyzer: New driver for AD5933/4 Impedance Converter, Network Analyzer michael.hennerich
2011-07-28 14:03 ` Jonathan Cameron
2011-07-28 15:19   ` Michael Hennerich
2011-07-28 15:54     ` Jonathan Cameron
2011-08-01  8:56       ` Michael Hennerich
2011-08-01  9:14         ` Jonathan Cameron
2011-08-01  9:48           ` Michael Hennerich
2011-08-01 11:23       ` Michael Hennerich
2011-08-01 11:42         ` Jonathan Cameron
2011-08-01 15:28 michael.hennerich
2011-08-01 16:23 ` Jonathan Cameron
2011-08-01 16:38   ` Jonathan Cameron
2011-08-01 19:03   ` Hennerich, Michael
2011-08-02  6:48 michael.hennerich

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.