diff options
Diffstat (limited to 'drivers/iio/amplifiers')
| -rw-r--r-- | drivers/iio/amplifiers/Kconfig | 19 | ||||
| -rw-r--r-- | drivers/iio/amplifiers/Makefile | 6 | ||||
| -rw-r--r-- | drivers/iio/amplifiers/ad8366.c | 213 | 
3 files changed, 238 insertions, 0 deletions
diff --git a/drivers/iio/amplifiers/Kconfig b/drivers/iio/amplifiers/Kconfig new file mode 100644 index 00000000000..e9c5f2cd925 --- /dev/null +++ b/drivers/iio/amplifiers/Kconfig @@ -0,0 +1,19 @@ +# +# Gain Amplifiers, etc. +# +# When adding new entries keep the list in alphabetical order + +menu "Amplifiers" + +config AD8366 +	tristate "Analog Devices AD8366 VGA" +	depends on SPI +	select BITREVERSE +	help +	  Say yes here to build support for Analog Devices AD8366 +	  SPI Dual-Digital Variable Gain Amplifier (VGA). + +	  To compile this driver as a module, choose M here: the +	  module will be called ad8366. + +endmenu diff --git a/drivers/iio/amplifiers/Makefile b/drivers/iio/amplifiers/Makefile new file mode 100644 index 00000000000..8da4b787898 --- /dev/null +++ b/drivers/iio/amplifiers/Makefile @@ -0,0 +1,6 @@ +# +# Makefile iio/amplifiers +# + +# When adding new entries keep the list in alphabetical order +obj-$(CONFIG_AD8366) += ad8366.o diff --git a/drivers/iio/amplifiers/ad8366.c b/drivers/iio/amplifiers/ad8366.c new file mode 100644 index 00000000000..ba6f6a91dff --- /dev/null +++ b/drivers/iio/amplifiers/ad8366.c @@ -0,0 +1,213 @@ +/* + * AD8366 SPI Dual-Digital Variable Gain Amplifier (VGA) + * + * Copyright 2012 Analog Devices Inc. + * + * Licensed under the GPL-2. + */ + +#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/module.h> +#include <linux/bitrev.h> + +#include <linux/iio/iio.h> +#include <linux/iio/sysfs.h> + +struct ad8366_state { +	struct spi_device	*spi; +	struct regulator	*reg; +	unsigned char		ch[2]; +	/* +	 * DMA (thus cache coherency maintenance) requires the +	 * transfer buffers to live in their own cache lines. +	 */ +	unsigned char		data[2] ____cacheline_aligned; +}; + +static int ad8366_write(struct iio_dev *indio_dev, +			unsigned char ch_a, char unsigned ch_b) +{ +	struct ad8366_state *st = iio_priv(indio_dev); +	int ret; + +	ch_a = bitrev8(ch_a & 0x3F); +	ch_b = bitrev8(ch_b & 0x3F); + +	st->data[0] = ch_b >> 4; +	st->data[1] = (ch_b << 4) | (ch_a >> 2); + +	ret = spi_write(st->spi, st->data, ARRAY_SIZE(st->data)); +	if (ret < 0) +		dev_err(&indio_dev->dev, "write failed (%d)", ret); + +	return ret; +} + +static int ad8366_read_raw(struct iio_dev *indio_dev, +			   struct iio_chan_spec const *chan, +			   int *val, +			   int *val2, +			   long m) +{ +	struct ad8366_state *st = iio_priv(indio_dev); +	int ret; +	unsigned code; + +	mutex_lock(&indio_dev->mlock); +	switch (m) { +	case IIO_CHAN_INFO_HARDWAREGAIN: +		code = st->ch[chan->channel]; + +		/* Values in dB */ +		code = code * 253 + 4500; +		*val = code / 1000; +		*val2 = (code % 1000) * 1000; + +		ret = IIO_VAL_INT_PLUS_MICRO_DB; +		break; +	default: +		ret = -EINVAL; +	} +	mutex_unlock(&indio_dev->mlock); + +	return ret; +}; + +static int ad8366_write_raw(struct iio_dev *indio_dev, +			    struct iio_chan_spec const *chan, +			    int val, +			    int val2, +			    long mask) +{ +	struct ad8366_state *st = iio_priv(indio_dev); +	unsigned code; +	int ret; + +	if (val < 0 || val2 < 0) +		return -EINVAL; + +	/* Values in dB */ +	code = (((u8)val * 1000) + ((u32)val2 / 1000)); + +	if (code > 20500 || code < 4500) +		return -EINVAL; + +	code = (code - 4500) / 253; + +	mutex_lock(&indio_dev->mlock); +	switch (mask) { +	case IIO_CHAN_INFO_HARDWAREGAIN: +		st->ch[chan->channel] = code; +		ret = ad8366_write(indio_dev, st->ch[0], st->ch[1]); +		break; +	default: +		ret = -EINVAL; +	} +	mutex_unlock(&indio_dev->mlock); + +	return ret; +} + +static const struct iio_info ad8366_info = { +	.read_raw = &ad8366_read_raw, +	.write_raw = &ad8366_write_raw, +	.driver_module = THIS_MODULE, +}; + +#define AD8366_CHAN(_channel) {				\ +	.type = IIO_VOLTAGE,				\ +	.output = 1,					\ +	.indexed = 1,					\ +	.channel = _channel,				\ +	.info_mask_separate = BIT(IIO_CHAN_INFO_HARDWAREGAIN),\ +} + +static const struct iio_chan_spec ad8366_channels[] = { +	AD8366_CHAN(0), +	AD8366_CHAN(1), +}; + +static int ad8366_probe(struct spi_device *spi) +{ +	struct iio_dev *indio_dev; +	struct ad8366_state *st; +	int ret; + +	indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); +	if (indio_dev == NULL) +		return -ENOMEM; + +	st = iio_priv(indio_dev); + +	st->reg = devm_regulator_get(&spi->dev, "vcc"); +	if (!IS_ERR(st->reg)) { +		ret = regulator_enable(st->reg); +		if (ret) +			return ret; +	} + +	spi_set_drvdata(spi, indio_dev); +	st->spi = spi; + +	indio_dev->dev.parent = &spi->dev; +	indio_dev->name = spi_get_device_id(spi)->name; +	indio_dev->info = &ad8366_info; +	indio_dev->modes = INDIO_DIRECT_MODE; +	indio_dev->channels = ad8366_channels; +	indio_dev->num_channels = ARRAY_SIZE(ad8366_channels); + +	ret = iio_device_register(indio_dev); +	if (ret) +		goto error_disable_reg; + +	ad8366_write(indio_dev, 0 , 0); + +	return 0; + +error_disable_reg: +	if (!IS_ERR(st->reg)) +		regulator_disable(st->reg); + +	return ret; +} + +static int ad8366_remove(struct spi_device *spi) +{ +	struct iio_dev *indio_dev = spi_get_drvdata(spi); +	struct ad8366_state *st = iio_priv(indio_dev); +	struct regulator *reg = st->reg; + +	iio_device_unregister(indio_dev); + +	if (!IS_ERR(reg)) +		regulator_disable(reg); + +	return 0; +} + +static const struct spi_device_id ad8366_id[] = { +	{"ad8366", 0}, +	{} +}; + +static struct spi_driver ad8366_driver = { +	.driver = { +		.name	= KBUILD_MODNAME, +		.owner	= THIS_MODULE, +	}, +	.probe		= ad8366_probe, +	.remove		= ad8366_remove, +	.id_table	= ad8366_id, +}; + +module_spi_driver(ad8366_driver); + +MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>"); +MODULE_DESCRIPTION("Analog Devices AD8366 VGA"); +MODULE_LICENSE("GPL v2");  | 
