linux-iio.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH] staging:iio:dac: Add AD5764 driver
@ 2011-11-21  8:53 Lars-Peter Clausen
  2011-11-21  9:04 ` Shubhrajyoti Datta
  0 siblings, 1 reply; 4+ messages in thread
From: Lars-Peter Clausen @ 2011-11-21  8:53 UTC (permalink / raw)
  To: Jonathan Cameron
  Cc: Michael Hennerich, linux-iio, device-drivers-devel, drivers,
	Lars-Peter Clausen

This patch adds support for the Analog Devices AD5764, AD5764R, AD5744, AD5744R
quad channel analog-to-digital converter.

Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
---
 drivers/staging/iio/dac/Kconfig  |   10 +
 drivers/staging/iio/dac/Makefile |    1 +
 drivers/staging/iio/dac/ad5764.c |  394 ++++++++++++++++++++++++++++++++++++++
 3 files changed, 405 insertions(+), 0 deletions(-)
 create mode 100644 drivers/staging/iio/dac/ad5764.c

diff --git a/drivers/staging/iio/dac/Kconfig b/drivers/staging/iio/dac/Kconfig
index 0e8a814..bbc33dd 100644
--- a/drivers/staging/iio/dac/Kconfig
+++ b/drivers/staging/iio/dac/Kconfig
@@ -75,6 +75,16 @@ config AD5504
 	  To compile this driver as a module, choose M here: the
 	  module will be called ad5504.
 
+config AD5764
+	tristate "Analog Devices AD5764/64R/44/44R DAC driver"
+	depends on SPI_MASTER
+	help
+	  Say yes here to build support for Analog Devices AD5764, AD5764R, AD5744,
+	  AD5744R Digital to Analog Converter.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called ad5764.
+
 config AD5791
 	tristate "Analog Devices AD5760/AD5780/AD5781/AD5791 DAC SPI driver"
 	depends on SPI
diff --git a/drivers/staging/iio/dac/Makefile b/drivers/staging/iio/dac/Makefile
index 840e94e..8ab1d26 100644
--- a/drivers/staging/iio/dac/Makefile
+++ b/drivers/staging/iio/dac/Makefile
@@ -9,6 +9,7 @@ obj-$(CONFIG_AD5624R_SPI) += ad5624r_spi.o
 obj-$(CONFIG_AD5064) += ad5064.o
 obj-$(CONFIG_AD5504) += ad5504.o
 obj-$(CONFIG_AD5446) += ad5446.o
+obj-$(CONFIG_AD5764) += ad5764.o
 obj-$(CONFIG_AD5791) += ad5791.o
 obj-$(CONFIG_AD5686) += ad5686.o
 obj-$(CONFIG_MAX517) += max517.o
diff --git a/drivers/staging/iio/dac/ad5764.c b/drivers/staging/iio/dac/ad5764.c
new file mode 100644
index 0000000..50c1ec62
--- /dev/null
+++ b/drivers/staging/iio/dac/ad5764.c
@@ -0,0 +1,394 @@
+/*
+ * Analog devices AD5764, AD5764R, AD5744, AD5744R quad-channel
+ * Digital to Analog Converters driver
+ *
+ * Copyright 2011 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/spi/spi.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+#include <linux/regulator/consumer.h>
+
+#include "../iio.h"
+#include "../sysfs.h"
+#include "dac.h"
+
+#define AD5764_REG_SF_NOP			0x0
+#define AD5764_REG_SF_CONFIG			0x1
+#define AD5764_REG_SF_CLEAR			0x4
+#define AD5764_REG_SF_LOAD			0x5
+#define AD5764_REG_DATA(x)			((2 << 3) | (x))
+#define AD5764_REG_COARSE_GAIN(x)		((3 << 3) | (x))
+#define AD5764_REG_FINE_GAIN(x)			((4 << 3) | (x))
+#define AD5764_REG_OFFSET(x)			((5 << 3) | (x))
+
+#define AD5764_NUM_CHANNELS 4
+
+/**
+ * struct ad5764_chip_info - chip specific information
+ * @int_vref:	Value of the internal reference voltage in uV - 0 if external
+ *		reference voltage is used
+ * @channel	channel specification
+*/
+
+struct ad5764_chip_info {
+	unsigned long int_vref;
+	const struct iio_chan_spec *channels;
+};
+
+/**
+ * struct ad5764_state - driver instance specific data
+ * @spi:		spi_device
+ * @chip_info:		chip info
+ * @vref_reg:		vref supply regulators
+ * @data:		spi transfer buffers
+ */
+
+struct ad5764_state {
+	struct spi_device		*spi;
+	const struct ad5764_chip_info	*chip_info;
+	struct regulator_bulk_data	vref_reg[2];
+
+	/*
+	 * DMA (thus cache coherency maintenance) requires the
+	 * transfer buffers to live in their own cache lines.
+	 */
+	union {
+		__be32 d32;
+		u8 d8[4];
+	} data[2] ____cacheline_aligned;
+};
+
+enum ad5764_type {
+	ID_AD5744,
+	ID_AD5744R,
+	ID_AD5764,
+	ID_AD5764R,
+};
+
+#define AD5764_CHANNEL(_chan, _bits) {				\
+	.type = IIO_VOLTAGE,					\
+	.indexed = 1,						\
+	.output = 1,						\
+	.channel = (_chan),					\
+	.address = (_chan),					\
+	.info_mask = IIO_CHAN_INFO_OFFSET_SHARED_BIT |		\
+		IIO_CHAN_INFO_SCALE_SEPARATE_BIT |		\
+		IIO_CHAN_INFO_CALIBSCALE_SEPARATE_BIT |		\
+		IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT,		\
+	.scan_type = IIO_ST('u', (_bits), 16, 16 - (_bits))	\
+}
+
+#define DECLARE_AD5764_CHANNELS(_name, _bits) \
+const struct iio_chan_spec _name##_channels[] = { \
+	AD5764_CHANNEL(0, (_bits)), \
+	AD5764_CHANNEL(1, (_bits)), \
+	AD5764_CHANNEL(2, (_bits)), \
+	AD5764_CHANNEL(3, (_bits)), \
+};
+
+static DECLARE_AD5764_CHANNELS(ad5764, 16);
+static DECLARE_AD5764_CHANNELS(ad5744, 14);
+
+static const struct ad5764_chip_info ad5764_chip_infos[] = {
+	[ID_AD5744] = {
+		.int_vref = 0,
+		.channels = ad5744_channels,
+	},
+	[ID_AD5744R] = {
+		.int_vref = 5000000,
+		.channels = ad5744_channels,
+	},
+	[ID_AD5764] = {
+		.int_vref = 0,
+		.channels = ad5764_channels,
+	},
+	[ID_AD5764R] = {
+		.int_vref = 5000000,
+		.channels = ad5764_channels,
+	},
+};
+
+static int ad5764_write(struct iio_dev *indio_dev, unsigned int reg,
+	unsigned int val)
+{
+	struct ad5764_state *st = iio_priv(indio_dev);
+	int ret;
+
+	mutex_lock(&indio_dev->mlock);
+	st->data[0].d32 = cpu_to_be32((reg << 16) | val);
+
+	ret = spi_write(st->spi, &st->data[0].d8[1], 3);
+	mutex_unlock(&indio_dev->mlock);
+
+	return ret;
+}
+
+static int ad5764_read(struct iio_dev *indio_dev, unsigned int reg,
+	unsigned int *val)
+{
+	struct ad5764_state *st = iio_priv(indio_dev);
+	struct spi_message m;
+	int ret;
+	struct spi_transfer t[] = {
+		{
+			.tx_buf = &st->data[0].d8[1],
+			.len = 3,
+			.cs_change = 1,
+		}, {
+			.rx_buf = &st->data[1].d8[1],
+			.len = 3,
+		},
+	};
+
+	spi_message_init(&m);
+	spi_message_add_tail(&t[0], &m);
+	spi_message_add_tail(&t[1], &m);
+
+	mutex_lock(&indio_dev->mlock);
+
+	st->data[0].d32 = cpu_to_be32((1 << 23) | (reg << 16));
+
+	ret = spi_sync(st->spi, &m);
+	if (ret >= 0)
+		*val = be32_to_cpu(st->data[1].d32) & 0xffff;
+
+	mutex_unlock(&indio_dev->mlock);
+
+	return ret;
+}
+
+static int ad5764_chan_info_to_reg(struct iio_chan_spec const *chan, long info)
+{
+	switch (info) {
+	case 0:
+		return AD5764_REG_DATA(chan->address);
+	case IIO_CHAN_INFO_CALIBBIAS:
+		return AD5764_REG_OFFSET(chan->address);
+	case IIO_CHAN_INFO_CALIBSCALE:
+		return AD5764_REG_FINE_GAIN(chan->address);
+	default:
+		BUG();
+		break;
+	}
+
+	return 0;
+}
+
+static int ad5764_write_raw(struct iio_dev *indio_dev,
+	struct iio_chan_spec const *chan, int val, int val2, long info)
+{
+	const int max_val = (1 << chan->scan_type.realbits);
+	unsigned int reg;
+
+	switch (info) {
+	case 0:
+		if (val >= max_val || val < 0)
+			return -EINVAL;
+		val <<= chan->scan_type.shift;
+		break;
+	case IIO_CHAN_INFO_CALIBBIAS:
+		if (val >= 128 || val < -128)
+			return -EINVAL;
+		break;
+	case IIO_CHAN_INFO_CALIBSCALE:
+		if (val >= 32 || val < -32)
+			return -EINVAL;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	reg = ad5764_chan_info_to_reg(chan, info);
+	return ad5764_write(indio_dev, reg, (u16)val);
+}
+
+static int ad5764_get_channel_vref(struct ad5764_state *st,
+	unsigned int channel)
+{
+	if (st->chip_info->int_vref)
+		return st->chip_info->int_vref;
+	else
+		return regulator_get_voltage(st->vref_reg[channel / 2].consumer);
+}
+
+static int ad5764_read_raw(struct iio_dev *indio_dev,
+	struct iio_chan_spec const *chan, int *val, int *val2, long info)
+{
+	struct ad5764_state *st = iio_priv(indio_dev);
+	unsigned long scale_uv;
+	unsigned int reg;
+	int vref;
+	int ret;
+
+	switch (info) {
+	case 0:
+		reg = AD5764_REG_DATA(chan->address);
+		ret = ad5764_read(indio_dev, reg, val);
+		if (ret < 0)
+			return ret;
+		*val >>= chan->scan_type.shift;
+		return IIO_VAL_INT;
+	case IIO_CHAN_INFO_CALIBBIAS:
+		reg = AD5764_REG_OFFSET(chan->address);
+		ret = ad5764_read(indio_dev, reg, val);
+		if (ret < 0)
+			return ret;
+		*val = sign_extend32(*val, 7);
+		return IIO_VAL_INT;
+	case IIO_CHAN_INFO_CALIBSCALE:
+		reg = AD5764_REG_FINE_GAIN(chan->address);
+		ret = ad5764_read(indio_dev, reg, val);
+		if (ret < 0)
+			return ret;
+		*val = sign_extend32(*val, 5);
+		return IIO_VAL_INT;
+	case IIO_CHAN_INFO_SCALE:
+		/* vout = 4 * vref + ((dac_code / 65535) - 0.5) */
+		vref = ad5764_get_channel_vref(st, chan->channel);
+		if (vref < 0)
+			return vref;
+
+		scale_uv = (vref * 4 * 100) >> chan->scan_type.realbits;
+		*val = scale_uv / 100000;
+		*val2 = (scale_uv % 100000) * 10;
+		return IIO_VAL_INT_PLUS_MICRO;
+	case IIO_CHAN_INFO_OFFSET:
+		*val = -(1 << chan->scan_type.realbits) / 2;
+		return IIO_VAL_INT;
+	}
+
+	return -EINVAL;
+}
+
+static const struct iio_info ad5764_info = {
+	.read_raw = ad5764_read_raw,
+	.write_raw = ad5764_write_raw,
+	.driver_module = THIS_MODULE,
+};
+
+static int __devinit ad5764_probe(struct spi_device *spi)
+{
+	enum ad5764_type type = spi_get_device_id(spi)->driver_data;
+	struct iio_dev *indio_dev;
+	struct ad5764_state *st;
+	int ret;
+
+	indio_dev = iio_allocate_device(sizeof(*st));
+	if (indio_dev == NULL) {
+		dev_err(&spi->dev, "Failed to allocate iio device\n");
+		return -ENOMEM;
+	}
+
+	st = iio_priv(indio_dev);
+	spi_set_drvdata(spi, indio_dev);
+
+	st->spi = spi;
+	st->chip_info = &ad5764_chip_infos[type];
+
+	indio_dev->dev.parent = &spi->dev;
+	indio_dev->name = spi_get_device_id(spi)->name;
+	indio_dev->info = &ad5764_info;
+	indio_dev->modes = INDIO_DIRECT_MODE;
+	indio_dev->num_channels = AD5764_NUM_CHANNELS;
+	indio_dev->channels = st->chip_info->channels;
+
+	if (st->chip_info->int_vref == 0) {
+		st->vref_reg[0].supply = "vrefAB";
+		st->vref_reg[1].supply = "vrefCD";
+
+		ret = regulator_bulk_get(&st->spi->dev,
+			ARRAY_SIZE(st->vref_reg), st->vref_reg);
+		if (ret) {
+			dev_err(&spi->dev, "Failed to request vref regulators: %d\n",
+				ret);
+			goto error_free;
+		}
+
+		ret = regulator_bulk_enable(ARRAY_SIZE(st->vref_reg),
+			st->vref_reg);
+		if (ret) {
+			dev_err(&spi->dev, "Failed to enable vref regulators: %d\n",
+				ret);
+			goto error_free_reg;
+		}
+	}
+
+	ret = iio_device_register(indio_dev);
+	if (ret) {
+		dev_err(&spi->dev, "Failed to register iio device: %d\n", ret);
+		goto error_disable_reg;
+	}
+
+	return 0;
+
+error_disable_reg:
+	if (st->chip_info->int_vref == 0)
+		regulator_bulk_disable(ARRAY_SIZE(st->vref_reg), st->vref_reg);
+error_free_reg:
+	if (st->chip_info->int_vref == 0)
+		regulator_bulk_free(ARRAY_SIZE(st->vref_reg), st->vref_reg);
+error_free:
+	iio_free_device(indio_dev);
+
+	return ret;
+}
+
+static int __devexit ad5764_remove(struct spi_device *spi)
+{
+	struct iio_dev *indio_dev = spi_get_drvdata(spi);
+	struct ad5764_state *st = iio_priv(indio_dev);
+
+	iio_device_unregister(indio_dev);
+
+	if (st->chip_info->int_vref == 0) {
+		regulator_bulk_disable(ARRAY_SIZE(st->vref_reg), st->vref_reg);
+		regulator_bulk_free(ARRAY_SIZE(st->vref_reg), st->vref_reg);
+	}
+
+	iio_free_device(indio_dev);
+
+	return 0;
+}
+
+static const struct spi_device_id ad5764_ids[] = {
+	{ "ad5744", ID_AD5744 },
+	{ "ad5744r", ID_AD5744R },
+	{ "ad5764", ID_AD5764 },
+	{ "ad5764r", ID_AD5764R },
+	{ }
+};
+MODULE_DEVICE_TABLE(spi, ad5764_ids);
+
+static struct spi_driver ad5764_driver = {
+	.driver = {
+		.name = "ad5764",
+		.owner = THIS_MODULE,
+	},
+	.probe = ad5764_probe,
+	.remove = __devexit_p(ad5764_remove),
+	.id_table = ad5764_ids,
+};
+
+static __init int ad5764_spi_init(void)
+{
+	return spi_register_driver(&ad5764_driver);
+}
+module_init(ad5764_spi_init);
+
+static __exit void ad5764_spi_exit(void)
+{
+	spi_unregister_driver(&ad5764_driver);
+}
+module_exit(ad5764_spi_exit);
+
+MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
+MODULE_DESCRIPTION("Analog Devices AD5744/AD5744R/AD5764/AD5764R DAC");
+MODULE_LICENSE("GPL v2");
-- 
1.7.7.1


^ permalink raw reply related	[flat|nested] 4+ messages in thread

* Re: [PATCH] staging:iio:dac: Add AD5764 driver
  2011-11-21  8:53 Lars-Peter Clausen
@ 2011-11-21  9:04 ` Shubhrajyoti Datta
  2011-11-21 10:46   ` Lars-Peter Clausen
  0 siblings, 1 reply; 4+ messages in thread
From: Shubhrajyoti Datta @ 2011-11-21  9:04 UTC (permalink / raw)
  To: Lars-Peter Clausen
  Cc: Jonathan Cameron, Michael Hennerich, linux-iio,
	device-drivers-devel, drivers

Hi Peter,

On Mon, Nov 21, 2011 at 2:23 PM, Lars-Peter Clausen <lars@metafoo.de> wrote=
:
>
> This patch adds support for the Analog Devices AD5764, AD5764R, AD5744, A=
D5744R
> quad channel analog-to-digital converter.
>
> Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
> ---
> =A0drivers/staging/iio/dac/Kconfig =A0| =A0 10 +
> =A0drivers/staging/iio/dac/Makefile | =A0 =A01 +
> =A0drivers/staging/iio/dac/ad5764.c | =A0394 ++++++++++++++++++++++++++++=
++++++++++
> =A03 files changed, 405 insertions(+), 0 deletions(-)
> =A0create mode 100644 drivers/staging/iio/dac/ad5764.c
>
> diff --git a/drivers/staging/iio/dac/Kconfig b/drivers/staging/iio/dac/Kc=
onfig
> index 0e8a814..bbc33dd 100644
> --- a/drivers/staging/iio/dac/Kconfig
> +++ b/drivers/staging/iio/dac/Kconfig
> @@ -75,6 +75,16 @@ config AD5504
> =A0 =A0 =A0 =A0 =A0To compile this driver as a module, choose M here: the
> =A0 =A0 =A0 =A0 =A0module will be called ad5504.
>
> +config AD5764
> + =A0 =A0 =A0 tristate "Analog Devices AD5764/64R/44/44R DAC driver"
> + =A0 =A0 =A0 depends on SPI_MASTER
> + =A0 =A0 =A0 help
> + =A0 =A0 =A0 =A0 Say yes here to build support for Analog Devices AD5764=
, AD5764R, AD5744,
> + =A0 =A0 =A0 =A0 AD5744R Digital to Analog Converter.
> +
> + =A0 =A0 =A0 =A0 To compile this driver as a module, choose M here: the
> + =A0 =A0 =A0 =A0 module will be called ad5764.
> +
> =A0config AD5791
> =A0 =A0 =A0 =A0tristate "Analog Devices AD5760/AD5780/AD5781/AD5791 DAC S=
PI driver"
> =A0 =A0 =A0 =A0depends on SPI
> diff --git a/drivers/staging/iio/dac/Makefile b/drivers/staging/iio/dac/M=
akefile
> index 840e94e..8ab1d26 100644
> --- a/drivers/staging/iio/dac/Makefile
> +++ b/drivers/staging/iio/dac/Makefile
> @@ -9,6 +9,7 @@ obj-$(CONFIG_AD5624R_SPI) +=3D ad5624r_spi.o
> =A0obj-$(CONFIG_AD5064) +=3D ad5064.o
> =A0obj-$(CONFIG_AD5504) +=3D ad5504.o
> =A0obj-$(CONFIG_AD5446) +=3D ad5446.o
> +obj-$(CONFIG_AD5764) +=3D ad5764.o
> =A0obj-$(CONFIG_AD5791) +=3D ad5791.o
> =A0obj-$(CONFIG_AD5686) +=3D ad5686.o
> =A0obj-$(CONFIG_MAX517) +=3D max517.o
> diff --git a/drivers/staging/iio/dac/ad5764.c b/drivers/staging/iio/dac/a=
d5764.c
> new file mode 100644
> index 0000000..50c1ec62
> --- /dev/null
> +++ b/drivers/staging/iio/dac/ad5764.c
> @@ -0,0 +1,394 @@
> +/*
> + * Analog devices AD5764, AD5764R, AD5744, AD5744R quad-channel
> + * Digital to Analog Converters driver
> + *
> + * Copyright 2011 Analog Devices Inc.
> + *
> + * Licensed under the GPL-2.
> + */
> +
> +#include <linux/device.h>
> +#include <linux/err.h>
> +#include <linux/module.h>
> +#include <linux/kernel.h>
> +#include <linux/spi/spi.h>
> +#include <linux/slab.h>
> +#include <linux/sysfs.h>
> +#include <linux/regulator/consumer.h>
> +
> +#include "../iio.h"
> +#include "../sysfs.h"
> +#include "dac.h"
> +
> +#define AD5764_REG_SF_NOP =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A00x0
> +#define AD5764_REG_SF_CONFIG =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 0x1
> +#define AD5764_REG_SF_CLEAR =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A00x4
> +#define AD5764_REG_SF_LOAD =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 0x5
> +#define AD5764_REG_DATA(x) =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 ((2 <=
< 3) | (x))
> +#define AD5764_REG_COARSE_GAIN(x) =A0 =A0 =A0 =A0 =A0 =A0 =A0((3 << 3) |=
 (x))
> +#define AD5764_REG_FINE_GAIN(x) =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =
=A0 =A0((4 << 3) | (x))
> +#define AD5764_REG_OFFSET(x) =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 ((5 << =
3) | (x))
> +
> +#define AD5764_NUM_CHANNELS 4
> +
> +/**
> + * struct ad5764_chip_info - chip specific information
> + * @int_vref: =A0Value of the internal reference voltage in uV - 0 if ex=
ternal
> + * =A0 =A0 =A0 =A0 =A0 =A0 reference voltage is used
> + * @channel =A0 =A0channel specification
> +*/
> +
> +struct ad5764_chip_info {
> + =A0 =A0 =A0 unsigned long int_vref;
> + =A0 =A0 =A0 const struct iio_chan_spec *channels;
> +};
> +
> +/**
> + * struct ad5764_state - driver instance specific data
> + * @spi: =A0 =A0 =A0 =A0 =A0 =A0 =A0 spi_device
> + * @chip_info: =A0 =A0 =A0 =A0 chip info
> + * @vref_reg: =A0 =A0 =A0 =A0 =A0vref supply regulators
> + * @data: =A0 =A0 =A0 =A0 =A0 =A0 =A0spi transfer buffers
> + */
> +
> +struct ad5764_state {
> + =A0 =A0 =A0 struct spi_device =A0 =A0 =A0 =A0 =A0 =A0 =A0 *spi;
> + =A0 =A0 =A0 const struct ad5764_chip_info =A0 *chip_info;
> + =A0 =A0 =A0 struct regulator_bulk_data =A0 =A0 =A0vref_reg[2];
> +
> + =A0 =A0 =A0 /*
> + =A0 =A0 =A0 =A0* DMA (thus cache coherency maintenance) requires the
> + =A0 =A0 =A0 =A0* transfer buffers to live in their own cache lines.
> + =A0 =A0 =A0 =A0*/
> + =A0 =A0 =A0 union {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 __be32 d32;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 u8 d8[4];
> + =A0 =A0 =A0 } data[2] ____cacheline_aligned;
> +};
> +
> +enum ad5764_type {
> + =A0 =A0 =A0 ID_AD5744,
> + =A0 =A0 =A0 ID_AD5744R,
> + =A0 =A0 =A0 ID_AD5764,
> + =A0 =A0 =A0 ID_AD5764R,
> +};
> +
> +#define AD5764_CHANNEL(_chan, _bits) { =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =
=A0 =A0 =A0 =A0 \
> + =A0 =A0 =A0 .type =3D IIO_VOLTAGE, =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =
=A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0\
> + =A0 =A0 =A0 .indexed =3D 1, =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=
 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 \
> + =A0 =A0 =A0 .output =3D 1, =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =
=A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0\
> + =A0 =A0 =A0 .channel =3D (_chan), =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =
=A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 \
> + =A0 =A0 =A0 .address =3D (_chan), =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =
=A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 \
> + =A0 =A0 =A0 .info_mask =3D IIO_CHAN_INFO_OFFSET_SHARED_BIT | =A0 =A0 =
=A0 =A0 =A0\
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 IIO_CHAN_INFO_SCALE_SEPARATE_BIT | =A0 =A0 =
=A0 =A0 =A0 =A0 =A0\
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 IIO_CHAN_INFO_CALIBSCALE_SEPARATE_BIT | =A0=
 =A0 =A0 =A0 \
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT, =A0 =
=A0 =A0 =A0 =A0 \
> + =A0 =A0 =A0 .scan_type =3D IIO_ST('u', (_bits), 16, 16 - (_bits)) =A0 =
=A0 \
> +}
> +
> +#define DECLARE_AD5764_CHANNELS(_name, _bits) \
> +const struct iio_chan_spec _name##_channels[] =3D { \
> + =A0 =A0 =A0 AD5764_CHANNEL(0, (_bits)), \
> + =A0 =A0 =A0 AD5764_CHANNEL(1, (_bits)), \
> + =A0 =A0 =A0 AD5764_CHANNEL(2, (_bits)), \
> + =A0 =A0 =A0 AD5764_CHANNEL(3, (_bits)), \
> +};
> +
> +static DECLARE_AD5764_CHANNELS(ad5764, 16);
> +static DECLARE_AD5764_CHANNELS(ad5744, 14);
> +
> +static const struct ad5764_chip_info ad5764_chip_infos[] =3D {
> + =A0 =A0 =A0 [ID_AD5744] =3D {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 .int_vref =3D 0,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 .channels =3D ad5744_channels,
> + =A0 =A0 =A0 },
> + =A0 =A0 =A0 [ID_AD5744R] =3D {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 .int_vref =3D 5000000,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 .channels =3D ad5744_channels,
> + =A0 =A0 =A0 },
> + =A0 =A0 =A0 [ID_AD5764] =3D {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 .int_vref =3D 0,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 .channels =3D ad5764_channels,
> + =A0 =A0 =A0 },
> + =A0 =A0 =A0 [ID_AD5764R] =3D {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 .int_vref =3D 5000000,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 .channels =3D ad5764_channels,
> + =A0 =A0 =A0 },
> +};
> +
> +static int ad5764_write(struct iio_dev *indio_dev, unsigned int reg,
> + =A0 =A0 =A0 unsigned int val)
> +{
> + =A0 =A0 =A0 struct ad5764_state *st =3D iio_priv(indio_dev);
> + =A0 =A0 =A0 int ret;
> +
> + =A0 =A0 =A0 mutex_lock(&indio_dev->mlock);
> + =A0 =A0 =A0 st->data[0].d32 =3D cpu_to_be32((reg << 16) | val);
> +
> + =A0 =A0 =A0 ret =3D spi_write(st->spi, &st->data[0].d8[1], 3);
> + =A0 =A0 =A0 mutex_unlock(&indio_dev->mlock);
> +
> + =A0 =A0 =A0 return ret;
> +}
> +
> +static int ad5764_read(struct iio_dev *indio_dev, unsigned int reg,
> + =A0 =A0 =A0 unsigned int *val)
> +{
> + =A0 =A0 =A0 struct ad5764_state *st =3D iio_priv(indio_dev);
> + =A0 =A0 =A0 struct spi_message m;
> + =A0 =A0 =A0 int ret;
> + =A0 =A0 =A0 struct spi_transfer t[] =3D {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 .tx_buf =3D &st->data[0].d8=
[1],
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 .len =3D 3,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 .cs_change =3D 1,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 }, {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 .rx_buf =3D &st->data[1].d8=
[1],
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 .len =3D 3,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 },
> + =A0 =A0 =A0 };
> +
> + =A0 =A0 =A0 spi_message_init(&m);
> + =A0 =A0 =A0 spi_message_add_tail(&t[0], &m);
> + =A0 =A0 =A0 spi_message_add_tail(&t[1], &m);
> +
> + =A0 =A0 =A0 mutex_lock(&indio_dev->mlock);
> +
> + =A0 =A0 =A0 st->data[0].d32 =3D cpu_to_be32((1 << 23) | (reg << 16));
> +
> + =A0 =A0 =A0 ret =3D spi_sync(st->spi, &m);
> + =A0 =A0 =A0 if (ret >=3D 0)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 *val =3D be32_to_cpu(st->data[1].d32) & 0xf=
fff;
> +
> + =A0 =A0 =A0 mutex_unlock(&indio_dev->mlock);
> +
> + =A0 =A0 =A0 return ret;
> +}
> +
> +static int ad5764_chan_info_to_reg(struct iio_chan_spec const *chan, lon=
g info)
> +{
> + =A0 =A0 =A0 switch (info) {
> + =A0 =A0 =A0 case 0:
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 return AD5764_REG_DATA(chan->address);
> + =A0 =A0 =A0 case IIO_CHAN_INFO_CALIBBIAS:
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 return AD5764_REG_OFFSET(chan->address);
> + =A0 =A0 =A0 case IIO_CHAN_INFO_CALIBSCALE:
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 return AD5764_REG_FINE_GAIN(chan->address);
> + =A0 =A0 =A0 default:
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 BUG();
Should we BUG here?
Is it that severe  because it will halt the whole system?

> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 break;
> + =A0 =A0 =A0 }
> +
> + =A0 =A0 =A0 return 0;
> +}
> +
> +static int ad5764_write_raw(struct iio_dev *indio_dev,
> + =A0 =A0 =A0 struct iio_chan_spec const *chan, int val, int val2, long i=
nfo)
> +{
> + =A0 =A0 =A0 const int max_val =3D (1 << chan->scan_type.realbits);
> + =A0 =A0 =A0 unsigned int reg;
> +
> + =A0 =A0 =A0 switch (info) {
> + =A0 =A0 =A0 case 0:
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (val >=3D max_val || val < 0)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 return -EINVAL;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 val <<=3D chan->scan_type.shift;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 break;
> + =A0 =A0 =A0 case IIO_CHAN_INFO_CALIBBIAS:
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (val >=3D 128 || val < -128)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 return -EINVAL;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 break;
> + =A0 =A0 =A0 case IIO_CHAN_INFO_CALIBSCALE:
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (val >=3D 32 || val < -32)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 return -EINVAL;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 break;
> + =A0 =A0 =A0 default:
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 return -EINVAL;
> + =A0 =A0 =A0 }
> +
> + =A0 =A0 =A0 reg =3D ad5764_chan_info_to_reg(chan, info);
> + =A0 =A0 =A0 return ad5764_write(indio_dev, reg, (u16)val);
> +}
> +
> +static int ad5764_get_channel_vref(struct ad5764_state *st,
> + =A0 =A0 =A0 unsigned int channel)
> +{
> + =A0 =A0 =A0 if (st->chip_info->int_vref)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 return st->chip_info->int_vref;
> + =A0 =A0 =A0 else
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 return regulator_get_voltage(st->vref_reg[c=
hannel / 2].consumer);
> +}
> +
> +static int ad5764_read_raw(struct iio_dev *indio_dev,
> + =A0 =A0 =A0 struct iio_chan_spec const *chan, int *val, int *val2, long=
 info)
> +{
> + =A0 =A0 =A0 struct ad5764_state *st =3D iio_priv(indio_dev);
> + =A0 =A0 =A0 unsigned long scale_uv;
> + =A0 =A0 =A0 unsigned int reg;
> + =A0 =A0 =A0 int vref;
> + =A0 =A0 =A0 int ret;
> +
> + =A0 =A0 =A0 switch (info) {
> + =A0 =A0 =A0 case 0:
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 reg =3D AD5764_REG_DATA(chan->address);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 ret =3D ad5764_read(indio_dev, reg, val);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (ret < 0)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 return ret;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 *val >>=3D chan->scan_type.shift;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 return IIO_VAL_INT;
> + =A0 =A0 =A0 case IIO_CHAN_INFO_CALIBBIAS:
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 reg =3D AD5764_REG_OFFSET(chan->address);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 ret =3D ad5764_read(indio_dev, reg, val);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (ret < 0)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 return ret;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 *val =3D sign_extend32(*val, 7);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 return IIO_VAL_INT;
> + =A0 =A0 =A0 case IIO_CHAN_INFO_CALIBSCALE:
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 reg =3D AD5764_REG_FINE_GAIN(chan->address)=
;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 ret =3D ad5764_read(indio_dev, reg, val);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (ret < 0)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 return ret;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 *val =3D sign_extend32(*val, 5);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 return IIO_VAL_INT;
> + =A0 =A0 =A0 case IIO_CHAN_INFO_SCALE:
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 /* vout =3D 4 * vref + ((dac_code / 65535) =
- 0.5) */
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 vref =3D ad5764_get_channel_vref(st, chan->=
channel);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (vref < 0)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 return vref;
> +
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 scale_uv =3D (vref * 4 * 100) >> chan->scan=
_type.realbits;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 *val =3D scale_uv / 100000;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 *val2 =3D (scale_uv % 100000) * 10;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 return IIO_VAL_INT_PLUS_MICRO;
> + =A0 =A0 =A0 case IIO_CHAN_INFO_OFFSET:
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 *val =3D -(1 << chan->scan_type.realbits) /=
 2;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 return IIO_VAL_INT;
> + =A0 =A0 =A0 }
> +
> + =A0 =A0 =A0 return -EINVAL;
> +}
> +
> +static const struct iio_info ad5764_info =3D {
> + =A0 =A0 =A0 .read_raw =3D ad5764_read_raw,
> + =A0 =A0 =A0 .write_raw =3D ad5764_write_raw,
> + =A0 =A0 =A0 .driver_module =3D THIS_MODULE,
> +};
> +
> +static int __devinit ad5764_probe(struct spi_device *spi)
> +{
> + =A0 =A0 =A0 enum ad5764_type type =3D spi_get_device_id(spi)->driver_da=
ta;
> + =A0 =A0 =A0 struct iio_dev *indio_dev;
> + =A0 =A0 =A0 struct ad5764_state *st;
> + =A0 =A0 =A0 int ret;
> +
> + =A0 =A0 =A0 indio_dev =3D iio_allocate_device(sizeof(*st));
> + =A0 =A0 =A0 if (indio_dev =3D=3D NULL) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 dev_err(&spi->dev, "Failed to allocate iio =
device\n");
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 return -ENOMEM;
> + =A0 =A0 =A0 }
> +
> + =A0 =A0 =A0 st =3D iio_priv(indio_dev);
> + =A0 =A0 =A0 spi_set_drvdata(spi, indio_dev);
> +
> + =A0 =A0 =A0 st->spi =3D spi;
> + =A0 =A0 =A0 st->chip_info =3D &ad5764_chip_infos[type];
> +
> + =A0 =A0 =A0 indio_dev->dev.parent =3D &spi->dev;
> + =A0 =A0 =A0 indio_dev->name =3D spi_get_device_id(spi)->name;
> + =A0 =A0 =A0 indio_dev->info =3D &ad5764_info;
> + =A0 =A0 =A0 indio_dev->modes =3D INDIO_DIRECT_MODE;
> + =A0 =A0 =A0 indio_dev->num_channels =3D AD5764_NUM_CHANNELS;
> + =A0 =A0 =A0 indio_dev->channels =3D st->chip_info->channels;
> +
> + =A0 =A0 =A0 if (st->chip_info->int_vref =3D=3D 0) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 st->vref_reg[0].supply =3D "vrefAB";
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 st->vref_reg[1].supply =3D "vrefCD";
> +
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 ret =3D regulator_bulk_get(&st->spi->dev,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 ARRAY_SIZE(st->vref_reg), s=
t->vref_reg);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (ret) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 dev_err(&spi->dev, "Failed =
to request vref regulators: %d\n",
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 ret);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 goto error_free;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 }
> +
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 ret =3D regulator_bulk_enable(ARRAY_SIZE(st=
->vref_reg),
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 st->vref_reg);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (ret) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 dev_err(&spi->dev, "Failed =
to enable vref regulators: %d\n",
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 ret);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 goto error_free_reg;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 }
> + =A0 =A0 =A0 }
> +
> + =A0 =A0 =A0 ret =3D iio_device_register(indio_dev);
> + =A0 =A0 =A0 if (ret) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 dev_err(&spi->dev, "Failed to register iio =
device: %d\n", ret);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 goto error_disable_reg;
> + =A0 =A0 =A0 }
> +
> + =A0 =A0 =A0 return 0;
> +
> +error_disable_reg:
> + =A0 =A0 =A0 if (st->chip_info->int_vref =3D=3D 0)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 regulator_bulk_disable(ARRAY_SIZE(st->vref_=
reg), st->vref_reg);
> +error_free_reg:
> + =A0 =A0 =A0 if (st->chip_info->int_vref =3D=3D 0)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 regulator_bulk_free(ARRAY_SIZE(st->vref_reg=
), st->vref_reg);
> +error_free:
> + =A0 =A0 =A0 iio_free_device(indio_dev);
> +
> + =A0 =A0 =A0 return ret;
> +}
> +
> +static int __devexit ad5764_remove(struct spi_device *spi)
> +{
> + =A0 =A0 =A0 struct iio_dev *indio_dev =3D spi_get_drvdata(spi);
> + =A0 =A0 =A0 struct ad5764_state *st =3D iio_priv(indio_dev);
> +
> + =A0 =A0 =A0 iio_device_unregister(indio_dev);
> +
> + =A0 =A0 =A0 if (st->chip_info->int_vref =3D=3D 0) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 regulator_bulk_disable(ARRAY_SIZE(st->vref_=
reg), st->vref_reg);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 regulator_bulk_free(ARRAY_SIZE(st->vref_reg=
), st->vref_reg);
> + =A0 =A0 =A0 }
> +
> + =A0 =A0 =A0 iio_free_device(indio_dev);
> +
> + =A0 =A0 =A0 return 0;
> +}
> +
> +static const struct spi_device_id ad5764_ids[] =3D {
> + =A0 =A0 =A0 { "ad5744", ID_AD5744 },
> + =A0 =A0 =A0 { "ad5744r", ID_AD5744R },
> + =A0 =A0 =A0 { "ad5764", ID_AD5764 },
> + =A0 =A0 =A0 { "ad5764r", ID_AD5764R },
> + =A0 =A0 =A0 { }
> +};
> +MODULE_DEVICE_TABLE(spi, ad5764_ids);
> +
> +static struct spi_driver ad5764_driver =3D {
> + =A0 =A0 =A0 .driver =3D {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 .name =3D "ad5764",
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 .owner =3D THIS_MODULE,
> + =A0 =A0 =A0 },
> + =A0 =A0 =A0 .probe =3D ad5764_probe,
> + =A0 =A0 =A0 .remove =3D __devexit_p(ad5764_remove),
> + =A0 =A0 =A0 .id_table =3D ad5764_ids,
> +};
> +
> +static __init int ad5764_spi_init(void)
> +{
> + =A0 =A0 =A0 return spi_register_driver(&ad5764_driver);
> +}
> +module_init(ad5764_spi_init);
> +
> +static __exit void ad5764_spi_exit(void)
> +{
> + =A0 =A0 =A0 spi_unregister_driver(&ad5764_driver);
> +}
> +module_exit(ad5764_spi_exit);
> +
> +MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
> +MODULE_DESCRIPTION("Analog Devices AD5744/AD5744R/AD5764/AD5764R DAC");
> +MODULE_LICENSE("GPL v2");
> --
> 1.7.7.1
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-iio" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at =A0http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [PATCH] staging:iio:dac: Add AD5764 driver
  2011-11-21  9:04 ` Shubhrajyoti Datta
@ 2011-11-21 10:46   ` Lars-Peter Clausen
  0 siblings, 0 replies; 4+ messages in thread
From: Lars-Peter Clausen @ 2011-11-21 10:46 UTC (permalink / raw)
  To: Shubhrajyoti Datta
  Cc: Jonathan Cameron, Michael Hennerich, linux-iio,
	device-drivers-devel, drivers

On 11/21/2011 10:04 AM, Shubhrajyoti Datta wrote:

>> +static int ad5764_chan_info_to_reg(struct iio_chan_spec const *chan, long info)
>> +{
>> +       switch (info) {
>> +       case 0:
>> +               return AD5764_REG_DATA(chan->address);
>> +       case IIO_CHAN_INFO_CALIBBIAS:
>> +               return AD5764_REG_OFFSET(chan->address);
>> +       case IIO_CHAN_INFO_CALIBSCALE:
>> +               return AD5764_REG_FINE_GAIN(chan->address);
>> +       default:
>> +               BUG();
> Should we BUG here?
> Is it that severe  because it will halt the whole system?

given that this should only trigger if something went wrong really bad I
think it is OK to BUG here. As far as I can see the compiler will even
remove it, since it is in a dead code path.

- Lars

^ permalink raw reply	[flat|nested] 4+ messages in thread

* [PATCH] staging:iio:dac: Add AD5764 driver
@ 2011-12-05 16:05 Lars-Peter Clausen
  0 siblings, 0 replies; 4+ messages in thread
From: Lars-Peter Clausen @ 2011-12-05 16:05 UTC (permalink / raw)
  To: Greg Kroah-Hartman
  Cc: Jonathan Cameron, Michael Hennerich, devel, linux-iio,
	device-drivers-devel, drivers, Lars-Peter Clausen

This patch adds support for the Analog Devices AD5764, AD5764R, AD5744, AD5744R
quad channel analog-to-digital converter.

Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
Acked-by: Jonathan Cameron <jic23@kernel.org>
---
 drivers/staging/iio/dac/Kconfig  |   10 +
 drivers/staging/iio/dac/Makefile |    1 +
 drivers/staging/iio/dac/ad5764.c |  393 ++++++++++++++++++++++++++++++++++++++
 3 files changed, 404 insertions(+), 0 deletions(-)
 create mode 100644 drivers/staging/iio/dac/ad5764.c

diff --git a/drivers/staging/iio/dac/Kconfig b/drivers/staging/iio/dac/Kconfig
index a71defb..01b1059 100644
--- a/drivers/staging/iio/dac/Kconfig
+++ b/drivers/staging/iio/dac/Kconfig
@@ -62,6 +62,16 @@ config AD5504
 	  To compile this driver as a module, choose M here: the
 	  module will be called ad5504.
 
+config AD5764
+	tristate "Analog Devices AD5764/64R/44/44R DAC driver"
+	depends on SPI_MASTER
+	help
+	  Say yes here to build support for Analog Devices AD5764, AD5764R, AD5744,
+	  AD5744R Digital to Analog Converter.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called ad5764.
+
 config AD5791
 	tristate "Analog Devices AD5760/AD5780/AD5781/AD5791 DAC SPI driver"
 	depends on SPI
diff --git a/drivers/staging/iio/dac/Makefile b/drivers/staging/iio/dac/Makefile
index e75b0c8..2bd6b55 100644
--- a/drivers/staging/iio/dac/Makefile
+++ b/drivers/staging/iio/dac/Makefile
@@ -8,6 +8,7 @@ obj-$(CONFIG_AD5624R_SPI) += ad5624r_spi.o
 obj-$(CONFIG_AD5064) += ad5064.o
 obj-$(CONFIG_AD5504) += ad5504.o
 obj-$(CONFIG_AD5446) += ad5446.o
+obj-$(CONFIG_AD5764) += ad5764.o
 obj-$(CONFIG_AD5791) += ad5791.o
 obj-$(CONFIG_AD5686) += ad5686.o
 obj-$(CONFIG_MAX517) += max517.o
diff --git a/drivers/staging/iio/dac/ad5764.c b/drivers/staging/iio/dac/ad5764.c
new file mode 100644
index 0000000..ff91480
--- /dev/null
+++ b/drivers/staging/iio/dac/ad5764.c
@@ -0,0 +1,393 @@
+/*
+ * Analog devices AD5764, AD5764R, AD5744, AD5744R quad-channel
+ * Digital to Analog Converters driver
+ *
+ * Copyright 2011 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/spi/spi.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+#include <linux/regulator/consumer.h>
+
+#include "../iio.h"
+#include "../sysfs.h"
+#include "dac.h"
+
+#define AD5764_REG_SF_NOP			0x0
+#define AD5764_REG_SF_CONFIG			0x1
+#define AD5764_REG_SF_CLEAR			0x4
+#define AD5764_REG_SF_LOAD			0x5
+#define AD5764_REG_DATA(x)			((2 << 3) | (x))
+#define AD5764_REG_COARSE_GAIN(x)		((3 << 3) | (x))
+#define AD5764_REG_FINE_GAIN(x)			((4 << 3) | (x))
+#define AD5764_REG_OFFSET(x)			((5 << 3) | (x))
+
+#define AD5764_NUM_CHANNELS 4
+
+/**
+ * struct ad5764_chip_info - chip specific information
+ * @int_vref:	Value of the internal reference voltage in uV - 0 if external
+ *		reference voltage is used
+ * @channel	channel specification
+*/
+
+struct ad5764_chip_info {
+	unsigned long int_vref;
+	const struct iio_chan_spec *channels;
+};
+
+/**
+ * struct ad5764_state - driver instance specific data
+ * @spi:		spi_device
+ * @chip_info:		chip info
+ * @vref_reg:		vref supply regulators
+ * @data:		spi transfer buffers
+ */
+
+struct ad5764_state {
+	struct spi_device		*spi;
+	const struct ad5764_chip_info	*chip_info;
+	struct regulator_bulk_data	vref_reg[2];
+
+	/*
+	 * DMA (thus cache coherency maintenance) requires the
+	 * transfer buffers to live in their own cache lines.
+	 */
+	union {
+		__be32 d32;
+		u8 d8[4];
+	} data[2] ____cacheline_aligned;
+};
+
+enum ad5764_type {
+	ID_AD5744,
+	ID_AD5744R,
+	ID_AD5764,
+	ID_AD5764R,
+};
+
+#define AD5764_CHANNEL(_chan, _bits) {				\
+	.type = IIO_VOLTAGE,					\
+	.indexed = 1,						\
+	.output = 1,						\
+	.channel = (_chan),					\
+	.address = (_chan),					\
+	.info_mask = IIO_CHAN_INFO_OFFSET_SHARED_BIT |		\
+		IIO_CHAN_INFO_SCALE_SEPARATE_BIT |		\
+		IIO_CHAN_INFO_CALIBSCALE_SEPARATE_BIT |		\
+		IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT,		\
+	.scan_type = IIO_ST('u', (_bits), 16, 16 - (_bits))	\
+}
+
+#define DECLARE_AD5764_CHANNELS(_name, _bits) \
+const struct iio_chan_spec _name##_channels[] = { \
+	AD5764_CHANNEL(0, (_bits)), \
+	AD5764_CHANNEL(1, (_bits)), \
+	AD5764_CHANNEL(2, (_bits)), \
+	AD5764_CHANNEL(3, (_bits)), \
+};
+
+static DECLARE_AD5764_CHANNELS(ad5764, 16);
+static DECLARE_AD5764_CHANNELS(ad5744, 14);
+
+static const struct ad5764_chip_info ad5764_chip_infos[] = {
+	[ID_AD5744] = {
+		.int_vref = 0,
+		.channels = ad5744_channels,
+	},
+	[ID_AD5744R] = {
+		.int_vref = 5000000,
+		.channels = ad5744_channels,
+	},
+	[ID_AD5764] = {
+		.int_vref = 0,
+		.channels = ad5764_channels,
+	},
+	[ID_AD5764R] = {
+		.int_vref = 5000000,
+		.channels = ad5764_channels,
+	},
+};
+
+static int ad5764_write(struct iio_dev *indio_dev, unsigned int reg,
+	unsigned int val)
+{
+	struct ad5764_state *st = iio_priv(indio_dev);
+	int ret;
+
+	mutex_lock(&indio_dev->mlock);
+	st->data[0].d32 = cpu_to_be32((reg << 16) | val);
+
+	ret = spi_write(st->spi, &st->data[0].d8[1], 3);
+	mutex_unlock(&indio_dev->mlock);
+
+	return ret;
+}
+
+static int ad5764_read(struct iio_dev *indio_dev, unsigned int reg,
+	unsigned int *val)
+{
+	struct ad5764_state *st = iio_priv(indio_dev);
+	struct spi_message m;
+	int ret;
+	struct spi_transfer t[] = {
+		{
+			.tx_buf = &st->data[0].d8[1],
+			.len = 3,
+			.cs_change = 1,
+		}, {
+			.rx_buf = &st->data[1].d8[1],
+			.len = 3,
+		},
+	};
+
+	spi_message_init(&m);
+	spi_message_add_tail(&t[0], &m);
+	spi_message_add_tail(&t[1], &m);
+
+	mutex_lock(&indio_dev->mlock);
+
+	st->data[0].d32 = cpu_to_be32((1 << 23) | (reg << 16));
+
+	ret = spi_sync(st->spi, &m);
+	if (ret >= 0)
+		*val = be32_to_cpu(st->data[1].d32) & 0xffff;
+
+	mutex_unlock(&indio_dev->mlock);
+
+	return ret;
+}
+
+static int ad5764_chan_info_to_reg(struct iio_chan_spec const *chan, long info)
+{
+	switch (info) {
+	case 0:
+		return AD5764_REG_DATA(chan->address);
+	case IIO_CHAN_INFO_CALIBBIAS:
+		return AD5764_REG_OFFSET(chan->address);
+	case IIO_CHAN_INFO_CALIBSCALE:
+		return AD5764_REG_FINE_GAIN(chan->address);
+	default:
+		break;
+	}
+
+	return 0;
+}
+
+static int ad5764_write_raw(struct iio_dev *indio_dev,
+	struct iio_chan_spec const *chan, int val, int val2, long info)
+{
+	const int max_val = (1 << chan->scan_type.realbits);
+	unsigned int reg;
+
+	switch (info) {
+	case 0:
+		if (val >= max_val || val < 0)
+			return -EINVAL;
+		val <<= chan->scan_type.shift;
+		break;
+	case IIO_CHAN_INFO_CALIBBIAS:
+		if (val >= 128 || val < -128)
+			return -EINVAL;
+		break;
+	case IIO_CHAN_INFO_CALIBSCALE:
+		if (val >= 32 || val < -32)
+			return -EINVAL;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	reg = ad5764_chan_info_to_reg(chan, info);
+	return ad5764_write(indio_dev, reg, (u16)val);
+}
+
+static int ad5764_get_channel_vref(struct ad5764_state *st,
+	unsigned int channel)
+{
+	if (st->chip_info->int_vref)
+		return st->chip_info->int_vref;
+	else
+		return regulator_get_voltage(st->vref_reg[channel / 2].consumer);
+}
+
+static int ad5764_read_raw(struct iio_dev *indio_dev,
+	struct iio_chan_spec const *chan, int *val, int *val2, long info)
+{
+	struct ad5764_state *st = iio_priv(indio_dev);
+	unsigned long scale_uv;
+	unsigned int reg;
+	int vref;
+	int ret;
+
+	switch (info) {
+	case 0:
+		reg = AD5764_REG_DATA(chan->address);
+		ret = ad5764_read(indio_dev, reg, val);
+		if (ret < 0)
+			return ret;
+		*val >>= chan->scan_type.shift;
+		return IIO_VAL_INT;
+	case IIO_CHAN_INFO_CALIBBIAS:
+		reg = AD5764_REG_OFFSET(chan->address);
+		ret = ad5764_read(indio_dev, reg, val);
+		if (ret < 0)
+			return ret;
+		*val = sign_extend32(*val, 7);
+		return IIO_VAL_INT;
+	case IIO_CHAN_INFO_CALIBSCALE:
+		reg = AD5764_REG_FINE_GAIN(chan->address);
+		ret = ad5764_read(indio_dev, reg, val);
+		if (ret < 0)
+			return ret;
+		*val = sign_extend32(*val, 5);
+		return IIO_VAL_INT;
+	case IIO_CHAN_INFO_SCALE:
+		/* vout = 4 * vref + ((dac_code / 65535) - 0.5) */
+		vref = ad5764_get_channel_vref(st, chan->channel);
+		if (vref < 0)
+			return vref;
+
+		scale_uv = (vref * 4 * 100) >> chan->scan_type.realbits;
+		*val = scale_uv / 100000;
+		*val2 = (scale_uv % 100000) * 10;
+		return IIO_VAL_INT_PLUS_MICRO;
+	case IIO_CHAN_INFO_OFFSET:
+		*val = -(1 << chan->scan_type.realbits) / 2;
+		return IIO_VAL_INT;
+	}
+
+	return -EINVAL;
+}
+
+static const struct iio_info ad5764_info = {
+	.read_raw = ad5764_read_raw,
+	.write_raw = ad5764_write_raw,
+	.driver_module = THIS_MODULE,
+};
+
+static int __devinit ad5764_probe(struct spi_device *spi)
+{
+	enum ad5764_type type = spi_get_device_id(spi)->driver_data;
+	struct iio_dev *indio_dev;
+	struct ad5764_state *st;
+	int ret;
+
+	indio_dev = iio_allocate_device(sizeof(*st));
+	if (indio_dev == NULL) {
+		dev_err(&spi->dev, "Failed to allocate iio device\n");
+		return -ENOMEM;
+	}
+
+	st = iio_priv(indio_dev);
+	spi_set_drvdata(spi, indio_dev);
+
+	st->spi = spi;
+	st->chip_info = &ad5764_chip_infos[type];
+
+	indio_dev->dev.parent = &spi->dev;
+	indio_dev->name = spi_get_device_id(spi)->name;
+	indio_dev->info = &ad5764_info;
+	indio_dev->modes = INDIO_DIRECT_MODE;
+	indio_dev->num_channels = AD5764_NUM_CHANNELS;
+	indio_dev->channels = st->chip_info->channels;
+
+	if (st->chip_info->int_vref == 0) {
+		st->vref_reg[0].supply = "vrefAB";
+		st->vref_reg[1].supply = "vrefCD";
+
+		ret = regulator_bulk_get(&st->spi->dev,
+			ARRAY_SIZE(st->vref_reg), st->vref_reg);
+		if (ret) {
+			dev_err(&spi->dev, "Failed to request vref regulators: %d\n",
+				ret);
+			goto error_free;
+		}
+
+		ret = regulator_bulk_enable(ARRAY_SIZE(st->vref_reg),
+			st->vref_reg);
+		if (ret) {
+			dev_err(&spi->dev, "Failed to enable vref regulators: %d\n",
+				ret);
+			goto error_free_reg;
+		}
+	}
+
+	ret = iio_device_register(indio_dev);
+	if (ret) {
+		dev_err(&spi->dev, "Failed to register iio device: %d\n", ret);
+		goto error_disable_reg;
+	}
+
+	return 0;
+
+error_disable_reg:
+	if (st->chip_info->int_vref == 0)
+		regulator_bulk_disable(ARRAY_SIZE(st->vref_reg), st->vref_reg);
+error_free_reg:
+	if (st->chip_info->int_vref == 0)
+		regulator_bulk_free(ARRAY_SIZE(st->vref_reg), st->vref_reg);
+error_free:
+	iio_free_device(indio_dev);
+
+	return ret;
+}
+
+static int __devexit ad5764_remove(struct spi_device *spi)
+{
+	struct iio_dev *indio_dev = spi_get_drvdata(spi);
+	struct ad5764_state *st = iio_priv(indio_dev);
+
+	iio_device_unregister(indio_dev);
+
+	if (st->chip_info->int_vref == 0) {
+		regulator_bulk_disable(ARRAY_SIZE(st->vref_reg), st->vref_reg);
+		regulator_bulk_free(ARRAY_SIZE(st->vref_reg), st->vref_reg);
+	}
+
+	iio_free_device(indio_dev);
+
+	return 0;
+}
+
+static const struct spi_device_id ad5764_ids[] = {
+	{ "ad5744", ID_AD5744 },
+	{ "ad5744r", ID_AD5744R },
+	{ "ad5764", ID_AD5764 },
+	{ "ad5764r", ID_AD5764R },
+	{ }
+};
+MODULE_DEVICE_TABLE(spi, ad5764_ids);
+
+static struct spi_driver ad5764_driver = {
+	.driver = {
+		.name = "ad5764",
+		.owner = THIS_MODULE,
+	},
+	.probe = ad5764_probe,
+	.remove = __devexit_p(ad5764_remove),
+	.id_table = ad5764_ids,
+};
+
+static int __init ad5764_spi_init(void)
+{
+	return spi_register_driver(&ad5764_driver);
+}
+module_init(ad5764_spi_init);
+
+static void __exit ad5764_spi_exit(void)
+{
+	spi_unregister_driver(&ad5764_driver);
+}
+module_exit(ad5764_spi_exit);
+
+MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
+MODULE_DESCRIPTION("Analog Devices AD5744/AD5744R/AD5764/AD5764R DAC");
+MODULE_LICENSE("GPL v2");
-- 
1.7.7.3



^ permalink raw reply related	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2011-12-05 16:06 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2011-12-05 16:05 [PATCH] staging:iio:dac: Add AD5764 driver Lars-Peter Clausen
  -- strict thread matches above, loose matches on Subject: below --
2011-11-21  8:53 Lars-Peter Clausen
2011-11-21  9:04 ` Shubhrajyoti Datta
2011-11-21 10:46   ` Lars-Peter Clausen

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).