Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

ad525x_dpot: new driver for AD525x digital potentiometers

This driver supports the non-volatile digital potentiometers via I2C:
AD5258, AD5259, AD5251, AD5252, AD5253, AD5254, and AD5255

It provides a sysfs interface to each device for reading/writing which
is documented in Documentation/misc-devices/ad525x_dpot.txt.

Signed-off-by: Michael Hennerich <michael.hennerich@analog.com>
Signed-off-by: Chris Verges <chrisv@cyberswitching.com>
Signed-off-by: Mike Frysinger <vapier@gentoo.org>
Cc: Jean Delvare <khali@linux-fr.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>

authored by

Michael Hennerich and committed by
Linus Torvalds
4eb174be 00b55864

+738
+57
Documentation/misc-devices/ad525x_dpot.txt
··· 1 + --------------------------------- 2 + AD525x Digital Potentiometers 3 + --------------------------------- 4 + 5 + The ad525x_dpot driver exports a simple sysfs interface. This allows you to 6 + work with the immediate resistance settings as well as update the saved startup 7 + settings. Access to the factory programmed tolerance is also provided, but 8 + interpretation of this settings is required by the end application according to 9 + the specific part in use. 10 + 11 + --------- 12 + Files 13 + --------- 14 + 15 + Each dpot device will have a set of eeprom, rdac, and tolerance files. How 16 + many depends on the actual part you have, as will the range of allowed values. 17 + 18 + The eeprom files are used to program the startup value of the device. 19 + 20 + The rdac files are used to program the immediate value of the device. 21 + 22 + The tolerance files are the read-only factory programmed tolerance settings 23 + and may vary greatly on a part-by-part basis. For exact interpretation of 24 + this field, please consult the datasheet for your part. This is presented 25 + as a hex file for easier parsing. 26 + 27 + ----------- 28 + Example 29 + ----------- 30 + 31 + Locate the device in your sysfs tree. This is probably easiest by going into 32 + the common i2c directory and locating the device by the i2c slave address. 33 + 34 + # ls /sys/bus/i2c/devices/ 35 + 0-0022 0-0027 0-002f 36 + 37 + So assuming the device in question is on the first i2c bus and has the slave 38 + address of 0x2f, we descend (unrelated sysfs entries have been trimmed). 39 + 40 + # ls /sys/bus/i2c/devices/0-002f/ 41 + eeprom0 rdac0 tolerance0 42 + 43 + You can use simple reads/writes to access these files: 44 + 45 + # cd /sys/bus/i2c/devices/0-002f/ 46 + 47 + # cat eeprom0 48 + 0 49 + # echo 10 > eeprom0 50 + # cat eeprom0 51 + 10 52 + 53 + # cat rdac0 54 + 5 55 + # echo 3 > rdac0 56 + # cat rdac0 57 + 3
+14
drivers/misc/Kconfig
··· 13 13 14 14 if MISC_DEVICES 15 15 16 + config AD525X_DPOT 17 + tristate "Analog Devices AD525x Digital Potentiometers" 18 + depends on I2C && SYSFS 19 + help 20 + If you say yes here, you get support for the Analog Devices 21 + AD5258, AD5259, AD5251, AD5252, AD5253, AD5254 and AD5255 22 + digital potentiometer chips. 23 + 24 + See Documentation/misc-devices/ad525x_dpot.txt for the 25 + userspace interface. 26 + 27 + This driver can also be built as a module. If so, the module 28 + will be called ad525x_dpot. 29 + 16 30 config ATMEL_PWM 17 31 tristate "Atmel AT32/AT91 PWM support" 18 32 depends on AVR32 || ARCH_AT91SAM9263 || ARCH_AT91SAM9RL || ARCH_AT91CAP9
+1
drivers/misc/Makefile
··· 4 4 5 5 obj-$(CONFIG_IBM_ASM) += ibmasm/ 6 6 obj-$(CONFIG_HDPU_FEATURES) += hdpuftrs/ 7 + obj-$(CONFIG_AD525X_DPOT) += ad525x_dpot.o 7 8 obj-$(CONFIG_ATMEL_PWM) += atmel_pwm.o 8 9 obj-$(CONFIG_ATMEL_SSC) += atmel-ssc.o 9 10 obj-$(CONFIG_ATMEL_TCLIB) += atmel_tclib.o
+666
drivers/misc/ad525x_dpot.c
··· 1 + /* 2 + * ad525x_dpot: Driver for the Analog Devices AD525x digital potentiometers 3 + * Copyright (c) 2009 Analog Devices, Inc. 4 + * Author: Michael Hennerich <hennerich@blackfin.uclinux.org> 5 + * 6 + * DEVID #Wipers #Positions Resistor Options (kOhm) 7 + * AD5258 1 64 1, 10, 50, 100 8 + * AD5259 1 256 5, 10, 50, 100 9 + * AD5251 2 64 1, 10, 50, 100 10 + * AD5252 2 256 1, 10, 50, 100 11 + * AD5255 3 512 25, 250 12 + * AD5253 4 64 1, 10, 50, 100 13 + * AD5254 4 256 1, 10, 50, 100 14 + * 15 + * See Documentation/misc-devices/ad525x_dpot.txt for more info. 16 + * 17 + * derived from ad5258.c 18 + * Copyright (c) 2009 Cyber Switching, Inc. 19 + * Author: Chris Verges <chrisv@cyberswitching.com> 20 + * 21 + * derived from ad5252.c 22 + * Copyright (c) 2006 Michael Hennerich <hennerich@blackfin.uclinux.org> 23 + * 24 + * Licensed under the GPL-2 or later. 25 + */ 26 + 27 + #include <linux/module.h> 28 + #include <linux/device.h> 29 + #include <linux/kernel.h> 30 + #include <linux/init.h> 31 + #include <linux/slab.h> 32 + #include <linux/i2c.h> 33 + #include <linux/delay.h> 34 + 35 + #define DRIVER_NAME "ad525x_dpot" 36 + #define DRIVER_VERSION "0.1" 37 + 38 + enum dpot_devid { 39 + AD5258_ID, 40 + AD5259_ID, 41 + AD5251_ID, 42 + AD5252_ID, 43 + AD5253_ID, 44 + AD5254_ID, 45 + AD5255_ID, 46 + }; 47 + 48 + #define AD5258_MAX_POSITION 64 49 + #define AD5259_MAX_POSITION 256 50 + #define AD5251_MAX_POSITION 64 51 + #define AD5252_MAX_POSITION 256 52 + #define AD5253_MAX_POSITION 64 53 + #define AD5254_MAX_POSITION 256 54 + #define AD5255_MAX_POSITION 512 55 + 56 + #define AD525X_RDAC0 0 57 + #define AD525X_RDAC1 1 58 + #define AD525X_RDAC2 2 59 + #define AD525X_RDAC3 3 60 + 61 + #define AD525X_REG_TOL 0x18 62 + #define AD525X_TOL_RDAC0 (AD525X_REG_TOL | AD525X_RDAC0) 63 + #define AD525X_TOL_RDAC1 (AD525X_REG_TOL | AD525X_RDAC1) 64 + #define AD525X_TOL_RDAC2 (AD525X_REG_TOL | AD525X_RDAC2) 65 + #define AD525X_TOL_RDAC3 (AD525X_REG_TOL | AD525X_RDAC3) 66 + 67 + /* RDAC-to-EEPROM Interface Commands */ 68 + #define AD525X_I2C_RDAC (0x00 << 5) 69 + #define AD525X_I2C_EEPROM (0x01 << 5) 70 + #define AD525X_I2C_CMD (0x80) 71 + 72 + #define AD525X_DEC_ALL_6DB (AD525X_I2C_CMD | (0x4 << 3)) 73 + #define AD525X_INC_ALL_6DB (AD525X_I2C_CMD | (0x9 << 3)) 74 + #define AD525X_DEC_ALL (AD525X_I2C_CMD | (0x6 << 3)) 75 + #define AD525X_INC_ALL (AD525X_I2C_CMD | (0xB << 3)) 76 + 77 + static s32 ad525x_read(struct i2c_client *client, u8 reg); 78 + static s32 ad525x_write(struct i2c_client *client, u8 reg, u8 value); 79 + 80 + /* 81 + * Client data (each client gets its own) 82 + */ 83 + 84 + struct dpot_data { 85 + struct mutex update_lock; 86 + unsigned rdac_mask; 87 + unsigned max_pos; 88 + unsigned devid; 89 + }; 90 + 91 + /* sysfs functions */ 92 + 93 + static ssize_t sysfs_show_reg(struct device *dev, 94 + struct device_attribute *attr, char *buf, u32 reg) 95 + { 96 + struct i2c_client *client = to_i2c_client(dev); 97 + struct dpot_data *data = i2c_get_clientdata(client); 98 + s32 value; 99 + 100 + mutex_lock(&data->update_lock); 101 + value = ad525x_read(client, reg); 102 + mutex_unlock(&data->update_lock); 103 + 104 + if (value < 0) 105 + return -EINVAL; 106 + /* 107 + * Let someone else deal with converting this ... 108 + * the tolerance is a two-byte value where the MSB 109 + * is a sign + integer value, and the LSB is a 110 + * decimal value. See page 18 of the AD5258 111 + * datasheet (Rev. A) for more details. 112 + */ 113 + 114 + if (reg & AD525X_REG_TOL) 115 + return sprintf(buf, "0x%04x\n", value & 0xFFFF); 116 + else 117 + return sprintf(buf, "%u\n", value & data->rdac_mask); 118 + } 119 + 120 + static ssize_t sysfs_set_reg(struct device *dev, 121 + struct device_attribute *attr, 122 + const char *buf, size_t count, u32 reg) 123 + { 124 + struct i2c_client *client = to_i2c_client(dev); 125 + struct dpot_data *data = i2c_get_clientdata(client); 126 + unsigned long value; 127 + int err; 128 + 129 + err = strict_strtoul(buf, 10, &value); 130 + if (err) 131 + return err; 132 + 133 + if (value > data->rdac_mask) 134 + value = data->rdac_mask; 135 + 136 + mutex_lock(&data->update_lock); 137 + ad525x_write(client, reg, value); 138 + if (reg & AD525X_I2C_EEPROM) 139 + msleep(26); /* Sleep while the EEPROM updates */ 140 + mutex_unlock(&data->update_lock); 141 + 142 + return count; 143 + } 144 + 145 + static ssize_t sysfs_do_cmd(struct device *dev, 146 + struct device_attribute *attr, 147 + const char *buf, size_t count, u32 reg) 148 + { 149 + struct i2c_client *client = to_i2c_client(dev); 150 + struct dpot_data *data = i2c_get_clientdata(client); 151 + 152 + mutex_lock(&data->update_lock); 153 + ad525x_write(client, reg, 0); 154 + mutex_unlock(&data->update_lock); 155 + 156 + return count; 157 + } 158 + 159 + /* ------------------------------------------------------------------------- */ 160 + 161 + static ssize_t show_rdac0(struct device *dev, 162 + struct device_attribute *attr, char *buf) 163 + { 164 + return sysfs_show_reg(dev, attr, buf, AD525X_I2C_RDAC | AD525X_RDAC0); 165 + } 166 + 167 + static ssize_t set_rdac0(struct device *dev, 168 + struct device_attribute *attr, 169 + const char *buf, size_t count) 170 + { 171 + return sysfs_set_reg(dev, attr, buf, count, 172 + AD525X_I2C_RDAC | AD525X_RDAC0); 173 + } 174 + 175 + static DEVICE_ATTR(rdac0, S_IWUSR | S_IRUGO, show_rdac0, set_rdac0); 176 + 177 + static ssize_t show_eeprom0(struct device *dev, 178 + struct device_attribute *attr, char *buf) 179 + { 180 + return sysfs_show_reg(dev, attr, buf, AD525X_I2C_EEPROM | AD525X_RDAC0); 181 + } 182 + 183 + static ssize_t set_eeprom0(struct device *dev, 184 + struct device_attribute *attr, 185 + const char *buf, size_t count) 186 + { 187 + return sysfs_set_reg(dev, attr, buf, count, 188 + AD525X_I2C_EEPROM | AD525X_RDAC0); 189 + } 190 + 191 + static DEVICE_ATTR(eeprom0, S_IWUSR | S_IRUGO, show_eeprom0, set_eeprom0); 192 + 193 + static ssize_t show_tolerance0(struct device *dev, 194 + struct device_attribute *attr, char *buf) 195 + { 196 + return sysfs_show_reg(dev, attr, buf, 197 + AD525X_I2C_EEPROM | AD525X_TOL_RDAC0); 198 + } 199 + 200 + static DEVICE_ATTR(tolerance0, S_IRUGO, show_tolerance0, NULL); 201 + 202 + /* ------------------------------------------------------------------------- */ 203 + 204 + static ssize_t show_rdac1(struct device *dev, 205 + struct device_attribute *attr, char *buf) 206 + { 207 + return sysfs_show_reg(dev, attr, buf, AD525X_I2C_RDAC | AD525X_RDAC1); 208 + } 209 + 210 + static ssize_t set_rdac1(struct device *dev, 211 + struct device_attribute *attr, 212 + const char *buf, size_t count) 213 + { 214 + return sysfs_set_reg(dev, attr, buf, count, 215 + AD525X_I2C_RDAC | AD525X_RDAC1); 216 + } 217 + 218 + static DEVICE_ATTR(rdac1, S_IWUSR | S_IRUGO, show_rdac1, set_rdac1); 219 + 220 + static ssize_t show_eeprom1(struct device *dev, 221 + struct device_attribute *attr, char *buf) 222 + { 223 + return sysfs_show_reg(dev, attr, buf, AD525X_I2C_EEPROM | AD525X_RDAC1); 224 + } 225 + 226 + static ssize_t set_eeprom1(struct device *dev, 227 + struct device_attribute *attr, 228 + const char *buf, size_t count) 229 + { 230 + return sysfs_set_reg(dev, attr, buf, count, 231 + AD525X_I2C_EEPROM | AD525X_RDAC1); 232 + } 233 + 234 + static DEVICE_ATTR(eeprom1, S_IWUSR | S_IRUGO, show_eeprom1, set_eeprom1); 235 + 236 + static ssize_t show_tolerance1(struct device *dev, 237 + struct device_attribute *attr, char *buf) 238 + { 239 + return sysfs_show_reg(dev, attr, buf, 240 + AD525X_I2C_EEPROM | AD525X_TOL_RDAC1); 241 + } 242 + 243 + static DEVICE_ATTR(tolerance1, S_IRUGO, show_tolerance1, NULL); 244 + 245 + /* ------------------------------------------------------------------------- */ 246 + 247 + static ssize_t show_rdac2(struct device *dev, 248 + struct device_attribute *attr, char *buf) 249 + { 250 + return sysfs_show_reg(dev, attr, buf, AD525X_I2C_RDAC | AD525X_RDAC2); 251 + } 252 + 253 + static ssize_t set_rdac2(struct device *dev, 254 + struct device_attribute *attr, 255 + const char *buf, size_t count) 256 + { 257 + return sysfs_set_reg(dev, attr, buf, count, 258 + AD525X_I2C_RDAC | AD525X_RDAC2); 259 + } 260 + 261 + static DEVICE_ATTR(rdac2, S_IWUSR | S_IRUGO, show_rdac2, set_rdac2); 262 + 263 + static ssize_t show_eeprom2(struct device *dev, 264 + struct device_attribute *attr, char *buf) 265 + { 266 + return sysfs_show_reg(dev, attr, buf, AD525X_I2C_EEPROM | AD525X_RDAC2); 267 + } 268 + 269 + static ssize_t set_eeprom2(struct device *dev, 270 + struct device_attribute *attr, 271 + const char *buf, size_t count) 272 + { 273 + return sysfs_set_reg(dev, attr, buf, count, 274 + AD525X_I2C_EEPROM | AD525X_RDAC2); 275 + } 276 + 277 + static DEVICE_ATTR(eeprom2, S_IWUSR | S_IRUGO, show_eeprom2, set_eeprom2); 278 + 279 + static ssize_t show_tolerance2(struct device *dev, 280 + struct device_attribute *attr, char *buf) 281 + { 282 + return sysfs_show_reg(dev, attr, buf, 283 + AD525X_I2C_EEPROM | AD525X_TOL_RDAC2); 284 + } 285 + 286 + static DEVICE_ATTR(tolerance2, S_IRUGO, show_tolerance2, NULL); 287 + 288 + /* ------------------------------------------------------------------------- */ 289 + 290 + static ssize_t show_rdac3(struct device *dev, 291 + struct device_attribute *attr, char *buf) 292 + { 293 + return sysfs_show_reg(dev, attr, buf, AD525X_I2C_RDAC | AD525X_RDAC3); 294 + } 295 + 296 + static ssize_t set_rdac3(struct device *dev, 297 + struct device_attribute *attr, 298 + const char *buf, size_t count) 299 + { 300 + return sysfs_set_reg(dev, attr, buf, count, 301 + AD525X_I2C_RDAC | AD525X_RDAC3); 302 + } 303 + 304 + static DEVICE_ATTR(rdac3, S_IWUSR | S_IRUGO, show_rdac3, set_rdac3); 305 + 306 + static ssize_t show_eeprom3(struct device *dev, 307 + struct device_attribute *attr, char *buf) 308 + { 309 + return sysfs_show_reg(dev, attr, buf, AD525X_I2C_EEPROM | AD525X_RDAC3); 310 + } 311 + 312 + static ssize_t set_eeprom3(struct device *dev, 313 + struct device_attribute *attr, 314 + const char *buf, size_t count) 315 + { 316 + return sysfs_set_reg(dev, attr, buf, count, 317 + AD525X_I2C_EEPROM | AD525X_RDAC3); 318 + } 319 + 320 + static DEVICE_ATTR(eeprom3, S_IWUSR | S_IRUGO, show_eeprom3, set_eeprom3); 321 + 322 + static ssize_t show_tolerance3(struct device *dev, 323 + struct device_attribute *attr, char *buf) 324 + { 325 + return sysfs_show_reg(dev, attr, buf, 326 + AD525X_I2C_EEPROM | AD525X_TOL_RDAC3); 327 + } 328 + 329 + static DEVICE_ATTR(tolerance3, S_IRUGO, show_tolerance3, NULL); 330 + 331 + static struct attribute *ad525x_attributes_wipers[4][4] = { 332 + { 333 + &dev_attr_rdac0.attr, 334 + &dev_attr_eeprom0.attr, 335 + &dev_attr_tolerance0.attr, 336 + NULL 337 + }, { 338 + &dev_attr_rdac1.attr, 339 + &dev_attr_eeprom1.attr, 340 + &dev_attr_tolerance1.attr, 341 + NULL 342 + }, { 343 + &dev_attr_rdac2.attr, 344 + &dev_attr_eeprom2.attr, 345 + &dev_attr_tolerance2.attr, 346 + NULL 347 + }, { 348 + &dev_attr_rdac3.attr, 349 + &dev_attr_eeprom3.attr, 350 + &dev_attr_tolerance3.attr, 351 + NULL 352 + } 353 + }; 354 + 355 + static const struct attribute_group ad525x_group_wipers[] = { 356 + {.attrs = ad525x_attributes_wipers[AD525X_RDAC0]}, 357 + {.attrs = ad525x_attributes_wipers[AD525X_RDAC1]}, 358 + {.attrs = ad525x_attributes_wipers[AD525X_RDAC2]}, 359 + {.attrs = ad525x_attributes_wipers[AD525X_RDAC3]}, 360 + }; 361 + 362 + /* ------------------------------------------------------------------------- */ 363 + 364 + static ssize_t set_inc_all(struct device *dev, 365 + struct device_attribute *attr, 366 + const char *buf, size_t count) 367 + { 368 + return sysfs_do_cmd(dev, attr, buf, count, AD525X_INC_ALL); 369 + } 370 + 371 + static DEVICE_ATTR(inc_all, S_IWUSR, NULL, set_inc_all); 372 + 373 + static ssize_t set_dec_all(struct device *dev, 374 + struct device_attribute *attr, 375 + const char *buf, size_t count) 376 + { 377 + return sysfs_do_cmd(dev, attr, buf, count, AD525X_DEC_ALL); 378 + } 379 + 380 + static DEVICE_ATTR(dec_all, S_IWUSR, NULL, set_dec_all); 381 + 382 + static ssize_t set_inc_all_6db(struct device *dev, 383 + struct device_attribute *attr, 384 + const char *buf, size_t count) 385 + { 386 + return sysfs_do_cmd(dev, attr, buf, count, AD525X_INC_ALL_6DB); 387 + } 388 + 389 + static DEVICE_ATTR(inc_all_6db, S_IWUSR, NULL, set_inc_all_6db); 390 + 391 + static ssize_t set_dec_all_6db(struct device *dev, 392 + struct device_attribute *attr, 393 + const char *buf, size_t count) 394 + { 395 + return sysfs_do_cmd(dev, attr, buf, count, AD525X_DEC_ALL_6DB); 396 + } 397 + 398 + static DEVICE_ATTR(dec_all_6db, S_IWUSR, NULL, set_dec_all_6db); 399 + 400 + static struct attribute *ad525x_attributes_commands[] = { 401 + &dev_attr_inc_all.attr, 402 + &dev_attr_dec_all.attr, 403 + &dev_attr_inc_all_6db.attr, 404 + &dev_attr_dec_all_6db.attr, 405 + NULL 406 + }; 407 + 408 + static const struct attribute_group ad525x_group_commands = { 409 + .attrs = ad525x_attributes_commands, 410 + }; 411 + 412 + /* ------------------------------------------------------------------------- */ 413 + 414 + /* i2c device functions */ 415 + 416 + /** 417 + * ad525x_read - return the value contained in the specified register 418 + * on the AD5258 device. 419 + * @client: value returned from i2c_new_device() 420 + * @reg: the register to read 421 + * 422 + * If the tolerance register is specified, 2 bytes are returned. 423 + * Otherwise, 1 byte is returned. A negative value indicates an error 424 + * occurred while reading the register. 425 + */ 426 + static s32 ad525x_read(struct i2c_client *client, u8 reg) 427 + { 428 + struct dpot_data *data = i2c_get_clientdata(client); 429 + 430 + if ((reg & AD525X_REG_TOL) || (data->max_pos > 256)) 431 + return i2c_smbus_read_word_data(client, (reg & 0xF8) | 432 + ((reg & 0x7) << 1)); 433 + else 434 + return i2c_smbus_read_byte_data(client, reg); 435 + } 436 + 437 + /** 438 + * ad525x_write - store the given value in the specified register on 439 + * the AD5258 device. 440 + * @client: value returned from i2c_new_device() 441 + * @reg: the register to write 442 + * @value: the byte to store in the register 443 + * 444 + * For certain instructions that do not require a data byte, "NULL" 445 + * should be specified for the "value" parameter. These instructions 446 + * include NOP, RESTORE_FROM_EEPROM, and STORE_TO_EEPROM. 447 + * 448 + * A negative return value indicates an error occurred while reading 449 + * the register. 450 + */ 451 + static s32 ad525x_write(struct i2c_client *client, u8 reg, u8 value) 452 + { 453 + struct dpot_data *data = i2c_get_clientdata(client); 454 + 455 + /* Only write the instruction byte for certain commands */ 456 + if (reg & AD525X_I2C_CMD) 457 + return i2c_smbus_write_byte(client, reg); 458 + 459 + if (data->max_pos > 256) 460 + return i2c_smbus_write_word_data(client, (reg & 0xF8) | 461 + ((reg & 0x7) << 1), value); 462 + else 463 + /* All other registers require instruction + data bytes */ 464 + return i2c_smbus_write_byte_data(client, reg, value); 465 + } 466 + 467 + static int ad525x_probe(struct i2c_client *client, 468 + const struct i2c_device_id *id) 469 + { 470 + struct device *dev = &client->dev; 471 + struct dpot_data *data; 472 + int err = 0; 473 + 474 + dev_dbg(dev, "%s\n", __func__); 475 + 476 + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE)) { 477 + dev_err(dev, "missing I2C functionality for this driver\n"); 478 + goto exit; 479 + } 480 + 481 + data = kzalloc(sizeof(struct dpot_data), GFP_KERNEL); 482 + if (!data) { 483 + err = -ENOMEM; 484 + goto exit; 485 + } 486 + 487 + i2c_set_clientdata(client, data); 488 + mutex_init(&data->update_lock); 489 + 490 + switch (id->driver_data) { 491 + case AD5258_ID: 492 + data->max_pos = AD5258_MAX_POSITION; 493 + err = sysfs_create_group(&dev->kobj, 494 + &ad525x_group_wipers[AD525X_RDAC0]); 495 + break; 496 + case AD5259_ID: 497 + data->max_pos = AD5259_MAX_POSITION; 498 + err = sysfs_create_group(&dev->kobj, 499 + &ad525x_group_wipers[AD525X_RDAC0]); 500 + break; 501 + case AD5251_ID: 502 + data->max_pos = AD5251_MAX_POSITION; 503 + err = sysfs_create_group(&dev->kobj, 504 + &ad525x_group_wipers[AD525X_RDAC1]); 505 + err |= sysfs_create_group(&dev->kobj, 506 + &ad525x_group_wipers[AD525X_RDAC3]); 507 + err |= sysfs_create_group(&dev->kobj, &ad525x_group_commands); 508 + break; 509 + case AD5252_ID: 510 + data->max_pos = AD5252_MAX_POSITION; 511 + err = sysfs_create_group(&dev->kobj, 512 + &ad525x_group_wipers[AD525X_RDAC1]); 513 + err |= sysfs_create_group(&dev->kobj, 514 + &ad525x_group_wipers[AD525X_RDAC3]); 515 + err |= sysfs_create_group(&dev->kobj, &ad525x_group_commands); 516 + break; 517 + case AD5253_ID: 518 + data->max_pos = AD5253_MAX_POSITION; 519 + err = sysfs_create_group(&dev->kobj, 520 + &ad525x_group_wipers[AD525X_RDAC0]); 521 + err |= sysfs_create_group(&dev->kobj, 522 + &ad525x_group_wipers[AD525X_RDAC1]); 523 + err |= sysfs_create_group(&dev->kobj, 524 + &ad525x_group_wipers[AD525X_RDAC2]); 525 + err |= sysfs_create_group(&dev->kobj, 526 + &ad525x_group_wipers[AD525X_RDAC3]); 527 + err |= sysfs_create_group(&dev->kobj, &ad525x_group_commands); 528 + break; 529 + case AD5254_ID: 530 + data->max_pos = AD5254_MAX_POSITION; 531 + err = sysfs_create_group(&dev->kobj, 532 + &ad525x_group_wipers[AD525X_RDAC0]); 533 + err |= sysfs_create_group(&dev->kobj, 534 + &ad525x_group_wipers[AD525X_RDAC1]); 535 + err |= sysfs_create_group(&dev->kobj, 536 + &ad525x_group_wipers[AD525X_RDAC2]); 537 + err |= sysfs_create_group(&dev->kobj, 538 + &ad525x_group_wipers[AD525X_RDAC3]); 539 + err |= sysfs_create_group(&dev->kobj, &ad525x_group_commands); 540 + break; 541 + case AD5255_ID: 542 + data->max_pos = AD5255_MAX_POSITION; 543 + err = sysfs_create_group(&dev->kobj, 544 + &ad525x_group_wipers[AD525X_RDAC0]); 545 + err |= sysfs_create_group(&dev->kobj, 546 + &ad525x_group_wipers[AD525X_RDAC1]); 547 + err |= sysfs_create_group(&dev->kobj, 548 + &ad525x_group_wipers[AD525X_RDAC2]); 549 + err |= sysfs_create_group(&dev->kobj, &ad525x_group_commands); 550 + break; 551 + default: 552 + err = -ENODEV; 553 + goto exit_free; 554 + } 555 + 556 + if (err) { 557 + dev_err(dev, "failed to register sysfs hooks\n"); 558 + goto exit_free; 559 + } 560 + 561 + data->devid = id->driver_data; 562 + data->rdac_mask = data->max_pos - 1; 563 + 564 + dev_info(dev, "%s %d-Position Digital Potentiometer registered\n", 565 + id->name, data->max_pos); 566 + 567 + return 0; 568 + 569 + exit_free: 570 + kfree(data); 571 + i2c_set_clientdata(client, NULL); 572 + exit: 573 + dev_err(dev, "failed to create client\n"); 574 + return err; 575 + } 576 + 577 + static int __devexit ad525x_remove(struct i2c_client *client) 578 + { 579 + struct dpot_data *data = i2c_get_clientdata(client); 580 + struct device *dev = &client->dev; 581 + 582 + switch (data->devid) { 583 + case AD5258_ID: 584 + case AD5259_ID: 585 + sysfs_remove_group(&dev->kobj, 586 + &ad525x_group_wipers[AD525X_RDAC0]); 587 + break; 588 + case AD5251_ID: 589 + case AD5252_ID: 590 + sysfs_remove_group(&dev->kobj, 591 + &ad525x_group_wipers[AD525X_RDAC1]); 592 + sysfs_remove_group(&dev->kobj, 593 + &ad525x_group_wipers[AD525X_RDAC3]); 594 + sysfs_remove_group(&dev->kobj, &ad525x_group_commands); 595 + break; 596 + case AD5253_ID: 597 + case AD5254_ID: 598 + sysfs_remove_group(&dev->kobj, 599 + &ad525x_group_wipers[AD525X_RDAC0]); 600 + sysfs_remove_group(&dev->kobj, 601 + &ad525x_group_wipers[AD525X_RDAC1]); 602 + sysfs_remove_group(&dev->kobj, 603 + &ad525x_group_wipers[AD525X_RDAC2]); 604 + sysfs_remove_group(&dev->kobj, 605 + &ad525x_group_wipers[AD525X_RDAC3]); 606 + sysfs_remove_group(&dev->kobj, &ad525x_group_commands); 607 + break; 608 + case AD5255_ID: 609 + sysfs_remove_group(&dev->kobj, 610 + &ad525x_group_wipers[AD525X_RDAC0]); 611 + sysfs_remove_group(&dev->kobj, 612 + &ad525x_group_wipers[AD525X_RDAC1]); 613 + sysfs_remove_group(&dev->kobj, 614 + &ad525x_group_wipers[AD525X_RDAC2]); 615 + sysfs_remove_group(&dev->kobj, &ad525x_group_commands); 616 + break; 617 + } 618 + 619 + i2c_set_clientdata(client, NULL); 620 + kfree(data); 621 + 622 + return 0; 623 + } 624 + 625 + static const struct i2c_device_id ad525x_idtable[] = { 626 + {"ad5258", AD5258_ID}, 627 + {"ad5259", AD5259_ID}, 628 + {"ad5251", AD5251_ID}, 629 + {"ad5252", AD5252_ID}, 630 + {"ad5253", AD5253_ID}, 631 + {"ad5254", AD5254_ID}, 632 + {"ad5255", AD5255_ID}, 633 + {} 634 + }; 635 + 636 + MODULE_DEVICE_TABLE(i2c, ad525x_idtable); 637 + 638 + static struct i2c_driver ad525x_driver = { 639 + .driver = { 640 + .owner = THIS_MODULE, 641 + .name = DRIVER_NAME, 642 + }, 643 + .id_table = ad525x_idtable, 644 + .probe = ad525x_probe, 645 + .remove = __devexit_p(ad525x_remove), 646 + }; 647 + 648 + static int __init ad525x_init(void) 649 + { 650 + return i2c_add_driver(&ad525x_driver); 651 + } 652 + 653 + module_init(ad525x_init); 654 + 655 + static void __exit ad525x_exit(void) 656 + { 657 + i2c_del_driver(&ad525x_driver); 658 + } 659 + 660 + module_exit(ad525x_exit); 661 + 662 + MODULE_AUTHOR("Chris Verges <chrisv@cyberswitching.com>, " 663 + "Michael Hennerich <hennerich@blackfin.uclinux.org>, "); 664 + MODULE_DESCRIPTION("AD5258/9 digital potentiometer driver"); 665 + MODULE_LICENSE("GPL"); 666 + MODULE_VERSION(DRIVER_VERSION);