Commit | Line | Data |
---|---|---|
b5a49481 MH |
1 | /* |
2 | * AD5446 SPI DAC driver | |
3 | * | |
4 | * Copyright 2010 Analog Devices Inc. | |
5 | * | |
6 | * Licensed under the GPL-2 or later. | |
7 | */ | |
8 | ||
9 | #include <linux/interrupt.h> | |
10 | #include <linux/workqueue.h> | |
11 | #include <linux/device.h> | |
12 | #include <linux/kernel.h> | |
13 | #include <linux/slab.h> | |
14 | #include <linux/sysfs.h> | |
15 | #include <linux/list.h> | |
16 | #include <linux/spi/spi.h> | |
17 | #include <linux/regulator/consumer.h> | |
18 | #include <linux/err.h> | |
19 | ||
20 | #include "../iio.h" | |
21 | #include "../sysfs.h" | |
22 | #include "dac.h" | |
23 | ||
24 | #include "ad5446.h" | |
25 | ||
d846263d MH |
26 | static void ad5446_store_sample(struct ad5446_state *st, unsigned val) |
27 | { | |
28 | st->data.d16 = cpu_to_be16(AD5446_LOAD | | |
29 | (val << st->chip_info->left_shift)); | |
30 | } | |
31 | ||
32 | static void ad5542_store_sample(struct ad5446_state *st, unsigned val) | |
33 | { | |
34 | st->data.d16 = cpu_to_be16(val << st->chip_info->left_shift); | |
35 | } | |
36 | ||
37 | static void ad5620_store_sample(struct ad5446_state *st, unsigned val) | |
38 | { | |
39 | st->data.d16 = cpu_to_be16(AD5620_LOAD | | |
40 | (val << st->chip_info->left_shift)); | |
41 | } | |
42 | ||
43 | static void ad5660_store_sample(struct ad5446_state *st, unsigned val) | |
44 | { | |
45 | val |= AD5660_LOAD; | |
46 | st->data.d24[0] = (val >> 16) & 0xFF; | |
47 | st->data.d24[1] = (val >> 8) & 0xFF; | |
48 | st->data.d24[2] = val & 0xFF; | |
49 | } | |
50 | ||
bbed4dc7 MH |
51 | static void ad5620_store_pwr_down(struct ad5446_state *st, unsigned mode) |
52 | { | |
53 | st->data.d16 = cpu_to_be16(mode << 14); | |
54 | } | |
55 | ||
56 | static void ad5660_store_pwr_down(struct ad5446_state *st, unsigned mode) | |
57 | { | |
58 | unsigned val = mode << 16; | |
59 | ||
60 | st->data.d24[0] = (val >> 16) & 0xFF; | |
61 | st->data.d24[1] = (val >> 8) & 0xFF; | |
62 | st->data.d24[2] = val & 0xFF; | |
63 | } | |
64 | ||
b5a49481 MH |
65 | static ssize_t ad5446_write(struct device *dev, |
66 | struct device_attribute *attr, | |
67 | const char *buf, | |
68 | size_t len) | |
69 | { | |
70 | struct iio_dev *dev_info = dev_get_drvdata(dev); | |
86729fc4 | 71 | struct ad5446_state *st = iio_priv(dev_info); |
b5a49481 MH |
72 | int ret; |
73 | long val; | |
74 | ||
75 | ret = strict_strtol(buf, 10, &val); | |
76 | if (ret) | |
77 | goto error_ret; | |
78 | ||
79 | if (val > RES_MASK(st->chip_info->bits)) { | |
80 | ret = -EINVAL; | |
81 | goto error_ret; | |
82 | } | |
83 | ||
84 | mutex_lock(&dev_info->mlock); | |
bbed4dc7 | 85 | st->cached_val = val; |
d846263d | 86 | st->chip_info->store_sample(st, val); |
b5a49481 MH |
87 | ret = spi_sync(st->spi, &st->msg); |
88 | mutex_unlock(&dev_info->mlock); | |
89 | ||
90 | error_ret: | |
91 | return ret ? ret : len; | |
92 | } | |
93 | ||
94 | static IIO_DEV_ATTR_OUT_RAW(0, ad5446_write, 0); | |
95 | ||
96 | static ssize_t ad5446_show_scale(struct device *dev, | |
97 | struct device_attribute *attr, | |
98 | char *buf) | |
99 | { | |
100 | struct iio_dev *dev_info = dev_get_drvdata(dev); | |
86729fc4 | 101 | struct ad5446_state *st = iio_priv(dev_info); |
b5a49481 MH |
102 | /* Corresponds to Vref / 2^(bits) */ |
103 | unsigned int scale_uv = (st->vref_mv * 1000) >> st->chip_info->bits; | |
104 | ||
41135b1c | 105 | return sprintf(buf, "%d.%03d\n", scale_uv / 1000, scale_uv % 1000); |
b5a49481 MH |
106 | } |
107 | static IIO_DEVICE_ATTR(out_scale, S_IRUGO, ad5446_show_scale, NULL, 0); | |
108 | ||
bbed4dc7 MH |
109 | static ssize_t ad5446_write_powerdown_mode(struct device *dev, |
110 | struct device_attribute *attr, | |
111 | const char *buf, size_t len) | |
112 | { | |
113 | struct iio_dev *dev_info = dev_get_drvdata(dev); | |
86729fc4 | 114 | struct ad5446_state *st = iio_priv(dev_info); |
bbed4dc7 MH |
115 | |
116 | if (sysfs_streq(buf, "1kohm_to_gnd")) | |
117 | st->pwr_down_mode = MODE_PWRDWN_1k; | |
118 | else if (sysfs_streq(buf, "100kohm_to_gnd")) | |
119 | st->pwr_down_mode = MODE_PWRDWN_100k; | |
120 | else if (sysfs_streq(buf, "three_state")) | |
121 | st->pwr_down_mode = MODE_PWRDWN_TRISTATE; | |
122 | else | |
123 | return -EINVAL; | |
124 | ||
125 | return len; | |
126 | } | |
127 | ||
128 | static ssize_t ad5446_read_powerdown_mode(struct device *dev, | |
129 | struct device_attribute *attr, char *buf) | |
130 | { | |
131 | struct iio_dev *dev_info = dev_get_drvdata(dev); | |
86729fc4 | 132 | struct ad5446_state *st = iio_priv(dev_info); |
bbed4dc7 MH |
133 | |
134 | char mode[][15] = {"", "1kohm_to_gnd", "100kohm_to_gnd", "three_state"}; | |
135 | ||
136 | return sprintf(buf, "%s\n", mode[st->pwr_down_mode]); | |
137 | } | |
138 | ||
139 | static ssize_t ad5446_read_dac_powerdown(struct device *dev, | |
140 | struct device_attribute *attr, | |
141 | char *buf) | |
142 | { | |
143 | struct iio_dev *dev_info = dev_get_drvdata(dev); | |
86729fc4 | 144 | struct ad5446_state *st = iio_priv(dev_info); |
bbed4dc7 MH |
145 | |
146 | return sprintf(buf, "%d\n", st->pwr_down); | |
147 | } | |
148 | ||
149 | static ssize_t ad5446_write_dac_powerdown(struct device *dev, | |
150 | struct device_attribute *attr, | |
151 | const char *buf, size_t len) | |
152 | { | |
153 | struct iio_dev *dev_info = dev_get_drvdata(dev); | |
86729fc4 | 154 | struct ad5446_state *st = iio_priv(dev_info); |
bbed4dc7 MH |
155 | unsigned long readin; |
156 | int ret; | |
157 | ||
158 | ret = strict_strtol(buf, 10, &readin); | |
159 | if (ret) | |
160 | return ret; | |
161 | ||
162 | if (readin > 1) | |
163 | ret = -EINVAL; | |
164 | ||
165 | mutex_lock(&dev_info->mlock); | |
166 | st->pwr_down = readin; | |
167 | ||
168 | if (st->pwr_down) | |
169 | st->chip_info->store_pwr_down(st, st->pwr_down_mode); | |
170 | else | |
171 | st->chip_info->store_sample(st, st->cached_val); | |
172 | ||
173 | ret = spi_sync(st->spi, &st->msg); | |
174 | mutex_unlock(&dev_info->mlock); | |
175 | ||
176 | return ret ? ret : len; | |
177 | } | |
178 | ||
179 | static IIO_DEVICE_ATTR(out_powerdown_mode, S_IRUGO | S_IWUSR, | |
180 | ad5446_read_powerdown_mode, | |
181 | ad5446_write_powerdown_mode, 0); | |
182 | ||
183 | static IIO_CONST_ATTR(out_powerdown_mode_available, | |
184 | "1kohm_to_gnd 100kohm_to_gnd three_state"); | |
185 | ||
186 | static IIO_DEVICE_ATTR(out0_powerdown, S_IRUGO | S_IWUSR, | |
187 | ad5446_read_dac_powerdown, | |
188 | ad5446_write_dac_powerdown, 0); | |
189 | ||
b5a49481 MH |
190 | static struct attribute *ad5446_attributes[] = { |
191 | &iio_dev_attr_out0_raw.dev_attr.attr, | |
192 | &iio_dev_attr_out_scale.dev_attr.attr, | |
bbed4dc7 MH |
193 | &iio_dev_attr_out0_powerdown.dev_attr.attr, |
194 | &iio_dev_attr_out_powerdown_mode.dev_attr.attr, | |
195 | &iio_const_attr_out_powerdown_mode_available.dev_attr.attr, | |
b5a49481 MH |
196 | NULL, |
197 | }; | |
198 | ||
bbed4dc7 MH |
199 | static mode_t ad5446_attr_is_visible(struct kobject *kobj, |
200 | struct attribute *attr, int n) | |
201 | { | |
202 | struct device *dev = container_of(kobj, struct device, kobj); | |
203 | struct iio_dev *dev_info = dev_get_drvdata(dev); | |
86729fc4 | 204 | struct ad5446_state *st = iio_priv(dev_info); |
bbed4dc7 MH |
205 | |
206 | mode_t mode = attr->mode; | |
207 | ||
208 | if (!st->chip_info->store_pwr_down && | |
209 | (attr == &iio_dev_attr_out0_powerdown.dev_attr.attr || | |
210 | attr == &iio_dev_attr_out_powerdown_mode.dev_attr.attr || | |
211 | attr == | |
212 | &iio_const_attr_out_powerdown_mode_available.dev_attr.attr)) | |
213 | mode = 0; | |
214 | ||
215 | return mode; | |
216 | } | |
217 | ||
b5a49481 MH |
218 | static const struct attribute_group ad5446_attribute_group = { |
219 | .attrs = ad5446_attributes, | |
bbed4dc7 | 220 | .is_visible = ad5446_attr_is_visible, |
b5a49481 MH |
221 | }; |
222 | ||
223 | static const struct ad5446_chip_info ad5446_chip_info_tbl[] = { | |
224 | [ID_AD5444] = { | |
225 | .bits = 12, | |
226 | .storagebits = 16, | |
227 | .left_shift = 2, | |
d846263d | 228 | .store_sample = ad5446_store_sample, |
b5a49481 MH |
229 | }, |
230 | [ID_AD5446] = { | |
231 | .bits = 14, | |
232 | .storagebits = 16, | |
233 | .left_shift = 0, | |
d846263d | 234 | .store_sample = ad5446_store_sample, |
b5a49481 | 235 | }, |
67d1c1f4 MH |
236 | [ID_AD5541A] = { |
237 | .bits = 16, | |
238 | .storagebits = 16, | |
239 | .left_shift = 0, | |
240 | .store_sample = ad5542_store_sample, | |
241 | }, | |
b5a49481 MH |
242 | [ID_AD5542A] = { |
243 | .bits = 16, | |
244 | .storagebits = 16, | |
245 | .left_shift = 0, | |
d846263d | 246 | .store_sample = ad5542_store_sample, |
b5a49481 | 247 | }, |
0772268a MH |
248 | [ID_AD5543] = { |
249 | .bits = 16, | |
250 | .storagebits = 16, | |
251 | .left_shift = 0, | |
252 | .store_sample = ad5542_store_sample, | |
253 | }, | |
b5a49481 MH |
254 | [ID_AD5512A] = { |
255 | .bits = 12, | |
256 | .storagebits = 16, | |
257 | .left_shift = 4, | |
d846263d MH |
258 | .store_sample = ad5542_store_sample, |
259 | }, | |
0772268a MH |
260 | [ID_AD5553] = { |
261 | .bits = 14, | |
262 | .storagebits = 16, | |
263 | .left_shift = 0, | |
264 | .store_sample = ad5542_store_sample, | |
265 | }, | |
2b61535a MH |
266 | [ID_AD5601] = { |
267 | .bits = 8, | |
268 | .storagebits = 16, | |
269 | .left_shift = 6, | |
270 | .store_sample = ad5542_store_sample, | |
271 | .store_pwr_down = ad5620_store_pwr_down, | |
272 | }, | |
273 | [ID_AD5611] = { | |
274 | .bits = 10, | |
275 | .storagebits = 16, | |
276 | .left_shift = 4, | |
277 | .store_sample = ad5542_store_sample, | |
278 | .store_pwr_down = ad5620_store_pwr_down, | |
279 | }, | |
280 | [ID_AD5621] = { | |
281 | .bits = 12, | |
282 | .storagebits = 16, | |
283 | .left_shift = 2, | |
284 | .store_sample = ad5542_store_sample, | |
285 | .store_pwr_down = ad5620_store_pwr_down, | |
286 | }, | |
d846263d MH |
287 | [ID_AD5620_2500] = { |
288 | .bits = 12, | |
289 | .storagebits = 16, | |
290 | .left_shift = 2, | |
d846263d MH |
291 | .int_vref_mv = 2500, |
292 | .store_sample = ad5620_store_sample, | |
bbed4dc7 | 293 | .store_pwr_down = ad5620_store_pwr_down, |
d846263d MH |
294 | }, |
295 | [ID_AD5620_1250] = { | |
296 | .bits = 12, | |
297 | .storagebits = 16, | |
298 | .left_shift = 2, | |
d846263d MH |
299 | .int_vref_mv = 1250, |
300 | .store_sample = ad5620_store_sample, | |
bbed4dc7 | 301 | .store_pwr_down = ad5620_store_pwr_down, |
d846263d MH |
302 | }, |
303 | [ID_AD5640_2500] = { | |
304 | .bits = 14, | |
305 | .storagebits = 16, | |
306 | .left_shift = 0, | |
d846263d MH |
307 | .int_vref_mv = 2500, |
308 | .store_sample = ad5620_store_sample, | |
bbed4dc7 | 309 | .store_pwr_down = ad5620_store_pwr_down, |
d846263d MH |
310 | }, |
311 | [ID_AD5640_1250] = { | |
312 | .bits = 14, | |
313 | .storagebits = 16, | |
314 | .left_shift = 0, | |
d846263d MH |
315 | .int_vref_mv = 1250, |
316 | .store_sample = ad5620_store_sample, | |
bbed4dc7 | 317 | .store_pwr_down = ad5620_store_pwr_down, |
d846263d MH |
318 | }, |
319 | [ID_AD5660_2500] = { | |
320 | .bits = 16, | |
321 | .storagebits = 24, | |
322 | .left_shift = 0, | |
d846263d MH |
323 | .int_vref_mv = 2500, |
324 | .store_sample = ad5660_store_sample, | |
bbed4dc7 | 325 | .store_pwr_down = ad5660_store_pwr_down, |
d846263d MH |
326 | }, |
327 | [ID_AD5660_1250] = { | |
328 | .bits = 16, | |
329 | .storagebits = 24, | |
330 | .left_shift = 0, | |
d846263d MH |
331 | .int_vref_mv = 1250, |
332 | .store_sample = ad5660_store_sample, | |
bbed4dc7 | 333 | .store_pwr_down = ad5660_store_pwr_down, |
b5a49481 MH |
334 | }, |
335 | }; | |
336 | ||
6fe8135f JC |
337 | static const struct iio_info ad5446_info = { |
338 | .attrs = &ad5446_attribute_group, | |
339 | .driver_module = THIS_MODULE, | |
340 | }; | |
341 | ||
b5a49481 MH |
342 | static int __devinit ad5446_probe(struct spi_device *spi) |
343 | { | |
344 | struct ad5446_state *st; | |
86729fc4 JC |
345 | struct iio_dev *indio_dev; |
346 | struct regulator *reg; | |
b5a49481 MH |
347 | int ret, voltage_uv = 0; |
348 | ||
86729fc4 JC |
349 | reg = regulator_get(&spi->dev, "vcc"); |
350 | if (!IS_ERR(reg)) { | |
351 | ret = regulator_enable(reg); | |
b5a49481 MH |
352 | if (ret) |
353 | goto error_put_reg; | |
354 | ||
86729fc4 | 355 | voltage_uv = regulator_get_voltage(reg); |
b5a49481 MH |
356 | } |
357 | ||
86729fc4 JC |
358 | indio_dev = iio_allocate_device(sizeof(*st)); |
359 | if (indio_dev == NULL) { | |
360 | ret = -ENOMEM; | |
361 | goto error_disable_reg; | |
362 | } | |
363 | st = iio_priv(indio_dev); | |
b5a49481 MH |
364 | st->chip_info = |
365 | &ad5446_chip_info_tbl[spi_get_device_id(spi)->driver_data]; | |
366 | ||
86729fc4 JC |
367 | spi_set_drvdata(spi, indio_dev); |
368 | st->reg = reg; | |
b5a49481 MH |
369 | st->spi = spi; |
370 | ||
b5a49481 | 371 | /* Estabilish that the iio_dev is a child of the spi device */ |
86729fc4 JC |
372 | indio_dev->dev.parent = &spi->dev; |
373 | indio_dev->name = spi_get_device_id(spi)->name; | |
374 | indio_dev->info = &ad5446_info; | |
375 | indio_dev->modes = INDIO_DIRECT_MODE; | |
b5a49481 MH |
376 | |
377 | /* Setup default message */ | |
378 | ||
d846263d MH |
379 | st->xfer.tx_buf = &st->data; |
380 | st->xfer.len = st->chip_info->storagebits / 8; | |
b5a49481 MH |
381 | |
382 | spi_message_init(&st->msg); | |
383 | spi_message_add_tail(&st->xfer, &st->msg); | |
384 | ||
d846263d | 385 | switch (spi_get_device_id(spi)->driver_data) { |
bbed4dc7 MH |
386 | case ID_AD5620_2500: |
387 | case ID_AD5620_1250: | |
388 | case ID_AD5640_2500: | |
389 | case ID_AD5640_1250: | |
390 | case ID_AD5660_2500: | |
391 | case ID_AD5660_1250: | |
392 | st->vref_mv = st->chip_info->int_vref_mv; | |
393 | break; | |
394 | default: | |
395 | if (voltage_uv) | |
396 | st->vref_mv = voltage_uv / 1000; | |
397 | else | |
398 | dev_warn(&spi->dev, | |
399 | "reference voltage unspecified\n"); | |
d846263d | 400 | } |
b5a49481 | 401 | |
86729fc4 | 402 | ret = iio_device_register(indio_dev); |
b5a49481 MH |
403 | if (ret) |
404 | goto error_free_device; | |
405 | ||
406 | return 0; | |
407 | ||
408 | error_free_device: | |
86729fc4 | 409 | iio_free_device(indio_dev); |
b5a49481 | 410 | error_disable_reg: |
86729fc4 JC |
411 | if (!IS_ERR(reg)) |
412 | regulator_disable(reg); | |
b5a49481 | 413 | error_put_reg: |
86729fc4 JC |
414 | if (!IS_ERR(reg)) |
415 | regulator_put(reg); | |
416 | ||
b5a49481 MH |
417 | return ret; |
418 | } | |
419 | ||
420 | static int ad5446_remove(struct spi_device *spi) | |
421 | { | |
86729fc4 JC |
422 | struct iio_dev *indio_dev = spi_get_drvdata(spi); |
423 | struct ad5446_state *st = iio_priv(indio_dev); | |
424 | struct regulator *reg = st->reg; | |
b5a49481 MH |
425 | |
426 | iio_device_unregister(indio_dev); | |
86729fc4 JC |
427 | if (!IS_ERR(reg)) { |
428 | regulator_disable(reg); | |
429 | regulator_put(reg); | |
b5a49481 | 430 | } |
b5a49481 MH |
431 | return 0; |
432 | } | |
433 | ||
434 | static const struct spi_device_id ad5446_id[] = { | |
435 | {"ad5444", ID_AD5444}, | |
436 | {"ad5446", ID_AD5446}, | |
b5a49481 | 437 | {"ad5512a", ID_AD5512A}, |
67d1c1f4 | 438 | {"ad5541a", ID_AD5541A}, |
bd51c0b0 MH |
439 | {"ad5542a", ID_AD5542A}, |
440 | {"ad5543", ID_AD5543}, | |
441 | {"ad5553", ID_AD5553}, | |
2b61535a MH |
442 | {"ad5601", ID_AD5601}, |
443 | {"ad5611", ID_AD5611}, | |
444 | {"ad5621", ID_AD5621}, | |
d846263d MH |
445 | {"ad5620-2500", ID_AD5620_2500}, /* AD5620/40/60: */ |
446 | {"ad5620-1250", ID_AD5620_1250}, /* part numbers may look differently */ | |
447 | {"ad5640-2500", ID_AD5640_2500}, | |
448 | {"ad5640-1250", ID_AD5640_1250}, | |
449 | {"ad5660-2500", ID_AD5660_2500}, | |
450 | {"ad5660-1250", ID_AD5660_1250}, | |
b5a49481 MH |
451 | {} |
452 | }; | |
453 | ||
454 | static struct spi_driver ad5446_driver = { | |
455 | .driver = { | |
456 | .name = "ad5446", | |
457 | .bus = &spi_bus_type, | |
458 | .owner = THIS_MODULE, | |
459 | }, | |
460 | .probe = ad5446_probe, | |
461 | .remove = __devexit_p(ad5446_remove), | |
462 | .id_table = ad5446_id, | |
463 | }; | |
464 | ||
465 | static int __init ad5446_init(void) | |
466 | { | |
467 | return spi_register_driver(&ad5446_driver); | |
468 | } | |
469 | module_init(ad5446_init); | |
470 | ||
471 | static void __exit ad5446_exit(void) | |
472 | { | |
473 | spi_unregister_driver(&ad5446_driver); | |
474 | } | |
475 | module_exit(ad5446_exit); | |
476 | ||
477 | MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>"); | |
478 | MODULE_DESCRIPTION("Analog Devices AD5444/AD5446 DAC"); | |
479 | MODULE_LICENSE("GPL v2"); | |
480 | MODULE_ALIAS("spi:ad5446"); |