blob: 0f97ce02fcdb18961afea336d5e43c3720bb7d50 [file] [log] [blame]
Barry Song6790e292010-10-27 21:44:07 -04001/*
2 * AD5624R, AD5644R, AD5664R Digital to analog convertors spi driver
3 *
Michael Hennerich14f88f12011-03-09 16:01:45 +01004 * Copyright 2010-2011 Analog Devices Inc.
Barry Song6790e292010-10-27 21:44:07 -04005 *
Michael Hennerich14f88f12011-03-09 16:01:45 +01006 * Licensed under the GPL-2.
Barry Song6790e292010-10-27 21:44:07 -04007 */
8
9#include <linux/interrupt.h>
10#include <linux/gpio.h>
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>
Michael Hennerich14f88f12011-03-09 16:01:45 +010017#include <linux/regulator/consumer.h>
Barry Song6790e292010-10-27 21:44:07 -040018
19#include "../iio.h"
20#include "../sysfs.h"
21#include "dac.h"
22#include "ad5624r.h"
23
Michael Hennerich14f88f12011-03-09 16:01:45 +010024static const struct ad5624r_chip_info ad5624r_chip_info_tbl[] = {
25 [ID_AD5624R3] = {
26 .bits = 12,
27 .int_vref_mv = 1250,
28 },
29 [ID_AD5644R3] = {
30 .bits = 14,
31 .int_vref_mv = 1250,
32 },
33 [ID_AD5664R3] = {
34 .bits = 16,
35 .int_vref_mv = 1250,
36 },
37 [ID_AD5624R5] = {
38 .bits = 12,
39 .int_vref_mv = 2500,
40 },
41 [ID_AD5644R5] = {
42 .bits = 14,
43 .int_vref_mv = 2500,
44 },
45 [ID_AD5664R5] = {
46 .bits = 16,
47 .int_vref_mv = 2500,
48 },
Barry Song6790e292010-10-27 21:44:07 -040049};
50
Michael Hennerichdf9cd102010-11-19 15:16:44 +010051static int ad5624r_spi_write(struct spi_device *spi,
52 u8 cmd, u8 addr, u16 val, u8 len)
Barry Song6790e292010-10-27 21:44:07 -040053{
Barry Song6790e292010-10-27 21:44:07 -040054 u32 data;
55 u8 msg[3];
56
57 /*
Michael Hennerich14f88f12011-03-09 16:01:45 +010058 * The input shift register is 24 bits wide. The first two bits are
59 * don't care bits. The next three are the command bits, C2 to C0,
60 * followed by the 3-bit DAC address, A2 to A0, and then the
61 * 16-, 14-, 12-bit data-word. The data-word comprises the 16-,
62 * 14-, 12-bit input code followed by 0, 2, or 4 don't care bits,
63 * for the AD5664R, AD5644R, and AD5624R, respectively.
Barry Song6790e292010-10-27 21:44:07 -040064 */
65 data = (0 << 22) | (cmd << 19) | (addr << 16) | (val << (16 - len));
66 msg[0] = data >> 16;
67 msg[1] = data >> 8;
68 msg[2] = data;
69
Michael Hennerichdf9cd102010-11-19 15:16:44 +010070 return spi_write(spi, msg, 3);
Barry Song6790e292010-10-27 21:44:07 -040071}
72
73static ssize_t ad5624r_write_dac(struct device *dev,
Michael Hennerich24d60502010-11-19 15:16:46 +010074 struct device_attribute *attr,
75 const char *buf, size_t len)
Barry Song6790e292010-10-27 21:44:07 -040076{
77 long readin;
78 int ret;
79 struct iio_dev *indio_dev = dev_get_drvdata(dev);
80 struct ad5624r_state *st = indio_dev->dev_data;
81 struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
82
83 ret = strict_strtol(buf, 10, &readin);
84 if (ret)
85 return ret;
86
Michael Hennerichdf9cd102010-11-19 15:16:44 +010087 ret = ad5624r_spi_write(st->us, AD5624R_CMD_WRITE_INPUT_N_UPDATE_N,
Michael Hennerich14f88f12011-03-09 16:01:45 +010088 this_attr->address, readin,
89 st->chip_info->bits);
Barry Song6790e292010-10-27 21:44:07 -040090 return ret ? ret : len;
91}
92
Michael Hennerich14f88f12011-03-09 16:01:45 +010093static ssize_t ad5624r_read_powerdown_mode(struct device *dev,
Michael Hennerich24d60502010-11-19 15:16:46 +010094 struct device_attribute *attr, char *buf)
Barry Song6790e292010-10-27 21:44:07 -040095{
96 struct iio_dev *indio_dev = dev_get_drvdata(dev);
97 struct ad5624r_state *st = indio_dev->dev_data;
98
Michael Hennerich14f88f12011-03-09 16:01:45 +010099 char mode[][15] = {"", "1kohm_to_gnd", "100kohm_to_gnd", "three_state"};
100
101 return sprintf(buf, "%s\n", mode[st->pwr_down_mode]);
Barry Song6790e292010-10-27 21:44:07 -0400102}
103
Michael Hennerich14f88f12011-03-09 16:01:45 +0100104static ssize_t ad5624r_write_powerdown_mode(struct device *dev,
Michael Hennerich24d60502010-11-19 15:16:46 +0100105 struct device_attribute *attr,
106 const char *buf, size_t len)
Barry Song6790e292010-10-27 21:44:07 -0400107{
Barry Song6790e292010-10-27 21:44:07 -0400108 struct iio_dev *indio_dev = dev_get_drvdata(dev);
109 struct ad5624r_state *st = indio_dev->dev_data;
Michael Hennerich14f88f12011-03-09 16:01:45 +0100110 int ret;
Barry Song6790e292010-10-27 21:44:07 -0400111
Michael Hennerich14f88f12011-03-09 16:01:45 +0100112 if (sysfs_streq(buf, "1kohm_to_gnd"))
113 st->pwr_down_mode = AD5624R_LDAC_PWRDN_1K;
114 else if (sysfs_streq(buf, "100kohm_to_gnd"))
115 st->pwr_down_mode = AD5624R_LDAC_PWRDN_100K;
116 else if (sysfs_streq(buf, "three_state"))
117 st->pwr_down_mode = AD5624R_LDAC_PWRDN_3STATE;
118 else
119 ret = -EINVAL;
Barry Song6790e292010-10-27 21:44:07 -0400120
121 return ret ? ret : len;
122}
123
Michael Hennerich14f88f12011-03-09 16:01:45 +0100124static ssize_t ad5624r_read_dac_powerdown(struct device *dev,
Michael Hennerich24d60502010-11-19 15:16:46 +0100125 struct device_attribute *attr,
126 char *buf)
Barry Song6790e292010-10-27 21:44:07 -0400127{
128 struct iio_dev *indio_dev = dev_get_drvdata(dev);
129 struct ad5624r_state *st = indio_dev->dev_data;
130 struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
131
Michael Hennerich14f88f12011-03-09 16:01:45 +0100132 return sprintf(buf, "%d\n",
133 !!(st->pwr_down_mask & (1 << this_attr->address)));
Barry Song6790e292010-10-27 21:44:07 -0400134}
135
Michael Hennerich14f88f12011-03-09 16:01:45 +0100136static ssize_t ad5624r_write_dac_powerdown(struct device *dev,
Michael Hennerich24d60502010-11-19 15:16:46 +0100137 struct device_attribute *attr,
138 const char *buf, size_t len)
Barry Song6790e292010-10-27 21:44:07 -0400139{
140 long readin;
141 int ret;
142 struct iio_dev *indio_dev = dev_get_drvdata(dev);
143 struct ad5624r_state *st = indio_dev->dev_data;
144 struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
145
146 ret = strict_strtol(buf, 10, &readin);
147 if (ret)
148 return ret;
149
Michael Hennerich14f88f12011-03-09 16:01:45 +0100150 if (readin == 1)
151 st->pwr_down_mask |= (1 << this_attr->address);
152 else if (!readin)
153 st->pwr_down_mask &= ~(1 << this_attr->address);
154 else
155 ret = -EINVAL;
156
Michael Hennerichdf9cd102010-11-19 15:16:44 +0100157 ret = ad5624r_spi_write(st->us, AD5624R_CMD_POWERDOWN_DAC, 0,
Michael Hennerich14f88f12011-03-09 16:01:45 +0100158 (st->pwr_down_mode << 4) |
159 st->pwr_down_mask, 16);
Barry Song6790e292010-10-27 21:44:07 -0400160
161 return ret ? ret : len;
162}
163
Michael Hennerich14f88f12011-03-09 16:01:45 +0100164static ssize_t ad5624r_show_scale(struct device *dev,
165 struct device_attribute *attr,
166 char *buf)
Barry Song6790e292010-10-27 21:44:07 -0400167{
Michael Hennerich14f88f12011-03-09 16:01:45 +0100168 struct iio_dev *dev_info = dev_get_drvdata(dev);
169 struct ad5624r_state *st = iio_dev_get_devdata(dev_info);
170 /* Corresponds to Vref / 2^(bits) */
171 unsigned int scale_uv = (st->vref_mv * 1000) >> st->chip_info->bits;
Barry Song6790e292010-10-27 21:44:07 -0400172
Michael Hennerich14f88f12011-03-09 16:01:45 +0100173 return sprintf(buf, "%d.%03d\n", scale_uv / 1000, scale_uv % 1000);
Barry Song6790e292010-10-27 21:44:07 -0400174}
Michael Hennerich14f88f12011-03-09 16:01:45 +0100175static IIO_DEVICE_ATTR(out_scale, S_IRUGO, ad5624r_show_scale, NULL, 0);
Barry Song6790e292010-10-27 21:44:07 -0400176
Michael Hennerich14f88f12011-03-09 16:01:45 +0100177static ssize_t ad5624r_show_name(struct device *dev,
178 struct device_attribute *attr,
179 char *buf)
Barry Song6790e292010-10-27 21:44:07 -0400180{
Michael Hennerich14f88f12011-03-09 16:01:45 +0100181 struct iio_dev *dev_info = dev_get_drvdata(dev);
182 struct ad5624r_state *st = iio_dev_get_devdata(dev_info);
Barry Song6790e292010-10-27 21:44:07 -0400183
Michael Hennerich14f88f12011-03-09 16:01:45 +0100184 return sprintf(buf, "%s\n", spi_get_device_id(st->us)->name);
Barry Song6790e292010-10-27 21:44:07 -0400185}
Michael Hennerich14f88f12011-03-09 16:01:45 +0100186static IIO_DEVICE_ATTR(name, S_IRUGO, ad5624r_show_name, NULL, 0);
Barry Song6790e292010-10-27 21:44:07 -0400187
Michael Hennerich51f8ad32010-11-19 15:16:43 +0100188static IIO_DEV_ATTR_OUT_RAW(0, ad5624r_write_dac, AD5624R_ADDR_DAC0);
189static IIO_DEV_ATTR_OUT_RAW(1, ad5624r_write_dac, AD5624R_ADDR_DAC1);
190static IIO_DEV_ATTR_OUT_RAW(2, ad5624r_write_dac, AD5624R_ADDR_DAC2);
191static IIO_DEV_ATTR_OUT_RAW(3, ad5624r_write_dac, AD5624R_ADDR_DAC3);
Barry Song6790e292010-10-27 21:44:07 -0400192
Michael Hennerich14f88f12011-03-09 16:01:45 +0100193static IIO_DEVICE_ATTR(out_powerdown_mode, S_IRUGO |
194 S_IWUSR, ad5624r_read_powerdown_mode,
195 ad5624r_write_powerdown_mode, 0);
Barry Song6790e292010-10-27 21:44:07 -0400196
Michael Hennerich14f88f12011-03-09 16:01:45 +0100197static IIO_CONST_ATTR(out_powerdown_mode_available,
198 "1kohm_to_gnd 100kohm_to_gnd three_state");
Barry Song6790e292010-10-27 21:44:07 -0400199
Michael Hennerich14f88f12011-03-09 16:01:45 +0100200#define IIO_DEV_ATTR_DAC_POWERDOWN(_num, _show, _store, _addr) \
201 IIO_DEVICE_ATTR(out##_num##_powerdown, \
202 S_IRUGO | S_IWUSR, _show, _store, _addr)
203
204static IIO_DEV_ATTR_DAC_POWERDOWN(0, ad5624r_read_dac_powerdown,
205 ad5624r_write_dac_powerdown, 0);
206static IIO_DEV_ATTR_DAC_POWERDOWN(1, ad5624r_read_dac_powerdown,
207 ad5624r_write_dac_powerdown, 1);
208static IIO_DEV_ATTR_DAC_POWERDOWN(2, ad5624r_read_dac_powerdown,
209 ad5624r_write_dac_powerdown, 2);
210static IIO_DEV_ATTR_DAC_POWERDOWN(3, ad5624r_read_dac_powerdown,
211 ad5624r_write_dac_powerdown, 3);
Barry Song6790e292010-10-27 21:44:07 -0400212
213static struct attribute *ad5624r_attributes[] = {
Michael Hennerich51f8ad32010-11-19 15:16:43 +0100214 &iio_dev_attr_out0_raw.dev_attr.attr,
215 &iio_dev_attr_out1_raw.dev_attr.attr,
216 &iio_dev_attr_out2_raw.dev_attr.attr,
217 &iio_dev_attr_out3_raw.dev_attr.attr,
Michael Hennerich14f88f12011-03-09 16:01:45 +0100218 &iio_dev_attr_out0_powerdown.dev_attr.attr,
219 &iio_dev_attr_out1_powerdown.dev_attr.attr,
220 &iio_dev_attr_out2_powerdown.dev_attr.attr,
221 &iio_dev_attr_out3_powerdown.dev_attr.attr,
222 &iio_dev_attr_out_powerdown_mode.dev_attr.attr,
223 &iio_const_attr_out_powerdown_mode_available.dev_attr.attr,
224 &iio_dev_attr_out_scale.dev_attr.attr,
225 &iio_dev_attr_name.dev_attr.attr,
Barry Song6790e292010-10-27 21:44:07 -0400226 NULL,
227};
228
229static const struct attribute_group ad5624r_attribute_group = {
230 .attrs = ad5624r_attributes,
231};
232
233static int __devinit ad5624r_probe(struct spi_device *spi)
234{
Barry Song6790e292010-10-27 21:44:07 -0400235 struct ad5624r_state *st;
Michael Hennerich14f88f12011-03-09 16:01:45 +0100236 int ret, voltage_uv = 0;
Barry Song6790e292010-10-27 21:44:07 -0400237
238 st = kzalloc(sizeof(*st), GFP_KERNEL);
239 if (st == NULL) {
240 ret = -ENOMEM;
241 goto error_ret;
242 }
243 spi_set_drvdata(spi, st);
244
Michael Hennerich14f88f12011-03-09 16:01:45 +0100245 st->reg = regulator_get(&spi->dev, "vcc");
246 if (!IS_ERR(st->reg)) {
247 ret = regulator_enable(st->reg);
248 if (ret)
249 goto error_put_reg;
250
251 voltage_uv = regulator_get_voltage(st->reg);
252 }
253
254 st->chip_info =
255 &ad5624r_chip_info_tbl[spi_get_device_id(spi)->driver_data];
256
257 if (voltage_uv)
258 st->vref_mv = voltage_uv / 1000;
259 else
260 st->vref_mv = st->chip_info->int_vref_mv;
Barry Song6790e292010-10-27 21:44:07 -0400261
262 st->us = spi;
263 st->indio_dev = iio_allocate_device();
264 if (st->indio_dev == NULL) {
265 ret = -ENOMEM;
Michael Hennerich14f88f12011-03-09 16:01:45 +0100266 goto error_disable_reg;
Barry Song6790e292010-10-27 21:44:07 -0400267 }
268 st->indio_dev->dev.parent = &spi->dev;
Barry Song6790e292010-10-27 21:44:07 -0400269 st->indio_dev->attrs = &ad5624r_attribute_group;
270 st->indio_dev->dev_data = (void *)(st);
271 st->indio_dev->driver_module = THIS_MODULE;
272 st->indio_dev->modes = INDIO_DIRECT_MODE;
273
274 ret = iio_device_register(st->indio_dev);
275 if (ret)
276 goto error_free_dev;
277
Michael Hennerich14f88f12011-03-09 16:01:45 +0100278 ret = ad5624r_spi_write(spi, AD5624R_CMD_INTERNAL_REFER_SETUP, 0,
279 !!voltage_uv, 16);
280 if (ret)
281 goto error_free_dev;
Barry Song6790e292010-10-27 21:44:07 -0400282
283 return 0;
284
285error_free_dev:
286 iio_free_device(st->indio_dev);
Michael Hennerich14f88f12011-03-09 16:01:45 +0100287error_disable_reg:
288 if (!IS_ERR(st->reg))
289 regulator_disable(st->reg);
290error_put_reg:
291 if (!IS_ERR(st->reg))
292 regulator_put(st->reg);
293
Barry Song6790e292010-10-27 21:44:07 -0400294 kfree(st);
295error_ret:
296 return ret;
297}
298
299static int __devexit ad5624r_remove(struct spi_device *spi)
300{
301 struct ad5624r_state *st = spi_get_drvdata(spi);
302
303 iio_device_unregister(st->indio_dev);
Michael Hennerich14f88f12011-03-09 16:01:45 +0100304
305 if (!IS_ERR(st->reg)) {
306 regulator_disable(st->reg);
307 regulator_put(st->reg);
308 }
309
Barry Song6790e292010-10-27 21:44:07 -0400310 kfree(st);
311
312 return 0;
313}
314
Michael Hennerichece30c12010-11-19 15:16:45 +0100315static const struct spi_device_id ad5624r_id[] = {
Michael Hennerich14f88f12011-03-09 16:01:45 +0100316 {"ad5624r3", ID_AD5624R3},
317 {"ad5644r3", ID_AD5644R3},
318 {"ad5664r3", ID_AD5664R3},
319 {"ad5624r5", ID_AD5624R5},
320 {"ad5644r5", ID_AD5644R5},
321 {"ad5664r5", ID_AD5664R5},
Michael Hennerichece30c12010-11-19 15:16:45 +0100322 {}
323};
324
Barry Song6790e292010-10-27 21:44:07 -0400325static struct spi_driver ad5624r_driver = {
326 .driver = {
Michael Hennerich24d60502010-11-19 15:16:46 +0100327 .name = "ad5624r",
328 .owner = THIS_MODULE,
329 },
Barry Song6790e292010-10-27 21:44:07 -0400330 .probe = ad5624r_probe,
331 .remove = __devexit_p(ad5624r_remove),
Michael Hennerichece30c12010-11-19 15:16:45 +0100332 .id_table = ad5624r_id,
Barry Song6790e292010-10-27 21:44:07 -0400333};
334
335static __init int ad5624r_spi_init(void)
336{
337 return spi_register_driver(&ad5624r_driver);
338}
339module_init(ad5624r_spi_init);
340
341static __exit void ad5624r_spi_exit(void)
342{
343 spi_unregister_driver(&ad5624r_driver);
344}
345module_exit(ad5624r_spi_exit);
346
347MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>");
348MODULE_DESCRIPTION("Analog Devices AD5624/44/64R DAC spi driver");
349MODULE_LICENSE("GPL v2");