]>
Commit | Line | Data |
---|---|---|
da6fcbdf CC |
1 | /* |
2 | * Driver for ADI Direct Digital Synthesis ad5930 | |
3 | * | |
4 | * Copyright (c) 2010-2010 Analog Devices Inc. | |
5 | * | |
6 | * This program is free software; you can redistribute it and/or modify | |
7 | * it under the terms of the GNU General Public License version 2 as | |
8 | * published by the Free Software Foundation. | |
9 | * | |
10 | */ | |
11 | #include <linux/types.h> | |
12 | #include <linux/mutex.h> | |
13 | #include <linux/device.h> | |
14 | #include <linux/spi/spi.h> | |
15 | #include <linux/slab.h> | |
16 | #include <linux/sysfs.h> | |
99c97852 | 17 | #include <linux/module.h> |
da6fcbdf | 18 | |
06458e27 JC |
19 | #include <linux/iio/iio.h> |
20 | #include <linux/iio/sysfs.h> | |
da6fcbdf CC |
21 | |
22 | #define DRV_NAME "ad5930" | |
23 | ||
24 | #define value_mask (u16)0xf000 | |
25 | #define addr_shift 12 | |
26 | ||
27 | /* Register format: 4 bits addr + 12 bits value */ | |
28 | struct ad5903_config { | |
29 | u16 control; | |
30 | u16 incnum; | |
31 | u16 frqdelt[2]; | |
32 | u16 incitvl; | |
33 | u16 buritvl; | |
34 | u16 strtfrq[2]; | |
35 | }; | |
36 | ||
37 | struct ad5930_state { | |
38 | struct mutex lock; | |
da6fcbdf CC |
39 | struct spi_device *sdev; |
40 | }; | |
41 | ||
42 | static ssize_t ad5930_set_parameter(struct device *dev, | |
43 | struct device_attribute *attr, | |
44 | const char *buf, | |
45 | size_t len) | |
46 | { | |
47 | struct spi_message msg; | |
48 | struct spi_transfer xfer; | |
49 | int ret; | |
50 | struct ad5903_config *config = (struct ad5903_config *)buf; | |
b671bb3b | 51 | struct iio_dev *idev = dev_to_iio_dev(dev); |
7074e1d4 | 52 | struct ad5930_state *st = iio_priv(idev); |
da6fcbdf CC |
53 | |
54 | config->control = (config->control & ~value_mask); | |
55 | config->incnum = (config->control & ~value_mask) | (1 << addr_shift); | |
56 | config->frqdelt[0] = (config->control & ~value_mask) | (2 << addr_shift); | |
57 | config->frqdelt[1] = (config->control & ~value_mask) | 3 << addr_shift; | |
58 | config->incitvl = (config->control & ~value_mask) | 4 << addr_shift; | |
59 | config->buritvl = (config->control & ~value_mask) | 8 << addr_shift; | |
60 | config->strtfrq[0] = (config->control & ~value_mask) | 0xc << addr_shift; | |
61 | config->strtfrq[1] = (config->control & ~value_mask) | 0xd << addr_shift; | |
62 | ||
63 | xfer.len = len; | |
64 | xfer.tx_buf = config; | |
65 | mutex_lock(&st->lock); | |
66 | ||
67 | spi_message_init(&msg); | |
68 | spi_message_add_tail(&xfer, &msg); | |
69 | ret = spi_sync(st->sdev, &msg); | |
70 | if (ret) | |
71 | goto error_ret; | |
72 | error_ret: | |
73 | mutex_unlock(&st->lock); | |
74 | ||
75 | return ret ? ret : len; | |
76 | } | |
77 | ||
78 | static IIO_DEVICE_ATTR(dds, S_IWUSR, NULL, ad5930_set_parameter, 0); | |
79 | ||
80 | static struct attribute *ad5930_attributes[] = { | |
81 | &iio_dev_attr_dds.dev_attr.attr, | |
82 | NULL, | |
83 | }; | |
84 | ||
85 | static const struct attribute_group ad5930_attribute_group = { | |
da6fcbdf CC |
86 | .attrs = ad5930_attributes, |
87 | }; | |
88 | ||
6fe8135f JC |
89 | static const struct iio_info ad5930_info = { |
90 | .attrs = &ad5930_attribute_group, | |
6fe8135f JC |
91 | .driver_module = THIS_MODULE, |
92 | }; | |
93 | ||
4ae1c61f | 94 | static int ad5930_probe(struct spi_device *spi) |
da6fcbdf CC |
95 | { |
96 | struct ad5930_state *st; | |
7074e1d4 | 97 | struct iio_dev *idev; |
da6fcbdf CC |
98 | int ret = 0; |
99 | ||
7cbb7537 | 100 | idev = iio_device_alloc(sizeof(*st)); |
7074e1d4 | 101 | if (idev == NULL) { |
da6fcbdf CC |
102 | ret = -ENOMEM; |
103 | goto error_ret; | |
104 | } | |
7074e1d4 JC |
105 | spi_set_drvdata(spi, idev); |
106 | st = iio_priv(idev); | |
da6fcbdf CC |
107 | |
108 | mutex_init(&st->lock); | |
109 | st->sdev = spi; | |
7074e1d4 JC |
110 | idev->dev.parent = &spi->dev; |
111 | idev->info = &ad5930_info; | |
112 | idev->modes = INDIO_DIRECT_MODE; | |
da6fcbdf | 113 | |
7074e1d4 | 114 | ret = iio_device_register(idev); |
da6fcbdf CC |
115 | if (ret) |
116 | goto error_free_dev; | |
117 | spi->max_speed_hz = 2000000; | |
118 | spi->mode = SPI_MODE_3; | |
119 | spi->bits_per_word = 16; | |
120 | spi_setup(spi); | |
121 | ||
122 | return 0; | |
123 | ||
124 | error_free_dev: | |
7cbb7537 | 125 | iio_device_free(idev); |
da6fcbdf CC |
126 | error_ret: |
127 | return ret; | |
128 | } | |
129 | ||
447d4f29 | 130 | static int ad5930_remove(struct spi_device *spi) |
da6fcbdf | 131 | { |
7074e1d4 | 132 | iio_device_unregister(spi_get_drvdata(spi)); |
7cbb7537 | 133 | iio_device_free(spi_get_drvdata(spi)); |
da6fcbdf CC |
134 | |
135 | return 0; | |
136 | } | |
137 | ||
138 | static struct spi_driver ad5930_driver = { | |
139 | .driver = { | |
140 | .name = DRV_NAME, | |
141 | .owner = THIS_MODULE, | |
142 | }, | |
143 | .probe = ad5930_probe, | |
e543acf0 | 144 | .remove = ad5930_remove, |
da6fcbdf | 145 | }; |
ae6ae6fe | 146 | module_spi_driver(ad5930_driver); |
da6fcbdf CC |
147 | |
148 | MODULE_AUTHOR("Cliff Cai"); | |
149 | MODULE_DESCRIPTION("Analog Devices ad5930 driver"); | |
150 | MODULE_LICENSE("GPL v2"); | |
55e4390c | 151 | MODULE_ALIAS("spi:" DRV_NAME); |