Commit | Line | Data |
---|---|---|
12b9d5bf | 1 | /* |
bd5a793f | 2 | * AD9833/AD9834/AD9837/AD9838 SPI DDS driver |
12b9d5bf | 3 | * |
bd5a793f | 4 | * Copyright 2010-2011 Analog Devices Inc. |
12b9d5bf | 5 | * |
bd5a793f | 6 | * Licensed under the GPL-2. |
12b9d5bf MH |
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> |
12b9d5bf MH |
20 | #include <asm/div64.h> |
21 | ||
22 | #include "../iio.h" | |
23 | #include "../sysfs.h" | |
24 | #include "dds.h" | |
25 | ||
26 | #include "ad9834.h" | |
27 | ||
28 | static unsigned int ad9834_calc_freqreg(unsigned long mclk, unsigned long fout) | |
29 | { | |
30 | unsigned long long freqreg = (u64) fout * (u64) (1 << AD9834_FREQ_BITS); | |
31 | do_div(freqreg, mclk); | |
32 | return freqreg; | |
33 | } | |
34 | ||
35 | static int ad9834_write_frequency(struct ad9834_state *st, | |
36 | unsigned long addr, unsigned long fout) | |
37 | { | |
38 | unsigned long regval; | |
39 | ||
40 | if (fout > (st->mclk / 2)) | |
41 | return -EINVAL; | |
42 | ||
43 | regval = ad9834_calc_freqreg(st->mclk, fout); | |
44 | ||
45 | st->freq_data[0] = cpu_to_be16(addr | (regval & | |
46 | RES_MASK(AD9834_FREQ_BITS / 2))); | |
47 | st->freq_data[1] = cpu_to_be16(addr | ((regval >> | |
48 | (AD9834_FREQ_BITS / 2)) & | |
49 | RES_MASK(AD9834_FREQ_BITS / 2))); | |
50 | ||
bd5a793f | 51 | return spi_sync(st->spi, &st->freq_msg); |
12b9d5bf MH |
52 | } |
53 | ||
54 | static int ad9834_write_phase(struct ad9834_state *st, | |
55 | unsigned long addr, unsigned long phase) | |
56 | { | |
57 | if (phase > (1 << AD9834_PHASE_BITS)) | |
58 | return -EINVAL; | |
59 | st->data = cpu_to_be16(addr | phase); | |
60 | ||
61 | return spi_sync(st->spi, &st->msg); | |
62 | } | |
63 | ||
64 | static ssize_t ad9834_write(struct device *dev, | |
65 | struct device_attribute *attr, | |
66 | const char *buf, | |
67 | size_t len) | |
68 | { | |
638e59fc JC |
69 | struct iio_dev *indio_dev = dev_get_drvdata(dev); |
70 | struct ad9834_state *st = iio_priv(indio_dev); | |
12b9d5bf MH |
71 | struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); |
72 | int ret; | |
73 | long val; | |
74 | ||
75 | ret = strict_strtoul(buf, 10, &val); | |
76 | if (ret) | |
77 | goto error_ret; | |
78 | ||
638e59fc | 79 | mutex_lock(&indio_dev->mlock); |
12b9d5bf MH |
80 | switch (this_attr->address) { |
81 | case AD9834_REG_FREQ0: | |
82 | case AD9834_REG_FREQ1: | |
83 | ret = ad9834_write_frequency(st, this_attr->address, val); | |
84 | break; | |
85 | case AD9834_REG_PHASE0: | |
86 | case AD9834_REG_PHASE1: | |
87 | ret = ad9834_write_phase(st, this_attr->address, val); | |
88 | break; | |
89 | case AD9834_OPBITEN: | |
90 | if (st->control & AD9834_MODE) { | |
91 | ret = -EINVAL; /* AD9843 reserved mode */ | |
92 | break; | |
93 | } | |
94 | ||
95 | if (val) | |
96 | st->control |= AD9834_OPBITEN; | |
97 | else | |
98 | st->control &= ~AD9834_OPBITEN; | |
99 | ||
100 | st->data = cpu_to_be16(AD9834_REG_CMD | st->control); | |
101 | ret = spi_sync(st->spi, &st->msg); | |
102 | break; | |
103 | case AD9834_PIN_SW: | |
104 | if (val) | |
105 | st->control |= AD9834_PIN_SW; | |
106 | else | |
107 | st->control &= ~AD9834_PIN_SW; | |
108 | st->data = cpu_to_be16(AD9834_REG_CMD | st->control); | |
109 | ret = spi_sync(st->spi, &st->msg); | |
110 | break; | |
111 | case AD9834_FSEL: | |
112 | case AD9834_PSEL: | |
113 | if (val == 0) | |
114 | st->control &= ~(this_attr->address | AD9834_PIN_SW); | |
115 | else if (val == 1) { | |
116 | st->control |= this_attr->address; | |
117 | st->control &= ~AD9834_PIN_SW; | |
118 | } else { | |
119 | ret = -EINVAL; | |
120 | break; | |
121 | } | |
122 | st->data = cpu_to_be16(AD9834_REG_CMD | st->control); | |
123 | ret = spi_sync(st->spi, &st->msg); | |
124 | break; | |
125 | case AD9834_RESET: | |
126 | if (val) | |
127 | st->control &= ~AD9834_RESET; | |
128 | else | |
129 | st->control |= AD9834_RESET; | |
130 | ||
131 | st->data = cpu_to_be16(AD9834_REG_CMD | st->control); | |
132 | ret = spi_sync(st->spi, &st->msg); | |
133 | break; | |
134 | default: | |
135 | ret = -ENODEV; | |
136 | } | |
638e59fc | 137 | mutex_unlock(&indio_dev->mlock); |
12b9d5bf MH |
138 | |
139 | error_ret: | |
140 | return ret ? ret : len; | |
141 | } | |
142 | ||
143 | static ssize_t ad9834_store_wavetype(struct device *dev, | |
144 | struct device_attribute *attr, | |
145 | const char *buf, | |
146 | size_t len) | |
147 | { | |
638e59fc JC |
148 | struct iio_dev *indio_dev = dev_get_drvdata(dev); |
149 | struct ad9834_state *st = iio_priv(indio_dev); | |
12b9d5bf MH |
150 | struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); |
151 | int ret = 0; | |
bd5a793f | 152 | bool is_ad9833_7 = (st->devid == ID_AD9833) || (st->devid == ID_AD9837); |
12b9d5bf | 153 | |
638e59fc | 154 | mutex_lock(&indio_dev->mlock); |
12b9d5bf MH |
155 | |
156 | switch (this_attr->address) { | |
157 | case 0: | |
158 | if (sysfs_streq(buf, "sine")) { | |
159 | st->control &= ~AD9834_MODE; | |
bd5a793f | 160 | if (is_ad9833_7) |
12b9d5bf MH |
161 | st->control &= ~AD9834_OPBITEN; |
162 | } else if (sysfs_streq(buf, "triangle")) { | |
bd5a793f | 163 | if (is_ad9833_7) { |
12b9d5bf MH |
164 | st->control &= ~AD9834_OPBITEN; |
165 | st->control |= AD9834_MODE; | |
166 | } else if (st->control & AD9834_OPBITEN) { | |
167 | ret = -EINVAL; /* AD9843 reserved mode */ | |
168 | } else { | |
169 | st->control |= AD9834_MODE; | |
170 | } | |
bd5a793f | 171 | } else if (is_ad9833_7 && sysfs_streq(buf, "square")) { |
12b9d5bf MH |
172 | st->control &= ~AD9834_MODE; |
173 | st->control |= AD9834_OPBITEN; | |
174 | } else { | |
175 | ret = -EINVAL; | |
176 | } | |
177 | ||
178 | break; | |
179 | case 1: | |
180 | if (sysfs_streq(buf, "square") && | |
181 | !(st->control & AD9834_MODE)) { | |
182 | st->control &= ~AD9834_MODE; | |
183 | st->control |= AD9834_OPBITEN; | |
184 | } else { | |
185 | ret = -EINVAL; | |
186 | } | |
187 | break; | |
188 | default: | |
189 | ret = -EINVAL; | |
190 | break; | |
191 | } | |
192 | ||
193 | if (!ret) { | |
194 | st->data = cpu_to_be16(AD9834_REG_CMD | st->control); | |
195 | ret = spi_sync(st->spi, &st->msg); | |
196 | } | |
638e59fc | 197 | mutex_unlock(&indio_dev->mlock); |
12b9d5bf MH |
198 | |
199 | return ret ? ret : len; | |
200 | } | |
201 | ||
12b9d5bf MH |
202 | static ssize_t ad9834_show_out0_wavetype_available(struct device *dev, |
203 | struct device_attribute *attr, | |
204 | char *buf) | |
205 | { | |
638e59fc JC |
206 | struct iio_dev *indio_dev = dev_get_drvdata(dev); |
207 | struct ad9834_state *st = iio_priv(indio_dev); | |
12b9d5bf MH |
208 | char *str; |
209 | ||
bd5a793f | 210 | if ((st->devid == ID_AD9833) || (st->devid == ID_AD9837)) |
12b9d5bf MH |
211 | str = "sine triangle square"; |
212 | else if (st->control & AD9834_OPBITEN) | |
213 | str = "sine"; | |
214 | else | |
215 | str = "sine triangle"; | |
216 | ||
217 | return sprintf(buf, "%s\n", str); | |
218 | } | |
219 | ||
220 | ||
221 | static IIO_DEVICE_ATTR(dds0_out0_wavetype_available, S_IRUGO, | |
222 | ad9834_show_out0_wavetype_available, NULL, 0); | |
223 | ||
224 | static ssize_t ad9834_show_out1_wavetype_available(struct device *dev, | |
225 | struct device_attribute *attr, | |
226 | char *buf) | |
227 | { | |
638e59fc JC |
228 | struct iio_dev *indio_dev = dev_get_drvdata(dev); |
229 | struct ad9834_state *st = iio_priv(indio_dev); | |
12b9d5bf MH |
230 | char *str; |
231 | ||
232 | if (st->control & AD9834_MODE) | |
233 | str = ""; | |
234 | else | |
235 | str = "square"; | |
236 | ||
237 | return sprintf(buf, "%s\n", str); | |
238 | } | |
239 | ||
240 | static IIO_DEVICE_ATTR(dds0_out1_wavetype_available, S_IRUGO, | |
241 | ad9834_show_out1_wavetype_available, NULL, 0); | |
242 | ||
243 | /** | |
244 | * see dds.h for further information | |
245 | */ | |
246 | ||
247 | static IIO_DEV_ATTR_FREQ(0, 0, S_IWUSR, NULL, ad9834_write, AD9834_REG_FREQ0); | |
248 | static IIO_DEV_ATTR_FREQ(0, 1, S_IWUSR, NULL, ad9834_write, AD9834_REG_FREQ1); | |
249 | static IIO_DEV_ATTR_FREQSYMBOL(0, S_IWUSR, NULL, ad9834_write, AD9834_FSEL); | |
250 | static IIO_CONST_ATTR_FREQ_SCALE(0, "1"); /* 1Hz */ | |
251 | ||
252 | static IIO_DEV_ATTR_PHASE(0, 0, S_IWUSR, NULL, ad9834_write, AD9834_REG_PHASE0); | |
253 | static IIO_DEV_ATTR_PHASE(0, 1, S_IWUSR, NULL, ad9834_write, AD9834_REG_PHASE1); | |
254 | static IIO_DEV_ATTR_PHASESYMBOL(0, S_IWUSR, NULL, ad9834_write, AD9834_PSEL); | |
255 | static IIO_CONST_ATTR_PHASE_SCALE(0, "0.0015339808"); /* 2PI/2^12 rad*/ | |
256 | ||
257 | static IIO_DEV_ATTR_PINCONTROL_EN(0, S_IWUSR, NULL, | |
258 | ad9834_write, AD9834_PIN_SW); | |
259 | static IIO_DEV_ATTR_OUT_ENABLE(0, S_IWUSR, NULL, ad9834_write, AD9834_RESET); | |
260 | static IIO_DEV_ATTR_OUTY_ENABLE(0, 1, S_IWUSR, NULL, | |
261 | ad9834_write, AD9834_OPBITEN); | |
262 | static IIO_DEV_ATTR_OUT_WAVETYPE(0, 0, ad9834_store_wavetype, 0); | |
263 | static IIO_DEV_ATTR_OUT_WAVETYPE(0, 1, ad9834_store_wavetype, 1); | |
264 | ||
265 | static struct attribute *ad9834_attributes[] = { | |
266 | &iio_dev_attr_dds0_freq0.dev_attr.attr, | |
267 | &iio_dev_attr_dds0_freq1.dev_attr.attr, | |
268 | &iio_const_attr_dds0_freq_scale.dev_attr.attr, | |
269 | &iio_dev_attr_dds0_phase0.dev_attr.attr, | |
270 | &iio_dev_attr_dds0_phase1.dev_attr.attr, | |
271 | &iio_const_attr_dds0_phase_scale.dev_attr.attr, | |
272 | &iio_dev_attr_dds0_pincontrol_en.dev_attr.attr, | |
273 | &iio_dev_attr_dds0_freqsymbol.dev_attr.attr, | |
274 | &iio_dev_attr_dds0_phasesymbol.dev_attr.attr, | |
275 | &iio_dev_attr_dds0_out_enable.dev_attr.attr, | |
276 | &iio_dev_attr_dds0_out1_enable.dev_attr.attr, | |
277 | &iio_dev_attr_dds0_out0_wavetype.dev_attr.attr, | |
278 | &iio_dev_attr_dds0_out1_wavetype.dev_attr.attr, | |
279 | &iio_dev_attr_dds0_out0_wavetype_available.dev_attr.attr, | |
280 | &iio_dev_attr_dds0_out1_wavetype_available.dev_attr.attr, | |
12b9d5bf MH |
281 | NULL, |
282 | }; | |
283 | ||
587a1f16 | 284 | static umode_t ad9834_attr_is_visible(struct kobject *kobj, |
12b9d5bf MH |
285 | struct attribute *attr, int n) |
286 | { | |
287 | struct device *dev = container_of(kobj, struct device, kobj); | |
638e59fc JC |
288 | struct iio_dev *indio_dev = dev_get_drvdata(dev); |
289 | struct ad9834_state *st = iio_priv(indio_dev); | |
12b9d5bf | 290 | |
587a1f16 | 291 | umode_t mode = attr->mode; |
12b9d5bf | 292 | |
bd5a793f MH |
293 | if (((st->devid == ID_AD9833) || (st->devid == ID_AD9837)) && |
294 | ((attr == &iio_dev_attr_dds0_out1_enable.dev_attr.attr) || | |
12b9d5bf MH |
295 | (attr == &iio_dev_attr_dds0_out1_wavetype.dev_attr.attr) || |
296 | (attr == | |
bd5a793f MH |
297 | &iio_dev_attr_dds0_out1_wavetype_available.dev_attr.attr) || |
298 | (attr == &iio_dev_attr_dds0_pincontrol_en.dev_attr.attr))) | |
12b9d5bf MH |
299 | mode = 0; |
300 | ||
301 | return mode; | |
302 | } | |
303 | ||
304 | static const struct attribute_group ad9834_attribute_group = { | |
305 | .attrs = ad9834_attributes, | |
306 | .is_visible = ad9834_attr_is_visible, | |
307 | }; | |
308 | ||
6fe8135f JC |
309 | static const struct iio_info ad9834_info = { |
310 | .attrs = &ad9834_attribute_group, | |
311 | .driver_module = THIS_MODULE, | |
312 | }; | |
313 | ||
12b9d5bf MH |
314 | static int __devinit ad9834_probe(struct spi_device *spi) |
315 | { | |
316 | struct ad9834_platform_data *pdata = spi->dev.platform_data; | |
317 | struct ad9834_state *st; | |
8b7163a5 JC |
318 | struct iio_dev *indio_dev; |
319 | struct regulator *reg; | |
12b9d5bf MH |
320 | int ret; |
321 | ||
322 | if (!pdata) { | |
323 | dev_dbg(&spi->dev, "no platform data?\n"); | |
324 | return -ENODEV; | |
325 | } | |
326 | ||
8b7163a5 JC |
327 | reg = regulator_get(&spi->dev, "vcc"); |
328 | if (!IS_ERR(reg)) { | |
329 | ret = regulator_enable(reg); | |
12b9d5bf MH |
330 | if (ret) |
331 | goto error_put_reg; | |
332 | } | |
333 | ||
8b7163a5 JC |
334 | indio_dev = iio_allocate_device(sizeof(*st)); |
335 | if (indio_dev == NULL) { | |
12b9d5bf MH |
336 | ret = -ENOMEM; |
337 | goto error_disable_reg; | |
338 | } | |
8b7163a5 JC |
339 | spi_set_drvdata(spi, indio_dev); |
340 | st = iio_priv(indio_dev); | |
341 | st->mclk = pdata->mclk; | |
342 | st->spi = spi; | |
343 | st->devid = spi_get_device_id(spi)->driver_data; | |
344 | st->reg = reg; | |
345 | indio_dev->dev.parent = &spi->dev; | |
346 | indio_dev->name = spi_get_device_id(spi)->name; | |
347 | indio_dev->info = &ad9834_info; | |
348 | indio_dev->modes = INDIO_DIRECT_MODE; | |
12b9d5bf MH |
349 | |
350 | /* Setup default messages */ | |
351 | ||
352 | st->xfer.tx_buf = &st->data; | |
353 | st->xfer.len = 2; | |
354 | ||
355 | spi_message_init(&st->msg); | |
356 | spi_message_add_tail(&st->xfer, &st->msg); | |
357 | ||
358 | st->freq_xfer[0].tx_buf = &st->freq_data[0]; | |
359 | st->freq_xfer[0].len = 2; | |
360 | st->freq_xfer[0].cs_change = 1; | |
361 | st->freq_xfer[1].tx_buf = &st->freq_data[1]; | |
362 | st->freq_xfer[1].len = 2; | |
363 | ||
364 | spi_message_init(&st->freq_msg); | |
365 | spi_message_add_tail(&st->freq_xfer[0], &st->freq_msg); | |
366 | spi_message_add_tail(&st->freq_xfer[1], &st->freq_msg); | |
367 | ||
368 | st->control = AD9834_B28 | AD9834_RESET; | |
369 | ||
370 | if (!pdata->en_div2) | |
371 | st->control |= AD9834_DIV2; | |
372 | ||
373 | if (!pdata->en_signbit_msb_out && (st->devid == ID_AD9834)) | |
374 | st->control |= AD9834_SIGN_PIB; | |
375 | ||
376 | st->data = cpu_to_be16(AD9834_REG_CMD | st->control); | |
377 | ret = spi_sync(st->spi, &st->msg); | |
378 | if (ret) { | |
379 | dev_err(&spi->dev, "device init failed\n"); | |
380 | goto error_free_device; | |
381 | } | |
382 | ||
383 | ret = ad9834_write_frequency(st, AD9834_REG_FREQ0, pdata->freq0); | |
384 | if (ret) | |
385 | goto error_free_device; | |
386 | ||
387 | ret = ad9834_write_frequency(st, AD9834_REG_FREQ1, pdata->freq1); | |
388 | if (ret) | |
389 | goto error_free_device; | |
390 | ||
391 | ret = ad9834_write_phase(st, AD9834_REG_PHASE0, pdata->phase0); | |
392 | if (ret) | |
393 | goto error_free_device; | |
394 | ||
395 | ret = ad9834_write_phase(st, AD9834_REG_PHASE1, pdata->phase1); | |
396 | if (ret) | |
397 | goto error_free_device; | |
398 | ||
8b7163a5 | 399 | ret = iio_device_register(indio_dev); |
12b9d5bf MH |
400 | if (ret) |
401 | goto error_free_device; | |
402 | ||
403 | return 0; | |
404 | ||
405 | error_free_device: | |
8b7163a5 | 406 | iio_free_device(indio_dev); |
12b9d5bf | 407 | error_disable_reg: |
8b7163a5 JC |
408 | if (!IS_ERR(reg)) |
409 | regulator_disable(reg); | |
12b9d5bf | 410 | error_put_reg: |
8b7163a5 JC |
411 | if (!IS_ERR(reg)) |
412 | regulator_put(reg); | |
12b9d5bf MH |
413 | return ret; |
414 | } | |
415 | ||
416 | static int __devexit ad9834_remove(struct spi_device *spi) | |
417 | { | |
8b7163a5 JC |
418 | struct iio_dev *indio_dev = spi_get_drvdata(spi); |
419 | struct ad9834_state *st = iio_priv(indio_dev); | |
8b7163a5 JC |
420 | |
421 | iio_device_unregister(indio_dev); | |
d2fffd6c JC |
422 | if (!IS_ERR(st->reg)) { |
423 | regulator_disable(st->reg); | |
424 | regulator_put(st->reg); | |
12b9d5bf | 425 | } |
d2fffd6c | 426 | iio_free_device(indio_dev); |
8b7163a5 | 427 | |
12b9d5bf MH |
428 | return 0; |
429 | } | |
430 | ||
431 | static const struct spi_device_id ad9834_id[] = { | |
432 | {"ad9833", ID_AD9833}, | |
433 | {"ad9834", ID_AD9834}, | |
bd5a793f MH |
434 | {"ad9837", ID_AD9837}, |
435 | {"ad9838", ID_AD9838}, | |
12b9d5bf MH |
436 | {} |
437 | }; | |
438 | ||
439 | static struct spi_driver ad9834_driver = { | |
440 | .driver = { | |
441 | .name = "ad9834", | |
442 | .bus = &spi_bus_type, | |
443 | .owner = THIS_MODULE, | |
444 | }, | |
445 | .probe = ad9834_probe, | |
446 | .remove = __devexit_p(ad9834_remove), | |
447 | .id_table = ad9834_id, | |
448 | }; | |
449 | ||
450 | static int __init ad9834_init(void) | |
451 | { | |
452 | return spi_register_driver(&ad9834_driver); | |
453 | } | |
454 | module_init(ad9834_init); | |
455 | ||
456 | static void __exit ad9834_exit(void) | |
457 | { | |
458 | spi_unregister_driver(&ad9834_driver); | |
459 | } | |
460 | module_exit(ad9834_exit); | |
461 | ||
462 | MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>"); | |
bd5a793f | 463 | MODULE_DESCRIPTION("Analog Devices AD9833/AD9834/AD9837/AD9838 DDS"); |
12b9d5bf MH |
464 | MODULE_LICENSE("GPL v2"); |
465 | MODULE_ALIAS("spi:ad9834"); |