diff options
author | Michael Hennerich <michael.hennerich@analog.com> | 2012-05-11 05:36:54 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2012-05-14 16:15:39 -0400 |
commit | e71d42e03c60d215bde295d2b986bbc96878c7d6 (patch) | |
tree | 45256c147754138d15da7bf9856e5efeae931d66 /drivers/iio | |
parent | b65d62122fa57035fae06a78e0b65622a1e1bc34 (diff) |
iio: amplifiers: New driver for AD8366 Dual-Digital Variable Gain Amplifier
Changes since V1:
Apply review feedback:
Introduce and use IIO_CHAN_INFO_HARDWAREGAIN
Introduce and use Use IIO_VAL_INT_PLUS_MICRO_DB
Modify out of staging include paths.
Convert to new iio core API naming.
Changes since V2:
more sanity checking in write_raw
Signed-off-by: Michael Hennerich <michael.hennerich@analog.com>
Acked-by: Jonathan Cameron <jic23@kernel.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/iio')
-rw-r--r-- | drivers/iio/Kconfig | 2 | ||||
-rw-r--r-- | drivers/iio/Makefile | 2 | ||||
-rw-r--r-- | drivers/iio/amplifiers/Kconfig | 17 | ||||
-rw-r--r-- | drivers/iio/amplifiers/Makefile | 5 | ||||
-rw-r--r-- | drivers/iio/amplifiers/ad8366.c | 222 |
5 files changed, 248 insertions, 0 deletions
diff --git a/drivers/iio/Kconfig b/drivers/iio/Kconfig index 3ab7d48d0b0a..bb1e35c811da 100644 --- a/drivers/iio/Kconfig +++ b/drivers/iio/Kconfig | |||
@@ -48,4 +48,6 @@ config IIO_CONSUMERS_PER_TRIGGER | |||
48 | This value controls the maximum number of consumers that a | 48 | This value controls the maximum number of consumers that a |
49 | given trigger may handle. Default is 2. | 49 | given trigger may handle. Default is 2. |
50 | 50 | ||
51 | source "drivers/iio/amplifiers/Kconfig" | ||
52 | |||
51 | endif # IIO | 53 | endif # IIO |
diff --git a/drivers/iio/Makefile b/drivers/iio/Makefile index d5fc57d12eac..55524916aa56 100644 --- a/drivers/iio/Makefile +++ b/drivers/iio/Makefile | |||
@@ -8,3 +8,5 @@ industrialio-$(CONFIG_IIO_BUFFER) += industrialio-buffer.o | |||
8 | industrialio-$(CONFIG_IIO_TRIGGER) += industrialio-trigger.o | 8 | industrialio-$(CONFIG_IIO_TRIGGER) += industrialio-trigger.o |
9 | 9 | ||
10 | obj-$(CONFIG_IIO_KFIFO_BUF) += kfifo_buf.o | 10 | obj-$(CONFIG_IIO_KFIFO_BUF) += kfifo_buf.o |
11 | |||
12 | obj-y += amplifiers/ | ||
diff --git a/drivers/iio/amplifiers/Kconfig b/drivers/iio/amplifiers/Kconfig new file mode 100644 index 000000000000..05d707ed7d4f --- /dev/null +++ b/drivers/iio/amplifiers/Kconfig | |||
@@ -0,0 +1,17 @@ | |||
1 | # | ||
2 | # Gain Amplifiers, etc. | ||
3 | # | ||
4 | menu "Amplifiers" | ||
5 | |||
6 | config AD8366 | ||
7 | tristate "Analog Devices AD8366 VGA" | ||
8 | depends on SPI | ||
9 | select BITREVERSE | ||
10 | help | ||
11 | Say yes here to build support for Analog Devices AD8366 | ||
12 | SPI Dual-Digital Variable Gain Amplifier (VGA). | ||
13 | |||
14 | To compile this driver as a module, choose M here: the | ||
15 | module will be called ad8366. | ||
16 | |||
17 | endmenu | ||
diff --git a/drivers/iio/amplifiers/Makefile b/drivers/iio/amplifiers/Makefile new file mode 100644 index 000000000000..a6ca366908e0 --- /dev/null +++ b/drivers/iio/amplifiers/Makefile | |||
@@ -0,0 +1,5 @@ | |||
1 | # | ||
2 | # Makefile iio/amplifiers | ||
3 | # | ||
4 | |||
5 | obj-$(CONFIG_AD8366) += ad8366.o | ||
diff --git a/drivers/iio/amplifiers/ad8366.c b/drivers/iio/amplifiers/ad8366.c new file mode 100644 index 000000000000..d8281cdbfc4a --- /dev/null +++ b/drivers/iio/amplifiers/ad8366.c | |||
@@ -0,0 +1,222 @@ | |||
1 | /* | ||
2 | * AD8366 SPI Dual-Digital Variable Gain Amplifier (VGA) | ||
3 | * | ||
4 | * Copyright 2012 Analog Devices Inc. | ||
5 | * | ||
6 | * Licensed under the GPL-2. | ||
7 | */ | ||
8 | |||
9 | #include <linux/device.h> | ||
10 | #include <linux/kernel.h> | ||
11 | #include <linux/slab.h> | ||
12 | #include <linux/sysfs.h> | ||
13 | #include <linux/spi/spi.h> | ||
14 | #include <linux/regulator/consumer.h> | ||
15 | #include <linux/err.h> | ||
16 | #include <linux/module.h> | ||
17 | #include <linux/bitrev.h> | ||
18 | |||
19 | #include <linux/iio/iio.h> | ||
20 | #include <linux/iio/sysfs.h> | ||
21 | |||
22 | struct ad8366_state { | ||
23 | struct spi_device *spi; | ||
24 | struct regulator *reg; | ||
25 | unsigned char ch[2]; | ||
26 | /* | ||
27 | * DMA (thus cache coherency maintenance) requires the | ||
28 | * transfer buffers to live in their own cache lines. | ||
29 | */ | ||
30 | unsigned char data[2] ____cacheline_aligned; | ||
31 | }; | ||
32 | |||
33 | static int ad8366_write(struct iio_dev *indio_dev, | ||
34 | unsigned char ch_a, char unsigned ch_b) | ||
35 | { | ||
36 | struct ad8366_state *st = iio_priv(indio_dev); | ||
37 | int ret; | ||
38 | |||
39 | ch_a = bitrev8(ch_a & 0x3F); | ||
40 | ch_b = bitrev8(ch_b & 0x3F); | ||
41 | |||
42 | st->data[0] = ch_b >> 4; | ||
43 | st->data[1] = (ch_b << 4) | (ch_a >> 2); | ||
44 | |||
45 | ret = spi_write(st->spi, st->data, ARRAY_SIZE(st->data)); | ||
46 | if (ret < 0) | ||
47 | dev_err(&indio_dev->dev, "write failed (%d)", ret); | ||
48 | |||
49 | return ret; | ||
50 | } | ||
51 | |||
52 | static int ad8366_read_raw(struct iio_dev *indio_dev, | ||
53 | struct iio_chan_spec const *chan, | ||
54 | int *val, | ||
55 | int *val2, | ||
56 | long m) | ||
57 | { | ||
58 | struct ad8366_state *st = iio_priv(indio_dev); | ||
59 | int ret; | ||
60 | unsigned code; | ||
61 | |||
62 | mutex_lock(&indio_dev->mlock); | ||
63 | switch (m) { | ||
64 | case IIO_CHAN_INFO_HARDWAREGAIN: | ||
65 | code = st->ch[chan->channel]; | ||
66 | |||
67 | /* Values in dB */ | ||
68 | code = code * 253 + 4500; | ||
69 | *val = code / 1000; | ||
70 | *val2 = (code % 1000) * 1000; | ||
71 | |||
72 | ret = IIO_VAL_INT_PLUS_MICRO_DB; | ||
73 | break; | ||
74 | default: | ||
75 | ret = -EINVAL; | ||
76 | } | ||
77 | mutex_unlock(&indio_dev->mlock); | ||
78 | |||
79 | return ret; | ||
80 | }; | ||
81 | |||
82 | static int ad8366_write_raw(struct iio_dev *indio_dev, | ||
83 | struct iio_chan_spec const *chan, | ||
84 | int val, | ||
85 | int val2, | ||
86 | long mask) | ||
87 | { | ||
88 | struct ad8366_state *st = iio_priv(indio_dev); | ||
89 | unsigned code; | ||
90 | int ret; | ||
91 | |||
92 | if (val < 0 || val2 < 0) | ||
93 | return -EINVAL; | ||
94 | |||
95 | /* Values in dB */ | ||
96 | code = (((u8)val * 1000) + ((u32)val2 / 1000)); | ||
97 | |||
98 | if (code > 20500 || code < 4500) | ||
99 | return -EINVAL; | ||
100 | |||
101 | code = (code - 4500) / 253; | ||
102 | |||
103 | mutex_lock(&indio_dev->mlock); | ||
104 | switch (mask) { | ||
105 | case IIO_CHAN_INFO_HARDWAREGAIN: | ||
106 | st->ch[chan->channel] = code; | ||
107 | ret = ad8366_write(indio_dev, st->ch[0], st->ch[1]); | ||
108 | break; | ||
109 | default: | ||
110 | ret = -EINVAL; | ||
111 | } | ||
112 | mutex_unlock(&indio_dev->mlock); | ||
113 | |||
114 | return ret; | ||
115 | } | ||
116 | |||
117 | static const struct iio_info ad8366_info = { | ||
118 | .read_raw = &ad8366_read_raw, | ||
119 | .write_raw = &ad8366_write_raw, | ||
120 | .driver_module = THIS_MODULE, | ||
121 | }; | ||
122 | |||
123 | #define AD8366_CHAN(_channel) { \ | ||
124 | .type = IIO_VOLTAGE, \ | ||
125 | .output = 1, \ | ||
126 | .indexed = 1, \ | ||
127 | .channel = _channel, \ | ||
128 | .info_mask = IIO_CHAN_INFO_HARDWAREGAIN_SEPARATE_BIT,\ | ||
129 | } | ||
130 | |||
131 | static const struct iio_chan_spec ad8366_channels[] = { | ||
132 | AD8366_CHAN(0), | ||
133 | AD8366_CHAN(1), | ||
134 | }; | ||
135 | |||
136 | static int __devinit ad8366_probe(struct spi_device *spi) | ||
137 | { | ||
138 | struct iio_dev *indio_dev; | ||
139 | struct ad8366_state *st; | ||
140 | int ret; | ||
141 | |||
142 | indio_dev = iio_device_alloc(sizeof(*st)); | ||
143 | if (indio_dev == NULL) | ||
144 | return -ENOMEM; | ||
145 | |||
146 | st = iio_priv(indio_dev); | ||
147 | |||
148 | st->reg = regulator_get(&spi->dev, "vcc"); | ||
149 | if (!IS_ERR(st->reg)) { | ||
150 | ret = regulator_enable(st->reg); | ||
151 | if (ret) | ||
152 | goto error_put_reg; | ||
153 | } | ||
154 | |||
155 | spi_set_drvdata(spi, indio_dev); | ||
156 | st->spi = spi; | ||
157 | |||
158 | indio_dev->dev.parent = &spi->dev; | ||
159 | indio_dev->name = spi_get_device_id(spi)->name; | ||
160 | indio_dev->info = &ad8366_info; | ||
161 | indio_dev->modes = INDIO_DIRECT_MODE; | ||
162 | indio_dev->channels = ad8366_channels; | ||
163 | indio_dev->num_channels = ARRAY_SIZE(ad8366_channels); | ||
164 | |||
165 | ret = iio_device_register(indio_dev); | ||
166 | if (ret) | ||
167 | goto error_disable_reg; | ||
168 | |||
169 | ad8366_write(indio_dev, 0 , 0); | ||
170 | |||
171 | return 0; | ||
172 | |||
173 | error_disable_reg: | ||
174 | if (!IS_ERR(st->reg)) | ||
175 | regulator_disable(st->reg); | ||
176 | error_put_reg: | ||
177 | if (!IS_ERR(st->reg)) | ||
178 | regulator_put(st->reg); | ||
179 | |||
180 | iio_device_free(indio_dev); | ||
181 | |||
182 | return ret; | ||
183 | } | ||
184 | |||
185 | static int __devexit ad8366_remove(struct spi_device *spi) | ||
186 | { | ||
187 | struct iio_dev *indio_dev = spi_get_drvdata(spi); | ||
188 | struct ad8366_state *st = iio_priv(indio_dev); | ||
189 | struct regulator *reg = st->reg; | ||
190 | |||
191 | iio_device_unregister(indio_dev); | ||
192 | |||
193 | if (!IS_ERR(reg)) { | ||
194 | regulator_disable(reg); | ||
195 | regulator_put(reg); | ||
196 | } | ||
197 | |||
198 | iio_device_free(indio_dev); | ||
199 | |||
200 | return 0; | ||
201 | } | ||
202 | |||
203 | static const struct spi_device_id ad8366_id[] = { | ||
204 | {"ad8366", 0}, | ||
205 | {} | ||
206 | }; | ||
207 | |||
208 | static struct spi_driver ad8366_driver = { | ||
209 | .driver = { | ||
210 | .name = KBUILD_MODNAME, | ||
211 | .owner = THIS_MODULE, | ||
212 | }, | ||
213 | .probe = ad8366_probe, | ||
214 | .remove = __devexit_p(ad8366_remove), | ||
215 | .id_table = ad8366_id, | ||
216 | }; | ||
217 | |||
218 | module_spi_driver(ad8366_driver); | ||
219 | |||
220 | MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>"); | ||
221 | MODULE_DESCRIPTION("Analog Devices AD8366 VGA"); | ||
222 | MODULE_LICENSE("GPL v2"); | ||