diff options
author | Michael Hennerich <michael.hennerich@analog.com> | 2010-05-25 01:33:14 +0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2010-05-25 19:07:04 +0400 |
commit | 6c536e4ce8edd61fdc4ab68e19ae164a54fc958f (patch) | |
tree | 1d09ef6de4c04a4bd597b060d668fce2eac14681 /drivers/misc/ad525x_dpot.c | |
parent | 0c53b9fbcca8870e4f4b248f4ed5fdadd43a01b6 (diff) | |
download | linux-6c536e4ce8edd61fdc4ab68e19ae164a54fc958f.tar.xz |
ad525x_dpot: add support for SPI parts
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>
Diffstat (limited to 'drivers/misc/ad525x_dpot.c')
-rw-r--r-- | drivers/misc/ad525x_dpot.c | 629 |
1 files changed, 313 insertions, 316 deletions
diff --git a/drivers/misc/ad525x_dpot.c b/drivers/misc/ad525x_dpot.c index ce92088bf0b8..a41c2de0eae8 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@blackfin.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,182 @@ #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); |