Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit b9618c0c authored by Michael Hennerich's avatar Michael Hennerich Committed by Greg Kroah-Hartman
Browse files

staging: IIO: ADC: New driver for AD7606/AD7606-6/AD7606-4



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: default avatarMichael Hennerich <michael.hennerich@analog.com>
Acked-by: default avatarJonathan Cameron <jic23@cam.ac.uk>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent 363907af
Loading
Loading
Loading
Loading
+25 −0
Original line number Diff line number Diff line
@@ -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@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@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@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@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
+28 −0
Original line number Diff line number Diff line
@@ -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
+6 −0
Original line number Diff line number Diff line
@@ -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
+117 −0
Original line number Diff line number Diff line
/*
 * 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_ */
+556 −0
Original line number Diff line number Diff line
/*
 * 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@blackfin.uclinux.org>");
MODULE_DESCRIPTION("Analog Devices AD7606 ADC");
MODULE_LICENSE("GPL v2");
Loading