]>
Commit | Line | Data |
---|---|---|
69d900a6 | 1 | /* |
ba1c2bb2 | 2 | * AD5760, AD5780, AD5781, AD5791 Voltage Output Digital to Analog Converter |
69d900a6 MH |
3 | * |
4 | * Copyright 2011 Analog Devices Inc. | |
5 | * | |
6 | * Licensed under the GPL-2. | |
7 | */ | |
8 | ||
9 | #include <linux/interrupt.h> | |
69d900a6 MH |
10 | #include <linux/fs.h> |
11 | #include <linux/device.h> | |
12 | #include <linux/kernel.h> | |
13 | #include <linux/spi/spi.h> | |
14 | #include <linux/slab.h> | |
15 | #include <linux/sysfs.h> | |
16 | #include <linux/regulator/consumer.h> | |
99c97852 | 17 | #include <linux/module.h> |
69d900a6 MH |
18 | |
19 | #include "../iio.h" | |
20 | #include "../sysfs.h" | |
21 | #include "dac.h" | |
22 | #include "ad5791.h" | |
23 | ||
24 | static int ad5791_spi_write(struct spi_device *spi, u8 addr, u32 val) | |
25 | { | |
26 | union { | |
27 | u32 d32; | |
28 | u8 d8[4]; | |
29 | } data; | |
30 | ||
31 | data.d32 = cpu_to_be32(AD5791_CMD_WRITE | | |
32 | AD5791_ADDR(addr) | | |
33 | (val & AD5791_DAC_MASK)); | |
34 | ||
35 | return spi_write(spi, &data.d8[1], 3); | |
36 | } | |
37 | ||
38 | static int ad5791_spi_read(struct spi_device *spi, u8 addr, u32 *val) | |
39 | { | |
40 | union { | |
41 | u32 d32; | |
42 | u8 d8[4]; | |
43 | } data[3]; | |
44 | int ret; | |
45 | struct spi_message msg; | |
46 | struct spi_transfer xfers[] = { | |
47 | { | |
48 | .tx_buf = &data[0].d8[1], | |
49 | .bits_per_word = 8, | |
50 | .len = 3, | |
51 | .cs_change = 1, | |
52 | }, { | |
53 | .tx_buf = &data[1].d8[1], | |
54 | .rx_buf = &data[2].d8[1], | |
55 | .bits_per_word = 8, | |
56 | .len = 3, | |
57 | }, | |
58 | }; | |
59 | ||
60 | data[0].d32 = cpu_to_be32(AD5791_CMD_READ | | |
61 | AD5791_ADDR(addr)); | |
62 | data[1].d32 = cpu_to_be32(AD5791_ADDR(AD5791_ADDR_NOOP)); | |
63 | ||
64 | spi_message_init(&msg); | |
65 | spi_message_add_tail(&xfers[0], &msg); | |
66 | spi_message_add_tail(&xfers[1], &msg); | |
67 | ret = spi_sync(spi, &msg); | |
68 | ||
69 | *val = be32_to_cpu(data[2].d32); | |
70 | ||
71 | return ret; | |
72 | } | |
73 | ||
c5b99396 JC |
74 | #define AD5791_CHAN(bits, shift) { \ |
75 | .type = IIO_VOLTAGE, \ | |
76 | .output = 1, \ | |
77 | .indexed = 1, \ | |
78 | .address = AD5791_ADDR_DAC0, \ | |
79 | .channel = 0, \ | |
c8a9f805 JC |
80 | .info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT | \ |
81 | IIO_CHAN_INFO_OFFSET_SHARED_BIT, \ | |
c5b99396 | 82 | .scan_type = IIO_ST('u', bits, 24, shift) \ |
69d900a6 MH |
83 | } |
84 | ||
c5b99396 JC |
85 | static const struct iio_chan_spec ad5791_channels[] = { |
86 | [ID_AD5760] = AD5791_CHAN(16, 4), | |
87 | [ID_AD5780] = AD5791_CHAN(18, 2), | |
88 | [ID_AD5781] = AD5791_CHAN(18, 2), | |
89 | [ID_AD5791] = AD5791_CHAN(20, 0) | |
90 | }; | |
69d900a6 MH |
91 | |
92 | static ssize_t ad5791_read_powerdown_mode(struct device *dev, | |
93 | struct device_attribute *attr, char *buf) | |
94 | { | |
95 | struct iio_dev *indio_dev = dev_get_drvdata(dev); | |
f5730d52 | 96 | struct ad5791_state *st = iio_priv(indio_dev); |
69d900a6 MH |
97 | |
98 | const char mode[][14] = {"6kohm_to_gnd", "three_state"}; | |
99 | ||
100 | return sprintf(buf, "%s\n", mode[st->pwr_down_mode]); | |
101 | } | |
102 | ||
103 | static ssize_t ad5791_write_powerdown_mode(struct device *dev, | |
104 | struct device_attribute *attr, | |
105 | const char *buf, size_t len) | |
106 | { | |
107 | struct iio_dev *indio_dev = dev_get_drvdata(dev); | |
f5730d52 | 108 | struct ad5791_state *st = iio_priv(indio_dev); |
69d900a6 MH |
109 | int ret; |
110 | ||
111 | if (sysfs_streq(buf, "6kohm_to_gnd")) | |
112 | st->pwr_down_mode = AD5791_DAC_PWRDN_6K; | |
113 | else if (sysfs_streq(buf, "three_state")) | |
114 | st->pwr_down_mode = AD5791_DAC_PWRDN_3STATE; | |
115 | else | |
116 | ret = -EINVAL; | |
117 | ||
118 | return ret ? ret : len; | |
119 | } | |
120 | ||
121 | static ssize_t ad5791_read_dac_powerdown(struct device *dev, | |
122 | struct device_attribute *attr, | |
123 | char *buf) | |
124 | { | |
125 | struct iio_dev *indio_dev = dev_get_drvdata(dev); | |
f5730d52 | 126 | struct ad5791_state *st = iio_priv(indio_dev); |
69d900a6 MH |
127 | |
128 | return sprintf(buf, "%d\n", st->pwr_down); | |
129 | } | |
130 | ||
131 | static ssize_t ad5791_write_dac_powerdown(struct device *dev, | |
132 | struct device_attribute *attr, | |
133 | const char *buf, size_t len) | |
134 | { | |
135 | long readin; | |
136 | int ret; | |
137 | struct iio_dev *indio_dev = dev_get_drvdata(dev); | |
f5730d52 | 138 | struct ad5791_state *st = iio_priv(indio_dev); |
69d900a6 MH |
139 | |
140 | ret = strict_strtol(buf, 10, &readin); | |
141 | if (ret) | |
142 | return ret; | |
143 | ||
144 | if (readin == 0) { | |
145 | st->pwr_down = false; | |
146 | st->ctrl &= ~(AD5791_CTRL_OPGND | AD5791_CTRL_DACTRI); | |
147 | } else if (readin == 1) { | |
148 | st->pwr_down = true; | |
149 | if (st->pwr_down_mode == AD5791_DAC_PWRDN_6K) | |
150 | st->ctrl |= AD5791_CTRL_OPGND; | |
151 | else if (st->pwr_down_mode == AD5791_DAC_PWRDN_3STATE) | |
152 | st->ctrl |= AD5791_CTRL_DACTRI; | |
153 | } else | |
154 | ret = -EINVAL; | |
155 | ||
156 | ret = ad5791_spi_write(st->spi, AD5791_ADDR_CTRL, st->ctrl); | |
157 | ||
158 | return ret ? ret : len; | |
159 | } | |
160 | ||
f1a61a88 | 161 | static IIO_DEVICE_ATTR(out_voltage_powerdown_mode, S_IRUGO | |
69d900a6 MH |
162 | S_IWUSR, ad5791_read_powerdown_mode, |
163 | ad5791_write_powerdown_mode, 0); | |
164 | ||
f1a61a88 | 165 | static IIO_CONST_ATTR(out_voltage_powerdown_mode_available, |
69d900a6 MH |
166 | "6kohm_to_gnd three_state"); |
167 | ||
168 | #define IIO_DEV_ATTR_DAC_POWERDOWN(_num, _show, _store, _addr) \ | |
f1a61a88 | 169 | IIO_DEVICE_ATTR(out_voltage##_num##_powerdown, \ |
69d900a6 MH |
170 | S_IRUGO | S_IWUSR, _show, _store, _addr) |
171 | ||
172 | static IIO_DEV_ATTR_DAC_POWERDOWN(0, ad5791_read_dac_powerdown, | |
173 | ad5791_write_dac_powerdown, 0); | |
174 | ||
175 | static struct attribute *ad5791_attributes[] = { | |
f1a61a88 LPC |
176 | &iio_dev_attr_out_voltage0_powerdown.dev_attr.attr, |
177 | &iio_dev_attr_out_voltage_powerdown_mode.dev_attr.attr, | |
178 | &iio_const_attr_out_voltage_powerdown_mode_available.dev_attr.attr, | |
69d900a6 MH |
179 | NULL, |
180 | }; | |
181 | ||
182 | static const struct attribute_group ad5791_attribute_group = { | |
183 | .attrs = ad5791_attributes, | |
184 | }; | |
185 | ||
69d900a6 MH |
186 | static int ad5791_get_lin_comp(unsigned int span) |
187 | { | |
188 | if (span <= 10000) | |
189 | return AD5791_LINCOMP_0_10; | |
190 | else if (span <= 12000) | |
191 | return AD5791_LINCOMP_10_12; | |
192 | else if (span <= 16000) | |
193 | return AD5791_LINCOMP_12_16; | |
194 | else if (span <= 19000) | |
195 | return AD5791_LINCOMP_16_19; | |
196 | else | |
197 | return AD5791_LINCOMP_19_20; | |
198 | } | |
199 | ||
ba1c2bb2 MH |
200 | static int ad5780_get_lin_comp(unsigned int span) |
201 | { | |
202 | if (span <= 10000) | |
203 | return AD5780_LINCOMP_0_10; | |
204 | else | |
205 | return AD5780_LINCOMP_10_20; | |
206 | } | |
ba1c2bb2 MH |
207 | static const struct ad5791_chip_info ad5791_chip_info_tbl[] = { |
208 | [ID_AD5760] = { | |
ba1c2bb2 MH |
209 | .get_lin_comp = ad5780_get_lin_comp, |
210 | }, | |
211 | [ID_AD5780] = { | |
ba1c2bb2 MH |
212 | .get_lin_comp = ad5780_get_lin_comp, |
213 | }, | |
214 | [ID_AD5781] = { | |
ba1c2bb2 MH |
215 | .get_lin_comp = ad5791_get_lin_comp, |
216 | }, | |
217 | [ID_AD5791] = { | |
ba1c2bb2 MH |
218 | .get_lin_comp = ad5791_get_lin_comp, |
219 | }, | |
220 | }; | |
221 | ||
c5b99396 JC |
222 | static int ad5791_read_raw(struct iio_dev *indio_dev, |
223 | struct iio_chan_spec const *chan, | |
224 | int *val, | |
225 | int *val2, | |
226 | long m) | |
227 | { | |
228 | struct ad5791_state *st = iio_priv(indio_dev); | |
9dc9961d | 229 | u64 val64; |
c5b99396 JC |
230 | int ret; |
231 | ||
232 | switch (m) { | |
233 | case 0: | |
234 | ret = ad5791_spi_read(st->spi, chan->address, val); | |
235 | if (ret) | |
236 | return ret; | |
237 | *val &= AD5791_DAC_MASK; | |
238 | *val >>= chan->scan_type.shift; | |
c5b99396 | 239 | return IIO_VAL_INT; |
c8a9f805 | 240 | case IIO_CHAN_INFO_SCALE: |
c5b99396 | 241 | *val = 0; |
75bb23a2 | 242 | *val2 = (((u64)st->vref_mv) * 1000000ULL) >> chan->scan_type.realbits; |
c5b99396 | 243 | return IIO_VAL_INT_PLUS_MICRO; |
c8a9f805 | 244 | case IIO_CHAN_INFO_OFFSET: |
9dc9961d LPC |
245 | val64 = (((u64)st->vref_neg_mv) << chan->scan_type.realbits); |
246 | do_div(val64, st->vref_mv); | |
247 | *val = -val64; | |
248 | return IIO_VAL_INT; | |
c5b99396 JC |
249 | default: |
250 | return -EINVAL; | |
251 | } | |
252 | ||
253 | }; | |
254 | ||
255 | ||
256 | static int ad5791_write_raw(struct iio_dev *indio_dev, | |
257 | struct iio_chan_spec const *chan, | |
258 | int val, | |
259 | int val2, | |
260 | long mask) | |
261 | { | |
262 | struct ad5791_state *st = iio_priv(indio_dev); | |
263 | ||
264 | switch (mask) { | |
265 | case 0: | |
021c0a38 | 266 | val &= AD5791_RES_MASK(chan->scan_type.realbits); |
c5b99396 JC |
267 | val <<= chan->scan_type.shift; |
268 | ||
269 | return ad5791_spi_write(st->spi, chan->address, val); | |
270 | ||
271 | default: | |
272 | return -EINVAL; | |
273 | } | |
274 | } | |
275 | ||
6fe8135f | 276 | static const struct iio_info ad5791_info = { |
c5b99396 JC |
277 | .read_raw = &ad5791_read_raw, |
278 | .write_raw = &ad5791_write_raw, | |
6fe8135f JC |
279 | .attrs = &ad5791_attribute_group, |
280 | .driver_module = THIS_MODULE, | |
281 | }; | |
282 | ||
69d900a6 MH |
283 | static int __devinit ad5791_probe(struct spi_device *spi) |
284 | { | |
285 | struct ad5791_platform_data *pdata = spi->dev.platform_data; | |
f5730d52 | 286 | struct iio_dev *indio_dev; |
69d900a6 MH |
287 | struct ad5791_state *st; |
288 | int ret, pos_voltage_uv = 0, neg_voltage_uv = 0; | |
289 | ||
26a54797 JC |
290 | indio_dev = iio_allocate_device(sizeof(*st)); |
291 | if (indio_dev == NULL) { | |
292 | ret = -ENOMEM; | |
293 | goto error_ret; | |
294 | } | |
295 | st = iio_priv(indio_dev); | |
296 | st->reg_vdd = regulator_get(&spi->dev, "vdd"); | |
297 | if (!IS_ERR(st->reg_vdd)) { | |
298 | ret = regulator_enable(st->reg_vdd); | |
69d900a6 MH |
299 | if (ret) |
300 | goto error_put_reg_pos; | |
301 | ||
26a54797 | 302 | pos_voltage_uv = regulator_get_voltage(st->reg_vdd); |
69d900a6 MH |
303 | } |
304 | ||
26a54797 JC |
305 | st->reg_vss = regulator_get(&spi->dev, "vss"); |
306 | if (!IS_ERR(st->reg_vss)) { | |
307 | ret = regulator_enable(st->reg_vss); | |
69d900a6 MH |
308 | if (ret) |
309 | goto error_put_reg_neg; | |
310 | ||
26a54797 | 311 | neg_voltage_uv = regulator_get_voltage(st->reg_vss); |
69d900a6 MH |
312 | } |
313 | ||
f5730d52 JC |
314 | st->pwr_down = true; |
315 | st->spi = spi; | |
316 | ||
9dc9961d LPC |
317 | if (!IS_ERR(st->reg_vss) && !IS_ERR(st->reg_vdd)) { |
318 | st->vref_mv = (pos_voltage_uv + neg_voltage_uv) / 1000; | |
319 | st->vref_neg_mv = neg_voltage_uv / 1000; | |
320 | } else if (pdata) { | |
321 | st->vref_mv = pdata->vref_pos_mv + pdata->vref_neg_mv; | |
322 | st->vref_neg_mv = pdata->vref_neg_mv; | |
323 | } else { | |
69d900a6 | 324 | dev_warn(&spi->dev, "reference voltage unspecified\n"); |
9dc9961d | 325 | } |
69d900a6 MH |
326 | |
327 | ret = ad5791_spi_write(spi, AD5791_ADDR_SW_CTRL, AD5791_SWCTRL_RESET); | |
328 | if (ret) | |
26a54797 | 329 | goto error_disable_reg_neg; |
69d900a6 | 330 | |
c5b99396 JC |
331 | st->chip_info = &ad5791_chip_info_tbl[spi_get_device_id(spi) |
332 | ->driver_data]; | |
69d900a6 MH |
333 | |
334 | ||
ba1c2bb2 MH |
335 | st->ctrl = AD5761_CTRL_LINCOMP(st->chip_info->get_lin_comp(st->vref_mv)) |
336 | | ((pdata && pdata->use_rbuf_gain2) ? 0 : AD5791_CTRL_RBUF) | | |
69d900a6 MH |
337 | AD5791_CTRL_BIN2SC; |
338 | ||
339 | ret = ad5791_spi_write(spi, AD5791_ADDR_CTRL, st->ctrl | | |
340 | AD5791_CTRL_OPGND | AD5791_CTRL_DACTRI); | |
341 | if (ret) | |
26a54797 | 342 | goto error_disable_reg_neg; |
69d900a6 | 343 | |
f5730d52 JC |
344 | spi_set_drvdata(spi, indio_dev); |
345 | indio_dev->dev.parent = &spi->dev; | |
346 | indio_dev->info = &ad5791_info; | |
347 | indio_dev->modes = INDIO_DIRECT_MODE; | |
c5b99396 JC |
348 | indio_dev->channels |
349 | = &ad5791_channels[spi_get_device_id(spi)->driver_data]; | |
350 | indio_dev->num_channels = 1; | |
351 | indio_dev->name = spi_get_device_id(st->spi)->name; | |
f5730d52 | 352 | ret = iio_device_register(indio_dev); |
69d900a6 | 353 | if (ret) |
26a54797 | 354 | goto error_disable_reg_neg; |
69d900a6 MH |
355 | |
356 | return 0; | |
357 | ||
69d900a6 | 358 | error_disable_reg_neg: |
26a54797 JC |
359 | if (!IS_ERR(st->reg_vss)) |
360 | regulator_disable(st->reg_vss); | |
69d900a6 | 361 | error_put_reg_neg: |
26a54797 JC |
362 | if (!IS_ERR(st->reg_vss)) |
363 | regulator_put(st->reg_vss); | |
69d900a6 | 364 | |
26a54797 JC |
365 | if (!IS_ERR(st->reg_vdd)) |
366 | regulator_disable(st->reg_vdd); | |
69d900a6 | 367 | error_put_reg_pos: |
26a54797 JC |
368 | if (!IS_ERR(st->reg_vdd)) |
369 | regulator_put(st->reg_vdd); | |
370 | iio_free_device(indio_dev); | |
371 | error_ret: | |
69d900a6 | 372 | |
69d900a6 MH |
373 | return ret; |
374 | } | |
375 | ||
376 | static int __devexit ad5791_remove(struct spi_device *spi) | |
377 | { | |
f5730d52 JC |
378 | struct iio_dev *indio_dev = spi_get_drvdata(spi); |
379 | struct ad5791_state *st = iio_priv(indio_dev); | |
69d900a6 | 380 | |
d2fffd6c | 381 | iio_device_unregister(indio_dev); |
69d900a6 | 382 | if (!IS_ERR(st->reg_vdd)) { |
26a54797 JC |
383 | regulator_disable(st->reg_vdd); |
384 | regulator_put(st->reg_vdd); | |
69d900a6 MH |
385 | } |
386 | ||
387 | if (!IS_ERR(st->reg_vss)) { | |
26a54797 JC |
388 | regulator_disable(st->reg_vss); |
389 | regulator_put(st->reg_vss); | |
69d900a6 | 390 | } |
d2fffd6c | 391 | iio_free_device(indio_dev); |
26d25ae3 | 392 | |
69d900a6 MH |
393 | return 0; |
394 | } | |
395 | ||
396 | static const struct spi_device_id ad5791_id[] = { | |
ba1c2bb2 MH |
397 | {"ad5760", ID_AD5760}, |
398 | {"ad5780", ID_AD5780}, | |
69d900a6 | 399 | {"ad5781", ID_AD5781}, |
ba1c2bb2 | 400 | {"ad5791", ID_AD5791}, |
69d900a6 MH |
401 | {} |
402 | }; | |
55e4390c | 403 | MODULE_DEVICE_TABLE(spi, ad5791_id); |
69d900a6 MH |
404 | |
405 | static struct spi_driver ad5791_driver = { | |
406 | .driver = { | |
407 | .name = "ad5791", | |
408 | .owner = THIS_MODULE, | |
409 | }, | |
410 | .probe = ad5791_probe, | |
411 | .remove = __devexit_p(ad5791_remove), | |
412 | .id_table = ad5791_id, | |
413 | }; | |
414 | ||
415 | static __init int ad5791_spi_init(void) | |
416 | { | |
417 | return spi_register_driver(&ad5791_driver); | |
418 | } | |
419 | module_init(ad5791_spi_init); | |
420 | ||
421 | static __exit void ad5791_spi_exit(void) | |
422 | { | |
423 | spi_unregister_driver(&ad5791_driver); | |
424 | } | |
425 | module_exit(ad5791_spi_exit); | |
426 | ||
427 | MODULE_AUTHOR("Michael Hennerich <[email protected]>"); | |
ba1c2bb2 | 428 | MODULE_DESCRIPTION("Analog Devices AD5760/AD5780/AD5781/AD5791 DAC"); |
69d900a6 | 429 | MODULE_LICENSE("GPL v2"); |