[PATCH 539/961] staging: IIO: ADC: New driver for AD7606/AD7606-6/AD7606-4

Greg Kroah-Hartman gregkh at suse.de
Wed Mar 16 21:03:02 UTC 2011


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

This patch adds support for the:
AD7606/AD7606-6/AD7606-4 8/6/4-Channel Data Acquisition
system (DAS) with 16-Bit, Bipolar, Simultaneous Sampling ADC.

Changes since V1:
IIO: ADC: New driver for AD7606/AD7606-6/AD7606-4: Apply review feedback

Rename sysfs node oversampling to oversampling_ratio.
Kconfig: Add GPIOLIB dependency.
Use range in mV to better match HWMON.
Rename ad7606_check_oversampling.
Fix various comments and style.
Reorder is_visible cases.
Use new gpio_request_one/array and friends.
Drop check for SPI max_speed_hz.

Changes since V2:
IIO: ADC: New driver for AD7606/AD7606-6/AD7606-4: Apply review feedback

Documentation: specify unit
Avoid raise condition in ad7606_scan_direct()
Check return value of bus ops read_block()

Changes since V3:
IIO: ADC: New driver for AD7606/AD7606-6/AD7606-4: Add missing include file

Add linux/sched.h

Changes since V4:
IIO: ADC: New driver for AD7606/AD7606-6/AD7606-4: Fix kconfig declaration

consistently use tristate to avoid configuration mismatches

Signed-off-by: Michael Hennerich <michael.hennerich at analog.com>
Acked-by: Jonathan Cameron <jic23 at cam.ac.uk>
Signed-off-by: Greg Kroah-Hartman <gregkh at suse.de>
---
 drivers/staging/iio/Documentation/sysfs-bus-iio |   25 +
 drivers/staging/iio/adc/Kconfig                 |   28 ++
 drivers/staging/iio/adc/Makefile                |    6 +
 drivers/staging/iio/adc/ad7606.h                |  117 +++++
 drivers/staging/iio/adc/ad7606_core.c           |  556 +++++++++++++++++++++++
 drivers/staging/iio/adc/ad7606_par.c            |  188 ++++++++
 drivers/staging/iio/adc/ad7606_ring.c           |  266 +++++++++++
 drivers/staging/iio/adc/ad7606_spi.c            |  126 +++++
 8 files changed, 1312 insertions(+), 0 deletions(-)
 create mode 100644 drivers/staging/iio/adc/ad7606.h
 create mode 100644 drivers/staging/iio/adc/ad7606_core.c
 create mode 100644 drivers/staging/iio/adc/ad7606_par.c
 create mode 100644 drivers/staging/iio/adc/ad7606_ring.c
 create mode 100644 drivers/staging/iio/adc/ad7606_spi.c

diff --git a/drivers/staging/iio/Documentation/sysfs-bus-iio b/drivers/staging/iio/Documentation/sysfs-bus-iio
index 8e5d8d1..da25bc7 100644
--- a/drivers/staging/iio/Documentation/sysfs-bus-iio
+++ b/drivers/staging/iio/Documentation/sysfs-bus-iio
@@ -53,6 +53,31 @@ Description:
 		When the internal sampling clock can only take a small
 		discrete set of values, this file lists those available.
 
+What:		/sys/bus/iio/devices/deviceX/range
+KernelVersion:	2.6.38
+Contact:	linux-iio at vger.kernel.org
+Description:
+		Hardware dependent ADC Full Scale Range in mVolt.
+
+What:		/sys/bus/iio/devices/deviceX/range_available
+KernelVersion:	2.6.38
+Contact:	linux-iio at vger.kernel.org
+Description:
+		Hardware dependent supported vales for ADC Full Scale Range.
+
+What:		/sys/bus/iio/devices/deviceX/oversampling_ratio
+KernelVersion:	2.6.38
+Contact:	linux-iio at vger.kernel.org
+Description:
+		Hardware dependent ADC oversampling. Controls the sampling ratio
+		of the digital filter if available.
+
+What:		/sys/bus/iio/devices/deviceX/oversampling_ratio_available
+KernelVersion:	2.6.38
+Contact:	linux-iio at vger.kernel.org
+Description:
+		Hardware dependent values supported by the oversampling filter.
+
 What:		/sys/bus/iio/devices/deviceX/inY_raw
 What:		/sys/bus/iio/devices/deviceX/inY_supply_raw
 KernelVersion:	2.6.35
diff --git a/drivers/staging/iio/adc/Kconfig b/drivers/staging/iio/adc/Kconfig
index 86869cd..5613b30 100644
--- a/drivers/staging/iio/adc/Kconfig
+++ b/drivers/staging/iio/adc/Kconfig
@@ -62,6 +62,34 @@ config AD7314
 	  Say yes here to build support for Analog Devices AD7314
 	  temperature sensors.
 
+config AD7606
+	tristate "Analog Devices AD7606 ADC driver"
+	depends on GPIOLIB
+	select IIO_RING_BUFFER
+	select IIO_TRIGGER
+	select IIO_SW_RING
+	help
+	  Say yes here to build support for Analog Devices:
+	  ad7606, ad7606-6, ad7606-4 analog to digital convertors (ADC).
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called ad7606.
+
+config AD7606_IFACE_PARALLEL
+	tristate "parallel interface support"
+	depends on AD7606
+	help
+	  Say yes here to include parallel interface support on the AD7606
+	  ADC driver.
+
+config AD7606_IFACE_SPI
+	tristate "spi interface support"
+	depends on AD7606
+	depends on SPI
+	help
+	  Say yes here to include parallel interface support on the AD7606
+	  ADC driver.
+
 config AD799X
 	tristate "Analog Devices AD799x ADC driver"
 	depends on I2C
diff --git a/drivers/staging/iio/adc/Makefile b/drivers/staging/iio/adc/Makefile
index 6f231a2..cb73346 100644
--- a/drivers/staging/iio/adc/Makefile
+++ b/drivers/staging/iio/adc/Makefile
@@ -7,6 +7,12 @@ max1363-y += max1363_ring.o
 
 obj-$(CONFIG_MAX1363) += max1363.o
 
+ad7606-y := ad7606_core.o
+ad7606-$(CONFIG_IIO_RING_BUFFER) += ad7606_ring.o
+ad7606-$(CONFIG_AD7606_IFACE_PARALLEL) += ad7606_par.o
+ad7606-$(CONFIG_AD7606_IFACE_SPI) += ad7606_spi.o
+obj-$(CONFIG_AD7606) += ad7606.o
+
 ad799x-y := ad799x_core.o
 ad799x-$(CONFIG_AD799X_RING_BUFFER) += ad799x_ring.o
 obj-$(CONFIG_AD799X) += ad799x.o
diff --git a/drivers/staging/iio/adc/ad7606.h b/drivers/staging/iio/adc/ad7606.h
new file mode 100644
index 0000000..338bade
--- /dev/null
+++ b/drivers/staging/iio/adc/ad7606.h
@@ -0,0 +1,117 @@
+/*
+ * AD7606 ADC driver
+ *
+ * Copyright 2011 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2.
+ */
+
+#ifndef IIO_ADC_AD7606_H_
+#define IIO_ADC_AD7606_H_
+
+/*
+ * TODO: struct ad7606_platform_data needs to go into include/linux/iio
+ */
+
+/**
+ * struct ad7606_platform_data - platform/board specifc information
+ * @default_os:		default oversampling value {0, 2, 4, 8, 16, 32, 64}
+ * @default_range:	default range +/-{5000, 10000} mVolt
+ * @gpio_convst:	number of gpio connected to the CONVST pin
+ * @gpio_reset:		gpio connected to the RESET pin, if not used set to -1
+ * @gpio_range:		gpio connected to the RANGE pin, if not used set to -1
+ * @gpio_os0:		gpio connected to the OS0 pin, if not used set to -1
+ * @gpio_os1:		gpio connected to the OS1 pin, if not used set to -1
+ * @gpio_os2:		gpio connected to the OS2 pin, if not used set to -1
+ * @gpio_frstdata:	gpio connected to the FRSTDAT pin, if not used set to -1
+ * @gpio_stby:		gpio connected to the STBY pin, if not used set to -1
+ */
+
+struct ad7606_platform_data {
+	unsigned			default_os;
+	unsigned			default_range;
+	unsigned			gpio_convst;
+	unsigned			gpio_reset;
+	unsigned			gpio_range;
+	unsigned			gpio_os0;
+	unsigned			gpio_os1;
+	unsigned			gpio_os2;
+	unsigned			gpio_frstdata;
+	unsigned			gpio_stby;
+};
+
+/**
+ * struct ad7606_chip_info - chip specifc information
+ * @name:		indentification string for chip
+ * @bits:		accuracy of the adc in bits
+ * @bits:		output coding [s]igned or [u]nsigned
+ * @int_vref_mv:	the internal reference voltage
+ * @num_channels:	number of physical inputs on chip
+ */
+
+struct ad7606_chip_info {
+	char				name[10];
+	u8				bits;
+	char				sign;
+	u16				int_vref_mv;
+	unsigned			num_channels;
+};
+
+/**
+ * struct ad7606_state - driver instance specific data
+ */
+
+struct ad7606_state {
+	struct iio_dev			*indio_dev;
+	struct device			*dev;
+	const struct ad7606_chip_info	*chip_info;
+	struct ad7606_platform_data	*pdata;
+	struct regulator		*reg;
+	struct work_struct		poll_work;
+	wait_queue_head_t		wq_data_avail;
+	atomic_t			protect_ring;
+	size_t				d_size;
+	const struct ad7606_bus_ops	*bops;
+	int				irq;
+	unsigned			id;
+	unsigned			range;
+	unsigned			oversampling;
+	bool				done;
+	bool				have_frstdata;
+	bool				have_os;
+	bool				have_stby;
+	bool				have_reset;
+	bool				have_range;
+	void __iomem			*base_address;
+
+	/*
+	 * DMA (thus cache coherency maintenance) requires the
+	 * transfer buffers to live in their own cache lines.
+	 */
+
+	unsigned short			data[8] ____cacheline_aligned;
+};
+
+struct ad7606_bus_ops {
+	/* more methods added in future? */
+	int (*read_block)(struct device *, int, void *);
+};
+
+void ad7606_suspend(struct ad7606_state *st);
+void ad7606_resume(struct ad7606_state *st);
+struct ad7606_state *ad7606_probe(struct device *dev, int irq,
+			      void __iomem *base_address, unsigned id,
+			      const struct ad7606_bus_ops *bops);
+int ad7606_remove(struct ad7606_state *st);
+int ad7606_reset(struct ad7606_state *st);
+
+enum ad7606_supported_device_ids {
+	ID_AD7606_8,
+	ID_AD7606_6,
+	ID_AD7606_4
+};
+
+int ad7606_scan_from_ring(struct ad7606_state *st, unsigned ch);
+int ad7606_register_ring_funcs_and_init(struct iio_dev *indio_dev);
+void ad7606_ring_cleanup(struct iio_dev *indio_dev);
+#endif /* IIO_ADC_AD7606_H_ */
diff --git a/drivers/staging/iio/adc/ad7606_core.c b/drivers/staging/iio/adc/ad7606_core.c
new file mode 100644
index 0000000..4c700f0
--- /dev/null
+++ b/drivers/staging/iio/adc/ad7606_core.c
@@ -0,0 +1,556 @@
+/*
+ * AD7606 SPI ADC driver
+ *
+ * Copyright 2011 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/interrupt.h>
+#include <linux/workqueue.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+#include <linux/list.h>
+#include <linux/regulator/consumer.h>
+#include <linux/err.h>
+#include <linux/gpio.h>
+#include <linux/delay.h>
+#include <linux/sched.h>
+
+#include "../iio.h"
+#include "../sysfs.h"
+#include "../ring_generic.h"
+#include "adc.h"
+
+#include "ad7606.h"
+
+int ad7606_reset(struct ad7606_state *st)
+{
+	if (st->have_reset) {
+		gpio_set_value(st->pdata->gpio_reset, 1);
+		ndelay(100); /* t_reset >= 100ns */
+		gpio_set_value(st->pdata->gpio_reset, 0);
+		return 0;
+	}
+
+	return -ENODEV;
+}
+
+static int ad7606_scan_direct(struct ad7606_state *st, unsigned ch)
+{
+	int ret;
+
+	st->done = false;
+	gpio_set_value(st->pdata->gpio_convst, 1);
+
+	ret = wait_event_interruptible(st->wq_data_avail, st->done);
+	if (ret)
+		goto error_ret;
+
+	if (st->have_frstdata) {
+		ret = st->bops->read_block(st->dev, 1, st->data);
+		if (ret)
+			goto error_ret;
+		if (!gpio_get_value(st->pdata->gpio_frstdata)) {
+			/* This should never happen */
+			ad7606_reset(st);
+			ret = -EIO;
+			goto error_ret;
+		}
+		ret = st->bops->read_block(st->dev,
+			st->chip_info->num_channels - 1, &st->data[1]);
+		if (ret)
+			goto error_ret;
+	} else {
+		ret = st->bops->read_block(st->dev,
+			st->chip_info->num_channels, st->data);
+		if (ret)
+			goto error_ret;
+	}
+
+	ret = st->data[ch];
+
+error_ret:
+	gpio_set_value(st->pdata->gpio_convst, 0);
+
+	return ret;
+}
+
+static ssize_t ad7606_scan(struct device *dev,
+			    struct device_attribute *attr,
+			    char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad7606_state *st = dev_info->dev_data;
+	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+	int ret;
+
+	mutex_lock(&dev_info->mlock);
+	if (iio_ring_enabled(dev_info))
+		ret = ad7606_scan_from_ring(st, this_attr->address);
+	else
+		ret = ad7606_scan_direct(st, this_attr->address);
+	mutex_unlock(&dev_info->mlock);
+
+	if (ret < 0)
+		return ret;
+
+	return sprintf(buf, "%d\n", (short) ret);
+}
+
+static IIO_DEV_ATTR_IN_RAW(0, ad7606_scan, 0);
+static IIO_DEV_ATTR_IN_RAW(1, ad7606_scan, 1);
+static IIO_DEV_ATTR_IN_RAW(2, ad7606_scan, 2);
+static IIO_DEV_ATTR_IN_RAW(3, ad7606_scan, 3);
+static IIO_DEV_ATTR_IN_RAW(4, ad7606_scan, 4);
+static IIO_DEV_ATTR_IN_RAW(5, ad7606_scan, 5);
+static IIO_DEV_ATTR_IN_RAW(6, ad7606_scan, 6);
+static IIO_DEV_ATTR_IN_RAW(7, ad7606_scan, 7);
+
+static ssize_t ad7606_show_scale(struct device *dev,
+				struct device_attribute *attr,
+				char *buf)
+{
+	/* Driver currently only support internal vref */
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad7606_state *st = iio_dev_get_devdata(dev_info);
+	unsigned int scale_uv = (st->range * 1000 * 2) >> st->chip_info->bits;
+
+	return sprintf(buf, "%d.%03d\n", scale_uv / 1000, scale_uv % 1000);
+}
+static IIO_DEVICE_ATTR(in_scale, S_IRUGO, ad7606_show_scale, NULL, 0);
+
+static ssize_t ad7606_show_name(struct device *dev,
+				 struct device_attribute *attr,
+				 char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad7606_state *st = iio_dev_get_devdata(dev_info);
+
+	return sprintf(buf, "%s\n", st->chip_info->name);
+}
+
+static IIO_DEVICE_ATTR(name, S_IRUGO, ad7606_show_name, NULL, 0);
+
+static ssize_t ad7606_show_range(struct device *dev,
+			struct device_attribute *attr, char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad7606_state *st = iio_dev_get_devdata(dev_info);
+
+	return sprintf(buf, "%u\n", st->range);
+}
+
+static ssize_t ad7606_store_range(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t count)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad7606_state *st = iio_dev_get_devdata(dev_info);
+	unsigned long lval;
+
+	if (strict_strtoul(buf, 10, &lval))
+		return -EINVAL;
+	if (!(lval == 5000 || lval == 10000)) {
+		dev_err(dev, "range is not supported\n");
+		return -EINVAL;
+	}
+	mutex_lock(&dev_info->mlock);
+	gpio_set_value(st->pdata->gpio_range, lval == 10000);
+	st->range = lval;
+	mutex_unlock(&dev_info->mlock);
+
+	return count;
+}
+
+static IIO_DEVICE_ATTR(range, S_IRUGO | S_IWUSR, \
+		       ad7606_show_range, ad7606_store_range, 0);
+static IIO_CONST_ATTR(range_available, "5000 10000");
+
+static ssize_t ad7606_show_oversampling_ratio(struct device *dev,
+			struct device_attribute *attr, char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad7606_state *st = iio_dev_get_devdata(dev_info);
+
+	return sprintf(buf, "%u\n", st->oversampling);
+}
+
+static int ad7606_oversampling_get_index(unsigned val)
+{
+	unsigned char supported[] = {0, 2, 4, 8, 16, 32, 64};
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(supported); i++)
+		if (val == supported[i])
+			return i;
+
+	return -EINVAL;
+}
+
+static ssize_t ad7606_store_oversampling_ratio(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t count)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad7606_state *st = iio_dev_get_devdata(dev_info);
+	unsigned long lval;
+	int ret;
+
+	if (strict_strtoul(buf, 10, &lval))
+		return -EINVAL;
+
+	ret = ad7606_oversampling_get_index(lval);
+	if (ret < 0) {
+		dev_err(dev, "oversampling %lu is not supported\n", lval);
+		return ret;
+	}
+
+	mutex_lock(&dev_info->mlock);
+	gpio_set_value(st->pdata->gpio_os0, (ret >> 0) & 1);
+	gpio_set_value(st->pdata->gpio_os1, (ret >> 1) & 1);
+	gpio_set_value(st->pdata->gpio_os1, (ret >> 2) & 1);
+	st->oversampling = lval;
+	mutex_unlock(&dev_info->mlock);
+
+	return count;
+}
+
+static IIO_DEVICE_ATTR(oversampling_ratio, S_IRUGO | S_IWUSR,
+		       ad7606_show_oversampling_ratio,
+		       ad7606_store_oversampling_ratio, 0);
+static IIO_CONST_ATTR(oversampling_ratio_available, "0 2 4 8 16 32 64");
+
+static struct attribute *ad7606_attributes[] = {
+	&iio_dev_attr_in0_raw.dev_attr.attr,
+	&iio_dev_attr_in1_raw.dev_attr.attr,
+	&iio_dev_attr_in2_raw.dev_attr.attr,
+	&iio_dev_attr_in3_raw.dev_attr.attr,
+	&iio_dev_attr_in4_raw.dev_attr.attr,
+	&iio_dev_attr_in5_raw.dev_attr.attr,
+	&iio_dev_attr_in6_raw.dev_attr.attr,
+	&iio_dev_attr_in7_raw.dev_attr.attr,
+	&iio_dev_attr_in_scale.dev_attr.attr,
+	&iio_dev_attr_name.dev_attr.attr,
+	&iio_dev_attr_range.dev_attr.attr,
+	&iio_const_attr_range_available.dev_attr.attr,
+	&iio_dev_attr_oversampling_ratio.dev_attr.attr,
+	&iio_const_attr_oversampling_ratio_available.dev_attr.attr,
+	NULL,
+};
+
+static mode_t ad7606_attr_is_visible(struct kobject *kobj,
+				     struct attribute *attr, int n)
+{
+	struct device *dev = container_of(kobj, struct device, kobj);
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad7606_state *st = iio_dev_get_devdata(dev_info);
+
+	mode_t mode = attr->mode;
+
+	if (st->chip_info->num_channels <= 6 &&
+		(attr == &iio_dev_attr_in7_raw.dev_attr.attr ||
+		attr == &iio_dev_attr_in6_raw.dev_attr.attr))
+		mode = 0;
+	else if (st->chip_info->num_channels <= 4 &&
+		(attr == &iio_dev_attr_in5_raw.dev_attr.attr ||
+		attr == &iio_dev_attr_in4_raw.dev_attr.attr))
+		mode = 0;
+	else if (!st->have_os &&
+		(attr == &iio_dev_attr_oversampling_ratio.dev_attr.attr ||
+		attr ==
+		&iio_const_attr_oversampling_ratio_available.dev_attr.attr))
+		mode = 0;
+	else if (!st->have_range &&
+		(attr == &iio_dev_attr_range.dev_attr.attr ||
+		attr == &iio_const_attr_range_available.dev_attr.attr))
+			mode = 0;
+
+	return mode;
+}
+
+static const struct attribute_group ad7606_attribute_group = {
+	.attrs = ad7606_attributes,
+	.is_visible = ad7606_attr_is_visible,
+};
+
+static const struct ad7606_chip_info ad7606_chip_info_tbl[] = {
+	/*
+	 * More devices added in future
+	 */
+	[ID_AD7606_8] = {
+		.name = "ad7606",
+		.bits = 16,
+		.sign = IIO_SCAN_EL_TYPE_SIGNED,
+		.int_vref_mv = 2500,
+		.num_channels = 8,
+	},
+	[ID_AD7606_6] = {
+		.name = "ad7606-6",
+		.bits = 16,
+		.sign = IIO_SCAN_EL_TYPE_SIGNED,
+		.int_vref_mv = 2500,
+		.num_channels = 6,
+	},
+	[ID_AD7606_4] = {
+		.name = "ad7606-4",
+		.bits = 16,
+		.sign = IIO_SCAN_EL_TYPE_SIGNED,
+		.int_vref_mv = 2500,
+		.num_channels = 4,
+	},
+};
+
+static int ad7606_request_gpios(struct ad7606_state *st)
+{
+	struct gpio gpio_array[3] = {
+		[0] = {
+			.gpio =  st->pdata->gpio_os0,
+			.flags = GPIOF_DIR_OUT | ((st->oversampling & 1) ?
+				 GPIOF_INIT_HIGH : GPIOF_INIT_LOW),
+			.label = "AD7606_OS0",
+		},
+		[1] = {
+			.gpio =  st->pdata->gpio_os1,
+			.flags = GPIOF_DIR_OUT | ((st->oversampling & 2) ?
+				 GPIOF_INIT_HIGH : GPIOF_INIT_LOW),
+			.label = "AD7606_OS1",
+		},
+		[2] = {
+			.gpio =  st->pdata->gpio_os2,
+			.flags = GPIOF_DIR_OUT | ((st->oversampling & 4) ?
+				 GPIOF_INIT_HIGH : GPIOF_INIT_LOW),
+			.label = "AD7606_OS2",
+		},
+	};
+	int ret;
+
+	ret = gpio_request_one(st->pdata->gpio_convst, GPIOF_OUT_INIT_LOW,
+			       "AD7606_CONVST");
+	if (ret) {
+		dev_err(st->dev, "failed to request GPIO CONVST\n");
+		return ret;
+	}
+
+	ret = gpio_request_array(gpio_array, ARRAY_SIZE(gpio_array));
+	if (!ret) {
+		st->have_os = true;
+	}
+
+	ret = gpio_request_one(st->pdata->gpio_reset, GPIOF_OUT_INIT_LOW,
+			       "AD7606_RESET");
+	if (!ret)
+		st->have_reset = true;
+
+	ret = gpio_request_one(st->pdata->gpio_range, GPIOF_DIR_OUT |
+			       ((st->range == 10000) ? GPIOF_INIT_HIGH :
+			       	GPIOF_INIT_LOW), "AD7606_RANGE");
+	if (!ret)
+		st->have_range = true;
+
+	ret = gpio_request_one(st->pdata->gpio_stby, GPIOF_OUT_INIT_HIGH,
+			       "AD7606_STBY");
+	if (!ret)
+		st->have_stby = true;
+
+	if (gpio_is_valid(st->pdata->gpio_frstdata)) {
+		ret = gpio_request_one(st->pdata->gpio_frstdata, GPIOF_IN,
+				       "AD7606_FRSTDATA");
+		if (!ret)
+			st->have_frstdata = true;
+	}
+
+	return 0;
+}
+
+static void ad7606_free_gpios(struct ad7606_state *st)
+{
+	if (st->have_range)
+		gpio_free(st->pdata->gpio_range);
+
+	if (st->have_stby)
+		gpio_free(st->pdata->gpio_stby);
+
+	if (st->have_os) {
+		gpio_free(st->pdata->gpio_os0);
+		gpio_free(st->pdata->gpio_os1);
+		gpio_free(st->pdata->gpio_os2);
+	}
+
+	if (st->have_reset)
+		gpio_free(st->pdata->gpio_reset);
+
+	if (st->have_frstdata)
+		gpio_free(st->pdata->gpio_frstdata);
+
+	gpio_free(st->pdata->gpio_convst);
+}
+
+/**
+ *  Interrupt handler
+ */
+static irqreturn_t ad7606_interrupt(int irq, void *dev_id)
+{
+	struct ad7606_state *st = dev_id;
+
+	if (iio_ring_enabled(st->indio_dev)) {
+		if (!work_pending(&st->poll_work))
+			schedule_work(&st->poll_work);
+	} else {
+		st->done = true;
+		wake_up_interruptible(&st->wq_data_avail);
+	}
+
+	return IRQ_HANDLED;
+};
+
+struct ad7606_state *ad7606_probe(struct device *dev, int irq,
+			      void __iomem *base_address,
+			      unsigned id,
+			      const struct ad7606_bus_ops *bops)
+{
+	struct ad7606_platform_data *pdata = dev->platform_data;
+	struct ad7606_state *st;
+	int ret;
+
+	st = kzalloc(sizeof(*st), GFP_KERNEL);
+	if (st == NULL) {
+		ret = -ENOMEM;
+		goto error_ret;
+	}
+
+	st->dev = dev;
+	st->id = id;
+	st->irq = irq;
+	st->bops = bops;
+	st->base_address = base_address;
+	st->range = pdata->default_range == 10000 ? 10000 : 5000;
+
+	ret = ad7606_oversampling_get_index(pdata->default_os);
+	if (ret < 0) {
+		dev_warn(dev, "oversampling %d is not supported\n",
+			 pdata->default_os);
+		st->oversampling = 0;
+	} else {
+		st->oversampling = pdata->default_os;
+	}
+
+	st->reg = regulator_get(dev, "vcc");
+	if (!IS_ERR(st->reg)) {
+		ret = regulator_enable(st->reg);
+		if (ret)
+			goto error_put_reg;
+	}
+
+	st->pdata = pdata;
+	st->chip_info = &ad7606_chip_info_tbl[id];
+
+	atomic_set(&st->protect_ring, 0);
+
+	st->indio_dev = iio_allocate_device();
+	if (st->indio_dev == NULL) {
+		ret = -ENOMEM;
+		goto error_disable_reg;
+	}
+
+	st->indio_dev->dev.parent = dev;
+	st->indio_dev->attrs = &ad7606_attribute_group;
+	st->indio_dev->dev_data = (void *)(st);
+	st->indio_dev->driver_module = THIS_MODULE;
+	st->indio_dev->modes = INDIO_DIRECT_MODE;
+
+	init_waitqueue_head(&st->wq_data_avail);
+
+	ret = ad7606_request_gpios(st);
+	if (ret)
+		goto error_free_device;
+
+	ret = ad7606_reset(st);
+	if (ret)
+		dev_warn(st->dev, "failed to RESET: no RESET GPIO specified\n");
+
+	ret = request_irq(st->irq, ad7606_interrupt,
+		IRQF_TRIGGER_FALLING, st->chip_info->name, st);
+	if (ret)
+		goto error_free_gpios;
+
+	ret = ad7606_register_ring_funcs_and_init(st->indio_dev);
+	if (ret)
+		goto error_free_irq;
+
+	ret = iio_device_register(st->indio_dev);
+	if (ret)
+		goto error_free_irq;
+
+	ret = iio_ring_buffer_register(st->indio_dev->ring, 0);
+	if (ret)
+		goto error_cleanup_ring;
+
+	return st;
+
+error_cleanup_ring:
+	ad7606_ring_cleanup(st->indio_dev);
+	iio_device_unregister(st->indio_dev);
+
+error_free_irq:
+	free_irq(st->irq, st);
+
+error_free_gpios:
+	ad7606_free_gpios(st);
+
+error_free_device:
+	iio_free_device(st->indio_dev);
+
+error_disable_reg:
+	if (!IS_ERR(st->reg))
+		regulator_disable(st->reg);
+error_put_reg:
+	if (!IS_ERR(st->reg))
+		regulator_put(st->reg);
+	kfree(st);
+error_ret:
+	return ERR_PTR(ret);
+}
+
+int ad7606_remove(struct ad7606_state *st)
+{
+	struct iio_dev *indio_dev = st->indio_dev;
+	iio_ring_buffer_unregister(indio_dev->ring);
+	ad7606_ring_cleanup(indio_dev);
+	iio_device_unregister(indio_dev);
+	free_irq(st->irq, st);
+	if (!IS_ERR(st->reg)) {
+		regulator_disable(st->reg);
+		regulator_put(st->reg);
+	}
+
+	ad7606_free_gpios(st);
+
+	kfree(st);
+	return 0;
+}
+
+void ad7606_suspend(struct ad7606_state *st)
+{
+	if (st->have_stby) {
+		if (st->have_range)
+			gpio_set_value(st->pdata->gpio_range, 1);
+		gpio_set_value(st->pdata->gpio_stby, 0);
+	}
+}
+
+void ad7606_resume(struct ad7606_state *st)
+{
+	if (st->have_stby) {
+		if (st->have_range)
+			gpio_set_value(st->pdata->gpio_range,
+					st->range == 10000);
+
+		gpio_set_value(st->pdata->gpio_stby, 1);
+		ad7606_reset(st);
+	}
+}
+
+MODULE_AUTHOR("Michael Hennerich <hennerich at blackfin.uclinux.org>");
+MODULE_DESCRIPTION("Analog Devices AD7606 ADC");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/staging/iio/adc/ad7606_par.c b/drivers/staging/iio/adc/ad7606_par.c
new file mode 100644
index 0000000..43a554c
--- /dev/null
+++ b/drivers/staging/iio/adc/ad7606_par.c
@@ -0,0 +1,188 @@
+/*
+ * AD7606 Parallel Interface ADC driver
+ *
+ * Copyright 2011 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/types.h>
+#include <linux/err.h>
+#include <linux/io.h>
+
+#include "ad7606.h"
+
+static int ad7606_par16_read_block(struct device *dev,
+				 int count, void *buf)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct ad7606_state *st = platform_get_drvdata(pdev);
+
+	insw((unsigned long) st->base_address, buf, count);
+
+	return 0;
+}
+
+static const struct ad7606_bus_ops ad7606_par16_bops = {
+	.read_block	= ad7606_par16_read_block,
+};
+
+static int ad7606_par8_read_block(struct device *dev,
+				 int count, void *buf)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct ad7606_state *st = platform_get_drvdata(pdev);
+
+	insb((unsigned long) st->base_address, buf, count * 2);
+
+	return 0;
+}
+
+static const struct ad7606_bus_ops ad7606_par8_bops = {
+	.read_block	= ad7606_par8_read_block,
+};
+
+static int __devinit ad7606_par_probe(struct platform_device *pdev)
+{
+	struct resource *res;
+	struct ad7606_state *st;
+	void __iomem *addr;
+	resource_size_t remap_size;
+	int ret, irq;
+
+	irq = platform_get_irq(pdev, 0);
+	if (irq < 0) {
+		dev_err(&pdev->dev, "no irq\n");
+		return -ENODEV;
+	}
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res)
+		return -ENODEV;
+
+	remap_size = resource_size(res);
+
+	/* Request the regions */
+	if (!request_mem_region(res->start, remap_size, "iio-ad7606")) {
+		ret = -EBUSY;
+		goto out1;
+	}
+	addr = ioremap(res->start, remap_size);
+	if (!addr) {
+		ret = -ENOMEM;
+		goto out1;
+	}
+
+	st = ad7606_probe(&pdev->dev, irq, addr,
+			  platform_get_device_id(pdev)->driver_data,
+			  remap_size > 1 ? &ad7606_par16_bops :
+			  &ad7606_par8_bops);
+
+	if (IS_ERR(st))  {
+		ret = PTR_ERR(st);
+		goto out2;
+	}
+
+	platform_set_drvdata(pdev, st);
+
+	return 0;
+
+out2:
+	iounmap(addr);
+out1:
+	release_mem_region(res->start, remap_size);
+
+	return ret;
+}
+
+static int __devexit ad7606_par_remove(struct platform_device *pdev)
+{
+	struct ad7606_state *st = platform_get_drvdata(pdev);
+	struct resource *res;
+
+	ad7606_remove(st);
+
+	iounmap(st->base_address);
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	release_mem_region(res->start, resource_size(res));
+
+	platform_set_drvdata(pdev, NULL);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static int ad7606_par_suspend(struct device *dev)
+{
+	struct ad7606_state *st = dev_get_drvdata(dev);
+
+	ad7606_suspend(st);
+
+	return 0;
+}
+
+static int ad7606_par_resume(struct device *dev)
+{
+	struct ad7606_state *st = dev_get_drvdata(dev);
+
+	ad7606_resume(st);
+
+	return 0;
+}
+
+static const struct dev_pm_ops ad7606_pm_ops = {
+	.suspend = ad7606_par_suspend,
+	.resume  = ad7606_par_resume,
+};
+#define AD7606_PAR_PM_OPS (&ad7606_pm_ops)
+
+#else
+#define AD7606_PAR_PM_OPS NULL
+#endif  /* CONFIG_PM */
+
+static struct platform_device_id ad7606_driver_ids[] = {
+	{
+		.name		= "ad7606-8",
+		.driver_data	= ID_AD7606_8,
+	}, {
+		.name		= "ad7606-6",
+		.driver_data	= ID_AD7606_6,
+	}, {
+		.name		= "ad7606-4",
+		.driver_data	= ID_AD7606_4,
+	},
+	{ }
+};
+
+MODULE_DEVICE_TABLE(platform, ad7606_driver_ids);
+
+static struct platform_driver ad7606_driver = {
+	.probe = ad7606_par_probe,
+	.remove	= __devexit_p(ad7606_par_remove),
+	.id_table = ad7606_driver_ids,
+	.driver = {
+		.name	 = "ad7606",
+		.owner	= THIS_MODULE,
+		.pm    = AD7606_PAR_PM_OPS,
+	},
+};
+
+static int __init ad7606_init(void)
+{
+	return platform_driver_register(&ad7606_driver);
+}
+
+static void __exit ad7606_cleanup(void)
+{
+	platform_driver_unregister(&ad7606_driver);
+}
+
+module_init(ad7606_init);
+module_exit(ad7606_cleanup);
+
+MODULE_AUTHOR("Michael Hennerich <hennerich at blackfin.uclinux.org>");
+MODULE_DESCRIPTION("Analog Devices AD7606 ADC");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:ad7606_par");
diff --git a/drivers/staging/iio/adc/ad7606_ring.c b/drivers/staging/iio/adc/ad7606_ring.c
new file mode 100644
index 0000000..9889680
--- /dev/null
+++ b/drivers/staging/iio/adc/ad7606_ring.c
@@ -0,0 +1,266 @@
+/*
+ * Copyright 2011 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2.
+ *
+ */
+
+#include <linux/interrupt.h>
+#include <linux/gpio.h>
+#include <linux/workqueue.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+
+#include "../iio.h"
+#include "../ring_generic.h"
+#include "../ring_sw.h"
+#include "../trigger.h"
+#include "../sysfs.h"
+
+#include "ad7606.h"
+
+static IIO_SCAN_EL_C(in0, 0, 0, NULL);
+static IIO_SCAN_EL_C(in1, 1, 0, NULL);
+static IIO_SCAN_EL_C(in2, 2, 0, NULL);
+static IIO_SCAN_EL_C(in3, 3, 0, NULL);
+static IIO_SCAN_EL_C(in4, 4, 0, NULL);
+static IIO_SCAN_EL_C(in5, 5, 0, NULL);
+static IIO_SCAN_EL_C(in6, 6, 0, NULL);
+static IIO_SCAN_EL_C(in7, 7, 0, NULL);
+
+static ssize_t ad7606_show_type(struct device *dev,
+				struct device_attribute *attr,
+				char *buf)
+{
+	struct iio_ring_buffer *ring = dev_get_drvdata(dev);
+	struct iio_dev *indio_dev = ring->indio_dev;
+	struct ad7606_state *st = indio_dev->dev_data;
+
+	return sprintf(buf, "%c%d/%d\n", st->chip_info->sign,
+		       st->chip_info->bits, st->chip_info->bits);
+}
+static IIO_DEVICE_ATTR(in_type, S_IRUGO, ad7606_show_type, NULL, 0);
+
+static struct attribute *ad7606_scan_el_attrs[] = {
+	&iio_scan_el_in0.dev_attr.attr,
+	&iio_const_attr_in0_index.dev_attr.attr,
+	&iio_scan_el_in1.dev_attr.attr,
+	&iio_const_attr_in1_index.dev_attr.attr,
+	&iio_scan_el_in2.dev_attr.attr,
+	&iio_const_attr_in2_index.dev_attr.attr,
+	&iio_scan_el_in3.dev_attr.attr,
+	&iio_const_attr_in3_index.dev_attr.attr,
+	&iio_scan_el_in4.dev_attr.attr,
+	&iio_const_attr_in4_index.dev_attr.attr,
+	&iio_scan_el_in5.dev_attr.attr,
+	&iio_const_attr_in5_index.dev_attr.attr,
+	&iio_scan_el_in6.dev_attr.attr,
+	&iio_const_attr_in6_index.dev_attr.attr,
+	&iio_scan_el_in7.dev_attr.attr,
+	&iio_const_attr_in7_index.dev_attr.attr,
+	&iio_dev_attr_in_type.dev_attr.attr,
+	NULL,
+};
+
+static mode_t ad7606_scan_el_attr_is_visible(struct kobject *kobj,
+				     struct attribute *attr, int n)
+{
+	struct device *dev = container_of(kobj, struct device, kobj);
+	struct iio_ring_buffer *ring = dev_get_drvdata(dev);
+	struct iio_dev *indio_dev = ring->indio_dev;
+	struct ad7606_state *st = indio_dev->dev_data;
+
+	mode_t mode = attr->mode;
+
+	if (st->chip_info->num_channels <= 6 &&
+		(attr == &iio_scan_el_in7.dev_attr.attr ||
+		attr == &iio_const_attr_in7_index.dev_attr.attr ||
+		attr == &iio_scan_el_in6.dev_attr.attr ||
+		attr == &iio_const_attr_in6_index.dev_attr.attr))
+		mode = 0;
+	else if (st->chip_info->num_channels <= 4 &&
+		(attr == &iio_scan_el_in5.dev_attr.attr ||
+		attr == &iio_const_attr_in5_index.dev_attr.attr ||
+		attr == &iio_scan_el_in4.dev_attr.attr ||
+		attr == &iio_const_attr_in4_index.dev_attr.attr))
+		mode = 0;
+
+	return mode;
+}
+
+static struct attribute_group ad7606_scan_el_group = {
+	.name = "scan_elements",
+	.attrs = ad7606_scan_el_attrs,
+	.is_visible = ad7606_scan_el_attr_is_visible,
+};
+
+int ad7606_scan_from_ring(struct ad7606_state *st, unsigned ch)
+{
+	struct iio_ring_buffer *ring = st->indio_dev->ring;
+	int ret;
+	u16 *ring_data;
+
+	ring_data = kmalloc(ring->access.get_bytes_per_datum(ring), GFP_KERNEL);
+	if (ring_data == NULL) {
+		ret = -ENOMEM;
+		goto error_ret;
+	}
+	ret = ring->access.read_last(ring, (u8 *) ring_data);
+	if (ret)
+		goto error_free_ring_data;
+
+	ret = ring_data[ch];
+
+error_free_ring_data:
+	kfree(ring_data);
+error_ret:
+	return ret;
+}
+
+/**
+ * ad7606_ring_preenable() setup the parameters of the ring before enabling
+ *
+ * The complex nature of the setting of the nuber of bytes per datum is due
+ * to this driver currently ensuring that the timestamp is stored at an 8
+ * byte boundary.
+ **/
+static int ad7606_ring_preenable(struct iio_dev *indio_dev)
+{
+	struct ad7606_state *st = indio_dev->dev_data;
+	struct iio_ring_buffer *ring = indio_dev->ring;
+	size_t d_size;
+
+	d_size = st->chip_info->num_channels *
+		 st->chip_info->bits / 8 + sizeof(s64);
+
+	if (d_size % sizeof(s64))
+		d_size += sizeof(s64) - (d_size % sizeof(s64));
+
+	if (ring->access.set_bytes_per_datum)
+		ring->access.set_bytes_per_datum(ring, d_size);
+
+	st->d_size = d_size;
+
+	return 0;
+}
+
+/**
+ * ad7606_poll_func_th() th of trigger launched polling to ring buffer
+ *
+ **/
+static void ad7606_poll_func_th(struct iio_dev *indio_dev, s64 time)
+{
+	struct ad7606_state *st = indio_dev->dev_data;
+	gpio_set_value(st->pdata->gpio_convst, 1);
+
+	return;
+}
+/**
+ * ad7606_poll_bh_to_ring() bh of trigger launched polling to ring buffer
+ * @work_s:	the work struct through which this was scheduled
+ *
+ * Currently there is no option in this driver to disable the saving of
+ * timestamps within the ring.
+ * I think the one copy of this at a time was to avoid problems if the
+ * trigger was set far too high and the reads then locked up the computer.
+ **/
+static void ad7606_poll_bh_to_ring(struct work_struct *work_s)
+{
+	struct ad7606_state *st = container_of(work_s, struct ad7606_state,
+						poll_work);
+	struct iio_dev *indio_dev = st->indio_dev;
+	struct iio_sw_ring_buffer *sw_ring = iio_to_sw_ring(indio_dev->ring);
+	struct iio_ring_buffer *ring = indio_dev->ring;
+	s64 time_ns;
+	__u8 *buf;
+	int ret;
+
+	/* Ensure only one copy of this function running at a time */
+	if (atomic_inc_return(&st->protect_ring) > 1)
+		return;
+
+	buf = kzalloc(st->d_size, GFP_KERNEL);
+	if (buf == NULL)
+		return;
+
+	if (st->have_frstdata) {
+		ret = st->bops->read_block(st->dev, 1, buf);
+		if (ret)
+			goto done;
+		if (!gpio_get_value(st->pdata->gpio_frstdata)) {
+			/* This should never happen. However
+			 * some signal glitch caused by bad PCB desgin or
+			 * electrostatic discharge, could cause an extra read
+			 * or clock. This allows recovery.
+			 */
+			ad7606_reset(st);
+			goto done;
+		}
+		ret = st->bops->read_block(st->dev,
+			st->chip_info->num_channels - 1, buf + 2);
+		if (ret)
+			goto done;
+	} else {
+		ret = st->bops->read_block(st->dev,
+			st->chip_info->num_channels, buf);
+		if (ret)
+			goto done;
+	}
+
+	time_ns = iio_get_time_ns();
+	memcpy(buf + st->d_size - sizeof(s64), &time_ns, sizeof(time_ns));
+
+	ring->access.store_to(&sw_ring->buf, buf, time_ns);
+done:
+	gpio_set_value(st->pdata->gpio_convst, 0);
+	kfree(buf);
+	atomic_dec(&st->protect_ring);
+}
+
+int ad7606_register_ring_funcs_and_init(struct iio_dev *indio_dev)
+{
+	struct ad7606_state *st = indio_dev->dev_data;
+	int ret;
+
+	indio_dev->ring = iio_sw_rb_allocate(indio_dev);
+	if (!indio_dev->ring) {
+		ret = -ENOMEM;
+		goto error_ret;
+	}
+
+	/* Effectively select the ring buffer implementation */
+	iio_ring_sw_register_funcs(&indio_dev->ring->access);
+	ret = iio_alloc_pollfunc(indio_dev, NULL, &ad7606_poll_func_th);
+	if (ret)
+		goto error_deallocate_sw_rb;
+
+	/* Ring buffer functions - here trigger setup related */
+
+	indio_dev->ring->preenable = &ad7606_ring_preenable;
+	indio_dev->ring->postenable = &iio_triggered_ring_postenable;
+	indio_dev->ring->predisable = &iio_triggered_ring_predisable;
+	indio_dev->ring->scan_el_attrs = &ad7606_scan_el_group;
+
+	INIT_WORK(&st->poll_work, &ad7606_poll_bh_to_ring);
+
+	/* Flag that polled ring buffering is possible */
+	indio_dev->modes |= INDIO_RING_TRIGGERED;
+	return 0;
+error_deallocate_sw_rb:
+	iio_sw_rb_free(indio_dev->ring);
+error_ret:
+	return ret;
+}
+
+void ad7606_ring_cleanup(struct iio_dev *indio_dev)
+{
+	if (indio_dev->trig) {
+		iio_put_trigger(indio_dev->trig);
+		iio_trigger_dettach_poll_func(indio_dev->trig,
+					      indio_dev->pollfunc);
+	}
+	kfree(indio_dev->pollfunc);
+	iio_sw_rb_free(indio_dev->ring);
+}
diff --git a/drivers/staging/iio/adc/ad7606_spi.c b/drivers/staging/iio/adc/ad7606_spi.c
new file mode 100644
index 0000000..d738491
--- /dev/null
+++ b/drivers/staging/iio/adc/ad7606_spi.c
@@ -0,0 +1,126 @@
+/*
+ * AD7606 SPI ADC driver
+ *
+ * Copyright 2011 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/module.h>
+#include <linux/spi/spi.h>
+#include <linux/types.h>
+#include <linux/err.h>
+#include "ad7606.h"
+
+#define MAX_SPI_FREQ_HZ		23500000	/* VDRIVE above 4.75 V */
+
+static int ad7606_spi_read_block(struct device *dev,
+				 int count, void *buf)
+{
+	struct spi_device *spi = to_spi_device(dev);
+	int i, ret;
+	unsigned short *data = buf;
+
+	ret = spi_read(spi, (u8 *)buf, count * 2);
+	if (ret < 0) {
+		dev_err(&spi->dev, "SPI read error\n");
+		return ret;
+	}
+
+	for (i = 0; i < count; i++)
+		data[i] = be16_to_cpu(data[i]);
+
+	return 0;
+}
+
+static const struct ad7606_bus_ops ad7606_spi_bops = {
+	.read_block	= ad7606_spi_read_block,
+};
+
+static int __devinit ad7606_spi_probe(struct spi_device *spi)
+{
+	struct ad7606_state *st;
+
+	st = ad7606_probe(&spi->dev, spi->irq, NULL,
+			   spi_get_device_id(spi)->driver_data,
+			   &ad7606_spi_bops);
+
+	if (IS_ERR(st))
+		return PTR_ERR(st);
+
+	spi_set_drvdata(spi, st);
+
+	return 0;
+}
+
+static int __devexit ad7606_spi_remove(struct spi_device *spi)
+{
+	struct ad7606_state *st = dev_get_drvdata(&spi->dev);
+
+	return ad7606_remove(st);
+}
+
+#ifdef CONFIG_PM
+static int ad7606_spi_suspend(struct device *dev)
+{
+	struct ad7606_state *st = dev_get_drvdata(dev);
+
+	ad7606_suspend(st);
+
+	return 0;
+}
+
+static int ad7606_spi_resume(struct device *dev)
+{
+	struct ad7606_state *st = dev_get_drvdata(dev);
+
+	ad7606_resume(st);
+
+	return 0;
+}
+
+static const struct dev_pm_ops ad7606_pm_ops = {
+	.suspend = ad7606_spi_suspend,
+	.resume  = ad7606_spi_resume,
+};
+#define AD7606_SPI_PM_OPS (&ad7606_pm_ops)
+
+#else
+#define AD7606_SPI_PM_OPS NULL
+#endif
+
+static const struct spi_device_id ad7606_id[] = {
+	{"ad7606-8", ID_AD7606_8},
+	{"ad7606-6", ID_AD7606_6},
+	{"ad7606-4", ID_AD7606_4},
+	{}
+};
+
+static struct spi_driver ad7606_driver = {
+	.driver = {
+		.name = "ad7606",
+		.bus = &spi_bus_type,
+		.owner = THIS_MODULE,
+		.pm    = AD7606_SPI_PM_OPS,
+	},
+	.probe = ad7606_spi_probe,
+	.remove = __devexit_p(ad7606_spi_remove),
+	.id_table = ad7606_id,
+};
+
+static int __init ad7606_spi_init(void)
+{
+	return spi_register_driver(&ad7606_driver);
+}
+module_init(ad7606_spi_init);
+
+static void __exit ad7606_spi_exit(void)
+{
+	spi_unregister_driver(&ad7606_driver);
+}
+module_exit(ad7606_spi_exit);
+
+MODULE_AUTHOR("Michael Hennerich <hennerich at blackfin.uclinux.org>");
+MODULE_DESCRIPTION("Analog Devices AD7606 ADC");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("spi:ad7606_spi");
-- 
1.7.4.1




More information about the devel mailing list