Release 4.10 drivers/staging/iio/adc/ad7606.c
/*
* AD7606 SPI ADC driver
*
* Copyright 2011 Analog Devices Inc.
*
* Licensed under the GPL-2.
*/
#include <linux/interrupt.h>
#include <linux/device.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/sysfs.h>
#include <linux/regulator/consumer.h>
#include <linux/err.h>
#include <linux/gpio/consumer.h>
#include <linux/delay.h>
#include <linux/sched.h>
#include <linux/module.h>
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
#include <linux/iio/buffer.h>
#include <linux/iio/trigger_consumer.h>
#include <linux/iio/triggered_buffer.h>
#include "ad7606.h"
static int ad7606_reset(struct ad7606_state *st)
{
if (st->gpio_reset) {
gpiod_set_value(st->gpio_reset, 1);
ndelay(100); /* t_reset >= 100ns */
gpiod_set_value(st->gpio_reset, 0);
return 0;
}
return -ENODEV;
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp |
michael hennerich | michael hennerich | 46 | 92.00% | 1 | 25.00% |
lars-peter clausen | lars-peter clausen | 3 | 6.00% | 2 | 50.00% |
jonathan cameron | jonathan cameron | 1 | 2.00% | 1 | 25.00% |
| Total | 50 | 100.00% | 4 | 100.00% |
static int ad7606_read_samples(struct ad7606_state *st)
{
unsigned int num = st->chip_info->num_channels;
u16 *data = st->data;
int ret;
/*
* The frstdata signal is set to high while and after reading the sample
* of the first channel and low for all other channels. This can be used
* to check that the incoming data is correctly aligned. During normal
* operation the data should never become unaligned, but some glitch or
* electrostatic discharge might cause an extra read or clock cycle.
* Monitoring the frstdata signal allows to recover from such failure
* situations.
*/
if (st->gpio_frstdata) {
ret = st->bops->read_block(st->dev, 1, data);
if (ret)
return ret;
if (!gpiod_get_value(st->gpio_frstdata)) {
ad7606_reset(st);
return -EIO;
}
data++;
num--;
}
return st->bops->read_block(st->dev, num, data);
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp |
michael hennerich | michael hennerich | 73 | 67.59% | 2 | 33.33% |
lars-peter clausen | lars-peter clausen | 34 | 31.48% | 3 | 50.00% |
jonathan cameron | jonathan cameron | 1 | 0.93% | 1 | 16.67% |
| Total | 108 | 100.00% | 6 | 100.00% |
static irqreturn_t ad7606_trigger_handler(int irq, void *p)
{
struct iio_poll_func *pf = p;
struct ad7606_state *st = iio_priv(pf->indio_dev);
gpiod_set_value(st->gpio_convst, 1);
return IRQ_HANDLED;
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp |
lars-peter clausen | lars-peter clausen | 44 | 100.00% | 1 | 100.00% |
| Total | 44 | 100.00% | 1 | 100.00% |
/**
* 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 = iio_priv_to_dev(st);
int ret;
ret = ad7606_read_samples(st);
if (ret == 0)
iio_push_to_buffers_with_timestamp(indio_dev, st->data,
iio_get_time_ns(indio_dev));
gpiod_set_value(st->gpio_convst, 0);
iio_trigger_notify_done(indio_dev->trig);
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp |
lars-peter clausen | lars-peter clausen | 82 | 100.00% | 1 | 100.00% |
| Total | 82 | 100.00% | 1 | 100.00% |
static int ad7606_scan_direct(struct iio_dev *indio_dev, unsigned int ch)
{
struct ad7606_state *st = iio_priv(indio_dev);
int ret;
st->done = false;
gpiod_set_value(st->gpio_convst, 1);
ret = wait_event_interruptible(st->wq_data_avail, st->done);
if (ret)
goto error_ret;
ret = ad7606_read_samples(st);
if (ret == 0)
ret = st->data[ch];
error_ret:
gpiod_set_value(st->gpio_convst, 0);
return ret;
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp |
lars-peter clausen | lars-peter clausen | 54 | 54.55% | 2 | 66.67% |
michael hennerich | michael hennerich | 45 | 45.45% | 1 | 33.33% |
| Total | 99 | 100.00% | 3 | 100.00% |
static int ad7606_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int *val,
int *val2,
long m)
{
int ret;
struct ad7606_state *st = iio_priv(indio_dev);
switch (m) {
case IIO_CHAN_INFO_RAW:
ret = iio_device_claim_direct_mode(indio_dev);
if (ret)
return ret;
ret = ad7606_scan_direct(indio_dev, chan->address);
iio_device_release_direct_mode(indio_dev);
if (ret < 0)
return ret;
*val = (short)ret;
return IIO_VAL_INT;
case IIO_CHAN_INFO_SCALE:
*val = st->range * 2;
*val2 = st->chip_info->channels[0].scan_type.realbits;
return IIO_VAL_FRACTIONAL_LOG2;
case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
*val = st->oversampling;
return IIO_VAL_INT;
}
return -EINVAL;
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp |
michael hennerich | michael hennerich | 119 | 80.41% | 4 | 44.44% |
eva rachel retuya | eva rachel retuya | 13 | 8.78% | 1 | 11.11% |
alison schofield | alison schofield | 7 | 4.73% | 1 | 11.11% |
lars-peter clausen | lars-peter clausen | 7 | 4.73% | 1 | 11.11% |
jonathan cameron | jonathan cameron | 2 | 1.35% | 2 | 22.22% |
| Total | 148 | 100.00% | 9 | 100.00% |
static ssize_t ad7606_show_range(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
struct ad7606_state *st = iio_priv(indio_dev);
return sprintf(buf, "%u\n", st->range);
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp |
michael hennerich | michael hennerich | 51 | 98.08% | 4 | 80.00% |
lars-peter clausen | lars-peter clausen | 1 | 1.92% | 1 | 20.00% |
| Total | 52 | 100.00% | 5 | 100.00% |
static ssize_t ad7606_store_range(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
struct ad7606_state *st = iio_priv(indio_dev);
unsigned long lval;
int ret;
ret = kstrtoul(buf, 10, &lval);
if (ret)
return ret;
if (!(lval == 5000 || lval == 10000))
return -EINVAL;
mutex_lock(&indio_dev->mlock);
gpiod_set_value(st->gpio_range, lval == 10000);
st->range = lval;
mutex_unlock(&indio_dev->mlock);
return count;
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp |
michael hennerich | michael hennerich | 110 | 89.43% | 4 | 57.14% |
aida mynzhasova | aida mynzhasova | 11 | 8.94% | 1 | 14.29% |
lars-peter clausen | lars-peter clausen | 2 | 1.63% | 2 | 28.57% |
| Total | 123 | 100.00% | 7 | 100.00% |
static IIO_DEVICE_ATTR(in_voltage_range, S_IRUGO | S_IWUSR,
ad7606_show_range, ad7606_store_range, 0);
static IIO_CONST_ATTR(in_voltage_range_available, "5000 10000");
static int ad7606_oversampling_get_index(unsigned int val)
{
unsigned char supported[] = {1, 2, 4, 8, 16, 32, 64};
int i;
for (i = 0; i < ARRAY_SIZE(supported); i++)
if (val == supported[i])
return i;
return -EINVAL;
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp |
michael hennerich | michael hennerich | 64 | 96.97% | 1 | 33.33% |
lars-peter clausen | lars-peter clausen | 1 | 1.52% | 1 | 33.33% |
alison schofield | alison schofield | 1 | 1.52% | 1 | 33.33% |
| Total | 66 | 100.00% | 3 | 100.00% |
static int ad7606_write_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int val,
int val2,
long mask)
{
struct ad7606_state *st = iio_priv(indio_dev);
int values[3];
int ret;
switch (mask) {
case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
if (val2)
return -EINVAL;
ret = ad7606_oversampling_get_index(val);
if (ret < 0)
return ret;
values[0] = (ret >> 0) & 1;
values[1] = (ret >> 1) & 1;
values[2] = (ret >> 2) & 1;
mutex_lock(&indio_dev->mlock);
gpiod_set_array_value(ARRAY_SIZE(values), st->gpio_os->desc,
values);
st->oversampling = val;
mutex_unlock(&indio_dev->mlock);
return 0;
default:
return -EINVAL;
}
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp |
michael hennerich | michael hennerich | 83 | 50.92% | 3 | 50.00% |
lars-peter clausen | lars-peter clausen | 44 | 26.99% | 1 | 16.67% |
eva rachel retuya | eva rachel retuya | 34 | 20.86% | 1 | 16.67% |
aida mynzhasova | aida mynzhasova | 2 | 1.23% | 1 | 16.67% |
| Total | 163 | 100.00% | 6 | 100.00% |
static IIO_CONST_ATTR(oversampling_ratio_available, "1 2 4 8 16 32 64");
static struct attribute *ad7606_attributes_os_and_range[] = {
&iio_dev_attr_in_voltage_range.dev_attr.attr,
&iio_const_attr_in_voltage_range_available.dev_attr.attr,
&iio_const_attr_oversampling_ratio_available.dev_attr.attr,
NULL,
};
static const struct attribute_group ad7606_attribute_group_os_and_range = {
.attrs = ad7606_attributes_os_and_range,
};
static struct attribute *ad7606_attributes_os[] = {
&iio_const_attr_oversampling_ratio_available.dev_attr.attr,
NULL,
};
static const struct attribute_group ad7606_attribute_group_os = {
.attrs = ad7606_attributes_os,
};
static struct attribute *ad7606_attributes_range[] = {
&iio_dev_attr_in_voltage_range.dev_attr.attr,
&iio_const_attr_in_voltage_range_available.dev_attr.attr,
NULL,
};
static const struct attribute_group ad7606_attribute_group_range = {
.attrs = ad7606_attributes_range,
};
#define AD7606_CHANNEL(num) \
{ \
.type = IIO_VOLTAGE, \
.indexed = 1, \
.channel = num, \
.address = num, \
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),\
.info_mask_shared_by_all = \
BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \
.scan_index = num, \
.scan_type = { \
.sign = 's', \
.realbits = 16, \
.storagebits = 16, \
.endianness = IIO_CPU, \
}, \
}
static const struct iio_chan_spec ad7606_channels[] = {
IIO_CHAN_SOFT_TIMESTAMP(8),
AD7606_CHANNEL(0),
AD7606_CHANNEL(1),
AD7606_CHANNEL(2),
AD7606_CHANNEL(3),
AD7606_CHANNEL(4),
AD7606_CHANNEL(5),
AD7606_CHANNEL(6),
AD7606_CHANNEL(7),
};
static const struct ad7606_chip_info ad7606_chip_info_tbl[] = {
/*
* More devices added in future
*/
[ID_AD7606_8] = {
.channels = ad7606_channels,
.num_channels = 9,
},
[ID_AD7606_6] = {
.channels = ad7606_channels,
.num_channels = 7,
},
[ID_AD7606_4] = {
.channels = ad7606_channels,
.num_channels = 5,
},
};
static int ad7606_request_gpios(struct ad7606_state *st)
{
struct device *dev = st->dev;
st->gpio_convst = devm_gpiod_get(dev, "conversion-start",
GPIOD_OUT_LOW);
if (IS_ERR(st->gpio_convst))
return PTR_ERR(st->gpio_convst);
st->gpio_reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW);
if (IS_ERR(st->gpio_reset))
return PTR_ERR(st->gpio_reset);
st->gpio_range = devm_gpiod_get_optional(dev, "range", GPIOD_OUT_LOW);
if (IS_ERR(st->gpio_range))
return PTR_ERR(st->gpio_range);
st->gpio_standby = devm_gpiod_get_optional(dev, "standby",
GPIOD_OUT_HIGH);
if (IS_ERR(st->gpio_standby))
return PTR_ERR(st->gpio_standby);
st->gpio_frstdata = devm_gpiod_get_optional(dev, "first-data",
GPIOD_IN);
if (IS_ERR(st->gpio_frstdata))
return PTR_ERR(st->gpio_frstdata);
st->gpio_os = devm_gpiod_get_array_optional(dev, "oversampling-ratio",
GPIOD_OUT_LOW);
return PTR_ERR_OR_ZERO(st->gpio_os);
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp |
lars-peter clausen | lars-peter clausen | 87 | 45.55% | 1 | 25.00% |
michael hennerich | michael hennerich | 70 | 36.65% | 1 | 25.00% |
jonathan cameron | jonathan cameron | 33 | 17.28% | 1 | 25.00% |
fengguang wu | fengguang wu | 1 | 0.52% | 1 | 25.00% |
| Total | 191 | 100.00% | 4 | 100.00% |
/**
* Interrupt handler
*/
static irqreturn_t ad7606_interrupt(int irq, void *dev_id)
{
struct iio_dev *indio_dev = dev_id;
struct ad7606_state *st = iio_priv(indio_dev);
if (iio_buffer_enabled(indio_dev)) {
schedule_work(&st->poll_work);
} else {
st->done = true;
wake_up_interruptible(&st->wq_data_avail);
}
return IRQ_HANDLED;
}Contributors
| Person | Tokens | Prop | Commits | CommitProp |
michael hennerich | michael hennerich | 66 | 98.51% | 2 | 66.67% |
jonathan cameron | jonathan cameron | 1 | 1.49% | 1 | 33.33% |
| Total | 67 | 100.00% | 3 | 100.00% |
;
static const struct iio_info ad7606_info_no_os_or_range = {
.driver_module = THIS_MODULE,
.read_raw = &ad7606_read_raw,
};
static const struct iio_info ad7606_info_os_and_range = {
.driver_module = THIS_MODULE,
.read_raw = &ad7606_read_raw,
.write_raw = &ad7606_write_raw,
.attrs = &ad7606_attribute_group_os_and_range,
};
static const struct iio_info ad7606_info_os = {
.driver_module = THIS_MODULE,
.read_raw = &ad7606_read_raw,
.write_raw = &ad7606_write_raw,
.attrs = &ad7606_attribute_group_os,
};
static const struct iio_info ad7606_info_range = {
.driver_module = THIS_MODULE,
.read_raw = &ad7606_read_raw,
.attrs = &ad7606_attribute_group_range,
};
int ad7606_probe(struct device *dev, int irq, void __iomem *base_address,
const char *name, unsigned int id,
const struct ad7606_bus_ops *bops)
{
struct ad7606_state *st;
int ret;
struct iio_dev *indio_dev;
indio_dev = devm_iio_device_alloc(dev, sizeof(*st));
if (!indio_dev)
return -ENOMEM;
st = iio_priv(indio_dev);
st->dev = dev;
st->bops = bops;
st->base_address = base_address;
st->range = 5000;
st->oversampling = 1;
INIT_WORK(&st->poll_work, &ad7606_poll_bh_to_ring);
st->reg = devm_regulator_get(dev, "avcc");
if (IS_ERR(st->reg))
return PTR_ERR(st->reg);
ret = regulator_enable(st->reg);
if (ret) {
dev_err(dev, "Failed to enable specified AVcc supply\n");
return ret;
}
ret = ad7606_request_gpios(st);
if (ret)
goto error_disable_reg;
st->chip_info = &ad7606_chip_info_tbl[id];
indio_dev->dev.parent = dev;
if (st->gpio_os) {
if (st->gpio_range)
indio_dev->info = &ad7606_info_os_and_range;
else
indio_dev->info = &ad7606_info_os;
} else {
if (st->gpio_range)
indio_dev->info = &ad7606_info_range;
else
indio_dev->info = &ad7606_info_no_os_or_range;
}
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->name = name;
indio_dev->channels = st->chip_info->channels;
indio_dev->num_channels = st->chip_info->num_channels;
init_waitqueue_head(&st->wq_data_avail);
ret = ad7606_reset(st);
if (ret)
dev_warn(st->dev, "failed to RESET: no RESET GPIO specified\n");
ret = request_irq(irq, ad7606_interrupt, IRQF_TRIGGER_FALLING, name,
indio_dev);
if (ret)
goto error_disable_reg;
ret = iio_triggered_buffer_setup(indio_dev, &ad7606_trigger_handler,
NULL, NULL);
if (ret)
goto error_free_irq;
ret = iio_device_register(indio_dev);
if (ret)
goto error_unregister_ring;
dev_set_drvdata(dev, indio_dev);
return 0;
error_unregister_ring:
iio_triggered_buffer_cleanup(indio_dev);
error_free_irq:
free_irq(irq, indio_dev);
error_disable_reg:
regulator_disable(st->reg);
return ret;
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp |
michael hennerich | michael hennerich | 271 | 66.26% | 3 | 17.65% |
jonathan cameron | jonathan cameron | 57 | 13.94% | 4 | 23.53% |
lars-peter clausen | lars-peter clausen | 49 | 11.98% | 6 | 35.29% |
eva rachel retuya | eva rachel retuya | 17 | 4.16% | 2 | 11.76% |
sachin kamat | sachin kamat | 14 | 3.42% | 1 | 5.88% |
alison schofield | alison schofield | 1 | 0.24% | 1 | 5.88% |
| Total | 409 | 100.00% | 17 | 100.00% |
EXPORT_SYMBOL_GPL(ad7606_probe);
int ad7606_remove(struct device *dev, int irq)
{
struct iio_dev *indio_dev = dev_get_drvdata(dev);
struct ad7606_state *st = iio_priv(indio_dev);
iio_device_unregister(indio_dev);
iio_triggered_buffer_cleanup(indio_dev);
free_irq(irq, indio_dev);
regulator_disable(st->reg);
return 0;
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp |
michael hennerich | michael hennerich | 40 | 66.67% | 2 | 33.33% |
lars-peter clausen | lars-peter clausen | 13 | 21.67% | 2 | 33.33% |
jonathan cameron | jonathan cameron | 7 | 11.67% | 2 | 33.33% |
| Total | 60 | 100.00% | 6 | 100.00% |
EXPORT_SYMBOL_GPL(ad7606_remove);
#ifdef CONFIG_PM_SLEEP
static int ad7606_suspend(struct device *dev)
{
struct iio_dev *indio_dev = dev_get_drvdata(dev);
struct ad7606_state *st = iio_priv(indio_dev);
if (st->gpio_standby) {
gpiod_set_value(st->gpio_range, 1);
gpiod_set_value(st->gpio_standby, 0);
}
return 0;
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp |
michael hennerich | michael hennerich | 38 | 63.33% | 2 | 40.00% |
lars-peter clausen | lars-peter clausen | 21 | 35.00% | 2 | 40.00% |
jonathan cameron | jonathan cameron | 1 | 1.67% | 1 | 20.00% |
| Total | 60 | 100.00% | 5 | 100.00% |
static int ad7606_resume(struct device *dev)
{
struct iio_dev *indio_dev = dev_get_drvdata(dev);
struct ad7606_state *st = iio_priv(indio_dev);
if (st->gpio_standby) {
gpiod_set_value(st->gpio_range, st->range == 10000);
gpiod_set_value(st->gpio_standby, 1);
ad7606_reset(st);
}
return 0;
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp |
michael hennerich | michael hennerich | 47 | 68.12% | 2 | 40.00% |
lars-peter clausen | lars-peter clausen | 21 | 30.43% | 2 | 40.00% |
jonathan cameron | jonathan cameron | 1 | 1.45% | 1 | 20.00% |
| Total | 69 | 100.00% | 5 | 100.00% |
SIMPLE_DEV_PM_OPS(ad7606_pm_ops, ad7606_suspend, ad7606_resume);
EXPORT_SYMBOL_GPL(ad7606_pm_ops);
#endif
MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
MODULE_DESCRIPTION("Analog Devices AD7606 ADC");
MODULE_LICENSE("GPL v2");
Overall Contributors
| Person | Tokens | Prop | Commits | CommitProp |
michael hennerich | michael hennerich | 1343 | 59.37% | 5 | 11.90% |
lars-peter clausen | lars-peter clausen | 498 | 22.02% | 13 | 30.95% |
jonathan cameron | jonathan cameron | 289 | 12.78% | 13 | 30.95% |
eva rachel retuya | eva rachel retuya | 77 | 3.40% | 3 | 7.14% |
michal marek | michal marek | 14 | 0.62% | 1 | 2.38% |
sachin kamat | sachin kamat | 14 | 0.62% | 1 | 2.38% |
aida mynzhasova | aida mynzhasova | 13 | 0.57% | 1 | 2.38% |
alison schofield | alison schofield | 9 | 0.40% | 2 | 4.76% |
paul gortmaker | paul gortmaker | 3 | 0.13% | 1 | 2.38% |
soren brinkmann | soren brinkmann | 1 | 0.04% | 1 | 2.38% |
fengguang wu | fengguang wu | 1 | 0.04% | 1 | 2.38% |
| Total | 2262 | 100.00% | 42 | 100.00% |
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.