Commit | Line | Data |
---|---|---|
4eb174be MH |
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); |