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