]>
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> | |
19 | ||
20 | #include "../iio.h" | |
21 | #include "../sysfs.h" | |
22 | #include "dac.h" | |
23 | ||
24 | #include "ad5446.h" | |
25 | ||
d846263d MH |
26 | static void ad5446_store_sample(struct ad5446_state *st, unsigned val) |
27 | { | |
28 | st->data.d16 = cpu_to_be16(AD5446_LOAD | | |
29 | (val << st->chip_info->left_shift)); | |
30 | } | |
31 | ||
32 | static void ad5542_store_sample(struct ad5446_state *st, unsigned val) | |
33 | { | |
34 | st->data.d16 = cpu_to_be16(val << st->chip_info->left_shift); | |
35 | } | |
36 | ||
37 | static void ad5620_store_sample(struct ad5446_state *st, unsigned val) | |
38 | { | |
39 | st->data.d16 = cpu_to_be16(AD5620_LOAD | | |
40 | (val << st->chip_info->left_shift)); | |
41 | } | |
42 | ||
43 | static void ad5660_store_sample(struct ad5446_state *st, unsigned val) | |
44 | { | |
45 | val |= AD5660_LOAD; | |
46 | st->data.d24[0] = (val >> 16) & 0xFF; | |
47 | st->data.d24[1] = (val >> 8) & 0xFF; | |
48 | st->data.d24[2] = val & 0xFF; | |
49 | } | |
50 | ||
b5a49481 MH |
51 | static ssize_t ad5446_write(struct device *dev, |
52 | struct device_attribute *attr, | |
53 | const char *buf, | |
54 | size_t len) | |
55 | { | |
56 | struct iio_dev *dev_info = dev_get_drvdata(dev); | |
57 | struct ad5446_state *st = dev_info->dev_data; | |
58 | int ret; | |
59 | long val; | |
60 | ||
61 | ret = strict_strtol(buf, 10, &val); | |
62 | if (ret) | |
63 | goto error_ret; | |
64 | ||
65 | if (val > RES_MASK(st->chip_info->bits)) { | |
66 | ret = -EINVAL; | |
67 | goto error_ret; | |
68 | } | |
69 | ||
70 | mutex_lock(&dev_info->mlock); | |
d846263d | 71 | st->chip_info->store_sample(st, val); |
b5a49481 MH |
72 | ret = spi_sync(st->spi, &st->msg); |
73 | mutex_unlock(&dev_info->mlock); | |
74 | ||
75 | error_ret: | |
76 | return ret ? ret : len; | |
77 | } | |
78 | ||
79 | static IIO_DEV_ATTR_OUT_RAW(0, ad5446_write, 0); | |
80 | ||
81 | static ssize_t ad5446_show_scale(struct device *dev, | |
82 | struct device_attribute *attr, | |
83 | char *buf) | |
84 | { | |
85 | struct iio_dev *dev_info = dev_get_drvdata(dev); | |
86 | struct ad5446_state *st = iio_dev_get_devdata(dev_info); | |
87 | /* Corresponds to Vref / 2^(bits) */ | |
88 | unsigned int scale_uv = (st->vref_mv * 1000) >> st->chip_info->bits; | |
89 | ||
41135b1c | 90 | return sprintf(buf, "%d.%03d\n", scale_uv / 1000, scale_uv % 1000); |
b5a49481 MH |
91 | } |
92 | static IIO_DEVICE_ATTR(out_scale, S_IRUGO, ad5446_show_scale, NULL, 0); | |
93 | ||
94 | static ssize_t ad5446_show_name(struct device *dev, | |
95 | struct device_attribute *attr, | |
96 | char *buf) | |
97 | { | |
98 | struct iio_dev *dev_info = dev_get_drvdata(dev); | |
99 | struct ad5446_state *st = iio_dev_get_devdata(dev_info); | |
100 | ||
101 | return sprintf(buf, "%s\n", spi_get_device_id(st->spi)->name); | |
102 | } | |
103 | static IIO_DEVICE_ATTR(name, S_IRUGO, ad5446_show_name, NULL, 0); | |
104 | ||
105 | static struct attribute *ad5446_attributes[] = { | |
106 | &iio_dev_attr_out0_raw.dev_attr.attr, | |
107 | &iio_dev_attr_out_scale.dev_attr.attr, | |
108 | &iio_dev_attr_name.dev_attr.attr, | |
109 | NULL, | |
110 | }; | |
111 | ||
112 | static const struct attribute_group ad5446_attribute_group = { | |
113 | .attrs = ad5446_attributes, | |
114 | }; | |
115 | ||
116 | static const struct ad5446_chip_info ad5446_chip_info_tbl[] = { | |
117 | [ID_AD5444] = { | |
118 | .bits = 12, | |
119 | .storagebits = 16, | |
120 | .left_shift = 2, | |
d846263d | 121 | .store_sample = ad5446_store_sample, |
b5a49481 MH |
122 | }, |
123 | [ID_AD5446] = { | |
124 | .bits = 14, | |
125 | .storagebits = 16, | |
126 | .left_shift = 0, | |
d846263d | 127 | .store_sample = ad5446_store_sample, |
b5a49481 MH |
128 | }, |
129 | [ID_AD5542A] = { | |
130 | .bits = 16, | |
131 | .storagebits = 16, | |
132 | .left_shift = 0, | |
d846263d | 133 | .store_sample = ad5542_store_sample, |
b5a49481 MH |
134 | }, |
135 | [ID_AD5512A] = { | |
136 | .bits = 12, | |
137 | .storagebits = 16, | |
138 | .left_shift = 4, | |
d846263d MH |
139 | .store_sample = ad5542_store_sample, |
140 | }, | |
141 | [ID_AD5620_2500] = { | |
142 | .bits = 12, | |
143 | .storagebits = 16, | |
144 | .left_shift = 2, | |
d846263d MH |
145 | .int_vref_mv = 2500, |
146 | .store_sample = ad5620_store_sample, | |
147 | }, | |
148 | [ID_AD5620_1250] = { | |
149 | .bits = 12, | |
150 | .storagebits = 16, | |
151 | .left_shift = 2, | |
d846263d MH |
152 | .int_vref_mv = 1250, |
153 | .store_sample = ad5620_store_sample, | |
154 | }, | |
155 | [ID_AD5640_2500] = { | |
156 | .bits = 14, | |
157 | .storagebits = 16, | |
158 | .left_shift = 0, | |
d846263d MH |
159 | .int_vref_mv = 2500, |
160 | .store_sample = ad5620_store_sample, | |
161 | }, | |
162 | [ID_AD5640_1250] = { | |
163 | .bits = 14, | |
164 | .storagebits = 16, | |
165 | .left_shift = 0, | |
d846263d MH |
166 | .int_vref_mv = 1250, |
167 | .store_sample = ad5620_store_sample, | |
168 | }, | |
169 | [ID_AD5660_2500] = { | |
170 | .bits = 16, | |
171 | .storagebits = 24, | |
172 | .left_shift = 0, | |
d846263d MH |
173 | .int_vref_mv = 2500, |
174 | .store_sample = ad5660_store_sample, | |
175 | }, | |
176 | [ID_AD5660_1250] = { | |
177 | .bits = 16, | |
178 | .storagebits = 24, | |
179 | .left_shift = 0, | |
d846263d MH |
180 | .int_vref_mv = 1250, |
181 | .store_sample = ad5660_store_sample, | |
b5a49481 MH |
182 | }, |
183 | }; | |
184 | ||
185 | static int __devinit ad5446_probe(struct spi_device *spi) | |
186 | { | |
187 | struct ad5446_state *st; | |
188 | int ret, voltage_uv = 0; | |
189 | ||
190 | st = kzalloc(sizeof(*st), GFP_KERNEL); | |
191 | if (st == NULL) { | |
192 | ret = -ENOMEM; | |
193 | goto error_ret; | |
194 | } | |
195 | ||
196 | st->reg = regulator_get(&spi->dev, "vcc"); | |
197 | if (!IS_ERR(st->reg)) { | |
198 | ret = regulator_enable(st->reg); | |
199 | if (ret) | |
200 | goto error_put_reg; | |
201 | ||
202 | voltage_uv = regulator_get_voltage(st->reg); | |
203 | } | |
204 | ||
205 | st->chip_info = | |
206 | &ad5446_chip_info_tbl[spi_get_device_id(spi)->driver_data]; | |
207 | ||
208 | spi_set_drvdata(spi, st); | |
209 | ||
210 | st->spi = spi; | |
211 | ||
212 | st->indio_dev = iio_allocate_device(); | |
213 | if (st->indio_dev == NULL) { | |
214 | ret = -ENOMEM; | |
215 | goto error_disable_reg; | |
216 | } | |
217 | ||
218 | /* Estabilish that the iio_dev is a child of the spi device */ | |
219 | st->indio_dev->dev.parent = &spi->dev; | |
220 | st->indio_dev->attrs = &ad5446_attribute_group; | |
221 | st->indio_dev->dev_data = (void *)(st); | |
222 | st->indio_dev->driver_module = THIS_MODULE; | |
223 | st->indio_dev->modes = INDIO_DIRECT_MODE; | |
224 | ||
225 | /* Setup default message */ | |
226 | ||
d846263d MH |
227 | st->xfer.tx_buf = &st->data; |
228 | st->xfer.len = st->chip_info->storagebits / 8; | |
b5a49481 MH |
229 | |
230 | spi_message_init(&st->msg); | |
231 | spi_message_add_tail(&st->xfer, &st->msg); | |
232 | ||
d846263d MH |
233 | switch (spi_get_device_id(spi)->driver_data) { |
234 | case ID_AD5620_2500: | |
235 | case ID_AD5620_1250: | |
236 | case ID_AD5640_2500: | |
237 | case ID_AD5640_1250: | |
238 | case ID_AD5660_2500: | |
239 | case ID_AD5660_1250: | |
240 | st->vref_mv = st->chip_info->int_vref_mv; | |
241 | break; | |
242 | default: | |
243 | if (voltage_uv) | |
244 | st->vref_mv = voltage_uv / 1000; | |
245 | else | |
246 | dev_warn(&spi->dev, | |
247 | "reference voltage unspecified\n"); | |
248 | } | |
b5a49481 MH |
249 | |
250 | ret = iio_device_register(st->indio_dev); | |
251 | if (ret) | |
252 | goto error_free_device; | |
253 | ||
254 | return 0; | |
255 | ||
256 | error_free_device: | |
257 | iio_free_device(st->indio_dev); | |
258 | error_disable_reg: | |
259 | if (!IS_ERR(st->reg)) | |
260 | regulator_disable(st->reg); | |
261 | error_put_reg: | |
262 | if (!IS_ERR(st->reg)) | |
263 | regulator_put(st->reg); | |
264 | kfree(st); | |
265 | error_ret: | |
266 | return ret; | |
267 | } | |
268 | ||
269 | static int ad5446_remove(struct spi_device *spi) | |
270 | { | |
271 | struct ad5446_state *st = spi_get_drvdata(spi); | |
272 | struct iio_dev *indio_dev = st->indio_dev; | |
273 | ||
274 | iio_device_unregister(indio_dev); | |
275 | if (!IS_ERR(st->reg)) { | |
276 | regulator_disable(st->reg); | |
277 | regulator_put(st->reg); | |
278 | } | |
279 | kfree(st); | |
280 | return 0; | |
281 | } | |
282 | ||
283 | static const struct spi_device_id ad5446_id[] = { | |
284 | {"ad5444", ID_AD5444}, | |
285 | {"ad5446", ID_AD5446}, | |
286 | {"ad5542a", ID_AD5542A}, | |
287 | {"ad5512a", ID_AD5512A}, | |
d846263d MH |
288 | {"ad5620-2500", ID_AD5620_2500}, /* AD5620/40/60: */ |
289 | {"ad5620-1250", ID_AD5620_1250}, /* part numbers may look differently */ | |
290 | {"ad5640-2500", ID_AD5640_2500}, | |
291 | {"ad5640-1250", ID_AD5640_1250}, | |
292 | {"ad5660-2500", ID_AD5660_2500}, | |
293 | {"ad5660-1250", ID_AD5660_1250}, | |
b5a49481 MH |
294 | {} |
295 | }; | |
296 | ||
297 | static struct spi_driver ad5446_driver = { | |
298 | .driver = { | |
299 | .name = "ad5446", | |
300 | .bus = &spi_bus_type, | |
301 | .owner = THIS_MODULE, | |
302 | }, | |
303 | .probe = ad5446_probe, | |
304 | .remove = __devexit_p(ad5446_remove), | |
305 | .id_table = ad5446_id, | |
306 | }; | |
307 | ||
308 | static int __init ad5446_init(void) | |
309 | { | |
310 | return spi_register_driver(&ad5446_driver); | |
311 | } | |
312 | module_init(ad5446_init); | |
313 | ||
314 | static void __exit ad5446_exit(void) | |
315 | { | |
316 | spi_unregister_driver(&ad5446_driver); | |
317 | } | |
318 | module_exit(ad5446_exit); | |
319 | ||
320 | MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>"); | |
321 | MODULE_DESCRIPTION("Analog Devices AD5444/AD5446 DAC"); | |
322 | MODULE_LICENSE("GPL v2"); | |
323 | MODULE_ALIAS("spi:ad5446"); |