1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * HMC425A and similar Gain Amplifiers |
4 | * |
5 | * Copyright 2020, 2024 Analog Devices Inc. |
6 | */ |
7 | |
8 | #include <linux/bits.h> |
9 | #include <linux/bitops.h> |
10 | #include <linux/device.h> |
11 | #include <linux/err.h> |
12 | #include <linux/gpio/consumer.h> |
13 | #include <linux/iio/iio.h> |
14 | #include <linux/iio/sysfs.h> |
15 | #include <linux/kernel.h> |
16 | #include <linux/math.h> |
17 | #include <linux/mod_devicetable.h> |
18 | #include <linux/module.h> |
19 | #include <linux/platform_device.h> |
20 | #include <linux/property.h> |
21 | #include <linux/slab.h> |
22 | #include <linux/regulator/consumer.h> |
23 | #include <linux/sysfs.h> |
24 | |
25 | /* |
26 | * The LTC6373 amplifier supports configuring gain using GPIO's with the following |
27 | * values (OUTPUT_V / INPUT_V): 0(shutdown), 0.25, 0.5, 1, 2, 4, 8, 16 |
28 | * |
29 | * Except for the shutdown value, all can be converted to dB using 20 * log10(x) |
30 | * From here, it is observed that all values are multiples of the '2' gain setting, |
31 | * with the correspondent of 6.020dB. |
32 | */ |
33 | #define LTC6373_CONVERSION_CONSTANT 6020 |
34 | #define LTC6373_MIN_GAIN_CODE 0x6 |
35 | #define LTC6373_CONVERSION_MASK GENMASK(2, 0) |
36 | #define LTC6373_SHUTDOWN GENMASK(2, 0) |
37 | |
38 | enum hmc425a_type { |
39 | ID_HMC425A, |
40 | ID_HMC540S, |
41 | ID_ADRF5740, |
42 | ID_LTC6373, |
43 | }; |
44 | |
45 | struct hmc425a_chip_info { |
46 | const char *name; |
47 | const struct iio_chan_spec *channels; |
48 | unsigned int num_channels; |
49 | unsigned int num_gpios; |
50 | int gain_min; |
51 | int gain_max; |
52 | int default_gain; |
53 | int powerdown_val; |
54 | bool has_powerdown; |
55 | |
56 | int (*gain_dB_to_code)(int gain, int *code); |
57 | int (*code_to_gain_dB)(int code, int *val, int *val2); |
58 | }; |
59 | |
60 | struct hmc425a_state { |
61 | struct mutex lock; /* protect sensor state */ |
62 | const struct hmc425a_chip_info *chip_info; |
63 | struct gpio_descs *gpios; |
64 | u32 gain; |
65 | bool powerdown; |
66 | }; |
67 | |
68 | static int gain_dB_to_code(struct hmc425a_state *st, int val, int val2, int *code) |
69 | { |
70 | const struct hmc425a_chip_info *inf = st->chip_info; |
71 | int gain; |
72 | |
73 | if (val < 0) |
74 | gain = (val * 1000) - (val2 / 1000); |
75 | else |
76 | gain = (val * 1000) + (val2 / 1000); |
77 | |
78 | if (gain > inf->gain_max || gain < inf->gain_min) |
79 | return -EINVAL; |
80 | if (st->powerdown) |
81 | return -EPERM; |
82 | |
83 | return st->chip_info->gain_dB_to_code(gain, code); |
84 | } |
85 | |
86 | static int hmc425a_gain_dB_to_code(int gain, int *code) |
87 | { |
88 | *code = ~((abs(gain) / 500) & 0x3F); |
89 | return 0; |
90 | } |
91 | |
92 | static int hmc540s_gain_dB_to_code(int gain, int *code) |
93 | { |
94 | *code = ~((abs(gain) / 1000) & 0xF); |
95 | return 0; |
96 | } |
97 | |
98 | static int adrf5740_gain_dB_to_code(int gain, int *code) |
99 | { |
100 | int temp = (abs(gain) / 2000) & 0xF; |
101 | |
102 | /* Bit [0-3]: 2dB 4dB 8dB 8dB */ |
103 | *code = temp & BIT(3) ? temp | BIT(2) : temp; |
104 | return 0; |
105 | } |
106 | |
107 | static int ltc6373_gain_dB_to_code(int gain, int *code) |
108 | { |
109 | *code = ~(DIV_ROUND_CLOSEST(gain, LTC6373_CONVERSION_CONSTANT) + 3) |
110 | & LTC6373_CONVERSION_MASK; |
111 | return 0; |
112 | } |
113 | |
114 | static int code_to_gain_dB(struct hmc425a_state *st, int *val, int *val2) |
115 | { |
116 | if (st->powerdown) |
117 | return -EPERM; |
118 | return st->chip_info->code_to_gain_dB(st->gain, val, val2); |
119 | } |
120 | |
121 | static int hmc425a_code_to_gain_dB(int code, int *val, int *val2) |
122 | { |
123 | *val = (~code * -500) / 1000; |
124 | *val2 = ((~code * -500) % 1000) * 1000; |
125 | return 0; |
126 | } |
127 | |
128 | static int hmc540s_code_to_gain_dB(int code, int *val, int *val2) |
129 | { |
130 | *val = (~code * -1000) / 1000; |
131 | *val2 = ((~code * -1000) % 1000) * 1000; |
132 | return 0; |
133 | } |
134 | |
135 | static int adrf5740_code_to_gain_dB(int code, int *val, int *val2) |
136 | { |
137 | /* |
138 | * Bit [0-3]: 2dB 4dB 8dB 8dB |
139 | * When BIT(3) is set, unset BIT(2) and use 3 as double the place value |
140 | */ |
141 | code = code & BIT(3) ? code & ~BIT(2) : code; |
142 | *val = (code * -2000) / 1000; |
143 | *val2 = ((code * -2000) % 1000) * 1000; |
144 | return 0; |
145 | } |
146 | |
147 | static int ltc6373_code_to_gain_dB(int code, int *val, int *val2) |
148 | { |
149 | int gain = ((~code & LTC6373_CONVERSION_MASK) - 3) * |
150 | LTC6373_CONVERSION_CONSTANT; |
151 | |
152 | *val = gain / 1000; |
153 | *val2 = (gain % 1000) * 1000; |
154 | return 0; |
155 | } |
156 | |
157 | static int hmc425a_write(struct iio_dev *indio_dev, u32 value) |
158 | { |
159 | struct hmc425a_state *st = iio_priv(indio_dev); |
160 | DECLARE_BITMAP(values, BITS_PER_TYPE(value)); |
161 | |
162 | values[0] = value; |
163 | |
164 | gpiod_set_array_value_cansleep(array_size: st->gpios->ndescs, desc_array: st->gpios->desc, |
165 | NULL, value_bitmap: values); |
166 | return 0; |
167 | } |
168 | |
169 | static int hmc425a_read_raw(struct iio_dev *indio_dev, |
170 | struct iio_chan_spec const *chan, int *val, |
171 | int *val2, long m) |
172 | { |
173 | struct hmc425a_state *st = iio_priv(indio_dev); |
174 | int ret; |
175 | |
176 | mutex_lock(&st->lock); |
177 | switch (m) { |
178 | case IIO_CHAN_INFO_HARDWAREGAIN: |
179 | ret = code_to_gain_dB(st, val, val2); |
180 | if (ret) |
181 | break; |
182 | ret = IIO_VAL_INT_PLUS_MICRO_DB; |
183 | break; |
184 | default: |
185 | ret = -EINVAL; |
186 | } |
187 | mutex_unlock(lock: &st->lock); |
188 | |
189 | return ret; |
190 | }; |
191 | |
192 | static int hmc425a_write_raw(struct iio_dev *indio_dev, |
193 | struct iio_chan_spec const *chan, int val, |
194 | int val2, long mask) |
195 | { |
196 | struct hmc425a_state *st = iio_priv(indio_dev); |
197 | int code = 0, ret; |
198 | |
199 | mutex_lock(&st->lock); |
200 | switch (mask) { |
201 | case IIO_CHAN_INFO_HARDWAREGAIN: |
202 | ret = gain_dB_to_code(st, val, val2, code: &code); |
203 | if (ret) |
204 | break; |
205 | st->gain = code; |
206 | |
207 | ret = hmc425a_write(indio_dev, value: st->gain); |
208 | break; |
209 | default: |
210 | ret = -EINVAL; |
211 | } |
212 | mutex_unlock(lock: &st->lock); |
213 | |
214 | return ret; |
215 | } |
216 | |
217 | static int hmc425a_write_raw_get_fmt(struct iio_dev *indio_dev, |
218 | struct iio_chan_spec const *chan, |
219 | long mask) |
220 | { |
221 | switch (mask) { |
222 | case IIO_CHAN_INFO_HARDWAREGAIN: |
223 | return IIO_VAL_INT_PLUS_MICRO_DB; |
224 | default: |
225 | return -EINVAL; |
226 | } |
227 | } |
228 | |
229 | static const struct iio_info hmc425a_info = { |
230 | .read_raw = &hmc425a_read_raw, |
231 | .write_raw = &hmc425a_write_raw, |
232 | .write_raw_get_fmt = &hmc425a_write_raw_get_fmt, |
233 | }; |
234 | |
235 | static ssize_t ltc6373_read_powerdown(struct iio_dev *indio_dev, |
236 | uintptr_t private, |
237 | const struct iio_chan_spec *chan, |
238 | char *buf) |
239 | { |
240 | struct hmc425a_state *st = iio_priv(indio_dev); |
241 | |
242 | return sysfs_emit(buf, fmt: "%d\n" , st->powerdown); |
243 | } |
244 | |
245 | static ssize_t ltc6373_write_powerdown(struct iio_dev *indio_dev, |
246 | uintptr_t private, |
247 | const struct iio_chan_spec *chan, |
248 | const char *buf, |
249 | size_t len) |
250 | { |
251 | struct hmc425a_state *st = iio_priv(indio_dev); |
252 | bool powerdown; |
253 | int code, ret; |
254 | |
255 | ret = kstrtobool(s: buf, res: &powerdown); |
256 | if (ret) |
257 | return ret; |
258 | |
259 | mutex_lock(&st->lock); |
260 | st->powerdown = powerdown; |
261 | code = (powerdown) ? LTC6373_SHUTDOWN : st->gain; |
262 | hmc425a_write(indio_dev, value: code); |
263 | mutex_unlock(lock: &st->lock); |
264 | return len; |
265 | } |
266 | |
267 | static const struct iio_chan_spec_ext_info ltc6373_ext_info[] = { |
268 | { |
269 | .name = "powerdown" , |
270 | .read = ltc6373_read_powerdown, |
271 | .write = ltc6373_write_powerdown, |
272 | .shared = IIO_SEPARATE, |
273 | }, |
274 | {} |
275 | }; |
276 | |
277 | #define HMC425A_CHAN(_channel) \ |
278 | { \ |
279 | .type = IIO_VOLTAGE, \ |
280 | .output = 1, \ |
281 | .indexed = 1, \ |
282 | .channel = _channel, \ |
283 | .info_mask_separate = BIT(IIO_CHAN_INFO_HARDWAREGAIN), \ |
284 | } |
285 | |
286 | #define LTC6373_CHAN(_channel) \ |
287 | { \ |
288 | .type = IIO_VOLTAGE, \ |
289 | .output = 1, \ |
290 | .indexed = 1, \ |
291 | .channel = _channel, \ |
292 | .info_mask_separate = BIT(IIO_CHAN_INFO_HARDWAREGAIN), \ |
293 | .ext_info = ltc6373_ext_info, \ |
294 | } |
295 | |
296 | static const struct iio_chan_spec hmc425a_channels[] = { |
297 | HMC425A_CHAN(0), |
298 | }; |
299 | |
300 | static const struct iio_chan_spec ltc6373_channels[] = { |
301 | LTC6373_CHAN(0), |
302 | }; |
303 | |
304 | static const struct hmc425a_chip_info hmc425a_chip_info_tbl[] = { |
305 | [ID_HMC425A] = { |
306 | .name = "hmc425a" , |
307 | .channels = hmc425a_channels, |
308 | .num_channels = ARRAY_SIZE(hmc425a_channels), |
309 | .num_gpios = 6, |
310 | .gain_min = -31500, |
311 | .gain_max = 0, |
312 | .default_gain = -0x40, /* set default gain -31.5db*/ |
313 | .gain_dB_to_code = hmc425a_gain_dB_to_code, |
314 | .code_to_gain_dB = hmc425a_code_to_gain_dB, |
315 | }, |
316 | [ID_HMC540S] = { |
317 | .name = "hmc540s" , |
318 | .channels = hmc425a_channels, |
319 | .num_channels = ARRAY_SIZE(hmc425a_channels), |
320 | .num_gpios = 4, |
321 | .gain_min = -15000, |
322 | .gain_max = 0, |
323 | .default_gain = -0x10, /* set default gain -15.0db*/ |
324 | .gain_dB_to_code = hmc540s_gain_dB_to_code, |
325 | .code_to_gain_dB = hmc540s_code_to_gain_dB, |
326 | }, |
327 | [ID_ADRF5740] = { |
328 | .name = "adrf5740" , |
329 | .channels = hmc425a_channels, |
330 | .num_channels = ARRAY_SIZE(hmc425a_channels), |
331 | .num_gpios = 4, |
332 | .gain_min = -22000, |
333 | .gain_max = 0, |
334 | .default_gain = 0xF, /* set default gain -22.0db*/ |
335 | .gain_dB_to_code = adrf5740_gain_dB_to_code, |
336 | .code_to_gain_dB = adrf5740_code_to_gain_dB, |
337 | }, |
338 | [ID_LTC6373] = { |
339 | .name = "ltc6373" , |
340 | .channels = ltc6373_channels, |
341 | .num_channels = ARRAY_SIZE(ltc6373_channels), |
342 | .num_gpios = 3, |
343 | .gain_min = -12041, /* gain setting x0.25*/ |
344 | .gain_max = 24082, /* gain setting x16 */ |
345 | .default_gain = LTC6373_MIN_GAIN_CODE, |
346 | .powerdown_val = LTC6373_SHUTDOWN, |
347 | .has_powerdown = true, |
348 | .gain_dB_to_code = ltc6373_gain_dB_to_code, |
349 | .code_to_gain_dB = ltc6373_code_to_gain_dB, |
350 | }, |
351 | }; |
352 | |
353 | static int hmc425a_probe(struct platform_device *pdev) |
354 | { |
355 | struct iio_dev *indio_dev; |
356 | struct hmc425a_state *st; |
357 | int ret; |
358 | |
359 | indio_dev = devm_iio_device_alloc(parent: &pdev->dev, sizeof_priv: sizeof(*st)); |
360 | if (!indio_dev) |
361 | return -ENOMEM; |
362 | |
363 | st = iio_priv(indio_dev); |
364 | |
365 | st->chip_info = device_get_match_data(dev: &pdev->dev); |
366 | indio_dev->num_channels = st->chip_info->num_channels; |
367 | indio_dev->channels = st->chip_info->channels; |
368 | indio_dev->name = st->chip_info->name; |
369 | st->gain = st->chip_info->default_gain; |
370 | |
371 | st->gpios = devm_gpiod_get_array(dev: &pdev->dev, con_id: "ctrl" , flags: GPIOD_OUT_LOW); |
372 | if (IS_ERR(ptr: st->gpios)) |
373 | return dev_err_probe(dev: &pdev->dev, err: PTR_ERR(ptr: st->gpios), |
374 | fmt: "failed to get gpios\n" ); |
375 | |
376 | if (st->gpios->ndescs != st->chip_info->num_gpios) { |
377 | dev_err(&pdev->dev, "%d GPIOs needed to operate\n" , |
378 | st->chip_info->num_gpios); |
379 | return -ENODEV; |
380 | } |
381 | |
382 | ret = devm_regulator_get_enable(dev: &pdev->dev, id: "vcc-supply" ); |
383 | if (ret) |
384 | return ret; |
385 | |
386 | mutex_init(&st->lock); |
387 | |
388 | indio_dev->info = &hmc425a_info; |
389 | indio_dev->modes = INDIO_DIRECT_MODE; |
390 | |
391 | if (st->chip_info->has_powerdown) { |
392 | st->powerdown = true; |
393 | hmc425a_write(indio_dev, value: st->chip_info->powerdown_val); |
394 | } else { |
395 | /* Set default gain */ |
396 | hmc425a_write(indio_dev, value: st->gain); |
397 | } |
398 | |
399 | return devm_iio_device_register(&pdev->dev, indio_dev); |
400 | } |
401 | |
402 | /* Match table for of_platform binding */ |
403 | static const struct of_device_id hmc425a_of_match[] = { |
404 | { .compatible = "adi,hmc425a" , |
405 | .data = &hmc425a_chip_info_tbl[ID_HMC425A]}, |
406 | { .compatible = "adi,hmc540s" , |
407 | .data = &hmc425a_chip_info_tbl[ID_HMC540S]}, |
408 | { .compatible = "adi,adrf5740" , |
409 | .data = &hmc425a_chip_info_tbl[ID_ADRF5740]}, |
410 | { .compatible = "adi,ltc6373" , |
411 | .data = &hmc425a_chip_info_tbl[ID_LTC6373]}, |
412 | {} |
413 | }; |
414 | MODULE_DEVICE_TABLE(of, hmc425a_of_match); |
415 | |
416 | static struct platform_driver hmc425a_driver = { |
417 | .driver = { |
418 | .name = KBUILD_MODNAME, |
419 | .of_match_table = hmc425a_of_match, |
420 | }, |
421 | .probe = hmc425a_probe, |
422 | }; |
423 | module_platform_driver(hmc425a_driver); |
424 | |
425 | MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>" ); |
426 | MODULE_DESCRIPTION("Analog Devices HMC425A and similar GPIO control Gain Amplifiers" ); |
427 | MODULE_LICENSE("GPL v2" ); |
428 | |