summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Hennerich <michael.hennerich@analog.com>2010-11-22 11:15:23 +0100
committerGreg Kroah-Hartman <gregkh@suse.de>2010-11-29 11:02:55 -0800
commitb5a49481754a5cbfdc47bd701208f77c5c9010c6 (patch)
tree6cd7ebb2244308e2faaee5e4eeca52856f5651e8
parent95bd48578bbe66fa5d16029c59dfbe95749a5f0b (diff)
staging: iio: dac: Enable driver support for AD5444 and AD5446 DA converters
Enable support for AD5444 and AD5446: 12-/14-Bit High Bandwidth Multiplying DACs with Serial Interface. staging: iio: dac: Add support for AD5541A, AD5512A digital to analog convertors staging: iio: dac: Fix according to review feedback Review feedback by Jonathan Cameron: Remove spurious new line. Document struct members. Remove redundant variable initialization. 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>
-rw-r--r--drivers/staging/iio/dac/Kconfig10
-rw-r--r--drivers/staging/iio/dac/Makefile1
-rw-r--r--drivers/staging/iio/dac/ad5446.c249
-rw-r--r--drivers/staging/iio/dac/ad5446.h46
4 files changed, 306 insertions, 0 deletions
diff --git a/drivers/staging/iio/dac/Kconfig b/drivers/staging/iio/dac/Kconfig
index 583df785fd25..9c497ccd3dce 100644
--- a/drivers/staging/iio/dac/Kconfig
+++ b/drivers/staging/iio/dac/Kconfig
@@ -9,3 +9,13 @@ config AD5624R_SPI
help
Say yes here to build support for Analog Devices AD5624R, AD5644R and
AD5664R convertors (DAC). This driver uses the common SPI interface.
+
+config AD5446
+ tristate "Analog Devices AD5444, AD5446 and AD5541A, AD5512A DAC SPI driver"
+ depends on SPI
+ help
+ Say yes here to build support for Analog Devices AD5444, AD5446
+ and AD5541A, AD5512A DACs.
+
+ To compile this driver as a module, choose M here: the
+ module will be called ad5446.
diff --git a/drivers/staging/iio/dac/Makefile b/drivers/staging/iio/dac/Makefile
index 7ddf05d5f1b4..7cf331b4e001 100644
--- a/drivers/staging/iio/dac/Makefile
+++ b/drivers/staging/iio/dac/Makefile
@@ -3,3 +3,4 @@
#
obj-$(CONFIG_AD5624R_SPI) += ad5624r_spi.o
+obj-$(CONFIG_AD5446) += ad5446.o
diff --git a/drivers/staging/iio/dac/ad5446.c b/drivers/staging/iio/dac/ad5446.c
new file mode 100644
index 000000000000..ac3165b0aeff
--- /dev/null
+++ b/drivers/staging/iio/dac/ad5446.c
@@ -0,0 +1,249 @@
+/*
+ * AD5446 SPI DAC driver
+ *
+ * Copyright 2010 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#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/spi/spi.h>
+#include <linux/regulator/consumer.h>
+#include <linux/err.h>
+
+#include "../iio.h"
+#include "../sysfs.h"
+#include "dac.h"
+
+#include "ad5446.h"
+
+static ssize_t ad5446_write(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t len)
+{
+ struct iio_dev *dev_info = dev_get_drvdata(dev);
+ struct ad5446_state *st = dev_info->dev_data;
+ int ret;
+ long val;
+
+ ret = strict_strtol(buf, 10, &val);
+ if (ret)
+ goto error_ret;
+
+ if (val > RES_MASK(st->chip_info->bits)) {
+ ret = -EINVAL;
+ goto error_ret;
+ }
+
+ mutex_lock(&dev_info->mlock);
+ switch (spi_get_device_id(st->spi)->driver_data) {
+ case ID_AD5444:
+ case ID_AD5446:
+ st->data = cpu_to_be16(AD5446_LOAD |
+ (val << st->chip_info->left_shift));
+ break;
+ case ID_AD5542A:
+ case ID_AD5512A:
+ st->data = cpu_to_be16(val << st->chip_info->left_shift);
+ break;
+ }
+
+ ret = spi_sync(st->spi, &st->msg);
+ mutex_unlock(&dev_info->mlock);
+
+error_ret:
+ return ret ? ret : len;
+}
+
+static IIO_DEV_ATTR_OUT_RAW(0, ad5446_write, 0);
+
+static ssize_t ad5446_show_scale(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct iio_dev *dev_info = dev_get_drvdata(dev);
+ struct ad5446_state *st = iio_dev_get_devdata(dev_info);
+ /* Corresponds to Vref / 2^(bits) */
+ unsigned int scale_uv = (st->vref_mv * 1000) >> st->chip_info->bits;
+
+ return sprintf(buf, "%d.%d\n", scale_uv / 1000, scale_uv % 1000);
+}
+static IIO_DEVICE_ATTR(out_scale, S_IRUGO, ad5446_show_scale, NULL, 0);
+
+static ssize_t ad5446_show_name(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct iio_dev *dev_info = dev_get_drvdata(dev);
+ struct ad5446_state *st = iio_dev_get_devdata(dev_info);
+
+ return sprintf(buf, "%s\n", spi_get_device_id(st->spi)->name);
+}
+static IIO_DEVICE_ATTR(name, S_IRUGO, ad5446_show_name, NULL, 0);
+
+static struct attribute *ad5446_attributes[] = {
+ &iio_dev_attr_out0_raw.dev_attr.attr,
+ &iio_dev_attr_out_scale.dev_attr.attr,
+ &iio_dev_attr_name.dev_attr.attr,
+ NULL,
+};
+
+static const struct attribute_group ad5446_attribute_group = {
+ .attrs = ad5446_attributes,
+};
+
+static const struct ad5446_chip_info ad5446_chip_info_tbl[] = {
+ [ID_AD5444] = {
+ .bits = 12,
+ .storagebits = 16,
+ .left_shift = 2,
+ .sign = 'u', /* IIO_SCAN_EL_TYPE_UNSIGNED */
+ },
+ [ID_AD5446] = {
+ .bits = 14,
+ .storagebits = 16,
+ .left_shift = 0,
+ .sign = 'u', /* IIO_SCAN_EL_TYPE_UNSIGNED */
+ },
+ [ID_AD5542A] = {
+ .bits = 16,
+ .storagebits = 16,
+ .left_shift = 0,
+ .sign = 'u', /* IIO_SCAN_EL_TYPE_UNSIGNED */
+ },
+ [ID_AD5512A] = {
+ .bits = 12,
+ .storagebits = 16,
+ .left_shift = 4,
+ .sign = 'u', /* IIO_SCAN_EL_TYPE_UNSIGNED */
+ },
+};
+
+static int __devinit ad5446_probe(struct spi_device *spi)
+{
+ struct ad5446_state *st;
+ int ret, voltage_uv = 0;
+
+ st = kzalloc(sizeof(*st), GFP_KERNEL);
+ if (st == NULL) {
+ ret = -ENOMEM;
+ goto error_ret;
+ }
+
+ st->reg = regulator_get(&spi->dev, "vcc");
+ if (!IS_ERR(st->reg)) {
+ ret = regulator_enable(st->reg);
+ if (ret)
+ goto error_put_reg;
+
+ voltage_uv = regulator_get_voltage(st->reg);
+ }
+
+ st->chip_info =
+ &ad5446_chip_info_tbl[spi_get_device_id(spi)->driver_data];
+
+ spi_set_drvdata(spi, st);
+
+ st->spi = spi;
+
+ st->indio_dev = iio_allocate_device();
+ if (st->indio_dev == NULL) {
+ ret = -ENOMEM;
+ goto error_disable_reg;
+ }
+
+ /* Estabilish that the iio_dev is a child of the spi device */
+ st->indio_dev->dev.parent = &spi->dev;
+ st->indio_dev->attrs = &ad5446_attribute_group;
+ st->indio_dev->dev_data = (void *)(st);
+ st->indio_dev->driver_module = THIS_MODULE;
+ st->indio_dev->modes = INDIO_DIRECT_MODE;
+
+ /* Setup default message */
+
+ st->xfer.tx_buf = &st->data,
+ st->xfer.len = 2,
+
+ spi_message_init(&st->msg);
+ spi_message_add_tail(&st->xfer, &st->msg);
+
+ if (voltage_uv)
+ st->vref_mv = voltage_uv / 1000;
+ else
+ dev_warn(&spi->dev, "reference voltage unspecified\n");
+
+ ret = iio_device_register(st->indio_dev);
+ if (ret)
+ goto error_free_device;
+
+ return 0;
+
+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 ret;
+}
+
+static int ad5446_remove(struct spi_device *spi)
+{
+ struct ad5446_state *st = spi_get_drvdata(spi);
+ struct iio_dev *indio_dev = st->indio_dev;
+
+ iio_device_unregister(indio_dev);
+ if (!IS_ERR(st->reg)) {
+ regulator_disable(st->reg);
+ regulator_put(st->reg);
+ }
+ kfree(st);
+ return 0;
+}
+
+static const struct spi_device_id ad5446_id[] = {
+ {"ad5444", ID_AD5444},
+ {"ad5446", ID_AD5446},
+ {"ad5542a", ID_AD5542A},
+ {"ad5512a", ID_AD5512A},
+ {}
+};
+
+static struct spi_driver ad5446_driver = {
+ .driver = {
+ .name = "ad5446",
+ .bus = &spi_bus_type,
+ .owner = THIS_MODULE,
+ },
+ .probe = ad5446_probe,
+ .remove = __devexit_p(ad5446_remove),
+ .id_table = ad5446_id,
+};
+
+static int __init ad5446_init(void)
+{
+ return spi_register_driver(&ad5446_driver);
+}
+module_init(ad5446_init);
+
+static void __exit ad5446_exit(void)
+{
+ spi_unregister_driver(&ad5446_driver);
+}
+module_exit(ad5446_exit);
+
+MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
+MODULE_DESCRIPTION("Analog Devices AD5444/AD5446 DAC");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("spi:ad5446");
diff --git a/drivers/staging/iio/dac/ad5446.h b/drivers/staging/iio/dac/ad5446.h
new file mode 100644
index 000000000000..24a9cda6b997
--- /dev/null
+++ b/drivers/staging/iio/dac/ad5446.h
@@ -0,0 +1,46 @@
+/*
+ * AD5446 SPI DAC driver
+ *
+ * Copyright 2010 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2 or later.
+ */
+#ifndef IIO_ADC_AD5446_H_
+#define IIO_ADC_AD5446_H_
+
+/* DAC Control Bits */
+
+#define AD5446_LOAD (0x0 << 14) /* Load and update */
+#define AD5446_SDO_DIS (0x1 << 14) /* Disable SDO */
+#define AD5446_NOP (0x2 << 14) /* No operation */
+#define AD5446_CLK_RISING (0x3 << 14) /* Clock data on rising edge */
+
+#define RES_MASK(bits) ((1 << (bits)) - 1)
+
+struct ad5446_chip_info {
+ u8 bits; /* number of DAC bits */
+ u8 storagebits; /* number of bits written to the DAC */
+ u8 left_shift; /* number of bits the datum must be shifted */
+ char sign; /* [s]igned or [u]nsigned */
+};
+
+struct ad5446_state {
+ struct iio_dev *indio_dev;
+ struct spi_device *spi;
+ const struct ad5446_chip_info *chip_info;
+ struct regulator *reg;
+ struct work_struct poll_work;
+ unsigned short vref_mv;
+ struct spi_transfer xfer;
+ struct spi_message msg;
+ unsigned short data;
+};
+
+enum ad5446_supported_device_ids {
+ ID_AD5444,
+ ID_AD5446,
+ ID_AD5542A,
+ ID_AD5512A,
+};
+
+#endif /* IIO_ADC_AD5446_H_ */