two minor comments below > This patch adds support for the Analog Devices AD7787, AD7788, AD7789, AD7790 > and AD7791 Sigma Delta Analog-to-Digital converters. > > Signed-off-by: Lars-Peter Clausen <lars@xxxxxxxxxx> > --- > drivers/iio/adc/Kconfig | 12 + > drivers/iio/adc/Makefile | 1 + > drivers/iio/adc/ad7791.c | 460 ++++++++++++++++++++++++++++++++++ > include/linux/platform_data/ad7791.h | 17 ++ > 4 files changed, 490 insertions(+) > create mode 100644 drivers/iio/adc/ad7791.c > create mode 100644 include/linux/platform_data/ad7791.h > > diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig > index a2c5071..d0ae71e 100644 > --- a/drivers/iio/adc/Kconfig > +++ b/drivers/iio/adc/Kconfig > @@ -18,6 +18,18 @@ config AD7266 > Say yes here to build support for Analog Devices AD7265 and AD7266 > ADCs. > > +config AD7791 > + tristate "Analog Devices AD7791 ADC driver" > + depends on SPI > + select AD_SIGMA_DELTA > + help > + Say yes here to build support for Analog Devices AD7787, AD7788, AD7789, > + AD7790 and AD7791 SPI analog to digital converters (ADC). If unsure, say > + N (but it is safe to say "Y"). > + > + To compile this driver as a module, choose M here: the module will be > + called ad7791. > + > config AT91_ADC > tristate "Atmel AT91 ADC" > depends on ARCH_AT91 > diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile > index 5989356..f187ff6 100644 > --- a/drivers/iio/adc/Makefile > +++ b/drivers/iio/adc/Makefile > @@ -4,4 +4,5 @@ > > obj-$(CONFIG_AD_SIGMA_DELTA) += ad_sigma_delta.o > obj-$(CONFIG_AD7266) += ad7266.o > +obj-$(CONFIG_AD7791) += ad7791.o > obj-$(CONFIG_AT91_ADC) += at91_adc.o > diff --git a/drivers/iio/adc/ad7791.c b/drivers/iio/adc/ad7791.c > new file mode 100644 > index 0000000..56b9ffc > --- /dev/null > +++ b/drivers/iio/adc/ad7791.c > @@ -0,0 +1,460 @@ > +/* > + * AD7787/AD7788/AD7789/AD7790/AD7791 SPI ADC driver > + * > + * Copyright 2012 Analog Devices Inc. > + * Author: Lars-Peter Clausen <lars@xxxxxxxxxx> > + * > + * 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/spi/spi.h> > +#include <linux/regulator/consumer.h> > +#include <linux/err.h> > +#include <linux/sched.h> > +#include <linux/delay.h> > +#include <linux/module.h> > + > +#include <linux/iio/iio.h> > +#include <linux/iio/sysfs.h> > +#include <linux/iio/buffer.h> > +#include <linux/iio/trigger.h> > +#include <linux/iio/trigger_consumer.h> > +#include <linux/iio/triggered_buffer.h> > +#include <linux/iio/adc/ad_sigma_delta.h> > + > +#include <linux/platform_data/ad7791.h> > + > +#define AD7791_REG_COMM 0x0 /* For writes */ > +#define AD7791_REG_STATUS 0x0 /* For reads */ > +#define AD7791_REG_MODE 0x1 > +#define AD7791_REG_FILTER 0x2 > +#define AD7791_REG_DATA 0x3 > + > +#define AD7791_MODE_CONTINUOUS 0x00 > +#define AD7791_MODE_SINGLE 0x02 > +#define AD7791_MODE_POWERDOWN 0x03 > + > +#define AD7791_CH_AIN1P_AIN1N 0x00 > +#define AD7791_CH_AIN2 0x01 > +#define AD7791_CH_AIN1N_AIN1N 0x02 > +#define AD7791_CH_AVDD_MONITOR 0x03 > + > +#define AD7791_FILTER_CLK_DIV_1 (0x0 << 4) > +#define AD7791_FILTER_CLK_DIV_2 (0x1 << 4) > +#define AD7791_FILTER_CLK_DIV_4 (0x2 << 4) > +#define AD7791_FILTER_CLK_DIV_8 (0x3 << 4) > +#define AD7791_FILTER_CLK_MASK (0x3 << 4) > +#define AD7791_FILTER_RATE_120 0x0 > +#define AD7791_FILTER_RATE_100 0x1 > +#define AD7791_FILTER_RATE_33_3 0x2 > +#define AD7791_FILTER_RATE_20 0x3 > +#define AD7791_FILTER_RATE_16_6 0x4 > +#define AD7791_FILTER_RATE_16_7 0x5 > +#define AD7791_FILTER_RATE_13_3 0x6 > +#define AD7791_FILTER_RATE_9_5 0x7 > +#define AD7791_FILTER_RATE_MASK 0x7 > + > +#define AD7791_MODE_BUFFER BIT(1) > +#define AD7791_MODE_UNIPOLAR BIT(2) > +#define AD7791_MODE_BURNOUT BIT(3) > +#define AD7791_MODE_SEL_MASK (0x3 << 6) > +#define AD7791_MODE_SEL(x) ((x) << 6) > + > +#define DECLARE_AD7787_CHANNELS(name, bits, storagebits) \ > +const struct iio_chan_spec name[] = { \ > + AD_SD_DIFF_CHANNEL(0, 0, 0, AD7791_CH_AIN1P_AIN1N, \ > + (bits), (storagebits), 0), \ > + AD_SD_CHANNEL(1, 1, AD7791_CH_AIN2, (bits), (storagebits), 0), \ > + AD_SD_SHORTED_CHANNEL(2, 0, AD7791_CH_AIN1N_AIN1N, \ > + (bits), (storagebits), 0), \ > + AD_SD_SUPPLY_CHANNEL(3, 2, AD7791_CH_AVDD_MONITOR, \ > + (bits), (storagebits), 0), \ > + IIO_CHAN_SOFT_TIMESTAMP(4), \ > +} > + > +#define DECLARE_AD7791_CHANNELS(name, bits, storagebits) \ > +const struct iio_chan_spec name[] = { \ > + AD_SD_DIFF_CHANNEL(0, 0, 0, AD7791_CH_AIN1P_AIN1N, \ > + (bits), (storagebits), 0), \ > + AD_SD_SHORTED_CHANNEL(1, 0, AD7791_CH_AIN1N_AIN1N, \ > + (bits), (storagebits), 0), \ > + AD_SD_SUPPLY_CHANNEL(2, 1, AD7791_CH_AVDD_MONITOR, \ > + (bits), (storagebits), 0), \ > + IIO_CHAN_SOFT_TIMESTAMP(3), \ > +} > + > +static DECLARE_AD7787_CHANNELS(ad7787_channels, 24, 32); > +static DECLARE_AD7791_CHANNELS(ad7790_channels, 16, 16); > +static DECLARE_AD7791_CHANNELS(ad7791_channels, 24, 32); > + > +enum { > + AD7787, > + AD7788, > + AD7789, > + AD7790, > + AD7791, > +}; > + > +enum ad7791_chip_info_flags { > + AD7791_FLAG_HAS_FILTER = (1 << 0), > + AD7791_FLAG_HAS_BUFFER = (1 << 1), > + AD7791_FLAG_HAS_UNIPOLAR = (1 << 2), > + AD7791_FLAG_HAS_BURNOUT = (1 << 3), > +}; > + > +struct ad7791_chip_info { > + const struct iio_chan_spec *channels; > + unsigned int num_channels; > + enum ad7791_chip_info_flags flags; > +}; > + > +static const struct ad7791_chip_info ad7791_chip_infos[] = { > + [AD7787] = { > + .channels = ad7787_channels, > + .num_channels = ARRAY_SIZE(ad7787_channels), > + .flags = AD7791_FLAG_HAS_FILTER | AD7791_FLAG_HAS_BUFFER | > + AD7791_FLAG_HAS_UNIPOLAR | AD7791_FLAG_HAS_BURNOUT, > + }, > + [AD7788] = { > + .channels = ad7790_channels, > + .num_channels = ARRAY_SIZE(ad7790_channels), > + .flags = AD7791_FLAG_HAS_UNIPOLAR, > + }, > + [AD7789] = { > + .channels = ad7791_channels, > + .num_channels = ARRAY_SIZE(ad7791_channels), > + .flags = AD7791_FLAG_HAS_UNIPOLAR, > + }, > + [AD7790] = { > + .channels = ad7790_channels, > + .num_channels = ARRAY_SIZE(ad7790_channels), > + .flags = AD7791_FLAG_HAS_FILTER | AD7791_FLAG_HAS_BUFFER | > + AD7791_FLAG_HAS_BURNOUT, > + }, > + [AD7791] = { > + .channels = ad7791_channels, > + .num_channels = ARRAY_SIZE(ad7791_channels), > + .flags = AD7791_FLAG_HAS_FILTER | AD7791_FLAG_HAS_BUFFER | > + AD7791_FLAG_HAS_UNIPOLAR | AD7791_FLAG_HAS_BURNOUT, > + }, > +}; > + > +struct ad7791_state { > + struct ad_sigma_delta sd; > + uint8_t mode; > + uint8_t filter; > + > + struct regulator *reg; > + const struct ad7791_chip_info *info; > +}; > + > +static struct ad7791_state *ad_sigma_delta_to_ad7791(struct ad_sigma_delta *sd) > +{ > + return container_of(sd, struct ad7791_state, sd); > +} > + > +static int ad7791_set_channel(struct ad_sigma_delta *sd, unsigned int channel) > +{ > + ad_sd_set_comm(sd, channel); > + > + return 0; > +} > + > +static int ad7791_set_mode(struct ad_sigma_delta *sd, unsigned int mode) > +{ > + struct ad7791_state *st = ad_sigma_delta_to_ad7791(sd); > + > + switch (mode) { > + case AD_SD_MODE_CONTINUOUS: > + mode = AD7791_MODE_CONTINUOUS; > + break; > + case AD_SD_MODE_SINGLE: > + mode = AD7791_MODE_SINGLE; > + break; > + case AD_SD_MODE_IDLE: > + case AD_SD_MODE_POWERDOWN: > + mode = AD7791_MODE_POWERDOWN; > + break; > + } > + > + st->mode &= ~AD7791_MODE_SEL_MASK; > + st->mode |= AD7791_MODE_SEL(mode); > + > + return ad_sd_write_reg(sd, AD7791_REG_MODE, sizeof(st->mode), st->mode); > +} > + > +static const struct ad_sigma_delta_info ad7791_sigma_delta_info = { > + .set_channel = ad7791_set_channel, > + .set_mode = ad7791_set_mode, > + .has_registers = true, > + .addr_shift = 4, > + .read_mask = BIT(3), > +}; > + > +static int ad7791_read_raw(struct iio_dev *indio_dev, > + const struct iio_chan_spec *chan, int *val, int *val2, long info) > +{ > + struct ad7791_state *st = iio_priv(indio_dev); > + unsigned long long scale_nv; > + bool unipolar = !!(st->mode & AD7791_MODE_UNIPOLAR); > + > + switch (info) { > + case IIO_CHAN_INFO_RAW: > + return ad_sigma_delta_single_conversion(indio_dev, chan, val); > + case IIO_CHAN_INFO_OFFSET: > + /** > + * Unipolar: 0 to VREF > + * Bipolar -VREF to VREF > + **/ > + if (unipolar) > + *val = 0; > + else > + *val = -(1 << (chan->scan_type.realbits - 1)); > + return IIO_VAL_INT; > + case IIO_CHAN_INFO_SCALE: > + /* The monitor channel uses an internal reference. */ > + if (chan->address == AD7791_CH_AVDD_MONITOR) { > + scale_nv = 5850000000ULL; > + } else { > + int voltage_uv; > + > + voltage_uv = regulator_get_voltage(st->reg); > + if (voltage_uv < 0) > + return voltage_uv; > + scale_nv = voltage_uv * 1000; > + } > + if (unipolar) > + scale_nv >>= chan->scan_type.realbits; > + else > + scale_nv >>= chan->scan_type.realbits - 1; > + *val2 = do_div(scale_nv, 1000000); > + *val = scale_nv; > + > + return IIO_VAL_INT_PLUS_NANO; > + } > + > + return -EINVAL; > +} > + > +static const char * const ad7791_sample_freq_avail[] = { > + [AD7791_FILTER_RATE_120] = "120", > + [AD7791_FILTER_RATE_100] = "100", > + [AD7791_FILTER_RATE_33_3] = "33.3", > + [AD7791_FILTER_RATE_20] = "20", > + [AD7791_FILTER_RATE_16_6] = "16.6", > + [AD7791_FILTER_RATE_16_7] = "16.7", just curious; why is this not in strictly decreasing order? IIO_CONST_ATTR_SAMP_FREQ_AVAIL is... > + [AD7791_FILTER_RATE_13_3] = "13.3", > + [AD7791_FILTER_RATE_9_5] = "9.5", > +}; > + > +static ssize_t ad7791_read_frequency(struct device *dev, > + struct device_attribute *attr, char *buf) > +{ > + struct iio_dev *indio_dev = dev_to_iio_dev(dev); > + struct ad7791_state *st = iio_priv(indio_dev); > + unsigned int rate = st->filter & AD7791_FILTER_RATE_MASK; > + > + return sprintf(buf, "%s\n", ad7791_sample_freq_avail[rate]); > +} > + > +static ssize_t ad7791_write_frequency(struct device *dev, > + struct device_attribute *attr, const char *buf, size_t len) > +{ > + struct iio_dev *indio_dev = dev_to_iio_dev(dev); > + struct ad7791_state *st = iio_priv(indio_dev); > + int i, ret; > + > + mutex_lock(&indio_dev->mlock); > + if (iio_buffer_enabled(indio_dev)) { > + mutex_unlock(&indio_dev->mlock); > + return -EBUSY; > + } > + mutex_unlock(&indio_dev->mlock); > + > + ret = -EINVAL; > + > + for (i = 0; i < ARRAY_SIZE(ad7791_sample_freq_avail); i++) { > + if (sysfs_streq(ad7791_sample_freq_avail[i], buf)) { > + > + mutex_lock(&indio_dev->mlock); > + st->filter &= ~AD7791_FILTER_RATE_MASK; > + st->filter |= i; > + ad_sd_write_reg(&st->sd, AD7791_REG_FILTER, > + sizeof(st->filter), st->filter); > + mutex_unlock(&indio_dev->mlock); > + ret = 0; > + break; > + } > + } > + > + return ret ? ret : len; > +} > + > +static IIO_DEV_ATTR_SAMP_FREQ(S_IWUSR | S_IRUGO, > + ad7791_read_frequency, > + ad7791_write_frequency); > + > +static IIO_CONST_ATTR_SAMP_FREQ_AVAIL("120 100 33.3 20 16.7 16.6 13.3 9.5"); > + > +static struct attribute *ad7791_attributes[] = { > + &iio_dev_attr_sampling_frequency.dev_attr.attr, > + &iio_const_attr_sampling_frequency_available.dev_attr.attr, > + NULL > +}; > + > +static const struct attribute_group ad7791_attribute_group = { > + .attrs = ad7791_attributes, > +}; > + > +static const struct iio_info ad7791_info = { > + .read_raw = &ad7791_read_raw, > + .attrs = &ad7791_attribute_group, > + .validate_trigger = ad_sd_validate_trigger, > + .driver_module = THIS_MODULE, > +}; > + > +static const struct iio_info ad7791_no_filter_info = { > + .read_raw = &ad7791_read_raw, > + .validate_trigger = ad_sd_validate_trigger, > + .driver_module = THIS_MODULE, > +}; > + > +static int __devinit ad7791_setup(struct ad7791_state *st, > + struct ad7791_platform_data *pdata) > +{ > + /* Set to poweron-reset default values */ > + st->mode = AD7791_MODE_BUFFER; > + st->filter = AD7791_FILTER_RATE_16_6; > + > + if (!pdata) > + return 0; > + > + if ((st->info->flags & AD7791_FLAG_HAS_BUFFER) && !pdata->buffered) there's one extra space before !pdata->buffered > + st->mode &= ~AD7791_MODE_BUFFER; > + > + if ((st->info->flags & AD7791_FLAG_HAS_BURNOUT) && > + pdata->burnout_current) > + st->mode |= AD7791_MODE_BURNOUT; > + > + if ((st->info->flags & AD7791_FLAG_HAS_UNIPOLAR) && pdata->unipolar) > + st->mode |= AD7791_MODE_UNIPOLAR; > + > + return ad_sd_write_reg(&st->sd, AD7791_REG_MODE, sizeof(st->mode), > + st->mode); > +} > + > +static int __devinit ad7791_probe(struct spi_device *spi) > +{ > + struct ad7791_platform_data *pdata = spi->dev.platform_data; > + struct iio_dev *indio_dev; > + struct ad7791_state *st; > + int ret; > + > + if (!spi->irq) { > + dev_err(&spi->dev, "Missing IRQ.\n"); > + return -ENXIO; > + } > + > + indio_dev = iio_device_alloc(sizeof(*st)); > + if (!indio_dev) > + return -ENOMEM; > + > + st = iio_priv(indio_dev); > + > + st->reg = regulator_get(&spi->dev, "refin"); > + if (IS_ERR(st->reg)) { > + ret = PTR_ERR(st->reg); > + goto err_iio_free; > + } > + > + ret = regulator_enable(st->reg); > + if (ret) > + goto error_put_reg; > + > + > + st->info = &ad7791_chip_infos[spi_get_device_id(spi)->driver_data]; > + ad_sd_init(&st->sd, indio_dev, spi, &ad7791_sigma_delta_info); > + > + spi_set_drvdata(spi, indio_dev); > + > + indio_dev->dev.parent = &spi->dev; > + indio_dev->name = spi_get_device_id(spi)->name; > + indio_dev->modes = INDIO_DIRECT_MODE; > + indio_dev->channels = st->info->channels; > + indio_dev->num_channels = st->info->num_channels; > + if (st->info->flags & AD7791_FLAG_HAS_FILTER) > + indio_dev->info = &ad7791_info; > + else > + indio_dev->info = &ad7791_no_filter_info; > + > + ret = ad_sd_setup_buffer_and_trigger(indio_dev); > + if (ret) > + goto error_disable_reg; > + > + ret = ad7791_setup(st, pdata); > + if (ret) > + goto error_remove_trigger; > + > + ret = iio_device_register(indio_dev); > + if (ret) > + goto error_remove_trigger; > + > + return 0; > + > +error_remove_trigger: > + ad_sd_cleanup_buffer_and_trigger(indio_dev); > +error_disable_reg: > + regulator_disable(st->reg); > +error_put_reg: > + regulator_put(st->reg); > +err_iio_free: > + iio_device_free(indio_dev); > + > + return ret; > +} > + > +static int __devexit ad7791_remove(struct spi_device *spi) > +{ > + struct iio_dev *indio_dev = spi_get_drvdata(spi); > + struct ad7791_state *st = iio_priv(indio_dev); > + > + iio_device_unregister(indio_dev); > + ad_sd_cleanup_buffer_and_trigger(indio_dev); > + > + regulator_disable(st->reg); > + regulator_put(st->reg); > + > + iio_device_free(indio_dev); > + > + return 0; > +} > + > +static const struct spi_device_id ad7791_spi_ids[] = { > + { "ad7787", AD7787 }, > + { "ad7788", AD7788 }, > + { "ad7789", AD7789 }, > + { "ad7790", AD7790 }, > + { "ad7791", AD7791 }, > + {} > +}; > +MODULE_DEVICE_TABLE(spi, ad7791_spi_ids); > + > +static struct spi_driver ad7791_driver = { > + .driver = { > + .name = "ad7791", > + .owner = THIS_MODULE, > + }, > + .probe = ad7791_probe, > + .remove = __devexit_p(ad7791_remove), > + .id_table = ad7791_spi_ids, > +}; > +module_spi_driver(ad7791_driver); > + > +MODULE_AUTHOR("Lars-Peter Clausen <lars@xxxxxxxxxx>"); > +MODULE_DESCRIPTION("Analog Device AD7787/AD7788/AD7789/AD7790/AD7791 ADC driver"); > +MODULE_LICENSE("GPL v2"); > diff --git a/include/linux/platform_data/ad7791.h b/include/linux/platform_data/ad7791.h > new file mode 100644 > index 0000000..317881f > --- /dev/null > +++ b/include/linux/platform_data/ad7791.h > @@ -0,0 +1,17 @@ > +#ifndef __LINUX_PLATFORM_DATA_AD7791__ > +#define __LINUX_PLATFORM_DATA_AD7791__ > + > +/** > + * struct ad7791_platform_data - AD7791 device platform data > + * @buffered: If set to true configure the device for buffered input mode. > + * @burnount_current: If set to true the 100mA burnout current is enabled. burnout_current > + * @unipolar: If set to true sample in unipolar mode, if set to false sample in > + * bipolar mode. > + */ > +struct ad7791_platform_data { > + bool buffered; > + bool burnout_current; > + bool unipolar; > +}; > + > +#endif > -- Peter Meerwald +43-664-2444418 (mobile) -- To unsubscribe from this list: send the line "unsubscribe linux-iio" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html