lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite for Android: free password hash cracker in your pocket
[<prev] [next>] [<thread-prev] [day] [month] [year] [list]
Date:	Sun,  9 May 2010 06:15:26 -0400
From:	Mike Frysinger <vapier@...too.org>
To:	linux-kernel@...r.kernel.org,
	Andrew Morton <akpm@...ux-foundation.org>
Cc:	uclinux-dist-devel@...ckfin.uclinux.org,
	Michael Hennerich <michael.hennerich@...log.com>
Subject: [PATCH 3/6] ad525x_dpot: add support for SPI parts

From: Michael Hennerich <michael.hennerich@...log.com>

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

Signed-off-by: Michael Hennerich <michael.hennerich@...log.com>
Signed-off-by: Mike Frysinger <vapier@...too.org>
---
 drivers/misc/Kconfig           |   30 ++-
 drivers/misc/Makefile          |    2 +
 drivers/misc/ad525x_dpot-i2c.c |  119 ++++++++
 drivers/misc/ad525x_dpot-spi.c |  172 +++++++++++
 drivers/misc/ad525x_dpot.c     |  628 ++++++++++++++++++++--------------------
 drivers/misc/ad525x_dpot.h     |  173 +++++++++++
 6 files changed, 805 insertions(+), 319 deletions(-)
 create mode 100644 drivers/misc/ad525x_dpot-i2c.c
 create mode 100644 drivers/misc/ad525x_dpot-spi.c
 create mode 100644 drivers/misc/ad525x_dpot.h

diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index 0d0d625..69e019e 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -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
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index 7b6f7ee..902cc48 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -5,6 +5,8 @@
 obj-$(CONFIG_IBM_ASM)		+= ibmasm/
 obj-$(CONFIG_HDPU_FEATURES)	+= hdpuftrs/
 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
index 0000000..971e61d
--- /dev/null
+++ b/drivers/misc/ad525x_dpot-i2c.c
@@ -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@...ckfin.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
index 0000000..b8c6df9
--- /dev/null
+++ b/drivers/misc/ad525x_dpot-spi.c
@@ -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@...ckfin.uclinux.org>");
+MODULE_DESCRIPTION("digital potentiometer SPI bus driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("spi:ad_dpot");
diff --git a/drivers/misc/ad525x_dpot.c b/drivers/misc/ad525x_dpot.c
index ce92088..4919204 100644
--- a/drivers/misc/ad525x_dpot.c
+++ b/drivers/misc/ad525x_dpot.c
@@ -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@...ckfin.uclinux.org>
  *
  * DEVID		#Wipers		#Positions 	Resistor Options (kOhm)
@@ -11,6 +11,32 @@
  * 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.
  *
@@ -28,77 +54,181 @@
 #include <linux/device.h>
 #include <linux/kernel.h>
 #include <linux/init.h>
-#include <linux/slab.h>
-#include <linux/i2c.h>
 #include <linux/delay.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 +241,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 +251,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 +263,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 +275,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 +310,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 +375,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 +392,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 +437,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@...erswitching.com>, "
-	      "Michael Hennerich <hennerich@...ckfin.uclinux.org>, ");
-MODULE_DESCRIPTION("AD5258/9 digital potentiometer driver");
+	      "Michael Hennerich <hennerich@...ckfin.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
index 0000000..99b388e
--- /dev/null
+++ b/drivers/misc/ad525x_dpot.h
@@ -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
-- 
1.7.1

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ