mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-25 21:24:08 +08:00
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: Michael Hennerich <michael.hennerich@analog.com> Acked-by: Jonathan Cameron <jic23@cam.ac.uk> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
This commit is contained in:
parent
363907af85
commit
b9618c0cac
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
drivers/staging/iio/adc/ad7606.h
Normal file
117
drivers/staging/iio/adc/ad7606.h
Normal file
@ -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_ */
|
556
drivers/staging/iio/adc/ad7606_core.c
Normal file
556
drivers/staging/iio/adc/ad7606_core.c
Normal file
@ -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@blackfin.uclinux.org>");
|
||||
MODULE_DESCRIPTION("Analog Devices AD7606 ADC");
|
||||
MODULE_LICENSE("GPL v2");
|
188
drivers/staging/iio/adc/ad7606_par.c
Normal file
188
drivers/staging/iio/adc/ad7606_par.c
Normal file
@ -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@blackfin.uclinux.org>");
|
||||
MODULE_DESCRIPTION("Analog Devices AD7606 ADC");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_ALIAS("platform:ad7606_par");
|
266
drivers/staging/iio/adc/ad7606_ring.c
Normal file
266
drivers/staging/iio/adc/ad7606_ring.c
Normal file
@ -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);
|
||||
}
|
126
drivers/staging/iio/adc/ad7606_spi.c
Normal file
126
drivers/staging/iio/adc/ad7606_spi.c
Normal file
@ -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@blackfin.uclinux.org>");
|
||||
MODULE_DESCRIPTION("Analog Devices AD7606 ADC");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_ALIAS("spi:ad7606_spi");
|
Loading…
Reference in New Issue
Block a user