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