ad525x_dpot: add support for SPI parts
authorMichael Hennerich <michael.hennerich@analog.com>
Mon, 24 May 2010 21:33:14 +0000 (14:33 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Tue, 25 May 2010 15:07:04 +0000 (08:07 -0700)
Split the bus logic out into separate files so that we can handle I2C and
SPI busses independently.  The new SPI bus logic brings in support for a
lot more parts:

AD5160, AD5161, AD5162, AD5165, AD5200, AD5201, AD5203,
AD5204, AD5206, AD5207, AD5231, AD5232, AD5233, AD5235,
AD5260, AD5262, AD5263, AD5290, AD5291, AD5292, AD5293,
AD7376, AD8400, AD8402, AD8403, ADN2850

[randy.dunlap@oracle.com: fix ad525X_dpot build]
Signed-off-by: Michael Hennerich <michael.hennerich@analog.com>
Signed-off-by: Mike Frysinger <vapier@gentoo.org>
Signed-off-by: Randy Dunlap <randy.dunlap@oracle.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
drivers/misc/Kconfig
drivers/misc/Makefile
drivers/misc/ad525x_dpot-i2c.c [new file with mode: 0644]
drivers/misc/ad525x_dpot-spi.c [new file with mode: 0644]
drivers/misc/ad525x_dpot.c
drivers/misc/ad525x_dpot.h [new file with mode: 0644]

index 0d0d625fece28849e985487fdebb9607c0c372cc..69e019e7d69c885ce6cbffc1f205a2365e5851c1 100644 (file)
@@ -14,11 +14,15 @@ menuconfig MISC_DEVICES
 if MISC_DEVICES
 
 config AD525X_DPOT
-       tristate "Analog Devices AD525x Digital Potentiometers"
-       depends on I2C && SYSFS
+       tristate "Analog Devices Digital Potentiometers"
+       depends on (I2C || SPI) && SYSFS
        help
          If you say yes here, you get support for the Analog Devices
-         AD5258, AD5259, AD5251, AD5252, AD5253, AD5254 and AD5255
+         AD5258, AD5259, AD5251, AD5252, AD5253, AD5254, AD5255
+         AD5160, AD5161, AD5162, AD5165, AD5200, AD5201, AD5203,
+         AD5204, AD5206, AD5207, AD5231, AD5232, AD5233, AD5235,
+         AD5260, AD5262, AD5263, AD5290, AD5291, AD5292, AD5293,
+         AD7376, AD8400, AD8402, AD8403, ADN2850
          digital potentiometer chips.
 
          See Documentation/misc-devices/ad525x_dpot.txt for the
@@ -27,6 +31,26 @@ config AD525X_DPOT
          This driver can also be built as a module.  If so, the module
          will be called ad525x_dpot.
 
+config AD525X_DPOT_I2C
+       tristate "support I2C bus connection"
+       depends on AD525X_DPOT && I2C
+       help
+         Say Y here if you have a digital potentiometers hooked to an I2C bus.
+
+         To compile this driver as a module, choose M here: the
+         module will be called ad525x_dpot-i2c.
+
+config AD525X_DPOT_SPI
+       tristate "support SPI bus connection"
+       depends on AD525X_DPOT && SPI_MASTER
+       help
+         Say Y here if you have a digital potentiometers hooked to an SPI bus.
+
+         If unsure, say N (but it's safe to say "Y").
+
+         To compile this driver as a module, choose M here: the
+         module will be called ad525x_dpot-spi.
+
 config ATMEL_PWM
        tristate "Atmel AT32/AT91 PWM support"
        depends on AVR32 || ARCH_AT91SAM9263 || ARCH_AT91SAM9RL || ARCH_AT91CAP9
index f12dc3e544029b63a85ec2c3e97e80f601487422..6ed06a19474acc23de3b93b732ecce25b9a55c40 100644 (file)
@@ -4,6 +4,8 @@
 
 obj-$(CONFIG_IBM_ASM)          += ibmasm/
 obj-$(CONFIG_AD525X_DPOT)      += ad525x_dpot.o
+obj-$(CONFIG_AD525X_DPOT_I2C)  += ad525x_dpot-i2c.o
+obj-$(CONFIG_AD525X_DPOT_SPI)  += ad525x_dpot-spi.o
 obj-$(CONFIG_ATMEL_PWM)                += atmel_pwm.o
 obj-$(CONFIG_ATMEL_SSC)                += atmel-ssc.o
 obj-$(CONFIG_ATMEL_TCLIB)      += atmel_tclib.o
diff --git a/drivers/misc/ad525x_dpot-i2c.c b/drivers/misc/ad525x_dpot-i2c.c
new file mode 100644 (file)
index 0000000..971e61d
--- /dev/null
@@ -0,0 +1,119 @@
+/*
+ * Driver for the Analog Devices digital potentiometers (I2C bus)
+ *
+ * Copyright (C) 2010 Michael Hennerich, Analog Devices Inc.
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <linux/i2c.h>
+#include <linux/module.h>
+
+#include "ad525x_dpot.h"
+
+/* ------------------------------------------------------------------------- */
+/* I2C bus functions */
+static int write_d8(void *client, u8 val)
+{
+       return i2c_smbus_write_byte(client, val);
+}
+
+static int write_r8d8(void *client, u8 reg, u8 val)
+{
+       return i2c_smbus_write_byte_data(client, reg, val);
+}
+
+static int write_r8d16(void *client, u8 reg, u16 val)
+{
+       return i2c_smbus_write_word_data(client, reg, val);
+}
+
+static int read_d8(void *client)
+{
+       return i2c_smbus_read_byte(client);
+}
+
+static int read_r8d8(void *client, u8 reg)
+{
+       return i2c_smbus_read_byte_data(client, reg);
+}
+
+static int read_r8d16(void *client, u8 reg)
+{
+       return i2c_smbus_read_word_data(client, reg);
+}
+
+static const struct ad_dpot_bus_ops bops = {
+       .read_d8        = read_d8,
+       .read_r8d8      = read_r8d8,
+       .read_r8d16     = read_r8d16,
+       .write_d8       = write_d8,
+       .write_r8d8     = write_r8d8,
+       .write_r8d16    = write_r8d16,
+};
+
+static int __devinit ad_dpot_i2c_probe(struct i2c_client *client,
+                                     const struct i2c_device_id *id)
+{
+       struct ad_dpot_bus_data bdata = {
+               .client = client,
+               .bops = &bops,
+       };
+
+       struct ad_dpot_id dpot_id = {
+               .name = (char *) &id->name,
+               .devid = id->driver_data,
+       };
+
+       if (!i2c_check_functionality(client->adapter,
+                                    I2C_FUNC_SMBUS_WORD_DATA)) {
+               dev_err(&client->dev, "SMBUS Word Data not Supported\n");
+               return -EIO;
+       }
+
+       return ad_dpot_probe(&client->dev, &bdata, &dpot_id);
+}
+
+static int __devexit ad_dpot_i2c_remove(struct i2c_client *client)
+{
+       return ad_dpot_remove(&client->dev);
+}
+
+static const struct i2c_device_id ad_dpot_id[] = {
+       {"ad5258", AD5258_ID},
+       {"ad5259", AD5259_ID},
+       {"ad5251", AD5251_ID},
+       {"ad5252", AD5252_ID},
+       {"ad5253", AD5253_ID},
+       {"ad5254", AD5254_ID},
+       {"ad5255", AD5255_ID},
+       {}
+};
+MODULE_DEVICE_TABLE(i2c, ad_dpot_id);
+
+static struct i2c_driver ad_dpot_i2c_driver = {
+       .driver = {
+               .name   = "ad_dpot",
+               .owner  = THIS_MODULE,
+       },
+       .probe          = ad_dpot_i2c_probe,
+       .remove         = __devexit_p(ad_dpot_i2c_remove),
+       .id_table       = ad_dpot_id,
+};
+
+static int __init ad_dpot_i2c_init(void)
+{
+       return i2c_add_driver(&ad_dpot_i2c_driver);
+}
+module_init(ad_dpot_i2c_init);
+
+static void __exit ad_dpot_i2c_exit(void)
+{
+       i2c_del_driver(&ad_dpot_i2c_driver);
+}
+module_exit(ad_dpot_i2c_exit);
+
+MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
+MODULE_DESCRIPTION("digital potentiometer I2C bus driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("i2c:ad_dpot");
diff --git a/drivers/misc/ad525x_dpot-spi.c b/drivers/misc/ad525x_dpot-spi.c
new file mode 100644 (file)
index 0000000..b8c6df9
--- /dev/null
@@ -0,0 +1,172 @@
+/*
+ * Driver for the Analog Devices digital potentiometers (SPI bus)
+ *
+ * Copyright (C) 2010 Michael Hennerich, Analog Devices Inc.
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <linux/spi/spi.h>
+#include <linux/module.h>
+
+#include "ad525x_dpot.h"
+
+static const struct ad_dpot_id ad_dpot_spi_devlist[] = {
+       {.name = "ad5160", .devid = AD5160_ID},
+       {.name = "ad5161", .devid = AD5161_ID},
+       {.name = "ad5162", .devid = AD5162_ID},
+       {.name = "ad5165", .devid = AD5165_ID},
+       {.name = "ad5200", .devid = AD5200_ID},
+       {.name = "ad5201", .devid = AD5201_ID},
+       {.name = "ad5203", .devid = AD5203_ID},
+       {.name = "ad5204", .devid = AD5204_ID},
+       {.name = "ad5206", .devid = AD5206_ID},
+       {.name = "ad5207", .devid = AD5207_ID},
+       {.name = "ad5231", .devid = AD5231_ID},
+       {.name = "ad5232", .devid = AD5232_ID},
+       {.name = "ad5233", .devid = AD5233_ID},
+       {.name = "ad5235", .devid = AD5235_ID},
+       {.name = "ad5260", .devid = AD5260_ID},
+       {.name = "ad5262", .devid = AD5262_ID},
+       {.name = "ad5263", .devid = AD5263_ID},
+       {.name = "ad5290", .devid = AD5290_ID},
+       {.name = "ad5291", .devid = AD5291_ID},
+       {.name = "ad5292", .devid = AD5292_ID},
+       {.name = "ad5293", .devid = AD5293_ID},
+       {.name = "ad7376", .devid = AD7376_ID},
+       {.name = "ad8400", .devid = AD8400_ID},
+       {.name = "ad8402", .devid = AD8402_ID},
+       {.name = "ad8403", .devid = AD8403_ID},
+       {.name = "adn2850", .devid = ADN2850_ID},
+       {}
+};
+
+/* ------------------------------------------------------------------------- */
+
+/* SPI bus functions */
+static int write8(void *client, u8 val)
+{
+       u8 data = val;
+       return spi_write(client, &data, 1);
+}
+
+static int write16(void *client, u8 reg, u8 val)
+{
+       u8 data[2] = {reg, val};
+       return spi_write(client, data, 1);
+}
+
+static int write24(void *client, u8 reg, u16 val)
+{
+       u8 data[3] = {reg, val >> 8, val};
+       return spi_write(client, data, 1);
+}
+
+static int read8(void *client)
+{
+       int ret;
+       u8 data;
+       ret = spi_read(client, &data, 1);
+       if (ret < 0)
+               return ret;
+
+       return data;
+}
+
+static int read16(void *client, u8 reg)
+{
+       int ret;
+       u8 buf_rx[2];
+
+       write16(client, reg, 0);
+       ret = spi_read(client, buf_rx, 2);
+       if (ret < 0)
+               return ret;
+
+       return (buf_rx[0] << 8) |  buf_rx[1];
+}
+
+static int read24(void *client, u8 reg)
+{
+       int ret;
+       u8 buf_rx[3];
+
+       write24(client, reg, 0);
+       ret = spi_read(client, buf_rx, 3);
+       if (ret < 0)
+               return ret;
+
+       return (buf_rx[1] << 8) |  buf_rx[2];
+}
+
+static const struct ad_dpot_bus_ops bops = {
+       .read_d8        = read8,
+       .read_r8d8      = read16,
+       .read_r8d16     = read24,
+       .write_d8       = write8,
+       .write_r8d8     = write16,
+       .write_r8d16    = write24,
+};
+
+static const struct ad_dpot_id *dpot_match_id(const struct ad_dpot_id *id,
+                                               char *name)
+{
+       while (id->name && id->name[0]) {
+               if (strcmp(name, id->name) == 0)
+                       return id;
+               id++;
+       }
+       return NULL;
+}
+
+static int __devinit ad_dpot_spi_probe(struct spi_device *spi)
+{
+       char *name = spi->dev.platform_data;
+       const struct ad_dpot_id *dpot_id;
+
+       struct ad_dpot_bus_data bdata = {
+               .client = spi,
+               .bops = &bops,
+       };
+
+       dpot_id = dpot_match_id(ad_dpot_spi_devlist, name);
+
+       if (dpot_id == NULL) {
+               dev_err(&spi->dev, "%s not in supported device list", name);
+               return -ENODEV;
+       }
+
+       return ad_dpot_probe(&spi->dev, &bdata, dpot_id);
+}
+
+static int __devexit ad_dpot_spi_remove(struct spi_device *spi)
+{
+       return ad_dpot_remove(&spi->dev);
+}
+
+static struct spi_driver ad_dpot_spi_driver = {
+       .driver = {
+               .name   = "ad_dpot",
+               .bus    = &spi_bus_type,
+               .owner  = THIS_MODULE,
+       },
+       .probe          = ad_dpot_spi_probe,
+       .remove         = __devexit_p(ad_dpot_spi_remove),
+};
+
+static int __init ad_dpot_spi_init(void)
+{
+       return spi_register_driver(&ad_dpot_spi_driver);
+}
+module_init(ad_dpot_spi_init);
+
+static void __exit ad_dpot_spi_exit(void)
+{
+       spi_unregister_driver(&ad_dpot_spi_driver);
+}
+module_exit(ad_dpot_spi_exit);
+
+MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
+MODULE_DESCRIPTION("digital potentiometer SPI bus driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("spi:ad_dpot");
index ce92088bf0b8bdb9902e7d3d482f6355265b9154..a41c2de0eae84cf9bb179a4f16f2d78b6dbe4197 100644 (file)
@@ -1,6 +1,6 @@
 /*
- * ad525x_dpot: Driver for the Analog Devices AD525x digital potentiometers
- * Copyright (c) 2009 Analog Devices, Inc.
+ * ad525x_dpot: Driver for the Analog Devices digital potentiometers
+ * Copyright (c) 2009-2010 Analog Devices, Inc.
  * Author: Michael Hennerich <hennerich@blackfin.uclinux.org>
  *
  * DEVID               #Wipers         #Positions      Resistor Options (kOhm)
  * AD5255              3               512             25, 250
  * AD5253              4               64              1, 10, 50, 100
  * AD5254              4               256             1, 10, 50, 100
+ * AD5160              1               256             5, 10, 50, 100
+ * AD5161              1               256             5, 10, 50, 100
+ * AD5162              2               256             2.5, 10, 50, 100
+ * AD5165              1               256             100
+ * AD5200              1               256             10, 50
+ * AD5201              1               33              10, 50
+ * AD5203              4               64              10, 100
+ * AD5204              4               256             10, 50, 100
+ * AD5206              6               256             10, 50, 100
+ * AD5207              2               256             10, 50, 100
+ * AD5231              1               1024            10, 50, 100
+ * AD5232              2               256             10, 50, 100
+ * AD5233              4               64              10, 50, 100
+ * AD5235              2               1024            25, 250
+ * AD5260              1               256             20, 50, 200
+ * AD5262              2               256             20, 50, 200
+ * AD5263              4               256             20, 50, 200
+ * AD5290              1               256             10, 50, 100
+ * AD5291              1               256             20
+ * AD5292              1               1024            20
+ * AD5293              1               1024            20
+ * AD7376              1               128             10, 50, 100, 1M
+ * AD8400              1               256             1, 10, 50, 100
+ * AD8402              2               256             1, 10, 50, 100
+ * AD8403              4               256             1, 10, 50, 100
+ * ADN2850             3               512             25, 250
  *
  * See Documentation/misc-devices/ad525x_dpot.txt for more info.
  *
 #include <linux/device.h>
 #include <linux/kernel.h>
 #include <linux/init.h>
-#include <linux/slab.h>
-#include <linux/i2c.h>
 #include <linux/delay.h>
+#include <linux/slab.h>
 
-#define DRIVER_NAME                    "ad525x_dpot"
-#define DRIVER_VERSION                 "0.1"
-
-enum dpot_devid {
-       AD5258_ID,
-       AD5259_ID,
-       AD5251_ID,
-       AD5252_ID,
-       AD5253_ID,
-       AD5254_ID,
-       AD5255_ID,
-};
+#define DRIVER_VERSION                 "0.2"
 
-#define AD5258_MAX_POSITION            64
-#define AD5259_MAX_POSITION            256
-#define AD5251_MAX_POSITION            64
-#define AD5252_MAX_POSITION            256
-#define AD5253_MAX_POSITION            64
-#define AD5254_MAX_POSITION            256
-#define AD5255_MAX_POSITION            512
-
-#define AD525X_RDAC0           0
-#define AD525X_RDAC1           1
-#define AD525X_RDAC2           2
-#define AD525X_RDAC3           3
-
-#define AD525X_REG_TOL         0x18
-#define AD525X_TOL_RDAC0       (AD525X_REG_TOL | AD525X_RDAC0)
-#define AD525X_TOL_RDAC1       (AD525X_REG_TOL | AD525X_RDAC1)
-#define AD525X_TOL_RDAC2       (AD525X_REG_TOL | AD525X_RDAC2)
-#define AD525X_TOL_RDAC3       (AD525X_REG_TOL | AD525X_RDAC3)
-
-/* RDAC-to-EEPROM Interface Commands */
-#define AD525X_I2C_RDAC                (0x00 << 5)
-#define AD525X_I2C_EEPROM      (0x01 << 5)
-#define AD525X_I2C_CMD         (0x80)
-
-#define AD525X_DEC_ALL_6DB     (AD525X_I2C_CMD | (0x4 << 3))
-#define AD525X_INC_ALL_6DB     (AD525X_I2C_CMD | (0x9 << 3))
-#define AD525X_DEC_ALL         (AD525X_I2C_CMD | (0x6 << 3))
-#define AD525X_INC_ALL         (AD525X_I2C_CMD | (0xB << 3))
-
-static s32 ad525x_read(struct i2c_client *client, u8 reg);
-static s32 ad525x_write(struct i2c_client *client, u8 reg, u16 value);
+#include "ad525x_dpot.h"
 
 /*
  * Client data (each client gets its own)
  */
 
 struct dpot_data {
+       struct ad_dpot_bus_data bdata;
        struct mutex update_lock;
        unsigned rdac_mask;
        unsigned max_pos;
-       unsigned devid;
+       unsigned long devid;
+       unsigned uid;
+       unsigned feat;
+       unsigned wipers;
+       u16 rdac_cache[8];
 };
 
+static inline int dpot_read_d8(struct dpot_data *dpot)
+{
+       return dpot->bdata.bops->read_d8(dpot->bdata.client);
+}
+
+static inline int dpot_read_r8d8(struct dpot_data *dpot, u8 reg)
+{
+       return dpot->bdata.bops->read_r8d8(dpot->bdata.client, reg);
+}
+
+static inline int dpot_read_r8d16(struct dpot_data *dpot, u8 reg)
+{
+       return dpot->bdata.bops->read_r8d16(dpot->bdata.client, reg);
+}
+
+static inline int dpot_write_d8(struct dpot_data *dpot, u8 val)
+{
+       return dpot->bdata.bops->write_d8(dpot->bdata.client, val);
+}
+
+static inline int dpot_write_r8d8(struct dpot_data *dpot, u8 reg, u16 val)
+{
+       return dpot->bdata.bops->write_r8d8(dpot->bdata.client, reg, val);
+}
+
+static inline int dpot_write_r8d16(struct dpot_data *dpot, u8 reg, u16 val)
+{
+       return dpot->bdata.bops->write_r8d16(dpot->bdata.client, reg, val);
+}
+
+static s32 dpot_read(struct dpot_data *dpot, u8 reg)
+{
+       unsigned val = 0;
+
+       if (dpot->feat & F_SPI) {
+               if (!(reg & (DPOT_ADDR_EEPROM | DPOT_ADDR_CMD))) {
+
+                       if (dpot->feat & F_RDACS_WONLY)
+                               return dpot->rdac_cache[reg & DPOT_RDAC_MASK];
+
+                       if (dpot->uid == DPOT_UID(AD5291_ID) ||
+                               dpot->uid == DPOT_UID(AD5292_ID) ||
+                               dpot->uid == DPOT_UID(AD5293_ID))
+                               return dpot_read_r8d8(dpot,
+                                       DPOT_AD5291_READ_RDAC << 2);
+
+                       val = DPOT_SPI_READ_RDAC;
+               } else if (reg & DPOT_ADDR_EEPROM) {
+                       val = DPOT_SPI_READ_EEPROM;
+               }
+
+               if (dpot->feat & F_SPI_16BIT)
+                       return dpot_read_r8d8(dpot, val);
+               else if (dpot->feat & F_SPI_24BIT)
+                       return dpot_read_r8d16(dpot, val);
+
+       } else { /* I2C */
+
+               if ((reg & DPOT_REG_TOL) || (dpot->max_pos > 256))
+                       return dpot_read_r8d16(dpot, (reg & 0xF8) |
+                                       ((reg & 0x7) << 1));
+               else
+                       return dpot_read_r8d8(dpot, reg);
+
+       }
+       return -EFAULT;
+}
+
+static s32 dpot_write(struct dpot_data *dpot, u8 reg, u16 value)
+{
+       unsigned val = 0;
+
+       if (dpot->feat & F_SPI) {
+               if (!(reg & (DPOT_ADDR_EEPROM | DPOT_ADDR_CMD))) {
+                       if (dpot->feat & F_RDACS_WONLY)
+                               dpot->rdac_cache[reg & DPOT_RDAC_MASK] = value;
+
+                       if (dpot->feat & F_AD_APPDATA) {
+                               if (dpot->feat & F_SPI_8BIT) {
+                                       val = ((reg & DPOT_RDAC_MASK) <<
+                                               DPOT_MAX_POS(dpot->devid)) |
+                                               value;
+                                       return dpot_write_d8(dpot, val);
+                               } else if (dpot->feat & F_SPI_16BIT) {
+                                       val = ((reg & DPOT_RDAC_MASK) <<
+                                               DPOT_MAX_POS(dpot->devid)) |
+                                               value;
+                                       return dpot_write_r8d8(dpot, val >> 8,
+                                               val & 0xFF);
+                               } else
+                                       BUG();
+                       } else {
+                               if (dpot->uid == DPOT_UID(AD5291_ID) ||
+                                       dpot->uid == DPOT_UID(AD5292_ID) ||
+                                       dpot->uid == DPOT_UID(AD5293_ID))
+                                       return dpot_write_r8d8(dpot,
+                                               (DPOT_AD5291_RDAC << 2) |
+                                               (value >> 8), value & 0xFF);
+
+                               val = DPOT_SPI_RDAC | (reg & DPOT_RDAC_MASK);
+                       }
+               } else if (reg & DPOT_ADDR_EEPROM) {
+                       val = DPOT_SPI_EEPROM | (reg & DPOT_RDAC_MASK);
+               } else if (reg & DPOT_ADDR_CMD) {
+                       switch (reg) {
+                       case DPOT_DEC_ALL_6DB:
+                               val = DPOT_SPI_DEC_ALL_6DB;
+                               break;
+                       case DPOT_INC_ALL_6DB:
+                               val = DPOT_SPI_INC_ALL_6DB;
+                               break;
+                       case DPOT_DEC_ALL:
+                               val = DPOT_SPI_DEC_ALL;
+                               break;
+                       case DPOT_INC_ALL:
+                               val = DPOT_SPI_INC_ALL;
+                               break;
+                       }
+               } else
+                       BUG();
+
+               if (dpot->feat & F_SPI_16BIT)
+                       return dpot_write_r8d8(dpot, val, value);
+               else if (dpot->feat & F_SPI_24BIT)
+                       return dpot_write_r8d16(dpot, val, value);
+       } else {
+               /* Only write the instruction byte for certain commands */
+               if (reg & DPOT_ADDR_CMD)
+                       return dpot_write_d8(dpot, reg);
+
+               if (dpot->max_pos > 256)
+                       return dpot_write_r8d16(dpot, (reg & 0xF8) |
+                                               ((reg & 0x7) << 1), value);
+               else
+                       /* All other registers require instruction + data bytes */
+                       return dpot_write_r8d8(dpot, reg, value);
+
+       }
+
+       return -EFAULT;
+}
+
 /* sysfs functions */
 
 static ssize_t sysfs_show_reg(struct device *dev,
-                             struct device_attribute *attr, char *buf, u32 reg)
+                             struct device_attribute *attr,
+                             char *buf, u32 reg)
 {
-       struct i2c_client *client = to_i2c_client(dev);
-       struct dpot_data *data = i2c_get_clientdata(client);
+       struct dpot_data *data = dev_get_drvdata(dev);
        s32 value;
 
        mutex_lock(&data->update_lock);
-       value = ad525x_read(client, reg);
+       value = dpot_read(data, reg);
        mutex_unlock(&data->update_lock);
 
        if (value < 0)
@@ -111,7 +242,7 @@ static ssize_t sysfs_show_reg(struct device *dev,
         * datasheet (Rev. A) for more details.
         */
 
-       if (reg & AD525X_REG_TOL)
+       if (reg & DPOT_REG_TOL)
                return sprintf(buf, "0x%04x\n", value & 0xFFFF);
        else
                return sprintf(buf, "%u\n", value & data->rdac_mask);
@@ -121,8 +252,7 @@ static ssize_t sysfs_set_reg(struct device *dev,
                             struct device_attribute *attr,
                             const char *buf, size_t count, u32 reg)
 {
-       struct i2c_client *client = to_i2c_client(dev);
-       struct dpot_data *data = i2c_get_clientdata(client);
+       struct dpot_data *data = dev_get_drvdata(dev);
        unsigned long value;
        int err;
 
@@ -134,8 +264,8 @@ static ssize_t sysfs_set_reg(struct device *dev,
                value = data->rdac_mask;
 
        mutex_lock(&data->update_lock);
-       ad525x_write(client, reg, value);
-       if (reg & AD525X_I2C_EEPROM)
+       dpot_write(data, reg, value);
+       if (reg & DPOT_ADDR_EEPROM)
                msleep(26);     /* Sleep while the EEPROM updates */
        mutex_unlock(&data->update_lock);
 
@@ -146,11 +276,10 @@ static ssize_t sysfs_do_cmd(struct device *dev,
                            struct device_attribute *attr,
                            const char *buf, size_t count, u32 reg)
 {
-       struct i2c_client *client = to_i2c_client(dev);
-       struct dpot_data *data = i2c_get_clientdata(client);
+       struct dpot_data *data = dev_get_drvdata(dev);
 
        mutex_lock(&data->update_lock);
-       ad525x_write(client, reg, 0);
+       dpot_write(data, reg, 0);
        mutex_unlock(&data->update_lock);
 
        return count;
@@ -182,51 +311,58 @@ static DEVICE_ATTR(name, S_IWUSR | S_IRUGO, show_##name, set_##name);
 DPOT_DEVICE_SHOW(name, reg) \
 static DEVICE_ATTR(name, S_IWUSR | S_IRUGO, show_##name, NULL);
 
-DPOT_DEVICE_SHOW_SET(rdac0, AD525X_I2C_RDAC | AD525X_RDAC0);
-DPOT_DEVICE_SHOW_SET(eeprom0, AD525X_I2C_EEPROM | AD525X_RDAC0);
-DPOT_DEVICE_SHOW_ONLY(tolerance0, AD525X_I2C_EEPROM | AD525X_TOL_RDAC0);
-
-DPOT_DEVICE_SHOW_SET(rdac1, AD525X_I2C_RDAC | AD525X_RDAC1);
-DPOT_DEVICE_SHOW_SET(eeprom1, AD525X_I2C_EEPROM | AD525X_RDAC1);
-DPOT_DEVICE_SHOW_ONLY(tolerance1, AD525X_I2C_EEPROM | AD525X_TOL_RDAC1);
-
-DPOT_DEVICE_SHOW_SET(rdac2, AD525X_I2C_RDAC | AD525X_RDAC2);
-DPOT_DEVICE_SHOW_SET(eeprom2, AD525X_I2C_EEPROM | AD525X_RDAC2);
-DPOT_DEVICE_SHOW_ONLY(tolerance2, AD525X_I2C_EEPROM | AD525X_TOL_RDAC2);
-
-DPOT_DEVICE_SHOW_SET(rdac3, AD525X_I2C_RDAC | AD525X_RDAC3);
-DPOT_DEVICE_SHOW_SET(eeprom3, AD525X_I2C_EEPROM | AD525X_RDAC3);
-DPOT_DEVICE_SHOW_ONLY(tolerance3, AD525X_I2C_EEPROM | AD525X_TOL_RDAC3);
-
-static struct attribute *ad525x_attributes_wipers[4][4] = {
-       {
-               &dev_attr_rdac0.attr,
-               &dev_attr_eeprom0.attr,
-               &dev_attr_tolerance0.attr,
-               NULL
-       }, {
-               &dev_attr_rdac1.attr,
-               &dev_attr_eeprom1.attr,
-               &dev_attr_tolerance1.attr,
-               NULL
-       }, {
-               &dev_attr_rdac2.attr,
-               &dev_attr_eeprom2.attr,
-               &dev_attr_tolerance2.attr,
-               NULL
-       }, {
-               &dev_attr_rdac3.attr,
-               &dev_attr_eeprom3.attr,
-               &dev_attr_tolerance3.attr,
-               NULL
-       }
+DPOT_DEVICE_SHOW_SET(rdac0, DPOT_ADDR_RDAC | DPOT_RDAC0);
+DPOT_DEVICE_SHOW_SET(eeprom0, DPOT_ADDR_EEPROM | DPOT_RDAC0);
+DPOT_DEVICE_SHOW_ONLY(tolerance0, DPOT_ADDR_EEPROM | DPOT_TOL_RDAC0);
+
+DPOT_DEVICE_SHOW_SET(rdac1, DPOT_ADDR_RDAC | DPOT_RDAC1);
+DPOT_DEVICE_SHOW_SET(eeprom1, DPOT_ADDR_EEPROM | DPOT_RDAC1);
+DPOT_DEVICE_SHOW_ONLY(tolerance1, DPOT_ADDR_EEPROM | DPOT_TOL_RDAC1);
+
+DPOT_DEVICE_SHOW_SET(rdac2, DPOT_ADDR_RDAC | DPOT_RDAC2);
+DPOT_DEVICE_SHOW_SET(eeprom2, DPOT_ADDR_EEPROM | DPOT_RDAC2);
+DPOT_DEVICE_SHOW_ONLY(tolerance2, DPOT_ADDR_EEPROM | DPOT_TOL_RDAC2);
+
+DPOT_DEVICE_SHOW_SET(rdac3, DPOT_ADDR_RDAC | DPOT_RDAC3);
+DPOT_DEVICE_SHOW_SET(eeprom3, DPOT_ADDR_EEPROM | DPOT_RDAC3);
+DPOT_DEVICE_SHOW_ONLY(tolerance3, DPOT_ADDR_EEPROM | DPOT_TOL_RDAC3);
+
+DPOT_DEVICE_SHOW_SET(rdac4, DPOT_ADDR_RDAC | DPOT_RDAC4);
+DPOT_DEVICE_SHOW_SET(eeprom4, DPOT_ADDR_EEPROM | DPOT_RDAC4);
+DPOT_DEVICE_SHOW_ONLY(tolerance4, DPOT_ADDR_EEPROM | DPOT_TOL_RDAC4);
+
+DPOT_DEVICE_SHOW_SET(rdac5, DPOT_ADDR_RDAC | DPOT_RDAC5);
+DPOT_DEVICE_SHOW_SET(eeprom5, DPOT_ADDR_EEPROM | DPOT_RDAC5);
+DPOT_DEVICE_SHOW_ONLY(tolerance5, DPOT_ADDR_EEPROM | DPOT_TOL_RDAC5);
+
+static const struct attribute *dpot_attrib_wipers[] = {
+       &dev_attr_rdac0.attr,
+       &dev_attr_rdac1.attr,
+       &dev_attr_rdac2.attr,
+       &dev_attr_rdac3.attr,
+       &dev_attr_rdac4.attr,
+       &dev_attr_rdac5.attr,
+       NULL
+};
+
+static const struct attribute *dpot_attrib_eeprom[] = {
+       &dev_attr_eeprom0.attr,
+       &dev_attr_eeprom1.attr,
+       &dev_attr_eeprom2.attr,
+       &dev_attr_eeprom3.attr,
+       &dev_attr_eeprom4.attr,
+       &dev_attr_eeprom5.attr,
+       NULL
 };
 
-static const struct attribute_group ad525x_group_wipers[] = {
-       {.attrs = ad525x_attributes_wipers[AD525X_RDAC0]},
-       {.attrs = ad525x_attributes_wipers[AD525X_RDAC1]},
-       {.attrs = ad525x_attributes_wipers[AD525X_RDAC2]},
-       {.attrs = ad525x_attributes_wipers[AD525X_RDAC3]},
+static const struct attribute *dpot_attrib_tolerance[] = {
+       &dev_attr_tolerance0.attr,
+       &dev_attr_tolerance1.attr,
+       &dev_attr_tolerance2.attr,
+       &dev_attr_tolerance3.attr,
+       &dev_attr_tolerance4.attr,
+       &dev_attr_tolerance5.attr,
+       NULL
 };
 
 /* ------------------------------------------------------------------------- */
@@ -240,10 +376,10 @@ set_##_name(struct device *dev, \
 } \
 static DEVICE_ATTR(_name, S_IWUSR | S_IRUGO, NULL, set_##_name);
 
-DPOT_DEVICE_DO_CMD(inc_all, AD525X_INC_ALL);
-DPOT_DEVICE_DO_CMD(dec_all, AD525X_DEC_ALL);
-DPOT_DEVICE_DO_CMD(inc_all_6db, AD525X_INC_ALL_6DB);
-DPOT_DEVICE_DO_CMD(dec_all_6db, AD525X_DEC_ALL_6DB);
+DPOT_DEVICE_DO_CMD(inc_all, DPOT_INC_ALL);
+DPOT_DEVICE_DO_CMD(dec_all, DPOT_DEC_ALL);
+DPOT_DEVICE_DO_CMD(inc_all_6db, DPOT_INC_ALL_6DB);
+DPOT_DEVICE_DO_CMD(dec_all_6db, DPOT_DEC_ALL_6DB);
 
 static struct attribute *ad525x_attributes_commands[] = {
        &dev_attr_inc_all.attr,
@@ -257,74 +393,44 @@ static const struct attribute_group ad525x_group_commands = {
        .attrs = ad525x_attributes_commands,
 };
 
-/* ------------------------------------------------------------------------- */
-
-/* i2c device functions */
-
-/**
- * ad525x_read - return the value contained in the specified register
- * on the AD5258 device.
- * @client: value returned from i2c_new_device()
- * @reg: the register to read
- *
- * If the tolerance register is specified, 2 bytes are returned.
- * Otherwise, 1 byte is returned.  A negative value indicates an error
- * occurred while reading the register.
- */
-static s32 ad525x_read(struct i2c_client *client, u8 reg)
+__devinit int ad_dpot_add_files(struct device *dev,
+               unsigned features, unsigned rdac)
 {
-       struct dpot_data *data = i2c_get_clientdata(client);
+       int err = sysfs_create_file(&dev->kobj,
+               dpot_attrib_wipers[rdac]);
+       if (features & F_CMD_EEP)
+               err |= sysfs_create_file(&dev->kobj,
+                       dpot_attrib_eeprom[rdac]);
+       if (features & F_CMD_TOL)
+               err |= sysfs_create_file(&dev->kobj,
+                       dpot_attrib_tolerance[rdac]);
 
-       if ((reg & AD525X_REG_TOL) || (data->max_pos > 256))
-               return i2c_smbus_read_word_data(client, (reg & 0xF8) |
-                                               ((reg & 0x7) << 1));
-       else
-               return i2c_smbus_read_byte_data(client, reg);
+       if (err)
+               dev_err(dev, "failed to register sysfs hooks for RDAC%d\n",
+                       rdac);
+
+       return err;
 }
 
-/**
- * ad525x_write - store the given value in the specified register on
- * the AD5258 device.
- * @client: value returned from i2c_new_device()
- * @reg: the register to write
- * @value: the byte to store in the register
- *
- * For certain instructions that do not require a data byte, "NULL"
- * should be specified for the "value" parameter.  These instructions
- * include NOP, RESTORE_FROM_EEPROM, and STORE_TO_EEPROM.
- *
- * A negative return value indicates an error occurred while reading
- * the register.
- */
-static s32 ad525x_write(struct i2c_client *client, u8 reg, u16 value)
+inline void ad_dpot_remove_files(struct device *dev,
+               unsigned features, unsigned rdac)
 {
-       struct dpot_data *data = i2c_get_clientdata(client);
-
-       /* Only write the instruction byte for certain commands */
-       if (reg & AD525X_I2C_CMD)
-               return i2c_smbus_write_byte(client, reg);
-
-       if (data->max_pos > 256)
-               return i2c_smbus_write_word_data(client, (reg & 0xF8) |
-                                               ((reg & 0x7) << 1), value);
-       else
-               /* All other registers require instruction + data bytes */
-               return i2c_smbus_write_byte_data(client, reg, value);
+       sysfs_remove_file(&dev->kobj,
+               dpot_attrib_wipers[rdac]);
+       if (features & F_CMD_EEP)
+               sysfs_remove_file(&dev->kobj,
+                       dpot_attrib_eeprom[rdac]);
+       if (features & F_CMD_TOL)
+               sysfs_remove_file(&dev->kobj,
+                       dpot_attrib_tolerance[rdac]);
 }
 
-static int ad525x_probe(struct i2c_client *client,
-                       const struct i2c_device_id *id)
+__devinit int ad_dpot_probe(struct device *dev,
+               struct ad_dpot_bus_data *bdata, const struct ad_dpot_id *id)
 {
-       struct device *dev = &client->dev;
-       struct dpot_data *data;
-       int err = 0;
-
-       dev_dbg(dev, "%s\n", __func__);
 
-       if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE)) {
-               dev_err(dev, "missing I2C functionality for this driver\n");
-               goto exit;
-       }
+       struct dpot_data *data;
+       int i, err = 0;
 
        data = kzalloc(sizeof(struct dpot_data), GFP_KERNEL);
        if (!data) {
@@ -332,183 +438,74 @@ static int ad525x_probe(struct i2c_client *client,
                goto exit;
        }
 
-       i2c_set_clientdata(client, data);
+       dev_set_drvdata(dev, data);
        mutex_init(&data->update_lock);
 
-       switch (id->driver_data) {
-       case AD5258_ID:
-               data->max_pos = AD5258_MAX_POSITION;
-               err = sysfs_create_group(&dev->kobj,
-                                      &ad525x_group_wipers[AD525X_RDAC0]);
-               break;
-       case AD5259_ID:
-               data->max_pos = AD5259_MAX_POSITION;
-               err = sysfs_create_group(&dev->kobj,
-                                      &ad525x_group_wipers[AD525X_RDAC0]);
-               break;
-       case AD5251_ID:
-               data->max_pos = AD5251_MAX_POSITION;
-               err = sysfs_create_group(&dev->kobj,
-                                      &ad525x_group_wipers[AD525X_RDAC1]);
-               err |= sysfs_create_group(&dev->kobj,
-                                      &ad525x_group_wipers[AD525X_RDAC3]);
-               err |= sysfs_create_group(&dev->kobj, &ad525x_group_commands);
-               break;
-       case AD5252_ID:
-               data->max_pos = AD5252_MAX_POSITION;
-               err = sysfs_create_group(&dev->kobj,
-                                      &ad525x_group_wipers[AD525X_RDAC1]);
-               err |= sysfs_create_group(&dev->kobj,
-                                      &ad525x_group_wipers[AD525X_RDAC3]);
-               err |= sysfs_create_group(&dev->kobj, &ad525x_group_commands);
-               break;
-       case AD5253_ID:
-               data->max_pos = AD5253_MAX_POSITION;
-               err = sysfs_create_group(&dev->kobj,
-                                      &ad525x_group_wipers[AD525X_RDAC0]);
-               err |= sysfs_create_group(&dev->kobj,
-                                      &ad525x_group_wipers[AD525X_RDAC1]);
-               err |= sysfs_create_group(&dev->kobj,
-                                      &ad525x_group_wipers[AD525X_RDAC2]);
-               err |= sysfs_create_group(&dev->kobj,
-                                      &ad525x_group_wipers[AD525X_RDAC3]);
-               err |= sysfs_create_group(&dev->kobj, &ad525x_group_commands);
-               break;
-       case AD5254_ID:
-               data->max_pos = AD5254_MAX_POSITION;
-               err = sysfs_create_group(&dev->kobj,
-                                      &ad525x_group_wipers[AD525X_RDAC0]);
-               err |= sysfs_create_group(&dev->kobj,
-                                      &ad525x_group_wipers[AD525X_RDAC1]);
-               err |= sysfs_create_group(&dev->kobj,
-                                      &ad525x_group_wipers[AD525X_RDAC2]);
-               err |= sysfs_create_group(&dev->kobj,
-                                      &ad525x_group_wipers[AD525X_RDAC3]);
-               err |= sysfs_create_group(&dev->kobj, &ad525x_group_commands);
-               break;
-       case AD5255_ID:
-               data->max_pos = AD5255_MAX_POSITION;
-               err = sysfs_create_group(&dev->kobj,
-                                      &ad525x_group_wipers[AD525X_RDAC0]);
-               err |= sysfs_create_group(&dev->kobj,
-                                      &ad525x_group_wipers[AD525X_RDAC1]);
-               err |= sysfs_create_group(&dev->kobj,
-                                      &ad525x_group_wipers[AD525X_RDAC2]);
-               err |= sysfs_create_group(&dev->kobj, &ad525x_group_commands);
-               break;
-       default:
-               err = -ENODEV;
-               goto exit_free;
-       }
+       data->bdata = *bdata;
+       data->devid = id->devid;
+
+       data->max_pos = 1 << DPOT_MAX_POS(data->devid);
+       data->rdac_mask = data->max_pos - 1;
+       data->feat = DPOT_FEAT(data->devid);
+       data->uid = DPOT_UID(data->devid);
+       data->wipers = DPOT_WIPERS(data->devid);
+
+       for (i = DPOT_RDAC0; i <= DPOT_RDAC5; i++)
+               if (data->wipers & (1 << i)) {
+                       err = ad_dpot_add_files(dev, data->feat, i);
+                       if (err)
+                               goto exit_remove_files;
+                       /* power-up midscale */
+                       if (data->feat & F_RDACS_WONLY)
+                               data->rdac_cache[i] = data->max_pos / 2;
+               }
+
+       if (data->feat & F_CMD_INC)
+               err = sysfs_create_group(&dev->kobj, &ad525x_group_commands);
 
        if (err) {
                dev_err(dev, "failed to register sysfs hooks\n");
                goto exit_free;
        }
 
-       data->devid = id->driver_data;
-       data->rdac_mask = data->max_pos - 1;
-
        dev_info(dev, "%s %d-Position Digital Potentiometer registered\n",
                 id->name, data->max_pos);
 
        return 0;
 
+exit_remove_files:
+       for (i = DPOT_RDAC0; i <= DPOT_RDAC5; i++)
+               if (data->wipers & (1 << i))
+                       ad_dpot_remove_files(dev, data->feat, i);
+
 exit_free:
        kfree(data);
-       i2c_set_clientdata(client, NULL);
+       dev_set_drvdata(dev, NULL);
 exit:
-       dev_err(dev, "failed to create client\n");
+       dev_err(dev, "failed to create client for %s ID 0x%lX\n",
+                       id->name, id->devid);
        return err;
 }
+EXPORT_SYMBOL(ad_dpot_probe);
 
-static int __devexit ad525x_remove(struct i2c_client *client)
+__devexit int ad_dpot_remove(struct device *dev)
 {
-       struct dpot_data *data = i2c_get_clientdata(client);
-       struct device *dev = &client->dev;
-
-       switch (data->devid) {
-       case AD5258_ID:
-       case AD5259_ID:
-               sysfs_remove_group(&dev->kobj,
-                                  &ad525x_group_wipers[AD525X_RDAC0]);
-               break;
-       case AD5251_ID:
-       case AD5252_ID:
-               sysfs_remove_group(&dev->kobj,
-                                  &ad525x_group_wipers[AD525X_RDAC1]);
-               sysfs_remove_group(&dev->kobj,
-                                  &ad525x_group_wipers[AD525X_RDAC3]);
-               sysfs_remove_group(&dev->kobj, &ad525x_group_commands);
-               break;
-       case AD5253_ID:
-       case AD5254_ID:
-               sysfs_remove_group(&dev->kobj,
-                                  &ad525x_group_wipers[AD525X_RDAC0]);
-               sysfs_remove_group(&dev->kobj,
-                                  &ad525x_group_wipers[AD525X_RDAC1]);
-               sysfs_remove_group(&dev->kobj,
-                                  &ad525x_group_wipers[AD525X_RDAC2]);
-               sysfs_remove_group(&dev->kobj,
-                                  &ad525x_group_wipers[AD525X_RDAC3]);
-               sysfs_remove_group(&dev->kobj, &ad525x_group_commands);
-               break;
-       case AD5255_ID:
-               sysfs_remove_group(&dev->kobj,
-                                  &ad525x_group_wipers[AD525X_RDAC0]);
-               sysfs_remove_group(&dev->kobj,
-                                  &ad525x_group_wipers[AD525X_RDAC1]);
-               sysfs_remove_group(&dev->kobj,
-                                  &ad525x_group_wipers[AD525X_RDAC2]);
-               sysfs_remove_group(&dev->kobj, &ad525x_group_commands);
-               break;
-       }
+       struct dpot_data *data = dev_get_drvdata(dev);
+       int i;
+
+       for (i = DPOT_RDAC0; i <= DPOT_RDAC5; i++)
+               if (data->wipers & (1 << i))
+                       ad_dpot_remove_files(dev, data->feat, i);
 
-       i2c_set_clientdata(client, NULL);
        kfree(data);
 
        return 0;
 }
+EXPORT_SYMBOL(ad_dpot_remove);
 
-static const struct i2c_device_id ad525x_idtable[] = {
-       {"ad5258", AD5258_ID},
-       {"ad5259", AD5259_ID},
-       {"ad5251", AD5251_ID},
-       {"ad5252", AD5252_ID},
-       {"ad5253", AD5253_ID},
-       {"ad5254", AD5254_ID},
-       {"ad5255", AD5255_ID},
-       {}
-};
-
-MODULE_DEVICE_TABLE(i2c, ad525x_idtable);
-
-static struct i2c_driver ad525x_driver = {
-       .driver = {
-                  .owner = THIS_MODULE,
-                  .name = DRIVER_NAME,
-                  },
-       .id_table = ad525x_idtable,
-       .probe = ad525x_probe,
-       .remove = __devexit_p(ad525x_remove),
-};
-
-static int __init ad525x_init(void)
-{
-       return i2c_add_driver(&ad525x_driver);
-}
-
-module_init(ad525x_init);
-
-static void __exit ad525x_exit(void)
-{
-       i2c_del_driver(&ad525x_driver);
-}
-
-module_exit(ad525x_exit);
 
 MODULE_AUTHOR("Chris Verges <chrisv@cyberswitching.com>, "
-             "Michael Hennerich <hennerich@blackfin.uclinux.org>");
-MODULE_DESCRIPTION("AD5258/9 digital potentiometer driver");
+             "Michael Hennerich <hennerich@blackfin.uclinux.org>");
+MODULE_DESCRIPTION("Digital potentiometer driver");
 MODULE_LICENSE("GPL");
 MODULE_VERSION(DRIVER_VERSION);
diff --git a/drivers/misc/ad525x_dpot.h b/drivers/misc/ad525x_dpot.h
new file mode 100644 (file)
index 0000000..99b388e
--- /dev/null
@@ -0,0 +1,173 @@
+/*
+ * Driver for the Analog Devices digital potentiometers
+ *
+ * Copyright (C) 2010 Michael Hennerich, Analog Devices Inc.
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#ifndef _AD_DPOT_H_
+#define _AD_DPOT_H_
+
+#include <linux/types.h>
+
+#define DPOT_CONF(features, wipers, max_pos, uid) \
+               (((features) << 18) | (((wipers) & 0xFF) << 10) | \
+               ((max_pos & 0xF) << 6) | (uid & 0x3F))
+
+#define DPOT_UID(conf) (conf & 0x3F)
+#define DPOT_MAX_POS(conf) ((conf >> 6) & 0xF)
+#define DPOT_WIPERS(conf) ((conf >> 10) & 0xFF)
+#define DPOT_FEAT(conf) (conf >> 18)
+
+#define BRDAC0 (1 << 0)
+#define BRDAC1 (1 << 1)
+#define BRDAC2 (1 << 2)
+#define BRDAC3 (1 << 3)
+#define BRDAC4 (1 << 4)
+#define BRDAC5 (1 << 5)
+
+#define F_CMD_INC              (1 << 0)        /* Features INC/DEC ALL, 6dB */
+#define F_CMD_EEP              (1 << 1)        /* Features EEPROM */
+#define F_CMD_TOL              (1 << 2)        /* RDACS are Read/Write + Tolerance REG */
+#define F_RDACS_RW             (1 << 3)        /* RDACS are Read/Write + Tolerance REG */
+#define F_RDACS_WONLY          (1 << 4)        /* RDACS are Write only */
+#define F_AD_APPDATA           (1 << 5)        /* RDAC Address append to data */
+#define F_SPI_8BIT             (1 << 6)        /* All SPI XFERS are 8-bit */
+#define F_SPI_16BIT            (1 << 7)        /* All SPI XFERS are 16-bit */
+#define F_SPI_24BIT            (1 << 8)        /* All SPI XFERS are 24-bit */
+
+#define F_RDACS_RW_TOL (F_RDACS_RW | F_CMD_EEP | F_CMD_TOL)
+#define F_RDACS_RW_EEP (F_RDACS_RW | F_CMD_EEP)
+#define F_SPI          (F_SPI_8BIT | F_SPI_16BIT | F_SPI_24BIT)
+
+enum dpot_devid {
+       AD5258_ID = DPOT_CONF(F_RDACS_RW_TOL, BRDAC0, 6, 0), /* I2C */
+       AD5259_ID = DPOT_CONF(F_RDACS_RW_TOL, BRDAC0, 8, 1),
+       AD5251_ID = DPOT_CONF(F_RDACS_RW_TOL | F_CMD_INC,
+                       BRDAC0 | BRDAC3, 6, 2),
+       AD5252_ID = DPOT_CONF(F_RDACS_RW_TOL | F_CMD_INC,
+                       BRDAC0 | BRDAC3, 8, 3),
+       AD5253_ID = DPOT_CONF(F_RDACS_RW_TOL | F_CMD_INC,
+                       BRDAC0 | BRDAC1 | BRDAC2 | BRDAC3, 6, 4),
+       AD5254_ID = DPOT_CONF(F_RDACS_RW_TOL | F_CMD_INC,
+                       BRDAC0 | BRDAC1 | BRDAC2 | BRDAC3, 8, 5),
+       AD5255_ID = DPOT_CONF(F_RDACS_RW_TOL | F_CMD_INC,
+                       BRDAC0 | BRDAC1 | BRDAC2, 9, 6),
+       AD5160_ID = DPOT_CONF(F_RDACS_WONLY | F_AD_APPDATA | F_SPI_8BIT,
+                       BRDAC0, 8, 7), /* SPI */
+       AD5161_ID = DPOT_CONF(F_RDACS_WONLY | F_AD_APPDATA | F_SPI_8BIT,
+                       BRDAC0, 8, 8),
+       AD5162_ID = DPOT_CONF(F_RDACS_WONLY | F_AD_APPDATA | F_SPI_16BIT,
+                       BRDAC0 | BRDAC1, 8, 9),
+       AD5165_ID = DPOT_CONF(F_RDACS_WONLY | F_AD_APPDATA | F_SPI_8BIT,
+                       BRDAC0, 8, 10),
+       AD5200_ID = DPOT_CONF(F_RDACS_WONLY | F_AD_APPDATA | F_SPI_8BIT,
+                       BRDAC0, 8, 11),
+       AD5201_ID = DPOT_CONF(F_RDACS_WONLY | F_AD_APPDATA | F_SPI_8BIT,
+                       BRDAC0, 5, 12),
+       AD5203_ID = DPOT_CONF(F_RDACS_WONLY | F_AD_APPDATA | F_SPI_8BIT,
+                       BRDAC0 | BRDAC1 | BRDAC2 | BRDAC3, 6, 13),
+       AD5204_ID = DPOT_CONF(F_RDACS_WONLY | F_AD_APPDATA | F_SPI_16BIT,
+                       BRDAC0 | BRDAC1 | BRDAC2 | BRDAC3, 8, 14),
+       AD5206_ID = DPOT_CONF(F_RDACS_WONLY | F_AD_APPDATA | F_SPI_16BIT,
+                       BRDAC0 | BRDAC1 | BRDAC2 | BRDAC3 | BRDAC4 | BRDAC5,
+                       8, 15),
+       AD5207_ID = DPOT_CONF(F_RDACS_WONLY | F_AD_APPDATA | F_SPI_16BIT,
+                       BRDAC0 | BRDAC1, 8, 16),
+       AD5231_ID = DPOT_CONF(F_RDACS_RW_EEP | F_CMD_INC | F_SPI_24BIT,
+                       BRDAC0, 10, 17),
+       AD5232_ID = DPOT_CONF(F_RDACS_RW_EEP | F_CMD_INC | F_SPI_16BIT,
+                       BRDAC0 | BRDAC1, 8, 18),
+       AD5233_ID = DPOT_CONF(F_RDACS_RW_EEP | F_CMD_INC | F_SPI_16BIT,
+                       BRDAC0 | BRDAC1 | BRDAC2 | BRDAC3, 6, 19),
+       AD5235_ID = DPOT_CONF(F_RDACS_RW_EEP | F_CMD_INC | F_SPI_24BIT,
+                       BRDAC0 | BRDAC1, 10, 20),
+       AD5260_ID = DPOT_CONF(F_RDACS_WONLY | F_AD_APPDATA | F_SPI_8BIT,
+                       BRDAC0, 8, 21),
+       AD5262_ID = DPOT_CONF(F_RDACS_WONLY | F_AD_APPDATA | F_SPI_16BIT,
+                       BRDAC0 | BRDAC1, 8, 22),
+       AD5263_ID = DPOT_CONF(F_RDACS_WONLY | F_AD_APPDATA | F_SPI_16BIT,
+                       BRDAC0 | BRDAC1 | BRDAC2 | BRDAC3, 8, 23),
+       AD5290_ID = DPOT_CONF(F_RDACS_WONLY | F_AD_APPDATA | F_SPI_8BIT,
+                       BRDAC0, 8, 24),
+       AD5291_ID = DPOT_CONF(F_RDACS_RW | F_SPI_16BIT, BRDAC0, 8, 25),
+       AD5292_ID = DPOT_CONF(F_RDACS_RW | F_SPI_16BIT, BRDAC0, 10, 26),
+       AD5293_ID = DPOT_CONF(F_RDACS_RW | F_SPI_16BIT, BRDAC0, 10, 27),
+       AD7376_ID = DPOT_CONF(F_RDACS_WONLY | F_AD_APPDATA | F_SPI_8BIT,
+                       BRDAC0, 7, 28),
+       AD8400_ID = DPOT_CONF(F_RDACS_WONLY | F_AD_APPDATA | F_SPI_8BIT,
+                       BRDAC0, 8, 29),
+       AD8402_ID = DPOT_CONF(F_RDACS_WONLY | F_AD_APPDATA | F_SPI_16BIT,
+                       BRDAC0 | BRDAC1, 8, 30),
+       AD8403_ID = DPOT_CONF(F_RDACS_WONLY | F_AD_APPDATA | F_SPI_16BIT,
+                       BRDAC0 | BRDAC1 | BRDAC2, 8, 31),
+       ADN2850_ID = DPOT_CONF(F_RDACS_RW_EEP | F_CMD_INC | F_SPI_24BIT,
+                       BRDAC0 | BRDAC1, 10, 32),
+};
+
+#define DPOT_RDAC0             0
+#define DPOT_RDAC1             1
+#define DPOT_RDAC2             2
+#define DPOT_RDAC3             3
+#define DPOT_RDAC4             4
+#define DPOT_RDAC5             5
+
+#define DPOT_RDAC_MASK         0x1F
+
+#define DPOT_REG_TOL           0x18
+#define DPOT_TOL_RDAC0         (DPOT_REG_TOL | DPOT_RDAC0)
+#define DPOT_TOL_RDAC1         (DPOT_REG_TOL | DPOT_RDAC1)
+#define DPOT_TOL_RDAC2         (DPOT_REG_TOL | DPOT_RDAC2)
+#define DPOT_TOL_RDAC3         (DPOT_REG_TOL | DPOT_RDAC3)
+#define DPOT_TOL_RDAC4         (DPOT_REG_TOL | DPOT_RDAC4)
+#define DPOT_TOL_RDAC5         (DPOT_REG_TOL | DPOT_RDAC5)
+
+/* RDAC-to-EEPROM Interface Commands */
+#define DPOT_ADDR_RDAC         (0x00 << 5)
+#define DPOT_ADDR_EEPROM       (0x01 << 5)
+#define DPOT_ADDR_CMD          (0x80)
+
+#define DPOT_DEC_ALL_6DB       (DPOT_ADDR_CMD | (0x4 << 3))
+#define DPOT_INC_ALL_6DB       (DPOT_ADDR_CMD | (0x9 << 3))
+#define DPOT_DEC_ALL           (DPOT_ADDR_CMD | (0x6 << 3))
+#define DPOT_INC_ALL           (DPOT_ADDR_CMD | (0xB << 3))
+
+#define DPOT_SPI_RDAC          0xB0
+#define DPOT_SPI_EEPROM                0x30
+#define DPOT_SPI_READ_RDAC     0xA0
+#define DPOT_SPI_READ_EEPROM   0x90
+#define DPOT_SPI_DEC_ALL_6DB   0x50
+#define DPOT_SPI_INC_ALL_6DB   0xD0
+#define DPOT_SPI_DEC_ALL       0x70
+#define DPOT_SPI_INC_ALL       0xF0
+
+/* AD5291/2/3 use special commands */
+#define DPOT_AD5291_RDAC       0x01
+#define DPOT_AD5291_READ_RDAC  0x02
+
+struct dpot_data;
+
+struct ad_dpot_bus_ops {
+       int (*read_d8) (void *client);
+       int (*read_r8d8) (void *client, u8 reg);
+       int (*read_r8d16) (void *client, u8 reg);
+       int (*write_d8) (void *client, u8 val);
+       int (*write_r8d8) (void *client, u8 reg, u8 val);
+       int (*write_r8d16) (void *client, u8 reg, u16 val);
+};
+
+struct ad_dpot_bus_data {
+       void *client;
+       const struct ad_dpot_bus_ops *bops;
+};
+
+struct ad_dpot_id {
+       char *name;
+       unsigned long devid;
+};
+
+int ad_dpot_probe(struct device *dev, struct ad_dpot_bus_data *bdata, const struct ad_dpot_id *id);
+int ad_dpot_remove(struct device *dev);
+
+#endif