iio:dac:ad5064: Add support for the ad5629r and ad5669r
authorLars-Peter Clausen <lars@metafoo.de>
Tue, 26 Jun 2012 09:04:36 +0000 (11:04 +0200)
committerJonathan Cameron <jic23@kernel.org>
Sat, 30 Jun 2012 09:15:09 +0000 (10:15 +0100)
The ad5629r and ad5669r are the I2C variants of the ad5628 and ad5668. Since the
ad5064 driver currently only supports SPI based devices the major part of this
patch focuses on adding support for I2C based devices. Adding support for the
actual parts boils down to adding entries for them to the device id table.

Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
Signed-off-by: Jonathan Cameron <jic23@kernel.org>
drivers/iio/dac/Kconfig
drivers/iio/dac/ad5064.c

index afd207e171cfe4c7121dd45932d02aee2eac74d0..1be15fa9d618296978bc2809cbc4ed1ca41f98dd 100644 (file)
@@ -4,12 +4,12 @@
 menu "Digital to analog converters"
 
 config AD5064
-       tristate "Analog Devices AD5064/64-1/65/44/45/24/25, AD5628/48/66/68 DAC driver"
-       depends on SPI
+       tristate "Analog Devices AD5064 and similar multi-channel DAC driver"
+       depends on (SPI_MASTER || I2C)
        help
          Say yes here to build support for Analog Devices AD5024, AD5025, AD5044,
-         AD5045, AD5064, AD5064-1, AD5065, AD5628, AD5648, AD5666, AD5668 Digital
-         to Analog Converter.
+         AD5045, AD5064, AD5064-1, AD5065, AD5628, AD5629R, AD5648, AD5666, AD5668,
+         AD5669R Digital to Analog Converter.
 
          To compile this driver as a module, choose M here: the
          module will be called ad5064.
index 276af02520afb61f5337e14d296a087da524515a..aa739c497f2bcf6b9d73ae8c9bcbf9ccb3faf7ea 100644 (file)
@@ -1,6 +1,6 @@
 /*
- * AD5024, AD5025, AD5044, AD5045, AD5064, AD5064-1, AD5065, AD5628, AD5648,
- * AD5666, AD5668 Digital to analog converters driver
+ * AD5024, AD5025, AD5044, AD5045, AD5064, AD5064-1, AD5065, AD5628, AD5629R,
+ * AD5648, AD5666, AD5668, AD5669R Digital to analog converters driver
  *
  * Copyright 2011 Analog Devices Inc.
  *
 #include <linux/module.h>
 #include <linux/kernel.h>
 #include <linux/spi/spi.h>
+#include <linux/i2c.h>
 #include <linux/slab.h>
 #include <linux/sysfs.h>
 #include <linux/regulator/consumer.h>
+#include <asm/unaligned.h>
 
 #include <linux/iio/iio.h>
 #include <linux/iio/sysfs.h>
@@ -62,9 +64,14 @@ struct ad5064_chip_info {
        unsigned int num_channels;
 };
 
+struct ad5064_state;
+
+typedef int (*ad5064_write_func)(struct ad5064_state *st, unsigned int cmd,
+               unsigned int addr, unsigned int val);
+
 /**
  * struct ad5064_state - driver instance specific data
- * @spi:               spi_device
+ * @dev:               the device for this driver instance
  * @chip_info:         chip model specific constants, available modes etc
  * @vref_reg:          vref supply regulators
  * @pwr_down:          whether channel is powered down
@@ -72,11 +79,12 @@ struct ad5064_chip_info {
  * @dac_cache:         current DAC raw value (chip does not support readback)
  * @use_internal_vref: set to true if the internal reference voltage should be
  *                     used.
- * @data:              spi transfer buffers
+ * @write:             register write callback
+ * @data:              i2c/spi transfer buffers
  */
 
 struct ad5064_state {
-       struct spi_device               *spi;
+       struct device                   *dev;
        const struct ad5064_chip_info   *chip_info;
        struct regulator_bulk_data      vref_reg[AD5064_MAX_VREFS];
        bool                            pwr_down[AD5064_MAX_DAC_CHANNELS];
@@ -84,11 +92,16 @@ struct ad5064_state {
        unsigned int                    dac_cache[AD5064_MAX_DAC_CHANNELS];
        bool                            use_internal_vref;
 
+       ad5064_write_func               write;
+
        /*
         * DMA (thus cache coherency maintenance) requires the
         * transfer buffers to live in their own cache lines.
         */
-       __be32 data ____cacheline_aligned;
+       union {
+               u8 i2c[3];
+               __be32 spi;
+       } data ____cacheline_aligned;
 };
 
 enum ad5064_type {
@@ -109,14 +122,31 @@ enum ad5064_type {
        ID_AD5668_2,
 };
 
+static int ad5064_i2c_write(struct ad5064_state *st, unsigned int cmd,
+       unsigned int addr, unsigned int val)
+{
+       struct i2c_client *i2c = to_i2c_client(st->dev);
+
+       st->data.i2c[0] = (cmd << 4) | addr;
+       put_unaligned_be16(val, &st->data.i2c[1]);
+       return i2c_master_send(i2c, st->data.i2c, 3);
+}
+
 static int ad5064_spi_write(struct ad5064_state *st, unsigned int cmd,
+       unsigned int addr, unsigned int val)
+{
+       struct spi_device *spi = to_spi_device(st->dev);
+
+       st->data.spi = cpu_to_be32(AD5064_CMD(cmd) | AD5064_ADDR(addr) | val);
+       return spi_write(spi, &st->data.spi, sizeof(st->data.spi));
+}
+
+static int ad5064_write(struct ad5064_state *st, unsigned int cmd,
        unsigned int addr, unsigned int val, unsigned int shift)
 {
        val <<= shift;
 
-       st->data = cpu_to_be32(AD5064_CMD(cmd) | AD5064_ADDR(addr) | val);
-
-       return spi_write(st->spi, &st->data, sizeof(st->data));
+       return st->write(st, cmd, addr, val);
 }
 
 static int ad5064_sync_powerdown_mode(struct ad5064_state *st,
@@ -130,7 +160,7 @@ static int ad5064_sync_powerdown_mode(struct ad5064_state *st,
        if (st->pwr_down[channel])
                val |= st->pwr_down_mode[channel] << 8;
 
-       ret = ad5064_spi_write(st, AD5064_CMD_POWERDOWN_DAC, 0, val, 0);
+       ret = ad5064_write(st, AD5064_CMD_POWERDOWN_DAC, 0, val, 0);
 
        return ret;
 }
@@ -251,7 +281,7 @@ static int ad5064_write_raw(struct iio_dev *indio_dev,
                        return -EINVAL;
 
                mutex_lock(&indio_dev->mlock);
-               ret = ad5064_spi_write(st, AD5064_CMD_WRITE_INPUT_N_UPDATE_N,
+               ret = ad5064_write(st, AD5064_CMD_WRITE_INPUT_N_UPDATE_N,
                                chan->address, val, chan->scan_type.shift);
                if (ret == 0)
                        st->dac_cache[chan->channel] = val;
@@ -413,9 +443,9 @@ static const char * const ad5064_vref_name(struct ad5064_state *st,
        return st->chip_info->shared_vref ? "vref" : ad5064_vref_names[vref];
 }
 
-static int __devinit ad5064_probe(struct spi_device *spi)
+static int __devinit ad5064_probe(struct device *dev, enum ad5064_type type,
+       const char *name, ad5064_write_func write)
 {
-       enum ad5064_type type = spi_get_device_id(spi)->driver_data;
        struct iio_dev *indio_dev;
        struct ad5064_state *st;
        unsigned int i;
@@ -426,24 +456,25 @@ static int __devinit ad5064_probe(struct spi_device *spi)
                return  -ENOMEM;
 
        st = iio_priv(indio_dev);
-       spi_set_drvdata(spi, indio_dev);
+       dev_set_drvdata(dev, indio_dev);
 
        st->chip_info = &ad5064_chip_info_tbl[type];
-       st->spi = spi;
+       st->dev = dev;
+       st->write = write;
 
        for (i = 0; i < ad5064_num_vref(st); ++i)
                st->vref_reg[i].supply = ad5064_vref_name(st, i);
 
-       ret = regulator_bulk_get(&st->spi->dev, ad5064_num_vref(st),
+       ret = regulator_bulk_get(dev, ad5064_num_vref(st),
                st->vref_reg);
        if (ret) {
                if (!st->chip_info->internal_vref)
                        goto error_free;
                st->use_internal_vref = true;
-               ret = ad5064_spi_write(st, AD5064_CMD_CONFIG, 0,
+               ret = ad5064_write(st, AD5064_CMD_CONFIG, 0,
                        AD5064_CONFIG_INT_VREF_ENABLE, 0);
                if (ret) {
-                       dev_err(&spi->dev, "Failed to enable internal vref: %d\n",
+                       dev_err(dev, "Failed to enable internal vref: %d\n",
                                ret);
                        goto error_free;
                }
@@ -458,8 +489,8 @@ static int __devinit ad5064_probe(struct spi_device *spi)
                st->dac_cache[i] = 0x8000;
        }
 
-       indio_dev->dev.parent = &spi->dev;
-       indio_dev->name = spi_get_device_id(spi)->name;
+       indio_dev->dev.parent = dev;
+       indio_dev->name = name;
        indio_dev->info = &ad5064_info;
        indio_dev->modes = INDIO_DIRECT_MODE;
        indio_dev->channels = st->chip_info->channels;
@@ -483,10 +514,9 @@ error_free:
        return ret;
 }
 
-
-static int __devexit ad5064_remove(struct spi_device *spi)
+static int __devexit ad5064_remove(struct device *dev)
 {
-       struct iio_dev *indio_dev = spi_get_drvdata(spi);
+       struct iio_dev *indio_dev = dev_get_drvdata(dev);
        struct ad5064_state *st = iio_priv(indio_dev);
 
        iio_device_unregister(indio_dev);
@@ -501,7 +531,22 @@ static int __devexit ad5064_remove(struct spi_device *spi)
        return 0;
 }
 
-static const struct spi_device_id ad5064_id[] = {
+#if IS_ENABLED(CONFIG_SPI_MASTER)
+
+static int __devinit ad5064_spi_probe(struct spi_device *spi)
+{
+       const struct spi_device_id *id = spi_get_device_id(spi);
+
+       return ad5064_probe(&spi->dev, id->driver_data, id->name,
+                               ad5064_spi_write);
+}
+
+static int __devexit ad5064_spi_remove(struct spi_device *spi)
+{
+       return ad5064_remove(&spi->dev);
+}
+
+static const struct spi_device_id ad5064_spi_ids[] = {
        {"ad5024", ID_AD5024},
        {"ad5025", ID_AD5025},
        {"ad5044", ID_AD5044},
@@ -520,19 +565,112 @@ static const struct spi_device_id ad5064_id[] = {
        {"ad5668-3", ID_AD5668_2}, /* similar enough to ad5668-2 */
        {}
 };
-MODULE_DEVICE_TABLE(spi, ad5064_id);
+MODULE_DEVICE_TABLE(spi, ad5064_spi_ids);
 
-static struct spi_driver ad5064_driver = {
+static struct spi_driver ad5064_spi_driver = {
        .driver = {
                   .name = "ad5064",
                   .owner = THIS_MODULE,
        },
-       .probe = ad5064_probe,
-       .remove = __devexit_p(ad5064_remove),
-       .id_table = ad5064_id,
+       .probe = ad5064_spi_probe,
+       .remove = __devexit_p(ad5064_spi_remove),
+       .id_table = ad5064_spi_ids,
 };
-module_spi_driver(ad5064_driver);
+
+static int __init ad5064_spi_register_driver(void)
+{
+       return spi_register_driver(&ad5064_spi_driver);
+}
+
+static void __exit ad5064_spi_unregister_driver(void)
+{
+       spi_unregister_driver(&ad5064_spi_driver);
+}
+
+#else
+
+static inline int ad5064_spi_register_driver(void) { return 0; }
+static inline void ad5064_spi_unregister_driver(void) { }
+
+#endif
+
+#if IS_ENABLED(CONFIG_I2C)
+
+static int __devinit ad5064_i2c_probe(struct i2c_client *i2c,
+       const struct i2c_device_id *id)
+{
+       return ad5064_probe(&i2c->dev, id->driver_data, id->name,
+                                               ad5064_i2c_write);
+}
+
+static int __devexit ad5064_i2c_remove(struct i2c_client *i2c)
+{
+       return ad5064_remove(&i2c->dev);
+}
+
+static const struct i2c_device_id ad5064_i2c_ids[] = {
+       {"ad5629-1", ID_AD5628_1},
+       {"ad5629-2", ID_AD5628_2},
+       {"ad5629-3", ID_AD5628_2}, /* similar enough to ad5629-2 */
+       {"ad5669-1", ID_AD5668_1},
+       {"ad5669-2", ID_AD5668_2},
+       {"ad5669-3", ID_AD5668_2}, /* similar enough to ad5669-2 */
+       {}
+};
+MODULE_DEVICE_TABLE(i2c, ad5064_i2c_ids);
+
+static struct i2c_driver ad5064_i2c_driver = {
+       .driver = {
+                  .name = "ad5064",
+                  .owner = THIS_MODULE,
+       },
+       .probe = ad5064_i2c_probe,
+       .remove = __devexit_p(ad5064_i2c_remove),
+       .id_table = ad5064_i2c_ids,
+};
+
+static int __init ad5064_i2c_register_driver(void)
+{
+       return i2c_add_driver(&ad5064_i2c_driver);
+}
+
+static void __exit ad5064_i2c_unregister_driver(void)
+{
+       i2c_del_driver(&ad5064_i2c_driver);
+}
+
+#else
+
+static inline int ad5064_i2c_register_driver(void) { return 0; }
+static inline void ad5064_i2c_unregister_driver(void) { }
+
+#endif
+
+static int __init ad5064_init(void)
+{
+       int ret;
+
+       ret = ad5064_spi_register_driver();
+       if (ret)
+               return ret;
+
+       ret = ad5064_i2c_register_driver();
+       if (ret) {
+               ad5064_spi_unregister_driver();
+               return ret;
+       }
+
+       return 0;
+}
+module_init(ad5064_init);
+
+static void __exit ad5064_exit(void)
+{
+       ad5064_i2c_unregister_driver();
+       ad5064_spi_unregister_driver();
+}
+module_exit(ad5064_exit);
 
 MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
-MODULE_DESCRIPTION("Analog Devices AD5024/25/44/45/64/64-1/65, AD5628/48/66/68 DAC");
+MODULE_DESCRIPTION("Analog Devices AD5024 and similar multi-channel DACs");
 MODULE_LICENSE("GPL v2");