diff options
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 @@ | |||
1 | /* | 1 | /* |
2 | * ad525x_dpot: Driver for the Analog Devices AD525x digital potentiometers | 2 | * ad525x_dpot: Driver for the Analog Devices digital potentiometers |
3 | * Copyright (c) 2009 Analog Devices, Inc. | 3 | * Copyright (c) 2009-2010 Analog Devices, Inc. |
4 | * Author: Michael Hennerich <hennerich@blackfin.uclinux.org> | 4 | * Author: Michael Hennerich <hennerich@blackfin.uclinux.org> |
5 | * | 5 | * |
6 | * DEVID #Wipers #Positions Resistor Options (kOhm) | 6 | * DEVID #Wipers #Positions Resistor Options (kOhm) |
@@ -11,6 +11,32 @@ | |||
11 | * AD5255 3 512 25, 250 | 11 | * AD5255 3 512 25, 250 |
12 | * AD5253 4 64 1, 10, 50, 100 | 12 | * AD5253 4 64 1, 10, 50, 100 |
13 | * AD5254 4 256 1, 10, 50, 100 | 13 | * AD5254 4 256 1, 10, 50, 100 |
14 | * AD5160 1 256 5, 10, 50, 100 | ||
15 | * AD5161 1 256 5, 10, 50, 100 | ||
16 | * AD5162 2 256 2.5, 10, 50, 100 | ||
17 | * AD5165 1 256 100 | ||
18 | * AD5200 1 256 10, 50 | ||
19 | * AD5201 1 33 10, 50 | ||
20 | * AD5203 4 64 10, 100 | ||
21 | * AD5204 4 256 10, 50, 100 | ||
22 | * AD5206 6 256 10, 50, 100 | ||
23 | * AD5207 2 256 10, 50, 100 | ||
24 | * AD5231 1 1024 10, 50, 100 | ||
25 | * AD5232 2 256 10, 50, 100 | ||
26 | * AD5233 4 64 10, 50, 100 | ||
27 | * AD5235 2 1024 25, 250 | ||
28 | * AD5260 1 256 20, 50, 200 | ||
29 | * AD5262 2 256 20, 50, 200 | ||
30 | * AD5263 4 256 20, 50, 200 | ||
31 | * AD5290 1 256 10, 50, 100 | ||
32 | * AD5291 1 256 20 | ||
33 | * AD5292 1 1024 20 | ||
34 | * AD5293 1 1024 20 | ||
35 | * AD7376 1 128 10, 50, 100, 1M | ||
36 | * AD8400 1 256 1, 10, 50, 100 | ||
37 | * AD8402 2 256 1, 10, 50, 100 | ||
38 | * AD8403 4 256 1, 10, 50, 100 | ||
39 | * ADN2850 3 512 25, 250 | ||
14 | * | 40 | * |
15 | * See Documentation/misc-devices/ad525x_dpot.txt for more info. | 41 | * See Documentation/misc-devices/ad525x_dpot.txt for more info. |
16 | * | 42 | * |
@@ -28,77 +54,182 @@ | |||
28 | #include <linux/device.h> | 54 | #include <linux/device.h> |
29 | #include <linux/kernel.h> | 55 | #include <linux/kernel.h> |
30 | #include <linux/init.h> | 56 | #include <linux/init.h> |
31 | #include <linux/slab.h> | ||
32 | #include <linux/i2c.h> | ||
33 | #include <linux/delay.h> | 57 | #include <linux/delay.h> |
58 | #include <linux/slab.h> | ||
34 | 59 | ||
35 | #define DRIVER_NAME "ad525x_dpot" | 60 | #define DRIVER_VERSION "0.2" |
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 | 61 | ||
48 | #define AD5258_MAX_POSITION 64 | 62 | #include "ad525x_dpot.h" |
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, u16 value); | ||
79 | 63 | ||
80 | /* | 64 | /* |
81 | * Client data (each client gets its own) | 65 | * Client data (each client gets its own) |
82 | */ | 66 | */ |
83 | 67 | ||
84 | struct dpot_data { | 68 | struct dpot_data { |
69 | struct ad_dpot_bus_data bdata; | ||
85 | struct mutex update_lock; | 70 | struct mutex update_lock; |
86 | unsigned rdac_mask; | 71 | unsigned rdac_mask; |
87 | unsigned max_pos; | 72 | unsigned max_pos; |
88 | unsigned devid; | 73 | unsigned long devid; |
74 | unsigned uid; | ||
75 | unsigned feat; | ||
76 | unsigned wipers; | ||
77 | u16 rdac_cache[8]; | ||
89 | }; | 78 | }; |
90 | 79 | ||
80 | static inline int dpot_read_d8(struct dpot_data *dpot) | ||
81 | { | ||
82 | return dpot->bdata.bops->read_d8(dpot->bdata.client); | ||
83 | } | ||
84 | |||
85 | static inline int dpot_read_r8d8(struct dpot_data *dpot, u8 reg) | ||
86 | { | ||
87 | return dpot->bdata.bops->read_r8d8(dpot->bdata.client, reg); | ||
88 | } | ||
89 | |||
90 | static inline int dpot_read_r8d16(struct dpot_data *dpot, u8 reg) | ||
91 | { | ||
92 | return dpot->bdata.bops->read_r8d16(dpot->bdata.client, reg); | ||
93 | } | ||
94 | |||
95 | static inline int dpot_write_d8(struct dpot_data *dpot, u8 val) | ||
96 | { | ||
97 | return dpot->bdata.bops->write_d8(dpot->bdata.client, val); | ||
98 | } | ||
99 | |||
100 | static inline int dpot_write_r8d8(struct dpot_data *dpot, u8 reg, u16 val) | ||
101 | { | ||
102 | return dpot->bdata.bops->write_r8d8(dpot->bdata.client, reg, val); | ||
103 | } | ||
104 | |||
105 | static inline int dpot_write_r8d16(struct dpot_data *dpot, u8 reg, u16 val) | ||
106 | { | ||
107 | return dpot->bdata.bops->write_r8d16(dpot->bdata.client, reg, val); | ||
108 | } | ||
109 | |||
110 | static s32 dpot_read(struct dpot_data *dpot, u8 reg) | ||
111 | { | ||
112 | unsigned val = 0; | ||
113 | |||
114 | if (dpot->feat & F_SPI) { | ||
115 | if (!(reg & (DPOT_ADDR_EEPROM | DPOT_ADDR_CMD))) { | ||
116 | |||
117 | if (dpot->feat & F_RDACS_WONLY) | ||
118 | return dpot->rdac_cache[reg & DPOT_RDAC_MASK]; | ||
119 | |||
120 | if (dpot->uid == DPOT_UID(AD5291_ID) || | ||
121 | dpot->uid == DPOT_UID(AD5292_ID) || | ||
122 | dpot->uid == DPOT_UID(AD5293_ID)) | ||
123 | return dpot_read_r8d8(dpot, | ||
124 | DPOT_AD5291_READ_RDAC << 2); | ||
125 | |||
126 | val = DPOT_SPI_READ_RDAC; | ||
127 | } else if (reg & DPOT_ADDR_EEPROM) { | ||
128 | val = DPOT_SPI_READ_EEPROM; | ||
129 | } | ||
130 | |||
131 | if (dpot->feat & F_SPI_16BIT) | ||
132 | return dpot_read_r8d8(dpot, val); | ||
133 | else if (dpot->feat & F_SPI_24BIT) | ||
134 | return dpot_read_r8d16(dpot, val); | ||
135 | |||
136 | } else { /* I2C */ | ||
137 | |||
138 | if ((reg & DPOT_REG_TOL) || (dpot->max_pos > 256)) | ||
139 | return dpot_read_r8d16(dpot, (reg & 0xF8) | | ||
140 | ((reg & 0x7) << 1)); | ||
141 | else | ||
142 | return dpot_read_r8d8(dpot, reg); | ||
143 | |||
144 | } | ||
145 | return -EFAULT; | ||
146 | } | ||
147 | |||
148 | static s32 dpot_write(struct dpot_data *dpot, u8 reg, u16 value) | ||
149 | { | ||
150 | unsigned val = 0; | ||
151 | |||
152 | if (dpot->feat & F_SPI) { | ||
153 | if (!(reg & (DPOT_ADDR_EEPROM | DPOT_ADDR_CMD))) { | ||
154 | if (dpot->feat & F_RDACS_WONLY) | ||
155 | dpot->rdac_cache[reg & DPOT_RDAC_MASK] = value; | ||
156 | |||
157 | if (dpot->feat & F_AD_APPDATA) { | ||
158 | if (dpot->feat & F_SPI_8BIT) { | ||
159 | val = ((reg & DPOT_RDAC_MASK) << | ||
160 | DPOT_MAX_POS(dpot->devid)) | | ||
161 | value; | ||
162 | return dpot_write_d8(dpot, val); | ||
163 | } else if (dpot->feat & F_SPI_16BIT) { | ||
164 | val = ((reg & DPOT_RDAC_MASK) << | ||
165 | DPOT_MAX_POS(dpot->devid)) | | ||
166 | value; | ||
167 | return dpot_write_r8d8(dpot, val >> 8, | ||
168 | val & 0xFF); | ||
169 | } else | ||
170 | BUG(); | ||
171 | } else { | ||
172 | if (dpot->uid == DPOT_UID(AD5291_ID) || | ||
173 | dpot->uid == DPOT_UID(AD5292_ID) || | ||
174 | dpot->uid == DPOT_UID(AD5293_ID)) | ||
175 | return dpot_write_r8d8(dpot, | ||
176 | (DPOT_AD5291_RDAC << 2) | | ||
177 | (value >> 8), value & 0xFF); | ||
178 | |||
179 | val = DPOT_SPI_RDAC | (reg & DPOT_RDAC_MASK); | ||
180 | } | ||
181 | } else if (reg & DPOT_ADDR_EEPROM) { | ||
182 | val = DPOT_SPI_EEPROM | (reg & DPOT_RDAC_MASK); | ||
183 | } else if (reg & DPOT_ADDR_CMD) { | ||
184 | switch (reg) { | ||
185 | case DPOT_DEC_ALL_6DB: | ||
186 | val = DPOT_SPI_DEC_ALL_6DB; | ||
187 | break; | ||
188 | case DPOT_INC_ALL_6DB: | ||
189 | val = DPOT_SPI_INC_ALL_6DB; | ||
190 | break; | ||
191 | case DPOT_DEC_ALL: | ||
192 | val = DPOT_SPI_DEC_ALL; | ||
193 | break; | ||
194 | case DPOT_INC_ALL: | ||
195 | val = DPOT_SPI_INC_ALL; | ||
196 | break; | ||
197 | } | ||
198 | } else | ||
199 | BUG(); | ||
200 | |||
201 | if (dpot->feat & F_SPI_16BIT) | ||
202 | return dpot_write_r8d8(dpot, val, value); | ||
203 | else if (dpot->feat & F_SPI_24BIT) | ||
204 | return dpot_write_r8d16(dpot, val, value); | ||
205 | } else { | ||
206 | /* Only write the instruction byte for certain commands */ | ||
207 | if (reg & DPOT_ADDR_CMD) | ||
208 | return dpot_write_d8(dpot, reg); | ||
209 | |||
210 | if (dpot->max_pos > 256) | ||
211 | return dpot_write_r8d16(dpot, (reg & 0xF8) | | ||
212 | ((reg & 0x7) << 1), value); | ||
213 | else | ||
214 | /* All other registers require instruction + data bytes */ | ||
215 | return dpot_write_r8d8(dpot, reg, value); | ||
216 | |||
217 | } | ||
218 | |||
219 | return -EFAULT; | ||
220 | } | ||
221 | |||
91 | /* sysfs functions */ | 222 | /* sysfs functions */ |
92 | 223 | ||
93 | static ssize_t sysfs_show_reg(struct device *dev, | 224 | static ssize_t sysfs_show_reg(struct device *dev, |
94 | struct device_attribute *attr, char *buf, u32 reg) | 225 | struct device_attribute *attr, |
226 | char *buf, u32 reg) | ||
95 | { | 227 | { |
96 | struct i2c_client *client = to_i2c_client(dev); | 228 | struct dpot_data *data = dev_get_drvdata(dev); |
97 | struct dpot_data *data = i2c_get_clientdata(client); | ||
98 | s32 value; | 229 | s32 value; |
99 | 230 | ||
100 | mutex_lock(&data->update_lock); | 231 | mutex_lock(&data->update_lock); |
101 | value = ad525x_read(client, reg); | 232 | value = dpot_read(data, reg); |
102 | mutex_unlock(&data->update_lock); | 233 | mutex_unlock(&data->update_lock); |
103 | 234 | ||
104 | if (value < 0) | 235 | if (value < 0) |
@@ -111,7 +242,7 @@ static ssize_t sysfs_show_reg(struct device *dev, | |||
111 | * datasheet (Rev. A) for more details. | 242 | * datasheet (Rev. A) for more details. |
112 | */ | 243 | */ |
113 | 244 | ||
114 | if (reg & AD525X_REG_TOL) | 245 | if (reg & DPOT_REG_TOL) |
115 | return sprintf(buf, "0x%04x\n", value & 0xFFFF); | 246 | return sprintf(buf, "0x%04x\n", value & 0xFFFF); |
116 | else | 247 | else |
117 | return sprintf(buf, "%u\n", value & data->rdac_mask); | 248 | return sprintf(buf, "%u\n", value & data->rdac_mask); |
@@ -121,8 +252,7 @@ static ssize_t sysfs_set_reg(struct device *dev, | |||
121 | struct device_attribute *attr, | 252 | struct device_attribute *attr, |
122 | const char *buf, size_t count, u32 reg) | 253 | const char *buf, size_t count, u32 reg) |
123 | { | 254 | { |
124 | struct i2c_client *client = to_i2c_client(dev); | 255 | struct dpot_data *data = dev_get_drvdata(dev); |
125 | struct dpot_data *data = i2c_get_clientdata(client); | ||
126 | unsigned long value; | 256 | unsigned long value; |
127 | int err; | 257 | int err; |
128 | 258 | ||
@@ -134,8 +264,8 @@ static ssize_t sysfs_set_reg(struct device *dev, | |||
134 | value = data->rdac_mask; | 264 | value = data->rdac_mask; |
135 | 265 | ||
136 | mutex_lock(&data->update_lock); | 266 | mutex_lock(&data->update_lock); |
137 | ad525x_write(client, reg, value); | 267 | dpot_write(data, reg, value); |
138 | if (reg & AD525X_I2C_EEPROM) | 268 | if (reg & DPOT_ADDR_EEPROM) |
139 | msleep(26); /* Sleep while the EEPROM updates */ | 269 | msleep(26); /* Sleep while the EEPROM updates */ |
140 | mutex_unlock(&data->update_lock); | 270 | mutex_unlock(&data->update_lock); |
141 | 271 | ||
@@ -146,11 +276,10 @@ static ssize_t sysfs_do_cmd(struct device *dev, | |||
146 | struct device_attribute *attr, | 276 | struct device_attribute *attr, |
147 | const char *buf, size_t count, u32 reg) | 277 | const char *buf, size_t count, u32 reg) |
148 | { | 278 | { |
149 | struct i2c_client *client = to_i2c_client(dev); | 279 | struct dpot_data *data = dev_get_drvdata(dev); |
150 | struct dpot_data *data = i2c_get_clientdata(client); | ||
151 | 280 | ||
152 | mutex_lock(&data->update_lock); | 281 | mutex_lock(&data->update_lock); |
153 | ad525x_write(client, reg, 0); | 282 | dpot_write(data, reg, 0); |
154 | mutex_unlock(&data->update_lock); | 283 | mutex_unlock(&data->update_lock); |
155 | 284 | ||
156 | return count; | 285 | return count; |
@@ -182,51 +311,58 @@ static DEVICE_ATTR(name, S_IWUSR | S_IRUGO, show_##name, set_##name); | |||
182 | DPOT_DEVICE_SHOW(name, reg) \ | 311 | DPOT_DEVICE_SHOW(name, reg) \ |
183 | static DEVICE_ATTR(name, S_IWUSR | S_IRUGO, show_##name, NULL); | 312 | static DEVICE_ATTR(name, S_IWUSR | S_IRUGO, show_##name, NULL); |
184 | 313 | ||
185 | DPOT_DEVICE_SHOW_SET(rdac0, AD525X_I2C_RDAC | AD525X_RDAC0); | 314 | DPOT_DEVICE_SHOW_SET(rdac0, DPOT_ADDR_RDAC | DPOT_RDAC0); |
186 | DPOT_DEVICE_SHOW_SET(eeprom0, AD525X_I2C_EEPROM | AD525X_RDAC0); | 315 | DPOT_DEVICE_SHOW_SET(eeprom0, DPOT_ADDR_EEPROM | DPOT_RDAC0); |
187 | DPOT_DEVICE_SHOW_ONLY(tolerance0, AD525X_I2C_EEPROM | AD525X_TOL_RDAC0); | 316 | DPOT_DEVICE_SHOW_ONLY(tolerance0, DPOT_ADDR_EEPROM | DPOT_TOL_RDAC0); |
188 | 317 | ||
189 | DPOT_DEVICE_SHOW_SET(rdac1, AD525X_I2C_RDAC | AD525X_RDAC1); | 318 | DPOT_DEVICE_SHOW_SET(rdac1, DPOT_ADDR_RDAC | DPOT_RDAC1); |
190 | DPOT_DEVICE_SHOW_SET(eeprom1, AD525X_I2C_EEPROM | AD525X_RDAC1); | 319 | DPOT_DEVICE_SHOW_SET(eeprom1, DPOT_ADDR_EEPROM | DPOT_RDAC1); |
191 | DPOT_DEVICE_SHOW_ONLY(tolerance1, AD525X_I2C_EEPROM | AD525X_TOL_RDAC1); | 320 | DPOT_DEVICE_SHOW_ONLY(tolerance1, DPOT_ADDR_EEPROM | DPOT_TOL_RDAC1); |
192 | 321 | ||
193 | DPOT_DEVICE_SHOW_SET(rdac2, AD525X_I2C_RDAC | AD525X_RDAC2); | 322 | DPOT_DEVICE_SHOW_SET(rdac2, DPOT_ADDR_RDAC | DPOT_RDAC2); |
194 | DPOT_DEVICE_SHOW_SET(eeprom2, AD525X_I2C_EEPROM | AD525X_RDAC2); | 323 | DPOT_DEVICE_SHOW_SET(eeprom2, DPOT_ADDR_EEPROM | DPOT_RDAC2); |
195 | DPOT_DEVICE_SHOW_ONLY(tolerance2, AD525X_I2C_EEPROM | AD525X_TOL_RDAC2); | 324 | DPOT_DEVICE_SHOW_ONLY(tolerance2, DPOT_ADDR_EEPROM | DPOT_TOL_RDAC2); |
196 | 325 | ||
197 | DPOT_DEVICE_SHOW_SET(rdac3, AD525X_I2C_RDAC | AD525X_RDAC3); | 326 | DPOT_DEVICE_SHOW_SET(rdac3, DPOT_ADDR_RDAC | DPOT_RDAC3); |
198 | DPOT_DEVICE_SHOW_SET(eeprom3, AD525X_I2C_EEPROM | AD525X_RDAC3); | 327 | DPOT_DEVICE_SHOW_SET(eeprom3, DPOT_ADDR_EEPROM | DPOT_RDAC3); |
199 | DPOT_DEVICE_SHOW_ONLY(tolerance3, AD525X_I2C_EEPROM | AD525X_TOL_RDAC3); | 328 | DPOT_DEVICE_SHOW_ONLY(tolerance3, DPOT_ADDR_EEPROM | DPOT_TOL_RDAC3); |
200 | 329 | ||
201 | static struct attribute *ad525x_attributes_wipers[4][4] = { | 330 | DPOT_DEVICE_SHOW_SET(rdac4, DPOT_ADDR_RDAC | DPOT_RDAC4); |
202 | { | 331 | DPOT_DEVICE_SHOW_SET(eeprom4, DPOT_ADDR_EEPROM | DPOT_RDAC4); |
203 | &dev_attr_rdac0.attr, | 332 | DPOT_DEVICE_SHOW_ONLY(tolerance4, DPOT_ADDR_EEPROM | DPOT_TOL_RDAC4); |
204 | &dev_attr_eeprom0.attr, | 333 | |
205 | &dev_attr_tolerance0.attr, | 334 | DPOT_DEVICE_SHOW_SET(rdac5, DPOT_ADDR_RDAC | DPOT_RDAC5); |
206 | NULL | 335 | DPOT_DEVICE_SHOW_SET(eeprom5, DPOT_ADDR_EEPROM | DPOT_RDAC5); |
207 | }, { | 336 | DPOT_DEVICE_SHOW_ONLY(tolerance5, DPOT_ADDR_EEPROM | DPOT_TOL_RDAC5); |
208 | &dev_attr_rdac1.attr, | 337 | |
209 | &dev_attr_eeprom1.attr, | 338 | static const struct attribute *dpot_attrib_wipers[] = { |
210 | &dev_attr_tolerance1.attr, | 339 | &dev_attr_rdac0.attr, |
211 | NULL | 340 | &dev_attr_rdac1.attr, |
212 | }, { | 341 | &dev_attr_rdac2.attr, |
213 | &dev_attr_rdac2.attr, | 342 | &dev_attr_rdac3.attr, |
214 | &dev_attr_eeprom2.attr, | 343 | &dev_attr_rdac4.attr, |
215 | &dev_attr_tolerance2.attr, | 344 | &dev_attr_rdac5.attr, |
216 | NULL | 345 | NULL |
217 | }, { | 346 | }; |
218 | &dev_attr_rdac3.attr, | 347 | |
219 | &dev_attr_eeprom3.attr, | 348 | static const struct attribute *dpot_attrib_eeprom[] = { |
220 | &dev_attr_tolerance3.attr, | 349 | &dev_attr_eeprom0.attr, |
221 | NULL | 350 | &dev_attr_eeprom1.attr, |
222 | } | 351 | &dev_attr_eeprom2.attr, |
352 | &dev_attr_eeprom3.attr, | ||
353 | &dev_attr_eeprom4.attr, | ||
354 | &dev_attr_eeprom5.attr, | ||
355 | NULL | ||
223 | }; | 356 | }; |
224 | 357 | ||
225 | static const struct attribute_group ad525x_group_wipers[] = { | 358 | static const struct attribute *dpot_attrib_tolerance[] = { |
226 | {.attrs = ad525x_attributes_wipers[AD525X_RDAC0]}, | 359 | &dev_attr_tolerance0.attr, |
227 | {.attrs = ad525x_attributes_wipers[AD525X_RDAC1]}, | 360 | &dev_attr_tolerance1.attr, |
228 | {.attrs = ad525x_attributes_wipers[AD525X_RDAC2]}, | 361 | &dev_attr_tolerance2.attr, |
229 | {.attrs = ad525x_attributes_wipers[AD525X_RDAC3]}, | 362 | &dev_attr_tolerance3.attr, |
363 | &dev_attr_tolerance4.attr, | ||
364 | &dev_attr_tolerance5.attr, | ||
365 | NULL | ||
230 | }; | 366 | }; |
231 | 367 | ||
232 | /* ------------------------------------------------------------------------- */ | 368 | /* ------------------------------------------------------------------------- */ |
@@ -240,10 +376,10 @@ set_##_name(struct device *dev, \ | |||
240 | } \ | 376 | } \ |
241 | static DEVICE_ATTR(_name, S_IWUSR | S_IRUGO, NULL, set_##_name); | 377 | static DEVICE_ATTR(_name, S_IWUSR | S_IRUGO, NULL, set_##_name); |
242 | 378 | ||
243 | DPOT_DEVICE_DO_CMD(inc_all, AD525X_INC_ALL); | 379 | DPOT_DEVICE_DO_CMD(inc_all, DPOT_INC_ALL); |
244 | DPOT_DEVICE_DO_CMD(dec_all, AD525X_DEC_ALL); | 380 | DPOT_DEVICE_DO_CMD(dec_all, DPOT_DEC_ALL); |
245 | DPOT_DEVICE_DO_CMD(inc_all_6db, AD525X_INC_ALL_6DB); | 381 | DPOT_DEVICE_DO_CMD(inc_all_6db, DPOT_INC_ALL_6DB); |
246 | DPOT_DEVICE_DO_CMD(dec_all_6db, AD525X_DEC_ALL_6DB); | 382 | DPOT_DEVICE_DO_CMD(dec_all_6db, DPOT_DEC_ALL_6DB); |
247 | 383 | ||
248 | static struct attribute *ad525x_attributes_commands[] = { | 384 | static struct attribute *ad525x_attributes_commands[] = { |
249 | &dev_attr_inc_all.attr, | 385 | &dev_attr_inc_all.attr, |
@@ -257,74 +393,44 @@ static const struct attribute_group ad525x_group_commands = { | |||
257 | .attrs = ad525x_attributes_commands, | 393 | .attrs = ad525x_attributes_commands, |
258 | }; | 394 | }; |
259 | 395 | ||
260 | /* ------------------------------------------------------------------------- */ | 396 | __devinit int ad_dpot_add_files(struct device *dev, |
261 | 397 | unsigned features, unsigned rdac) | |
262 | /* i2c device functions */ | ||
263 | |||
264 | /** | ||
265 | * ad525x_read - return the value contained in the specified register | ||
266 | * on the AD5258 device. | ||
267 | * @client: value returned from i2c_new_device() | ||
268 | * @reg: the register to read | ||
269 | * | ||
270 | * If the tolerance register is specified, 2 bytes are returned. | ||
271 | * Otherwise, 1 byte is returned. A negative value indicates an error | ||
272 | * occurred while reading the register. | ||
273 | */ | ||
274 | static s32 ad525x_read(struct i2c_client *client, u8 reg) | ||
275 | { | 398 | { |
276 | struct dpot_data *data = i2c_get_clientdata(client); | 399 | int err = sysfs_create_file(&dev->kobj, |
400 | dpot_attrib_wipers[rdac]); | ||
401 | if (features & F_CMD_EEP) | ||
402 | err |= sysfs_create_file(&dev->kobj, | ||
403 | dpot_attrib_eeprom[rdac]); | ||
404 | if (features & F_CMD_TOL) | ||
405 | err |= sysfs_create_file(&dev->kobj, | ||
406 | dpot_attrib_tolerance[rdac]); | ||
277 | 407 | ||
278 | if ((reg & AD525X_REG_TOL) || (data->max_pos > 256)) | 408 | if (err) |
279 | return i2c_smbus_read_word_data(client, (reg & 0xF8) | | 409 | dev_err(dev, "failed to register sysfs hooks for RDAC%d\n", |
280 | ((reg & 0x7) << 1)); | 410 | rdac); |
281 | else | 411 | |
282 | return i2c_smbus_read_byte_data(client, reg); | 412 | return err; |
283 | } | 413 | } |
284 | 414 | ||
285 | /** | 415 | inline void ad_dpot_remove_files(struct device *dev, |
286 | * ad525x_write - store the given value in the specified register on | 416 | unsigned features, unsigned rdac) |
287 | * the AD5258 device. | ||
288 | * @client: value returned from i2c_new_device() | ||
289 | * @reg: the register to write | ||
290 | * @value: the byte to store in the register | ||
291 | * | ||
292 | * For certain instructions that do not require a data byte, "NULL" | ||
293 | * should be specified for the "value" parameter. These instructions | ||
294 | * include NOP, RESTORE_FROM_EEPROM, and STORE_TO_EEPROM. | ||
295 | * | ||
296 | * A negative return value indicates an error occurred while reading | ||
297 | * the register. | ||
298 | */ | ||
299 | static s32 ad525x_write(struct i2c_client *client, u8 reg, u16 value) | ||
300 | { | 417 | { |
301 | struct dpot_data *data = i2c_get_clientdata(client); | 418 | sysfs_remove_file(&dev->kobj, |
302 | 419 | dpot_attrib_wipers[rdac]); | |
303 | /* Only write the instruction byte for certain commands */ | 420 | if (features & F_CMD_EEP) |
304 | if (reg & AD525X_I2C_CMD) | 421 | sysfs_remove_file(&dev->kobj, |
305 | return i2c_smbus_write_byte(client, reg); | 422 | dpot_attrib_eeprom[rdac]); |
306 | 423 | if (features & F_CMD_TOL) | |
307 | if (data->max_pos > 256) | 424 | sysfs_remove_file(&dev->kobj, |
308 | return i2c_smbus_write_word_data(client, (reg & 0xF8) | | 425 | dpot_attrib_tolerance[rdac]); |
309 | ((reg & 0x7) << 1), value); | ||
310 | else | ||
311 | /* All other registers require instruction + data bytes */ | ||
312 | return i2c_smbus_write_byte_data(client, reg, value); | ||
313 | } | 426 | } |
314 | 427 | ||
315 | static int ad525x_probe(struct i2c_client *client, | 428 | __devinit int ad_dpot_probe(struct device *dev, |
316 | const struct i2c_device_id *id) | 429 | struct ad_dpot_bus_data *bdata, const struct ad_dpot_id *id) |
317 | { | 430 | { |
318 | struct device *dev = &client->dev; | ||
319 | struct dpot_data *data; | ||
320 | int err = 0; | ||
321 | |||
322 | dev_dbg(dev, "%s\n", __func__); | ||
323 | 431 | ||
324 | if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE)) { | 432 | struct dpot_data *data; |
325 | dev_err(dev, "missing I2C functionality for this driver\n"); | 433 | int i, err = 0; |
326 | goto exit; | ||
327 | } | ||
328 | 434 | ||
329 | data = kzalloc(sizeof(struct dpot_data), GFP_KERNEL); | 435 | data = kzalloc(sizeof(struct dpot_data), GFP_KERNEL); |
330 | if (!data) { | 436 | if (!data) { |
@@ -332,183 +438,74 @@ static int ad525x_probe(struct i2c_client *client, | |||
332 | goto exit; | 438 | goto exit; |
333 | } | 439 | } |
334 | 440 | ||
335 | i2c_set_clientdata(client, data); | 441 | dev_set_drvdata(dev, data); |
336 | mutex_init(&data->update_lock); | 442 | mutex_init(&data->update_lock); |
337 | 443 | ||
338 | switch (id->driver_data) { | 444 | data->bdata = *bdata; |
339 | case AD5258_ID: | 445 | data->devid = id->devid; |
340 | data->max_pos = AD5258_MAX_POSITION; | 446 | |
341 | err = sysfs_create_group(&dev->kobj, | 447 | data->max_pos = 1 << DPOT_MAX_POS(data->devid); |
342 | &ad525x_group_wipers[AD525X_RDAC0]); | 448 | data->rdac_mask = data->max_pos - 1; |
343 | break; | 449 | data->feat = DPOT_FEAT(data->devid); |
344 | case AD5259_ID: | 450 | data->uid = DPOT_UID(data->devid); |
345 | data->max_pos = AD5259_MAX_POSITION; | 451 | data->wipers = DPOT_WIPERS(data->devid); |
346 | err = sysfs_create_group(&dev->kobj, | 452 | |
347 | &ad525x_group_wipers[AD525X_RDAC0]); | 453 | for (i = DPOT_RDAC0; i <= DPOT_RDAC5; i++) |
348 | break; | 454 | if (data->wipers & (1 << i)) { |
349 | case AD5251_ID: | 455 | err = ad_dpot_add_files(dev, data->feat, i); |
350 | data->max_pos = AD5251_MAX_POSITION; | 456 | if (err) |
351 | err = sysfs_create_group(&dev->kobj, | 457 | goto exit_remove_files; |
352 | &ad525x_group_wipers[AD525X_RDAC1]); | 458 | /* power-up midscale */ |
353 | err |= sysfs_create_group(&dev->kobj, | 459 | if (data->feat & F_RDACS_WONLY) |
354 | &ad525x_group_wipers[AD525X_RDAC3]); | 460 | data->rdac_cache[i] = data->max_pos / 2; |
355 | err |= sysfs_create_group(&dev->kobj, &ad525x_group_commands); | 461 | } |
356 | break; | 462 | |
357 | case AD5252_ID: | 463 | if (data->feat & F_CMD_INC) |
358 | data->max_pos = AD5252_MAX_POSITION; | 464 | err = sysfs_create_group(&dev->kobj, &ad525x_group_commands); |
359 | err = sysfs_create_group(&dev->kobj, | ||
360 | &ad525x_group_wipers[AD525X_RDAC1]); | ||
361 | err |= sysfs_create_group(&dev->kobj, | ||
362 | &ad525x_group_wipers[AD525X_RDAC3]); | ||
363 | err |= sysfs_create_group(&dev->kobj, &ad525x_group_commands); | ||
364 | break; | ||
365 | case AD5253_ID: | ||
366 | data->max_pos = AD5253_MAX_POSITION; | ||
367 | err = sysfs_create_group(&dev->kobj, | ||
368 | &ad525x_group_wipers[AD525X_RDAC0]); | ||
369 | err |= sysfs_create_group(&dev->kobj, | ||
370 | &ad525x_group_wipers[AD525X_RDAC1]); | ||
371 | err |= sysfs_create_group(&dev->kobj, | ||
372 | &ad525x_group_wipers[AD525X_RDAC2]); | ||
373 | err |= sysfs_create_group(&dev->kobj, | ||
374 | &ad525x_group_wipers[AD525X_RDAC3]); | ||
375 | err |= sysfs_create_group(&dev->kobj, &ad525x_group_commands); | ||
376 | break; | ||
377 | case AD5254_ID: | ||
378 | data->max_pos = AD5254_MAX_POSITION; | ||
379 | err = sysfs_create_group(&dev->kobj, | ||
380 | &ad525x_group_wipers[AD525X_RDAC0]); | ||
381 | err |= sysfs_create_group(&dev->kobj, | ||
382 | &ad525x_group_wipers[AD525X_RDAC1]); | ||
383 | err |= sysfs_create_group(&dev->kobj, | ||
384 | &ad525x_group_wipers[AD525X_RDAC2]); | ||
385 | err |= sysfs_create_group(&dev->kobj, | ||
386 | &ad525x_group_wipers[AD525X_RDAC3]); | ||
387 | err |= sysfs_create_group(&dev->kobj, &ad525x_group_commands); | ||
388 | break; | ||
389 | case AD5255_ID: | ||
390 | data->max_pos = AD5255_MAX_POSITION; | ||
391 | err = sysfs_create_group(&dev->kobj, | ||
392 | &ad525x_group_wipers[AD525X_RDAC0]); | ||
393 | err |= sysfs_create_group(&dev->kobj, | ||
394 | &ad525x_group_wipers[AD525X_RDAC1]); | ||
395 | err |= sysfs_create_group(&dev->kobj, | ||
396 | &ad525x_group_wipers[AD525X_RDAC2]); | ||
397 | err |= sysfs_create_group(&dev->kobj, &ad525x_group_commands); | ||
398 | break; | ||
399 | default: | ||
400 | err = -ENODEV; | ||
401 | goto exit_free; | ||
402 | } | ||
403 | 465 | ||
404 | if (err) { | 466 | if (err) { |
405 | dev_err(dev, "failed to register sysfs hooks\n"); | 467 | dev_err(dev, "failed to register sysfs hooks\n"); |
406 | goto exit_free; | 468 | goto exit_free; |
407 | } | 469 | } |
408 | 470 | ||
409 | data->devid = id->driver_data; | ||
410 | data->rdac_mask = data->max_pos - 1; | ||
411 | |||
412 | dev_info(dev, "%s %d-Position Digital Potentiometer registered\n", | 471 | dev_info(dev, "%s %d-Position Digital Potentiometer registered\n", |
413 | id->name, data->max_pos); | 472 | id->name, data->max_pos); |
414 | 473 | ||
415 | return 0; | 474 | return 0; |
416 | 475 | ||
476 | exit_remove_files: | ||
477 | for (i = DPOT_RDAC0; i <= DPOT_RDAC5; i++) | ||
478 | if (data->wipers & (1 << i)) | ||
479 | ad_dpot_remove_files(dev, data->feat, i); | ||
480 | |||
417 | exit_free: | 481 | exit_free: |
418 | kfree(data); | 482 | kfree(data); |
419 | i2c_set_clientdata(client, NULL); | 483 | dev_set_drvdata(dev, NULL); |
420 | exit: | 484 | exit: |
421 | dev_err(dev, "failed to create client\n"); | 485 | dev_err(dev, "failed to create client for %s ID 0x%lX\n", |
486 | id->name, id->devid); | ||
422 | return err; | 487 | return err; |
423 | } | 488 | } |
489 | EXPORT_SYMBOL(ad_dpot_probe); | ||
424 | 490 | ||
425 | static int __devexit ad525x_remove(struct i2c_client *client) | 491 | __devexit int ad_dpot_remove(struct device *dev) |
426 | { | 492 | { |
427 | struct dpot_data *data = i2c_get_clientdata(client); | 493 | struct dpot_data *data = dev_get_drvdata(dev); |
428 | struct device *dev = &client->dev; | 494 | int i; |
429 | 495 | ||
430 | switch (data->devid) { | 496 | for (i = DPOT_RDAC0; i <= DPOT_RDAC5; i++) |
431 | case AD5258_ID: | 497 | if (data->wipers & (1 << i)) |
432 | case AD5259_ID: | 498 | ad_dpot_remove_files(dev, data->feat, i); |
433 | sysfs_remove_group(&dev->kobj, | ||
434 | &ad525x_group_wipers[AD525X_RDAC0]); | ||
435 | break; | ||
436 | case AD5251_ID: | ||
437 | case AD5252_ID: | ||
438 | sysfs_remove_group(&dev->kobj, | ||
439 | &ad525x_group_wipers[AD525X_RDAC1]); | ||
440 | sysfs_remove_group(&dev->kobj, | ||
441 | &ad525x_group_wipers[AD525X_RDAC3]); | ||
442 | sysfs_remove_group(&dev->kobj, &ad525x_group_commands); | ||
443 | break; | ||
444 | case AD5253_ID: | ||
445 | case AD5254_ID: | ||
446 | sysfs_remove_group(&dev->kobj, | ||
447 | &ad525x_group_wipers[AD525X_RDAC0]); | ||
448 | sysfs_remove_group(&dev->kobj, | ||
449 | &ad525x_group_wipers[AD525X_RDAC1]); | ||
450 | sysfs_remove_group(&dev->kobj, | ||
451 | &ad525x_group_wipers[AD525X_RDAC2]); | ||
452 | sysfs_remove_group(&dev->kobj, | ||
453 | &ad525x_group_wipers[AD525X_RDAC3]); | ||
454 | sysfs_remove_group(&dev->kobj, &ad525x_group_commands); | ||
455 | break; | ||
456 | case AD5255_ID: | ||
457 | sysfs_remove_group(&dev->kobj, | ||
458 | &ad525x_group_wipers[AD525X_RDAC0]); | ||
459 | sysfs_remove_group(&dev->kobj, | ||
460 | &ad525x_group_wipers[AD525X_RDAC1]); | ||
461 | sysfs_remove_group(&dev->kobj, | ||
462 | &ad525x_group_wipers[AD525X_RDAC2]); | ||
463 | sysfs_remove_group(&dev->kobj, &ad525x_group_commands); | ||
464 | break; | ||
465 | } | ||
466 | 499 | ||
467 | i2c_set_clientdata(client, NULL); | ||
468 | kfree(data); | 500 | kfree(data); |
469 | 501 | ||
470 | return 0; | 502 | return 0; |
471 | } | 503 | } |
504 | EXPORT_SYMBOL(ad_dpot_remove); | ||
472 | 505 | ||
473 | static const struct i2c_device_id ad525x_idtable[] = { | ||
474 | {"ad5258", AD5258_ID}, | ||
475 | {"ad5259", AD5259_ID}, | ||
476 | {"ad5251", AD5251_ID}, | ||
477 | {"ad5252", AD5252_ID}, | ||
478 | {"ad5253", AD5253_ID}, | ||
479 | {"ad5254", AD5254_ID}, | ||
480 | {"ad5255", AD5255_ID}, | ||
481 | {} | ||
482 | }; | ||
483 | |||
484 | MODULE_DEVICE_TABLE(i2c, ad525x_idtable); | ||
485 | |||
486 | static struct i2c_driver ad525x_driver = { | ||
487 | .driver = { | ||
488 | .owner = THIS_MODULE, | ||
489 | .name = DRIVER_NAME, | ||
490 | }, | ||
491 | .id_table = ad525x_idtable, | ||
492 | .probe = ad525x_probe, | ||
493 | .remove = __devexit_p(ad525x_remove), | ||
494 | }; | ||
495 | |||
496 | static int __init ad525x_init(void) | ||
497 | { | ||
498 | return i2c_add_driver(&ad525x_driver); | ||
499 | } | ||
500 | |||
501 | module_init(ad525x_init); | ||
502 | |||
503 | static void __exit ad525x_exit(void) | ||
504 | { | ||
505 | i2c_del_driver(&ad525x_driver); | ||
506 | } | ||
507 | |||
508 | module_exit(ad525x_exit); | ||
509 | 506 | ||
510 | MODULE_AUTHOR("Chris Verges <chrisv@cyberswitching.com>, " | 507 | MODULE_AUTHOR("Chris Verges <chrisv@cyberswitching.com>, " |
511 | "Michael Hennerich <hennerich@blackfin.uclinux.org>, "); | 508 | "Michael Hennerich <hennerich@blackfin.uclinux.org>"); |
512 | MODULE_DESCRIPTION("AD5258/9 digital potentiometer driver"); | 509 | MODULE_DESCRIPTION("Digital potentiometer driver"); |
513 | MODULE_LICENSE("GPL"); | 510 | MODULE_LICENSE("GPL"); |
514 | MODULE_VERSION(DRIVER_VERSION); | 511 | MODULE_VERSION(DRIVER_VERSION); |