Commit | Line | Data |
---|---|---|
69d900a6 | 1 | /* |
9d41c5bb LPC |
2 | * AD5760, AD5780, AD5781, AD5790, AD5791 Voltage Output Digital to Analog |
3 | * Converter | |
69d900a6 MH |
4 | * |
5 | * Copyright 2011 Analog Devices Inc. | |
6 | * | |
7 | * Licensed under the GPL-2. | |
8 | */ | |
9 | ||
10 | #include <linux/interrupt.h> | |
69d900a6 MH |
11 | #include <linux/fs.h> |
12 | #include <linux/device.h> | |
13 | #include <linux/kernel.h> | |
14 | #include <linux/spi/spi.h> | |
15 | #include <linux/slab.h> | |
16 | #include <linux/sysfs.h> | |
17 | #include <linux/regulator/consumer.h> | |
99c97852 | 18 | #include <linux/module.h> |
69d900a6 | 19 | |
06458e27 JC |
20 | #include <linux/iio/iio.h> |
21 | #include <linux/iio/sysfs.h> | |
dbdc025b | 22 | #include <linux/iio/dac/ad5791.h> |
69d900a6 | 23 | |
20374d1a LPC |
24 | #define AD5791_RES_MASK(x) ((1 << (x)) - 1) |
25 | #define AD5791_DAC_MASK AD5791_RES_MASK(20) | |
26 | #define AD5791_DAC_MSB (1 << 19) | |
27 | ||
28 | #define AD5791_CMD_READ (1 << 23) | |
29 | #define AD5791_CMD_WRITE (0 << 23) | |
30 | #define AD5791_ADDR(addr) ((addr) << 20) | |
31 | ||
32 | /* Registers */ | |
33 | #define AD5791_ADDR_NOOP 0 | |
34 | #define AD5791_ADDR_DAC0 1 | |
35 | #define AD5791_ADDR_CTRL 2 | |
36 | #define AD5791_ADDR_CLRCODE 3 | |
37 | #define AD5791_ADDR_SW_CTRL 4 | |
38 | ||
39 | /* Control Register */ | |
40 | #define AD5791_CTRL_RBUF (1 << 1) | |
41 | #define AD5791_CTRL_OPGND (1 << 2) | |
42 | #define AD5791_CTRL_DACTRI (1 << 3) | |
43 | #define AD5791_CTRL_BIN2SC (1 << 4) | |
44 | #define AD5791_CTRL_SDODIS (1 << 5) | |
45 | #define AD5761_CTRL_LINCOMP(x) ((x) << 6) | |
46 | ||
47 | #define AD5791_LINCOMP_0_10 0 | |
48 | #define AD5791_LINCOMP_10_12 1 | |
49 | #define AD5791_LINCOMP_12_16 2 | |
50 | #define AD5791_LINCOMP_16_19 3 | |
51 | #define AD5791_LINCOMP_19_20 12 | |
52 | ||
53 | #define AD5780_LINCOMP_0_10 0 | |
54 | #define AD5780_LINCOMP_10_20 12 | |
55 | ||
56 | /* Software Control Register */ | |
57 | #define AD5791_SWCTRL_LDAC (1 << 0) | |
58 | #define AD5791_SWCTRL_CLR (1 << 1) | |
59 | #define AD5791_SWCTRL_RESET (1 << 2) | |
60 | ||
61 | #define AD5791_DAC_PWRDN_6K 0 | |
62 | #define AD5791_DAC_PWRDN_3STATE 1 | |
63 | ||
64 | /** | |
65 | * struct ad5791_chip_info - chip specific information | |
66 | * @get_lin_comp: function pointer to the device specific function | |
67 | */ | |
68 | ||
69 | struct ad5791_chip_info { | |
70 | int (*get_lin_comp) (unsigned int span); | |
71 | }; | |
72 | ||
73 | /** | |
74 | * struct ad5791_state - driver instance specific data | |
75 | * @us: spi_device | |
76 | * @reg_vdd: positive supply regulator | |
77 | * @reg_vss: negative supply regulator | |
78 | * @chip_info: chip model specific constants | |
79 | * @vref_mv: actual reference voltage used | |
80 | * @vref_neg_mv: voltage of the negative supply | |
81 | * @pwr_down_mode current power down mode | |
82 | */ | |
83 | ||
84 | struct ad5791_state { | |
85 | struct spi_device *spi; | |
86 | struct regulator *reg_vdd; | |
87 | struct regulator *reg_vss; | |
88 | const struct ad5791_chip_info *chip_info; | |
89 | unsigned short vref_mv; | |
90 | unsigned int vref_neg_mv; | |
91 | unsigned ctrl; | |
92 | unsigned pwr_down_mode; | |
93 | bool pwr_down; | |
94 | }; | |
95 | ||
96 | /** | |
97 | * ad5791_supported_device_ids: | |
98 | */ | |
99 | ||
100 | enum ad5791_supported_device_ids { | |
101 | ID_AD5760, | |
102 | ID_AD5780, | |
103 | ID_AD5781, | |
104 | ID_AD5791, | |
105 | }; | |
106 | ||
69d900a6 MH |
107 | static int ad5791_spi_write(struct spi_device *spi, u8 addr, u32 val) |
108 | { | |
109 | union { | |
110 | u32 d32; | |
111 | u8 d8[4]; | |
112 | } data; | |
113 | ||
114 | data.d32 = cpu_to_be32(AD5791_CMD_WRITE | | |
115 | AD5791_ADDR(addr) | | |
116 | (val & AD5791_DAC_MASK)); | |
117 | ||
118 | return spi_write(spi, &data.d8[1], 3); | |
119 | } | |
120 | ||
121 | static int ad5791_spi_read(struct spi_device *spi, u8 addr, u32 *val) | |
122 | { | |
123 | union { | |
124 | u32 d32; | |
125 | u8 d8[4]; | |
126 | } data[3]; | |
127 | int ret; | |
128 | struct spi_message msg; | |
129 | struct spi_transfer xfers[] = { | |
130 | { | |
131 | .tx_buf = &data[0].d8[1], | |
132 | .bits_per_word = 8, | |
133 | .len = 3, | |
134 | .cs_change = 1, | |
135 | }, { | |
136 | .tx_buf = &data[1].d8[1], | |
137 | .rx_buf = &data[2].d8[1], | |
138 | .bits_per_word = 8, | |
139 | .len = 3, | |
140 | }, | |
141 | }; | |
142 | ||
143 | data[0].d32 = cpu_to_be32(AD5791_CMD_READ | | |
144 | AD5791_ADDR(addr)); | |
145 | data[1].d32 = cpu_to_be32(AD5791_ADDR(AD5791_ADDR_NOOP)); | |
146 | ||
147 | spi_message_init(&msg); | |
148 | spi_message_add_tail(&xfers[0], &msg); | |
149 | spi_message_add_tail(&xfers[1], &msg); | |
150 | ret = spi_sync(spi, &msg); | |
151 | ||
152 | *val = be32_to_cpu(data[2].d32); | |
153 | ||
154 | return ret; | |
155 | } | |
156 | ||
4571b39b LPC |
157 | static const char * const ad5791_powerdown_modes[] = { |
158 | "6kohm_to_gnd", | |
159 | "three_state", | |
c5b99396 | 160 | }; |
69d900a6 | 161 | |
4571b39b LPC |
162 | static int ad5791_get_powerdown_mode(struct iio_dev *indio_dev, |
163 | const struct iio_chan_spec *chan) | |
69d900a6 | 164 | { |
f5730d52 | 165 | struct ad5791_state *st = iio_priv(indio_dev); |
69d900a6 | 166 | |
4571b39b | 167 | return st->pwr_down_mode; |
69d900a6 MH |
168 | } |
169 | ||
4571b39b LPC |
170 | static int ad5791_set_powerdown_mode(struct iio_dev *indio_dev, |
171 | const struct iio_chan_spec *chan, unsigned int mode) | |
69d900a6 | 172 | { |
f5730d52 | 173 | struct ad5791_state *st = iio_priv(indio_dev); |
69d900a6 | 174 | |
4571b39b | 175 | st->pwr_down_mode = mode; |
69d900a6 | 176 | |
4571b39b | 177 | return 0; |
69d900a6 MH |
178 | } |
179 | ||
4571b39b LPC |
180 | static const struct iio_enum ad5791_powerdown_mode_enum = { |
181 | .items = ad5791_powerdown_modes, | |
182 | .num_items = ARRAY_SIZE(ad5791_powerdown_modes), | |
183 | .get = ad5791_get_powerdown_mode, | |
184 | .set = ad5791_set_powerdown_mode, | |
185 | }; | |
186 | ||
187 | static ssize_t ad5791_read_dac_powerdown(struct iio_dev *indio_dev, | |
188 | uintptr_t private, const struct iio_chan_spec *chan, char *buf) | |
69d900a6 | 189 | { |
f5730d52 | 190 | struct ad5791_state *st = iio_priv(indio_dev); |
69d900a6 MH |
191 | |
192 | return sprintf(buf, "%d\n", st->pwr_down); | |
193 | } | |
194 | ||
4571b39b LPC |
195 | static ssize_t ad5791_write_dac_powerdown(struct iio_dev *indio_dev, |
196 | uintptr_t private, const struct iio_chan_spec *chan, const char *buf, | |
197 | size_t len) | |
69d900a6 | 198 | { |
4468cb55 | 199 | bool pwr_down; |
69d900a6 | 200 | int ret; |
f5730d52 | 201 | struct ad5791_state *st = iio_priv(indio_dev); |
69d900a6 | 202 | |
4468cb55 | 203 | ret = strtobool(buf, &pwr_down); |
69d900a6 MH |
204 | if (ret) |
205 | return ret; | |
206 | ||
4468cb55 | 207 | if (!pwr_down) { |
69d900a6 | 208 | st->ctrl &= ~(AD5791_CTRL_OPGND | AD5791_CTRL_DACTRI); |
4468cb55 | 209 | } else { |
69d900a6 MH |
210 | if (st->pwr_down_mode == AD5791_DAC_PWRDN_6K) |
211 | st->ctrl |= AD5791_CTRL_OPGND; | |
212 | else if (st->pwr_down_mode == AD5791_DAC_PWRDN_3STATE) | |
213 | st->ctrl |= AD5791_CTRL_DACTRI; | |
4468cb55 LPC |
214 | } |
215 | st->pwr_down = pwr_down; | |
69d900a6 MH |
216 | |
217 | ret = ad5791_spi_write(st->spi, AD5791_ADDR_CTRL, st->ctrl); | |
218 | ||
219 | return ret ? ret : len; | |
220 | } | |
221 | ||
69d900a6 MH |
222 | static int ad5791_get_lin_comp(unsigned int span) |
223 | { | |
224 | if (span <= 10000) | |
225 | return AD5791_LINCOMP_0_10; | |
226 | else if (span <= 12000) | |
227 | return AD5791_LINCOMP_10_12; | |
228 | else if (span <= 16000) | |
229 | return AD5791_LINCOMP_12_16; | |
230 | else if (span <= 19000) | |
231 | return AD5791_LINCOMP_16_19; | |
232 | else | |
233 | return AD5791_LINCOMP_19_20; | |
234 | } | |
235 | ||
ba1c2bb2 MH |
236 | static int ad5780_get_lin_comp(unsigned int span) |
237 | { | |
238 | if (span <= 10000) | |
239 | return AD5780_LINCOMP_0_10; | |
240 | else | |
241 | return AD5780_LINCOMP_10_20; | |
242 | } | |
ba1c2bb2 MH |
243 | static const struct ad5791_chip_info ad5791_chip_info_tbl[] = { |
244 | [ID_AD5760] = { | |
ba1c2bb2 MH |
245 | .get_lin_comp = ad5780_get_lin_comp, |
246 | }, | |
247 | [ID_AD5780] = { | |
ba1c2bb2 MH |
248 | .get_lin_comp = ad5780_get_lin_comp, |
249 | }, | |
250 | [ID_AD5781] = { | |
ba1c2bb2 MH |
251 | .get_lin_comp = ad5791_get_lin_comp, |
252 | }, | |
253 | [ID_AD5791] = { | |
ba1c2bb2 MH |
254 | .get_lin_comp = ad5791_get_lin_comp, |
255 | }, | |
256 | }; | |
257 | ||
c5b99396 JC |
258 | static int ad5791_read_raw(struct iio_dev *indio_dev, |
259 | struct iio_chan_spec const *chan, | |
260 | int *val, | |
261 | int *val2, | |
262 | long m) | |
263 | { | |
264 | struct ad5791_state *st = iio_priv(indio_dev); | |
9dc9961d | 265 | u64 val64; |
c5b99396 JC |
266 | int ret; |
267 | ||
268 | switch (m) { | |
09f4eb40 | 269 | case IIO_CHAN_INFO_RAW: |
c5b99396 JC |
270 | ret = ad5791_spi_read(st->spi, chan->address, val); |
271 | if (ret) | |
272 | return ret; | |
273 | *val &= AD5791_DAC_MASK; | |
274 | *val >>= chan->scan_type.shift; | |
c5b99396 | 275 | return IIO_VAL_INT; |
c8a9f805 | 276 | case IIO_CHAN_INFO_SCALE: |
c5b99396 | 277 | *val = 0; |
75bb23a2 | 278 | *val2 = (((u64)st->vref_mv) * 1000000ULL) >> chan->scan_type.realbits; |
c5b99396 | 279 | return IIO_VAL_INT_PLUS_MICRO; |
c8a9f805 | 280 | case IIO_CHAN_INFO_OFFSET: |
9dc9961d LPC |
281 | val64 = (((u64)st->vref_neg_mv) << chan->scan_type.realbits); |
282 | do_div(val64, st->vref_mv); | |
283 | *val = -val64; | |
284 | return IIO_VAL_INT; | |
c5b99396 JC |
285 | default: |
286 | return -EINVAL; | |
287 | } | |
288 | ||
289 | }; | |
290 | ||
4571b39b LPC |
291 | static const struct iio_chan_spec_ext_info ad5791_ext_info[] = { |
292 | { | |
293 | .name = "powerdown", | |
294 | .shared = true, | |
295 | .read = ad5791_read_dac_powerdown, | |
296 | .write = ad5791_write_dac_powerdown, | |
297 | }, | |
298 | IIO_ENUM("powerdown_mode", true, &ad5791_powerdown_mode_enum), | |
299 | IIO_ENUM_AVAILABLE("powerdown_mode", &ad5791_powerdown_mode_enum), | |
300 | { }, | |
301 | }; | |
302 | ||
303 | #define AD5791_CHAN(bits, shift) { \ | |
304 | .type = IIO_VOLTAGE, \ | |
305 | .output = 1, \ | |
306 | .indexed = 1, \ | |
307 | .address = AD5791_ADDR_DAC0, \ | |
308 | .channel = 0, \ | |
309 | .info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | \ | |
310 | IIO_CHAN_INFO_SCALE_SHARED_BIT | \ | |
311 | IIO_CHAN_INFO_OFFSET_SHARED_BIT, \ | |
312 | .scan_type = IIO_ST('u', bits, 24, shift), \ | |
313 | .ext_info = ad5791_ext_info, \ | |
314 | } | |
315 | ||
316 | static const struct iio_chan_spec ad5791_channels[] = { | |
317 | [ID_AD5760] = AD5791_CHAN(16, 4), | |
318 | [ID_AD5780] = AD5791_CHAN(18, 2), | |
319 | [ID_AD5781] = AD5791_CHAN(18, 2), | |
320 | [ID_AD5791] = AD5791_CHAN(20, 0) | |
321 | }; | |
c5b99396 JC |
322 | |
323 | static int ad5791_write_raw(struct iio_dev *indio_dev, | |
324 | struct iio_chan_spec const *chan, | |
325 | int val, | |
326 | int val2, | |
327 | long mask) | |
328 | { | |
329 | struct ad5791_state *st = iio_priv(indio_dev); | |
330 | ||
331 | switch (mask) { | |
09f4eb40 | 332 | case IIO_CHAN_INFO_RAW: |
021c0a38 | 333 | val &= AD5791_RES_MASK(chan->scan_type.realbits); |
c5b99396 JC |
334 | val <<= chan->scan_type.shift; |
335 | ||
336 | return ad5791_spi_write(st->spi, chan->address, val); | |
337 | ||
338 | default: | |
339 | return -EINVAL; | |
340 | } | |
341 | } | |
342 | ||
6fe8135f | 343 | static const struct iio_info ad5791_info = { |
c5b99396 JC |
344 | .read_raw = &ad5791_read_raw, |
345 | .write_raw = &ad5791_write_raw, | |
6fe8135f JC |
346 | .driver_module = THIS_MODULE, |
347 | }; | |
348 | ||
fc52692c | 349 | static int ad5791_probe(struct spi_device *spi) |
69d900a6 MH |
350 | { |
351 | struct ad5791_platform_data *pdata = spi->dev.platform_data; | |
f5730d52 | 352 | struct iio_dev *indio_dev; |
69d900a6 MH |
353 | struct ad5791_state *st; |
354 | int ret, pos_voltage_uv = 0, neg_voltage_uv = 0; | |
355 | ||
7cbb7537 | 356 | indio_dev = iio_device_alloc(sizeof(*st)); |
26a54797 JC |
357 | if (indio_dev == NULL) { |
358 | ret = -ENOMEM; | |
359 | goto error_ret; | |
360 | } | |
361 | st = iio_priv(indio_dev); | |
362 | st->reg_vdd = regulator_get(&spi->dev, "vdd"); | |
363 | if (!IS_ERR(st->reg_vdd)) { | |
364 | ret = regulator_enable(st->reg_vdd); | |
69d900a6 MH |
365 | if (ret) |
366 | goto error_put_reg_pos; | |
367 | ||
7e2dcc69 AL |
368 | ret = regulator_get_voltage(st->reg_vdd); |
369 | if (ret < 0) | |
370 | goto error_disable_reg_pos; | |
371 | ||
372 | pos_voltage_uv = ret; | |
69d900a6 MH |
373 | } |
374 | ||
26a54797 JC |
375 | st->reg_vss = regulator_get(&spi->dev, "vss"); |
376 | if (!IS_ERR(st->reg_vss)) { | |
377 | ret = regulator_enable(st->reg_vss); | |
69d900a6 MH |
378 | if (ret) |
379 | goto error_put_reg_neg; | |
380 | ||
7e2dcc69 AL |
381 | ret = regulator_get_voltage(st->reg_vss); |
382 | if (ret < 0) | |
383 | goto error_disable_reg_neg; | |
384 | ||
385 | neg_voltage_uv = ret; | |
69d900a6 MH |
386 | } |
387 | ||
f5730d52 JC |
388 | st->pwr_down = true; |
389 | st->spi = spi; | |
390 | ||
9dc9961d LPC |
391 | if (!IS_ERR(st->reg_vss) && !IS_ERR(st->reg_vdd)) { |
392 | st->vref_mv = (pos_voltage_uv + neg_voltage_uv) / 1000; | |
393 | st->vref_neg_mv = neg_voltage_uv / 1000; | |
394 | } else if (pdata) { | |
395 | st->vref_mv = pdata->vref_pos_mv + pdata->vref_neg_mv; | |
396 | st->vref_neg_mv = pdata->vref_neg_mv; | |
397 | } else { | |
69d900a6 | 398 | dev_warn(&spi->dev, "reference voltage unspecified\n"); |
9dc9961d | 399 | } |
69d900a6 MH |
400 | |
401 | ret = ad5791_spi_write(spi, AD5791_ADDR_SW_CTRL, AD5791_SWCTRL_RESET); | |
402 | if (ret) | |
26a54797 | 403 | goto error_disable_reg_neg; |
69d900a6 | 404 | |
c5b99396 JC |
405 | st->chip_info = &ad5791_chip_info_tbl[spi_get_device_id(spi) |
406 | ->driver_data]; | |
69d900a6 MH |
407 | |
408 | ||
ba1c2bb2 MH |
409 | st->ctrl = AD5761_CTRL_LINCOMP(st->chip_info->get_lin_comp(st->vref_mv)) |
410 | | ((pdata && pdata->use_rbuf_gain2) ? 0 : AD5791_CTRL_RBUF) | | |
69d900a6 MH |
411 | AD5791_CTRL_BIN2SC; |
412 | ||
413 | ret = ad5791_spi_write(spi, AD5791_ADDR_CTRL, st->ctrl | | |
414 | AD5791_CTRL_OPGND | AD5791_CTRL_DACTRI); | |
415 | if (ret) | |
26a54797 | 416 | goto error_disable_reg_neg; |
69d900a6 | 417 | |
f5730d52 JC |
418 | spi_set_drvdata(spi, indio_dev); |
419 | indio_dev->dev.parent = &spi->dev; | |
420 | indio_dev->info = &ad5791_info; | |
421 | indio_dev->modes = INDIO_DIRECT_MODE; | |
c5b99396 JC |
422 | indio_dev->channels |
423 | = &ad5791_channels[spi_get_device_id(spi)->driver_data]; | |
424 | indio_dev->num_channels = 1; | |
425 | indio_dev->name = spi_get_device_id(st->spi)->name; | |
f5730d52 | 426 | ret = iio_device_register(indio_dev); |
69d900a6 | 427 | if (ret) |
26a54797 | 428 | goto error_disable_reg_neg; |
69d900a6 MH |
429 | |
430 | return 0; | |
431 | ||
69d900a6 | 432 | error_disable_reg_neg: |
26a54797 JC |
433 | if (!IS_ERR(st->reg_vss)) |
434 | regulator_disable(st->reg_vss); | |
69d900a6 | 435 | error_put_reg_neg: |
26a54797 JC |
436 | if (!IS_ERR(st->reg_vss)) |
437 | regulator_put(st->reg_vss); | |
69d900a6 | 438 | |
7e2dcc69 | 439 | error_disable_reg_pos: |
26a54797 JC |
440 | if (!IS_ERR(st->reg_vdd)) |
441 | regulator_disable(st->reg_vdd); | |
69d900a6 | 442 | error_put_reg_pos: |
26a54797 JC |
443 | if (!IS_ERR(st->reg_vdd)) |
444 | regulator_put(st->reg_vdd); | |
7cbb7537 | 445 | iio_device_free(indio_dev); |
26a54797 | 446 | error_ret: |
69d900a6 | 447 | |
69d900a6 MH |
448 | return ret; |
449 | } | |
450 | ||
fc52692c | 451 | static int ad5791_remove(struct spi_device *spi) |
69d900a6 | 452 | { |
f5730d52 JC |
453 | struct iio_dev *indio_dev = spi_get_drvdata(spi); |
454 | struct ad5791_state *st = iio_priv(indio_dev); | |
69d900a6 | 455 | |
d2fffd6c | 456 | iio_device_unregister(indio_dev); |
69d900a6 | 457 | if (!IS_ERR(st->reg_vdd)) { |
26a54797 JC |
458 | regulator_disable(st->reg_vdd); |
459 | regulator_put(st->reg_vdd); | |
69d900a6 MH |
460 | } |
461 | ||
462 | if (!IS_ERR(st->reg_vss)) { | |
26a54797 JC |
463 | regulator_disable(st->reg_vss); |
464 | regulator_put(st->reg_vss); | |
69d900a6 | 465 | } |
7cbb7537 | 466 | iio_device_free(indio_dev); |
26d25ae3 | 467 | |
69d900a6 MH |
468 | return 0; |
469 | } | |
470 | ||
471 | static const struct spi_device_id ad5791_id[] = { | |
ba1c2bb2 MH |
472 | {"ad5760", ID_AD5760}, |
473 | {"ad5780", ID_AD5780}, | |
69d900a6 | 474 | {"ad5781", ID_AD5781}, |
9d41c5bb | 475 | {"ad5790", ID_AD5791}, |
ba1c2bb2 | 476 | {"ad5791", ID_AD5791}, |
69d900a6 MH |
477 | {} |
478 | }; | |
55e4390c | 479 | MODULE_DEVICE_TABLE(spi, ad5791_id); |
69d900a6 MH |
480 | |
481 | static struct spi_driver ad5791_driver = { | |
482 | .driver = { | |
483 | .name = "ad5791", | |
484 | .owner = THIS_MODULE, | |
485 | }, | |
486 | .probe = ad5791_probe, | |
fc52692c | 487 | .remove = ad5791_remove, |
69d900a6 MH |
488 | .id_table = ad5791_id, |
489 | }; | |
ae6ae6fe | 490 | module_spi_driver(ad5791_driver); |
69d900a6 MH |
491 | |
492 | MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>"); | |
9d41c5bb | 493 | MODULE_DESCRIPTION("Analog Devices AD5760/AD5780/AD5781/AD5790/AD5791 DAC"); |
69d900a6 | 494 | MODULE_LICENSE("GPL v2"); |