Commit | Line | Data |
---|---|---|
38ffa3a3 | 1 | // SPDX-License-Identifier: GPL-2.0 |
e71d42e0 | 2 | /* |
cee211f4 PC |
3 | * AD8366 and similar Gain Amplifiers |
4 | * This driver supports the following gain amplifiers: | |
5 | * AD8366 Dual-Digital Variable Gain Amplifier (VGA) | |
6 | * ADA4961 BiCMOS RF Digital Gain Amplifier (DGA) | |
075da9cd | 7 | * ADL5240 Digitally controlled variable gain amplifier (VGA) |
247d3b63 | 8 | * HMC792A 0.25 dB LSB GaAs MMIC 6-Bit Digital Attenuator |
cc74a38d | 9 | * HMC1119 0.25 dB LSB, 7-Bit, Silicon Digital Attenuator |
e71d42e0 | 10 | * |
38ffa3a3 | 11 | * Copyright 2012-2019 Analog Devices Inc. |
e71d42e0 MH |
12 | */ |
13 | ||
14 | #include <linux/device.h> | |
15 | #include <linux/kernel.h> | |
16 | #include <linux/slab.h> | |
17 | #include <linux/sysfs.h> | |
18 | #include <linux/spi/spi.h> | |
19 | #include <linux/regulator/consumer.h> | |
cee211f4 | 20 | #include <linux/gpio/consumer.h> |
e71d42e0 MH |
21 | #include <linux/err.h> |
22 | #include <linux/module.h> | |
23 | #include <linux/bitrev.h> | |
24 | ||
25 | #include <linux/iio/iio.h> | |
26 | #include <linux/iio/sysfs.h> | |
27 | ||
11ab555a AA |
28 | enum ad8366_type { |
29 | ID_AD8366, | |
cee211f4 | 30 | ID_ADA4961, |
075da9cd | 31 | ID_ADL5240, |
247d3b63 | 32 | ID_HMC792, |
cc74a38d | 33 | ID_HMC1119, |
11ab555a AA |
34 | }; |
35 | ||
36 | struct ad8366_info { | |
37 | int gain_min; | |
38 | int gain_max; | |
39 | }; | |
40 | ||
e71d42e0 MH |
41 | struct ad8366_state { |
42 | struct spi_device *spi; | |
43 | struct regulator *reg; | |
dbcf6b5d | 44 | struct mutex lock; /* protect sensor state */ |
cee211f4 | 45 | struct gpio_desc *reset_gpio; |
e71d42e0 | 46 | unsigned char ch[2]; |
11ab555a AA |
47 | enum ad8366_type type; |
48 | struct ad8366_info *info; | |
e71d42e0 | 49 | /* |
026bffa4 | 50 | * DMA (thus cache coherency maintenance) may require the |
e71d42e0 MH |
51 | * transfer buffers to live in their own cache lines. |
52 | */ | |
026bffa4 | 53 | unsigned char data[2] __aligned(IIO_DMA_MINALIGN); |
e71d42e0 MH |
54 | }; |
55 | ||
11ab555a AA |
56 | static struct ad8366_info ad8366_infos[] = { |
57 | [ID_AD8366] = { | |
58 | .gain_min = 4500, | |
59 | .gain_max = 20500, | |
60 | }, | |
cee211f4 PC |
61 | [ID_ADA4961] = { |
62 | .gain_min = -6000, | |
63 | .gain_max = 15000, | |
64 | }, | |
075da9cd AA |
65 | [ID_ADL5240] = { |
66 | .gain_min = -11500, | |
67 | .gain_max = 20000, | |
68 | }, | |
247d3b63 KSP |
69 | [ID_HMC792] = { |
70 | .gain_min = -15750, | |
71 | .gain_max = 0, | |
72 | }, | |
cc74a38d SC |
73 | [ID_HMC1119] = { |
74 | .gain_min = -31750, | |
75 | .gain_max = 0, | |
76 | }, | |
11ab555a AA |
77 | }; |
78 | ||
e71d42e0 | 79 | static int ad8366_write(struct iio_dev *indio_dev, |
ff6f4629 | 80 | unsigned char ch_a, unsigned char ch_b) |
e71d42e0 MH |
81 | { |
82 | struct ad8366_state *st = iio_priv(indio_dev); | |
83 | int ret; | |
84 | ||
11ab555a AA |
85 | switch (st->type) { |
86 | case ID_AD8366: | |
87 | ch_a = bitrev8(ch_a & 0x3F); | |
88 | ch_b = bitrev8(ch_b & 0x3F); | |
e71d42e0 | 89 | |
11ab555a AA |
90 | st->data[0] = ch_b >> 4; |
91 | st->data[1] = (ch_b << 4) | (ch_a >> 2); | |
92 | break; | |
cee211f4 PC |
93 | case ID_ADA4961: |
94 | st->data[0] = ch_a & 0x1F; | |
95 | break; | |
075da9cd AA |
96 | case ID_ADL5240: |
97 | st->data[0] = (ch_a & 0x3F); | |
98 | break; | |
247d3b63 | 99 | case ID_HMC792: |
cc74a38d SC |
100 | case ID_HMC1119: |
101 | st->data[0] = ch_a; | |
102 | break; | |
11ab555a | 103 | } |
e71d42e0 | 104 | |
11ab555a | 105 | ret = spi_write(st->spi, st->data, indio_dev->num_channels); |
e71d42e0 MH |
106 | if (ret < 0) |
107 | dev_err(&indio_dev->dev, "write failed (%d)", ret); | |
108 | ||
109 | return ret; | |
110 | } | |
111 | ||
112 | static int ad8366_read_raw(struct iio_dev *indio_dev, | |
113 | struct iio_chan_spec const *chan, | |
114 | int *val, | |
115 | int *val2, | |
116 | long m) | |
117 | { | |
118 | struct ad8366_state *st = iio_priv(indio_dev); | |
119 | int ret; | |
11ab555a | 120 | int code, gain = 0; |
e71d42e0 | 121 | |
dbcf6b5d | 122 | mutex_lock(&st->lock); |
e71d42e0 MH |
123 | switch (m) { |
124 | case IIO_CHAN_INFO_HARDWAREGAIN: | |
125 | code = st->ch[chan->channel]; | |
126 | ||
11ab555a AA |
127 | switch (st->type) { |
128 | case ID_AD8366: | |
129 | gain = code * 253 + 4500; | |
130 | break; | |
cee211f4 PC |
131 | case ID_ADA4961: |
132 | gain = 15000 - code * 1000; | |
133 | break; | |
075da9cd AA |
134 | case ID_ADL5240: |
135 | gain = 20000 - 31500 + code * 500; | |
136 | break; | |
247d3b63 KSP |
137 | case ID_HMC792: |
138 | gain = -1 * code * 500; | |
139 | break; | |
cc74a38d SC |
140 | case ID_HMC1119: |
141 | gain = -1 * code * 250; | |
142 | break; | |
11ab555a AA |
143 | } |
144 | ||
e71d42e0 | 145 | /* Values in dB */ |
11ab555a AA |
146 | *val = gain / 1000; |
147 | *val2 = (gain % 1000) * 1000; | |
e71d42e0 MH |
148 | |
149 | ret = IIO_VAL_INT_PLUS_MICRO_DB; | |
150 | break; | |
151 | default: | |
152 | ret = -EINVAL; | |
153 | } | |
dbcf6b5d | 154 | mutex_unlock(&st->lock); |
e71d42e0 MH |
155 | |
156 | return ret; | |
157 | }; | |
158 | ||
159 | static int ad8366_write_raw(struct iio_dev *indio_dev, | |
160 | struct iio_chan_spec const *chan, | |
161 | int val, | |
162 | int val2, | |
163 | long mask) | |
164 | { | |
165 | struct ad8366_state *st = iio_priv(indio_dev); | |
11ab555a AA |
166 | struct ad8366_info *inf = st->info; |
167 | int code = 0, gain; | |
e71d42e0 MH |
168 | int ret; |
169 | ||
e71d42e0 | 170 | /* Values in dB */ |
11ab555a AA |
171 | if (val < 0) |
172 | gain = (val * 1000) - (val2 / 1000); | |
173 | else | |
174 | gain = (val * 1000) + (val2 / 1000); | |
e71d42e0 | 175 | |
11ab555a | 176 | if (gain > inf->gain_max || gain < inf->gain_min) |
e71d42e0 MH |
177 | return -EINVAL; |
178 | ||
11ab555a AA |
179 | switch (st->type) { |
180 | case ID_AD8366: | |
181 | code = (gain - 4500) / 253; | |
182 | break; | |
cee211f4 PC |
183 | case ID_ADA4961: |
184 | code = (15000 - gain) / 1000; | |
185 | break; | |
075da9cd AA |
186 | case ID_ADL5240: |
187 | code = ((gain - 500 - 20000) / 500) & 0x3F; | |
188 | break; | |
247d3b63 KSP |
189 | case ID_HMC792: |
190 | code = (abs(gain) / 500) & 0x3F; | |
191 | break; | |
cc74a38d SC |
192 | case ID_HMC1119: |
193 | code = (abs(gain) / 250) & 0x7F; | |
194 | break; | |
11ab555a | 195 | } |
e71d42e0 | 196 | |
dbcf6b5d | 197 | mutex_lock(&st->lock); |
e71d42e0 MH |
198 | switch (mask) { |
199 | case IIO_CHAN_INFO_HARDWAREGAIN: | |
200 | st->ch[chan->channel] = code; | |
201 | ret = ad8366_write(indio_dev, st->ch[0], st->ch[1]); | |
202 | break; | |
203 | default: | |
204 | ret = -EINVAL; | |
205 | } | |
dbcf6b5d | 206 | mutex_unlock(&st->lock); |
e71d42e0 MH |
207 | |
208 | return ret; | |
209 | } | |
210 | ||
0cc97f2e BB |
211 | static int ad8366_write_raw_get_fmt(struct iio_dev *indio_dev, |
212 | struct iio_chan_spec const *chan, | |
213 | long mask) | |
214 | { | |
215 | switch (mask) { | |
216 | case IIO_CHAN_INFO_HARDWAREGAIN: | |
217 | return IIO_VAL_INT_PLUS_MICRO_DB; | |
218 | default: | |
219 | return -EINVAL; | |
220 | } | |
221 | } | |
222 | ||
e71d42e0 MH |
223 | static const struct iio_info ad8366_info = { |
224 | .read_raw = &ad8366_read_raw, | |
225 | .write_raw = &ad8366_write_raw, | |
0cc97f2e | 226 | .write_raw_get_fmt = &ad8366_write_raw_get_fmt, |
e71d42e0 MH |
227 | }; |
228 | ||
229 | #define AD8366_CHAN(_channel) { \ | |
230 | .type = IIO_VOLTAGE, \ | |
231 | .output = 1, \ | |
232 | .indexed = 1, \ | |
233 | .channel = _channel, \ | |
b34ec6f3 | 234 | .info_mask_separate = BIT(IIO_CHAN_INFO_HARDWAREGAIN),\ |
e71d42e0 MH |
235 | } |
236 | ||
237 | static const struct iio_chan_spec ad8366_channels[] = { | |
238 | AD8366_CHAN(0), | |
239 | AD8366_CHAN(1), | |
240 | }; | |
241 | ||
cee211f4 PC |
242 | static const struct iio_chan_spec ada4961_channels[] = { |
243 | AD8366_CHAN(0), | |
244 | }; | |
245 | ||
fc52692c | 246 | static int ad8366_probe(struct spi_device *spi) |
e71d42e0 MH |
247 | { |
248 | struct iio_dev *indio_dev; | |
249 | struct ad8366_state *st; | |
250 | int ret; | |
251 | ||
36db8c72 | 252 | indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); |
e71d42e0 MH |
253 | if (indio_dev == NULL) |
254 | return -ENOMEM; | |
255 | ||
256 | st = iio_priv(indio_dev); | |
257 | ||
36db8c72 | 258 | st->reg = devm_regulator_get(&spi->dev, "vcc"); |
e71d42e0 MH |
259 | if (!IS_ERR(st->reg)) { |
260 | ret = regulator_enable(st->reg); | |
261 | if (ret) | |
36db8c72 | 262 | return ret; |
e71d42e0 MH |
263 | } |
264 | ||
265 | spi_set_drvdata(spi, indio_dev); | |
dbcf6b5d | 266 | mutex_init(&st->lock); |
e71d42e0 | 267 | st->spi = spi; |
11ab555a AA |
268 | st->type = spi_get_device_id(spi)->driver_data; |
269 | ||
270 | switch (st->type) { | |
271 | case ID_AD8366: | |
272 | indio_dev->channels = ad8366_channels; | |
273 | indio_dev->num_channels = ARRAY_SIZE(ad8366_channels); | |
274 | break; | |
cee211f4 | 275 | case ID_ADA4961: |
075da9cd | 276 | case ID_ADL5240: |
247d3b63 | 277 | case ID_HMC792: |
cc74a38d | 278 | case ID_HMC1119: |
9ca39411 CY |
279 | st->reset_gpio = devm_gpiod_get_optional(&spi->dev, "reset", GPIOD_OUT_HIGH); |
280 | if (IS_ERR(st->reset_gpio)) { | |
281 | ret = PTR_ERR(st->reset_gpio); | |
282 | goto error_disable_reg; | |
283 | } | |
cee211f4 PC |
284 | indio_dev->channels = ada4961_channels; |
285 | indio_dev->num_channels = ARRAY_SIZE(ada4961_channels); | |
286 | break; | |
11ab555a AA |
287 | default: |
288 | dev_err(&spi->dev, "Invalid device ID\n"); | |
289 | ret = -EINVAL; | |
290 | goto error_disable_reg; | |
291 | } | |
e71d42e0 | 292 | |
11ab555a | 293 | st->info = &ad8366_infos[st->type]; |
e71d42e0 MH |
294 | indio_dev->name = spi_get_device_id(spi)->name; |
295 | indio_dev->info = &ad8366_info; | |
296 | indio_dev->modes = INDIO_DIRECT_MODE; | |
e71d42e0 | 297 | |
8b5e7c33 | 298 | ret = ad8366_write(indio_dev, 0, 0); |
2636d005 AA |
299 | if (ret < 0) |
300 | goto error_disable_reg; | |
301 | ||
e71d42e0 MH |
302 | ret = iio_device_register(indio_dev); |
303 | if (ret) | |
304 | goto error_disable_reg; | |
305 | ||
e71d42e0 MH |
306 | return 0; |
307 | ||
308 | error_disable_reg: | |
309 | if (!IS_ERR(st->reg)) | |
310 | regulator_disable(st->reg); | |
e71d42e0 MH |
311 | |
312 | return ret; | |
313 | } | |
314 | ||
a0386bba | 315 | static void ad8366_remove(struct spi_device *spi) |
e71d42e0 MH |
316 | { |
317 | struct iio_dev *indio_dev = spi_get_drvdata(spi); | |
318 | struct ad8366_state *st = iio_priv(indio_dev); | |
319 | struct regulator *reg = st->reg; | |
320 | ||
321 | iio_device_unregister(indio_dev); | |
322 | ||
d3789c3e | 323 | if (!IS_ERR(reg)) |
e71d42e0 | 324 | regulator_disable(reg); |
e71d42e0 MH |
325 | } |
326 | ||
327 | static const struct spi_device_id ad8366_id[] = { | |
11ab555a | 328 | {"ad8366", ID_AD8366}, |
cee211f4 | 329 | {"ada4961", ID_ADA4961}, |
075da9cd | 330 | {"adl5240", ID_ADL5240}, |
247d3b63 | 331 | {"hmc792a", ID_HMC792}, |
cc74a38d | 332 | {"hmc1119", ID_HMC1119}, |
e71d42e0 MH |
333 | {} |
334 | }; | |
ed199a11 | 335 | MODULE_DEVICE_TABLE(spi, ad8366_id); |
e71d42e0 MH |
336 | |
337 | static struct spi_driver ad8366_driver = { | |
338 | .driver = { | |
339 | .name = KBUILD_MODNAME, | |
e71d42e0 MH |
340 | }, |
341 | .probe = ad8366_probe, | |
fc52692c | 342 | .remove = ad8366_remove, |
e71d42e0 MH |
343 | .id_table = ad8366_id, |
344 | }; | |
345 | ||
346 | module_spi_driver(ad8366_driver); | |
347 | ||
9920ed25 | 348 | MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>"); |
cee211f4 | 349 | MODULE_DESCRIPTION("Analog Devices AD8366 and similar Gain Amplifiers"); |
e71d42e0 | 350 | MODULE_LICENSE("GPL v2"); |