xref: /linux/drivers/iio/amplifiers/hmc425a.c (revision 79790b6818e96c58fe2bffee1b418c16e64e7b80)
1a76838dfSMichael Hennerich // SPDX-License-Identifier: GPL-2.0
2a76838dfSMichael Hennerich /*
3a76838dfSMichael Hennerich  * HMC425A and similar Gain Amplifiers
4a76838dfSMichael Hennerich  *
5*a0e7a2b7SDumitru Ceclan  * Copyright 2020, 2024 Analog Devices Inc.
6a76838dfSMichael Hennerich  */
7a76838dfSMichael Hennerich 
8*a0e7a2b7SDumitru Ceclan #include <linux/bits.h>
9ed73c4f1SAna-Maria Cusco #include <linux/bitops.h>
10a76838dfSMichael Hennerich #include <linux/device.h>
11a76838dfSMichael Hennerich #include <linux/err.h>
12a76838dfSMichael Hennerich #include <linux/gpio/consumer.h>
13a76838dfSMichael Hennerich #include <linux/iio/iio.h>
14a76838dfSMichael Hennerich #include <linux/iio/sysfs.h>
15a76838dfSMichael Hennerich #include <linux/kernel.h>
16*a0e7a2b7SDumitru Ceclan #include <linux/math.h>
17201d11c5SAndy Shevchenko #include <linux/mod_devicetable.h>
18a76838dfSMichael Hennerich #include <linux/module.h>
19a76838dfSMichael Hennerich #include <linux/platform_device.h>
20201d11c5SAndy Shevchenko #include <linux/property.h>
21a76838dfSMichael Hennerich #include <linux/slab.h>
22a76838dfSMichael Hennerich #include <linux/regulator/consumer.h>
23a76838dfSMichael Hennerich #include <linux/sysfs.h>
24a76838dfSMichael Hennerich 
25*a0e7a2b7SDumitru Ceclan /*
26*a0e7a2b7SDumitru Ceclan  * The LTC6373 amplifier supports configuring gain using GPIO's with the following
27*a0e7a2b7SDumitru Ceclan  *  values (OUTPUT_V / INPUT_V): 0(shutdown), 0.25, 0.5, 1, 2, 4, 8, 16
28*a0e7a2b7SDumitru Ceclan  *
29*a0e7a2b7SDumitru Ceclan  * Except for the shutdown value, all can be converted to dB using 20 * log10(x)
30*a0e7a2b7SDumitru Ceclan  * From here, it is observed that all values are multiples of the '2' gain setting,
31*a0e7a2b7SDumitru Ceclan  *  with the correspondent of 6.020dB.
32*a0e7a2b7SDumitru Ceclan  */
33*a0e7a2b7SDumitru Ceclan #define LTC6373_CONVERSION_CONSTANT	6020
34*a0e7a2b7SDumitru Ceclan #define LTC6373_MIN_GAIN_CODE		0x6
35*a0e7a2b7SDumitru Ceclan #define LTC6373_CONVERSION_MASK		GENMASK(2, 0)
36*a0e7a2b7SDumitru Ceclan #define LTC6373_SHUTDOWN		GENMASK(2, 0)
37*a0e7a2b7SDumitru Ceclan 
38a76838dfSMichael Hennerich enum hmc425a_type {
39a76838dfSMichael Hennerich 	ID_HMC425A,
4077865a8fSMichael Hennerich 	ID_HMC540S,
41*a0e7a2b7SDumitru Ceclan 	ID_ADRF5740,
42*a0e7a2b7SDumitru Ceclan 	ID_LTC6373,
43a76838dfSMichael Hennerich };
44a76838dfSMichael Hennerich 
45a76838dfSMichael Hennerich struct hmc425a_chip_info {
46a76838dfSMichael Hennerich 	const char			*name;
47a76838dfSMichael Hennerich 	const struct iio_chan_spec	*channels;
48a76838dfSMichael Hennerich 	unsigned int			num_channels;
49a76838dfSMichael Hennerich 	unsigned int			num_gpios;
50a76838dfSMichael Hennerich 	int				gain_min;
51a76838dfSMichael Hennerich 	int				gain_max;
52a76838dfSMichael Hennerich 	int				default_gain;
53*a0e7a2b7SDumitru Ceclan 	int				powerdown_val;
54*a0e7a2b7SDumitru Ceclan 	bool				has_powerdown;
552edb2257SDumitru Ceclan 
562edb2257SDumitru Ceclan 	int				(*gain_dB_to_code)(int gain, int *code);
572edb2257SDumitru Ceclan 	int				(*code_to_gain_dB)(int code, int *val, int *val2);
58a76838dfSMichael Hennerich };
59a76838dfSMichael Hennerich 
60a76838dfSMichael Hennerich struct hmc425a_state {
61a76838dfSMichael Hennerich 	struct	mutex lock; /* protect sensor state */
6209ac57acSDumitru Ceclan 	const struct	hmc425a_chip_info *chip_info;
63a76838dfSMichael Hennerich 	struct	gpio_descs *gpios;
64a76838dfSMichael Hennerich 	u32	gain;
65*a0e7a2b7SDumitru Ceclan 	bool	powerdown;
66a76838dfSMichael Hennerich };
67a76838dfSMichael Hennerich 
gain_dB_to_code(struct hmc425a_state * st,int val,int val2,int * code)682edb2257SDumitru Ceclan static int gain_dB_to_code(struct hmc425a_state *st, int val, int val2, int *code)
692edb2257SDumitru Ceclan {
7009ac57acSDumitru Ceclan 	const struct hmc425a_chip_info *inf = st->chip_info;
712edb2257SDumitru Ceclan 	int gain;
722edb2257SDumitru Ceclan 
732edb2257SDumitru Ceclan 	if (val < 0)
742edb2257SDumitru Ceclan 		gain = (val * 1000) - (val2 / 1000);
752edb2257SDumitru Ceclan 	else
762edb2257SDumitru Ceclan 		gain = (val * 1000) + (val2 / 1000);
772edb2257SDumitru Ceclan 
782edb2257SDumitru Ceclan 	if (gain > inf->gain_max || gain < inf->gain_min)
792edb2257SDumitru Ceclan 		return -EINVAL;
80*a0e7a2b7SDumitru Ceclan 	if (st->powerdown)
81*a0e7a2b7SDumitru Ceclan 		return -EPERM;
822edb2257SDumitru Ceclan 
832edb2257SDumitru Ceclan 	return st->chip_info->gain_dB_to_code(gain, code);
842edb2257SDumitru Ceclan }
852edb2257SDumitru Ceclan 
hmc425a_gain_dB_to_code(int gain,int * code)862edb2257SDumitru Ceclan static int hmc425a_gain_dB_to_code(int gain, int *code)
872edb2257SDumitru Ceclan {
882edb2257SDumitru Ceclan 	*code = ~((abs(gain) / 500) & 0x3F);
892edb2257SDumitru Ceclan 	return 0;
902edb2257SDumitru Ceclan }
912edb2257SDumitru Ceclan 
hmc540s_gain_dB_to_code(int gain,int * code)922edb2257SDumitru Ceclan static int hmc540s_gain_dB_to_code(int gain, int *code)
932edb2257SDumitru Ceclan {
942edb2257SDumitru Ceclan 	*code = ~((abs(gain) / 1000) & 0xF);
952edb2257SDumitru Ceclan 	return 0;
962edb2257SDumitru Ceclan }
972edb2257SDumitru Ceclan 
adrf5740_gain_dB_to_code(int gain,int * code)982edb2257SDumitru Ceclan static int adrf5740_gain_dB_to_code(int gain, int *code)
992edb2257SDumitru Ceclan {
1002edb2257SDumitru Ceclan 	int temp = (abs(gain) / 2000) & 0xF;
1012edb2257SDumitru Ceclan 
1022edb2257SDumitru Ceclan 	/* Bit [0-3]: 2dB 4dB 8dB 8dB */
1032edb2257SDumitru Ceclan 	*code = temp & BIT(3) ? temp | BIT(2) : temp;
1042edb2257SDumitru Ceclan 	return 0;
1052edb2257SDumitru Ceclan }
1062edb2257SDumitru Ceclan 
ltc6373_gain_dB_to_code(int gain,int * code)107*a0e7a2b7SDumitru Ceclan static int ltc6373_gain_dB_to_code(int gain, int *code)
108*a0e7a2b7SDumitru Ceclan {
109*a0e7a2b7SDumitru Ceclan 	*code = ~(DIV_ROUND_CLOSEST(gain, LTC6373_CONVERSION_CONSTANT) + 3)
110*a0e7a2b7SDumitru Ceclan 		& LTC6373_CONVERSION_MASK;
111*a0e7a2b7SDumitru Ceclan 	return 0;
112*a0e7a2b7SDumitru Ceclan }
113*a0e7a2b7SDumitru Ceclan 
code_to_gain_dB(struct hmc425a_state * st,int * val,int * val2)1142edb2257SDumitru Ceclan static int code_to_gain_dB(struct hmc425a_state *st, int *val, int *val2)
1152edb2257SDumitru Ceclan {
116*a0e7a2b7SDumitru Ceclan 	if (st->powerdown)
117*a0e7a2b7SDumitru Ceclan 		return -EPERM;
1182edb2257SDumitru Ceclan 	return st->chip_info->code_to_gain_dB(st->gain, val, val2);
1192edb2257SDumitru Ceclan }
1202edb2257SDumitru Ceclan 
hmc425a_code_to_gain_dB(int code,int * val,int * val2)1212edb2257SDumitru Ceclan static int hmc425a_code_to_gain_dB(int code, int *val, int *val2)
1222edb2257SDumitru Ceclan {
1232edb2257SDumitru Ceclan 	*val = (~code * -500) / 1000;
1242edb2257SDumitru Ceclan 	*val2 = ((~code * -500) % 1000) * 1000;
1252edb2257SDumitru Ceclan 	return 0;
1262edb2257SDumitru Ceclan }
1272edb2257SDumitru Ceclan 
hmc540s_code_to_gain_dB(int code,int * val,int * val2)1282edb2257SDumitru Ceclan static int hmc540s_code_to_gain_dB(int code, int *val, int *val2)
1292edb2257SDumitru Ceclan {
1302edb2257SDumitru Ceclan 	*val = (~code * -1000) / 1000;
1312edb2257SDumitru Ceclan 	*val2 = ((~code * -1000) % 1000) * 1000;
1322edb2257SDumitru Ceclan 	return 0;
1332edb2257SDumitru Ceclan }
1342edb2257SDumitru Ceclan 
adrf5740_code_to_gain_dB(int code,int * val,int * val2)1352edb2257SDumitru Ceclan static int adrf5740_code_to_gain_dB(int code, int *val, int *val2)
1362edb2257SDumitru Ceclan {
1372edb2257SDumitru Ceclan 	/*
1382edb2257SDumitru Ceclan 	 * Bit [0-3]: 2dB 4dB 8dB 8dB
1392edb2257SDumitru Ceclan 	 * When BIT(3) is set, unset BIT(2) and use 3 as double the place value
1402edb2257SDumitru Ceclan 	 */
1412edb2257SDumitru Ceclan 	code = code & BIT(3) ? code & ~BIT(2) : code;
1422edb2257SDumitru Ceclan 	*val = (code * -2000) / 1000;
1432edb2257SDumitru Ceclan 	*val2 = ((code * -2000) % 1000) * 1000;
1442edb2257SDumitru Ceclan 	return 0;
1452edb2257SDumitru Ceclan }
1462edb2257SDumitru Ceclan 
ltc6373_code_to_gain_dB(int code,int * val,int * val2)147*a0e7a2b7SDumitru Ceclan static int ltc6373_code_to_gain_dB(int code, int *val, int *val2)
148*a0e7a2b7SDumitru Ceclan {
149*a0e7a2b7SDumitru Ceclan 	int gain = ((~code & LTC6373_CONVERSION_MASK) - 3) *
150*a0e7a2b7SDumitru Ceclan 		   LTC6373_CONVERSION_CONSTANT;
151*a0e7a2b7SDumitru Ceclan 
152*a0e7a2b7SDumitru Ceclan 	*val = gain / 1000;
153*a0e7a2b7SDumitru Ceclan 	*val2 = (gain % 1000) * 1000;
154*a0e7a2b7SDumitru Ceclan 	return 0;
155*a0e7a2b7SDumitru Ceclan }
156*a0e7a2b7SDumitru Ceclan 
hmc425a_write(struct iio_dev * indio_dev,u32 value)157a76838dfSMichael Hennerich static int hmc425a_write(struct iio_dev *indio_dev, u32 value)
158a76838dfSMichael Hennerich {
159a76838dfSMichael Hennerich 	struct hmc425a_state *st = iio_priv(indio_dev);
160a76838dfSMichael Hennerich 	DECLARE_BITMAP(values, BITS_PER_TYPE(value));
161a76838dfSMichael Hennerich 
162a76838dfSMichael Hennerich 	values[0] = value;
163a76838dfSMichael Hennerich 
164a76838dfSMichael Hennerich 	gpiod_set_array_value_cansleep(st->gpios->ndescs, st->gpios->desc,
165a76838dfSMichael Hennerich 				       NULL, values);
166a76838dfSMichael Hennerich 	return 0;
167a76838dfSMichael Hennerich }
168a76838dfSMichael Hennerich 
hmc425a_read_raw(struct iio_dev * indio_dev,struct iio_chan_spec const * chan,int * val,int * val2,long m)169a76838dfSMichael Hennerich static int hmc425a_read_raw(struct iio_dev *indio_dev,
170a76838dfSMichael Hennerich 			    struct iio_chan_spec const *chan, int *val,
171a76838dfSMichael Hennerich 			    int *val2, long m)
172a76838dfSMichael Hennerich {
173a76838dfSMichael Hennerich 	struct hmc425a_state *st = iio_priv(indio_dev);
174a76838dfSMichael Hennerich 	int ret;
175a76838dfSMichael Hennerich 
176a76838dfSMichael Hennerich 	mutex_lock(&st->lock);
177a76838dfSMichael Hennerich 	switch (m) {
178a76838dfSMichael Hennerich 	case IIO_CHAN_INFO_HARDWAREGAIN:
1792edb2257SDumitru Ceclan 		ret = code_to_gain_dB(st, val, val2);
1802edb2257SDumitru Ceclan 		if (ret)
181a76838dfSMichael Hennerich 			break;
182a76838dfSMichael Hennerich 		ret = IIO_VAL_INT_PLUS_MICRO_DB;
183a76838dfSMichael Hennerich 		break;
184a76838dfSMichael Hennerich 	default:
185a76838dfSMichael Hennerich 		ret = -EINVAL;
186a76838dfSMichael Hennerich 	}
187a76838dfSMichael Hennerich 	mutex_unlock(&st->lock);
188a76838dfSMichael Hennerich 
189a76838dfSMichael Hennerich 	return ret;
190a76838dfSMichael Hennerich };
191a76838dfSMichael Hennerich 
hmc425a_write_raw(struct iio_dev * indio_dev,struct iio_chan_spec const * chan,int val,int val2,long mask)192a76838dfSMichael Hennerich static int hmc425a_write_raw(struct iio_dev *indio_dev,
193a76838dfSMichael Hennerich 			     struct iio_chan_spec const *chan, int val,
194a76838dfSMichael Hennerich 			     int val2, long mask)
195a76838dfSMichael Hennerich {
196a76838dfSMichael Hennerich 	struct hmc425a_state *st = iio_priv(indio_dev);
1972edb2257SDumitru Ceclan 	int code = 0, ret;
198a76838dfSMichael Hennerich 
199a76838dfSMichael Hennerich 	mutex_lock(&st->lock);
200a76838dfSMichael Hennerich 	switch (mask) {
201a76838dfSMichael Hennerich 	case IIO_CHAN_INFO_HARDWAREGAIN:
2022edb2257SDumitru Ceclan 		ret = gain_dB_to_code(st, val, val2, &code);
2032edb2257SDumitru Ceclan 		if (ret)
2042edb2257SDumitru Ceclan 			break;
205a76838dfSMichael Hennerich 		st->gain = code;
206a76838dfSMichael Hennerich 
207a76838dfSMichael Hennerich 		ret = hmc425a_write(indio_dev, st->gain);
208a76838dfSMichael Hennerich 		break;
209a76838dfSMichael Hennerich 	default:
210a76838dfSMichael Hennerich 		ret = -EINVAL;
211a76838dfSMichael Hennerich 	}
212a76838dfSMichael Hennerich 	mutex_unlock(&st->lock);
213a76838dfSMichael Hennerich 
214a76838dfSMichael Hennerich 	return ret;
215a76838dfSMichael Hennerich }
216a76838dfSMichael Hennerich 
hmc425a_write_raw_get_fmt(struct iio_dev * indio_dev,struct iio_chan_spec const * chan,long mask)217a76838dfSMichael Hennerich static int hmc425a_write_raw_get_fmt(struct iio_dev *indio_dev,
218a76838dfSMichael Hennerich 				     struct iio_chan_spec const *chan,
219a76838dfSMichael Hennerich 				     long mask)
220a76838dfSMichael Hennerich {
221a76838dfSMichael Hennerich 	switch (mask) {
222a76838dfSMichael Hennerich 	case IIO_CHAN_INFO_HARDWAREGAIN:
223a76838dfSMichael Hennerich 		return IIO_VAL_INT_PLUS_MICRO_DB;
224a76838dfSMichael Hennerich 	default:
225a76838dfSMichael Hennerich 		return -EINVAL;
226a76838dfSMichael Hennerich 	}
227a76838dfSMichael Hennerich }
228a76838dfSMichael Hennerich 
229a76838dfSMichael Hennerich static const struct iio_info hmc425a_info = {
230a76838dfSMichael Hennerich 	.read_raw = &hmc425a_read_raw,
231a76838dfSMichael Hennerich 	.write_raw = &hmc425a_write_raw,
232a76838dfSMichael Hennerich 	.write_raw_get_fmt = &hmc425a_write_raw_get_fmt,
233a76838dfSMichael Hennerich };
234a76838dfSMichael Hennerich 
ltc6373_read_powerdown(struct iio_dev * indio_dev,uintptr_t private,const struct iio_chan_spec * chan,char * buf)235*a0e7a2b7SDumitru Ceclan static ssize_t ltc6373_read_powerdown(struct iio_dev *indio_dev,
236*a0e7a2b7SDumitru Ceclan 				      uintptr_t private,
237*a0e7a2b7SDumitru Ceclan 				      const struct iio_chan_spec *chan,
238*a0e7a2b7SDumitru Ceclan 				      char *buf)
239*a0e7a2b7SDumitru Ceclan {
240*a0e7a2b7SDumitru Ceclan 	struct hmc425a_state *st = iio_priv(indio_dev);
241*a0e7a2b7SDumitru Ceclan 
242*a0e7a2b7SDumitru Ceclan 	return sysfs_emit(buf, "%d\n", st->powerdown);
243*a0e7a2b7SDumitru Ceclan }
244*a0e7a2b7SDumitru Ceclan 
ltc6373_write_powerdown(struct iio_dev * indio_dev,uintptr_t private,const struct iio_chan_spec * chan,const char * buf,size_t len)245*a0e7a2b7SDumitru Ceclan static ssize_t ltc6373_write_powerdown(struct iio_dev *indio_dev,
246*a0e7a2b7SDumitru Ceclan 				       uintptr_t private,
247*a0e7a2b7SDumitru Ceclan 				       const struct iio_chan_spec *chan,
248*a0e7a2b7SDumitru Ceclan 				       const char *buf,
249*a0e7a2b7SDumitru Ceclan 				       size_t len)
250*a0e7a2b7SDumitru Ceclan {
251*a0e7a2b7SDumitru Ceclan 	struct hmc425a_state *st = iio_priv(indio_dev);
252*a0e7a2b7SDumitru Ceclan 	bool powerdown;
253*a0e7a2b7SDumitru Ceclan 	int code, ret;
254*a0e7a2b7SDumitru Ceclan 
255*a0e7a2b7SDumitru Ceclan 	ret = kstrtobool(buf, &powerdown);
256*a0e7a2b7SDumitru Ceclan 	if (ret)
257*a0e7a2b7SDumitru Ceclan 		return ret;
258*a0e7a2b7SDumitru Ceclan 
259*a0e7a2b7SDumitru Ceclan 	mutex_lock(&st->lock);
260*a0e7a2b7SDumitru Ceclan 	st->powerdown = powerdown;
261*a0e7a2b7SDumitru Ceclan 	code = (powerdown) ? LTC6373_SHUTDOWN : st->gain;
262*a0e7a2b7SDumitru Ceclan 	hmc425a_write(indio_dev, code);
263*a0e7a2b7SDumitru Ceclan 	mutex_unlock(&st->lock);
264*a0e7a2b7SDumitru Ceclan 	return len;
265*a0e7a2b7SDumitru Ceclan }
266*a0e7a2b7SDumitru Ceclan 
267*a0e7a2b7SDumitru Ceclan static const struct iio_chan_spec_ext_info ltc6373_ext_info[] = {
268*a0e7a2b7SDumitru Ceclan 	{
269*a0e7a2b7SDumitru Ceclan 		.name = "powerdown",
270*a0e7a2b7SDumitru Ceclan 		.read = ltc6373_read_powerdown,
271*a0e7a2b7SDumitru Ceclan 		.write = ltc6373_write_powerdown,
272*a0e7a2b7SDumitru Ceclan 		.shared = IIO_SEPARATE,
273*a0e7a2b7SDumitru Ceclan 	},
274*a0e7a2b7SDumitru Ceclan 	{}
275*a0e7a2b7SDumitru Ceclan };
276*a0e7a2b7SDumitru Ceclan 
277a76838dfSMichael Hennerich #define HMC425A_CHAN(_channel)						\
278a76838dfSMichael Hennerich {									\
279a76838dfSMichael Hennerich 	.type = IIO_VOLTAGE,						\
280a76838dfSMichael Hennerich 	.output = 1,							\
281a76838dfSMichael Hennerich 	.indexed = 1,							\
282a76838dfSMichael Hennerich 	.channel = _channel,						\
283a76838dfSMichael Hennerich 	.info_mask_separate = BIT(IIO_CHAN_INFO_HARDWAREGAIN),		\
284a76838dfSMichael Hennerich }
285a76838dfSMichael Hennerich 
286*a0e7a2b7SDumitru Ceclan #define LTC6373_CHAN(_channel)						\
287*a0e7a2b7SDumitru Ceclan {									\
288*a0e7a2b7SDumitru Ceclan 	.type = IIO_VOLTAGE,						\
289*a0e7a2b7SDumitru Ceclan 	.output = 1,							\
290*a0e7a2b7SDumitru Ceclan 	.indexed = 1,							\
291*a0e7a2b7SDumitru Ceclan 	.channel = _channel,						\
292*a0e7a2b7SDumitru Ceclan 	.info_mask_separate = BIT(IIO_CHAN_INFO_HARDWAREGAIN),		\
293*a0e7a2b7SDumitru Ceclan 	.ext_info = ltc6373_ext_info,					\
294*a0e7a2b7SDumitru Ceclan }
295*a0e7a2b7SDumitru Ceclan 
296a76838dfSMichael Hennerich static const struct iio_chan_spec hmc425a_channels[] = {
297a76838dfSMichael Hennerich 	HMC425A_CHAN(0),
298a76838dfSMichael Hennerich };
299a76838dfSMichael Hennerich 
300*a0e7a2b7SDumitru Ceclan static const struct iio_chan_spec ltc6373_channels[] = {
301*a0e7a2b7SDumitru Ceclan 	LTC6373_CHAN(0),
302*a0e7a2b7SDumitru Ceclan };
303*a0e7a2b7SDumitru Ceclan 
30409ac57acSDumitru Ceclan static const struct hmc425a_chip_info hmc425a_chip_info_tbl[] = {
305a76838dfSMichael Hennerich 	[ID_HMC425A] = {
306a76838dfSMichael Hennerich 		.name = "hmc425a",
307a76838dfSMichael Hennerich 		.channels = hmc425a_channels,
308a76838dfSMichael Hennerich 		.num_channels = ARRAY_SIZE(hmc425a_channels),
309a76838dfSMichael Hennerich 		.num_gpios = 6,
310a76838dfSMichael Hennerich 		.gain_min = -31500,
311a76838dfSMichael Hennerich 		.gain_max = 0,
312a76838dfSMichael Hennerich 		.default_gain = -0x40, /* set default gain -31.5db*/
3132edb2257SDumitru Ceclan 		.gain_dB_to_code = hmc425a_gain_dB_to_code,
3142edb2257SDumitru Ceclan 		.code_to_gain_dB = hmc425a_code_to_gain_dB,
315a76838dfSMichael Hennerich 	},
31677865a8fSMichael Hennerich 	[ID_HMC540S] = {
31777865a8fSMichael Hennerich 		.name = "hmc540s",
31877865a8fSMichael Hennerich 		.channels = hmc425a_channels,
31977865a8fSMichael Hennerich 		.num_channels = ARRAY_SIZE(hmc425a_channels),
32077865a8fSMichael Hennerich 		.num_gpios = 4,
32177865a8fSMichael Hennerich 		.gain_min = -15000,
32277865a8fSMichael Hennerich 		.gain_max = 0,
32377865a8fSMichael Hennerich 		.default_gain = -0x10, /* set default gain -15.0db*/
3242edb2257SDumitru Ceclan 		.gain_dB_to_code = hmc540s_gain_dB_to_code,
3252edb2257SDumitru Ceclan 		.code_to_gain_dB = hmc540s_code_to_gain_dB,
32677865a8fSMichael Hennerich 	},
327ed73c4f1SAna-Maria Cusco 	[ID_ADRF5740] = {
328ed73c4f1SAna-Maria Cusco 		.name = "adrf5740",
329ed73c4f1SAna-Maria Cusco 		.channels = hmc425a_channels,
330ed73c4f1SAna-Maria Cusco 		.num_channels = ARRAY_SIZE(hmc425a_channels),
331ed73c4f1SAna-Maria Cusco 		.num_gpios = 4,
332ed73c4f1SAna-Maria Cusco 		.gain_min = -22000,
333ed73c4f1SAna-Maria Cusco 		.gain_max = 0,
334ed73c4f1SAna-Maria Cusco 		.default_gain = 0xF, /* set default gain -22.0db*/
3352edb2257SDumitru Ceclan 		.gain_dB_to_code = adrf5740_gain_dB_to_code,
3362edb2257SDumitru Ceclan 		.code_to_gain_dB = adrf5740_code_to_gain_dB,
337ed73c4f1SAna-Maria Cusco 	},
338*a0e7a2b7SDumitru Ceclan 	[ID_LTC6373] = {
339*a0e7a2b7SDumitru Ceclan 		.name = "ltc6373",
340*a0e7a2b7SDumitru Ceclan 		.channels = ltc6373_channels,
341*a0e7a2b7SDumitru Ceclan 		.num_channels = ARRAY_SIZE(ltc6373_channels),
342*a0e7a2b7SDumitru Ceclan 		.num_gpios = 3,
343*a0e7a2b7SDumitru Ceclan 		.gain_min = -12041, /* gain setting x0.25*/
344*a0e7a2b7SDumitru Ceclan 		.gain_max = 24082,  /* gain setting x16  */
345*a0e7a2b7SDumitru Ceclan 		.default_gain = LTC6373_MIN_GAIN_CODE,
346*a0e7a2b7SDumitru Ceclan 		.powerdown_val = LTC6373_SHUTDOWN,
347*a0e7a2b7SDumitru Ceclan 		.has_powerdown = true,
348*a0e7a2b7SDumitru Ceclan 		.gain_dB_to_code = ltc6373_gain_dB_to_code,
349*a0e7a2b7SDumitru Ceclan 		.code_to_gain_dB = ltc6373_code_to_gain_dB,
350*a0e7a2b7SDumitru Ceclan 	},
351a76838dfSMichael Hennerich };
352a76838dfSMichael Hennerich 
hmc425a_probe(struct platform_device * pdev)353a76838dfSMichael Hennerich static int hmc425a_probe(struct platform_device *pdev)
354a76838dfSMichael Hennerich {
355a76838dfSMichael Hennerich 	struct iio_dev *indio_dev;
356a76838dfSMichael Hennerich 	struct hmc425a_state *st;
357a76838dfSMichael Hennerich 	int ret;
358a76838dfSMichael Hennerich 
359a76838dfSMichael Hennerich 	indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*st));
360a76838dfSMichael Hennerich 	if (!indio_dev)
361a76838dfSMichael Hennerich 		return -ENOMEM;
362a76838dfSMichael Hennerich 
363a76838dfSMichael Hennerich 	st = iio_priv(indio_dev);
364a76838dfSMichael Hennerich 
36509ac57acSDumitru Ceclan 	st->chip_info = device_get_match_data(&pdev->dev);
366a76838dfSMichael Hennerich 	indio_dev->num_channels = st->chip_info->num_channels;
367a76838dfSMichael Hennerich 	indio_dev->channels = st->chip_info->channels;
368a76838dfSMichael Hennerich 	indio_dev->name = st->chip_info->name;
369a76838dfSMichael Hennerich 	st->gain = st->chip_info->default_gain;
370a76838dfSMichael Hennerich 
371a76838dfSMichael Hennerich 	st->gpios = devm_gpiod_get_array(&pdev->dev, "ctrl", GPIOD_OUT_LOW);
3728facae29SKrzysztof Kozlowski 	if (IS_ERR(st->gpios))
3738facae29SKrzysztof Kozlowski 		return dev_err_probe(&pdev->dev, PTR_ERR(st->gpios),
3748facae29SKrzysztof Kozlowski 				     "failed to get gpios\n");
375a76838dfSMichael Hennerich 
376a76838dfSMichael Hennerich 	if (st->gpios->ndescs != st->chip_info->num_gpios) {
377a76838dfSMichael Hennerich 		dev_err(&pdev->dev, "%d GPIOs needed to operate\n",
378a76838dfSMichael Hennerich 			st->chip_info->num_gpios);
379a76838dfSMichael Hennerich 		return -ENODEV;
380a76838dfSMichael Hennerich 	}
381a76838dfSMichael Hennerich 
382563746c2SMatti Vaittinen 	ret = devm_regulator_get_enable(&pdev->dev, "vcc-supply");
383a76838dfSMichael Hennerich 	if (ret)
384a76838dfSMichael Hennerich 		return ret;
385a76838dfSMichael Hennerich 
386a76838dfSMichael Hennerich 	mutex_init(&st->lock);
387a76838dfSMichael Hennerich 
388a76838dfSMichael Hennerich 	indio_dev->info = &hmc425a_info;
389a76838dfSMichael Hennerich 	indio_dev->modes = INDIO_DIRECT_MODE;
390a76838dfSMichael Hennerich 
391*a0e7a2b7SDumitru Ceclan 	if (st->chip_info->has_powerdown) {
392*a0e7a2b7SDumitru Ceclan 		st->powerdown = true;
393*a0e7a2b7SDumitru Ceclan 		hmc425a_write(indio_dev, st->chip_info->powerdown_val);
394*a0e7a2b7SDumitru Ceclan 	} else {
395ed73c4f1SAna-Maria Cusco 		/* Set default gain */
396ed73c4f1SAna-Maria Cusco 		hmc425a_write(indio_dev, st->gain);
397*a0e7a2b7SDumitru Ceclan 	}
398ed73c4f1SAna-Maria Cusco 
399a76838dfSMichael Hennerich 	return devm_iio_device_register(&pdev->dev, indio_dev);
400a76838dfSMichael Hennerich }
401a76838dfSMichael Hennerich 
40209ac57acSDumitru Ceclan /* Match table for of_platform binding */
40309ac57acSDumitru Ceclan static const struct of_device_id hmc425a_of_match[] = {
40409ac57acSDumitru Ceclan 	{ .compatible = "adi,hmc425a",
40509ac57acSDumitru Ceclan 	  .data = &hmc425a_chip_info_tbl[ID_HMC425A]},
40609ac57acSDumitru Ceclan 	{ .compatible = "adi,hmc540s",
40709ac57acSDumitru Ceclan 	  .data = &hmc425a_chip_info_tbl[ID_HMC540S]},
40809ac57acSDumitru Ceclan 	{ .compatible = "adi,adrf5740",
40909ac57acSDumitru Ceclan 	  .data = &hmc425a_chip_info_tbl[ID_ADRF5740]},
410*a0e7a2b7SDumitru Ceclan 	{ .compatible = "adi,ltc6373",
411*a0e7a2b7SDumitru Ceclan 	  .data = &hmc425a_chip_info_tbl[ID_LTC6373]},
41209ac57acSDumitru Ceclan 	{}
41309ac57acSDumitru Ceclan };
41409ac57acSDumitru Ceclan MODULE_DEVICE_TABLE(of, hmc425a_of_match);
41509ac57acSDumitru Ceclan 
416a76838dfSMichael Hennerich static struct platform_driver hmc425a_driver = {
417a76838dfSMichael Hennerich 	.driver = {
418a76838dfSMichael Hennerich 		.name = KBUILD_MODNAME,
419a76838dfSMichael Hennerich 		.of_match_table = hmc425a_of_match,
420a76838dfSMichael Hennerich 	},
421a76838dfSMichael Hennerich 	.probe = hmc425a_probe,
422a76838dfSMichael Hennerich };
423a76838dfSMichael Hennerich module_platform_driver(hmc425a_driver);
424a76838dfSMichael Hennerich 
425a76838dfSMichael Hennerich MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>");
426a76838dfSMichael Hennerich MODULE_DESCRIPTION("Analog Devices HMC425A and similar GPIO control Gain Amplifiers");
427a76838dfSMichael Hennerich MODULE_LICENSE("GPL v2");
428