xref: /linux/drivers/iio/frequency/admfm2000.c (revision cbae17630954cb89f2bdf5bbadfd3aa81ea67283)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * ADMFM2000 Dual Microwave Down Converter
4  *
5  * Copyright 2024 Analog Devices Inc.
6  */
7 
8 #include <linux/device.h>
9 #include <linux/err.h>
10 #include <linux/gpio/consumer.h>
11 #include <linux/iio/iio.h>
12 #include <linux/kernel.h>
13 #include <linux/module.h>
14 #include <linux/platform_device.h>
15 #include <linux/property.h>
16 
17 #define ADMFM2000_MIXER_MODE		0
18 #define ADMFM2000_DIRECT_IF_MODE	1
19 #define ADMFM2000_DSA_GPIOS		5
20 #define ADMFM2000_MODE_GPIOS		2
21 #define ADMFM2000_MAX_GAIN		0
22 #define ADMFM2000_MIN_GAIN		-31000
23 #define ADMFM2000_DEFAULT_GAIN		-0x20
24 
25 struct admfm2000_state {
26 	struct mutex			lock; /* protect sensor state */
27 	struct gpio_desc		*sw1_ch[2];
28 	struct gpio_desc		*sw2_ch[2];
29 	struct gpio_desc		*dsa1_gpios[5];
30 	struct gpio_desc		*dsa2_gpios[5];
31 	u32				gain[2];
32 };
33 
34 static int admfm2000_mode(struct iio_dev *indio_dev, u32 chan, u32 mode)
35 {
36 	struct admfm2000_state *st = iio_priv(indio_dev);
37 	int i;
38 
39 	switch (mode) {
40 	case ADMFM2000_MIXER_MODE:
41 		for (i = 0; i < ADMFM2000_MODE_GPIOS; i++) {
42 			gpiod_set_value_cansleep(st->sw1_ch[i], (chan == 0) ? 1 : 0);
43 			gpiod_set_value_cansleep(st->sw2_ch[i], (chan == 0) ? 0 : 1);
44 		}
45 		return 0;
46 	case ADMFM2000_DIRECT_IF_MODE:
47 		for (i = 0; i < ADMFM2000_MODE_GPIOS; i++) {
48 			gpiod_set_value_cansleep(st->sw1_ch[i], (chan == 0) ? 0 : 1);
49 			gpiod_set_value_cansleep(st->sw2_ch[i], (chan == 0) ? 1 : 0);
50 		}
51 		return 0;
52 	default:
53 		return -EINVAL;
54 	}
55 }
56 
57 static int admfm2000_attenuation(struct iio_dev *indio_dev, u32 chan, u32 value)
58 {
59 	struct admfm2000_state *st = iio_priv(indio_dev);
60 	int i;
61 
62 	switch (chan) {
63 	case 0:
64 		for (i = 0; i < ADMFM2000_DSA_GPIOS; i++)
65 			gpiod_set_value_cansleep(st->dsa1_gpios[i], value & (1 << i));
66 		return 0;
67 	case 1:
68 		for (i = 0; i < ADMFM2000_DSA_GPIOS; i++)
69 			gpiod_set_value_cansleep(st->dsa2_gpios[i], value & (1 << i));
70 		return 0;
71 	default:
72 		return -EINVAL;
73 	}
74 }
75 
76 static int admfm2000_read_raw(struct iio_dev *indio_dev,
77 			      struct iio_chan_spec const *chan, int *val,
78 			      int *val2, long mask)
79 {
80 	struct admfm2000_state *st = iio_priv(indio_dev);
81 	int gain;
82 
83 	switch (mask) {
84 	case IIO_CHAN_INFO_HARDWAREGAIN:
85 		mutex_lock(&st->lock);
86 		gain = ~(st->gain[chan->channel]) * -1000;
87 		*val = gain / 1000;
88 		*val2 = (gain % 1000) * 1000;
89 		mutex_unlock(&st->lock);
90 
91 		return IIO_VAL_INT_PLUS_MICRO_DB;
92 	default:
93 		return -EINVAL;
94 	}
95 }
96 
97 static int admfm2000_write_raw(struct iio_dev *indio_dev,
98 			       struct iio_chan_spec const *chan, int val,
99 			       int val2, long mask)
100 {
101 	struct admfm2000_state *st = iio_priv(indio_dev);
102 	int gain, ret;
103 
104 	if (val < 0)
105 		gain = (val * 1000) - (val2 / 1000);
106 	else
107 		gain = (val * 1000) + (val2 / 1000);
108 
109 	if (gain > ADMFM2000_MAX_GAIN || gain < ADMFM2000_MIN_GAIN)
110 		return -EINVAL;
111 
112 	switch (mask) {
113 	case IIO_CHAN_INFO_HARDWAREGAIN:
114 		mutex_lock(&st->lock);
115 		st->gain[chan->channel] = ~((abs(gain) / 1000) & 0x1F);
116 
117 		ret = admfm2000_attenuation(indio_dev, chan->channel,
118 					    st->gain[chan->channel]);
119 		mutex_unlock(&st->lock);
120 		return ret;
121 	default:
122 		return -EINVAL;
123 	}
124 }
125 
126 static int admfm2000_write_raw_get_fmt(struct iio_dev *indio_dev,
127 				       struct iio_chan_spec const *chan,
128 				       long mask)
129 {
130 	switch (mask) {
131 	case IIO_CHAN_INFO_HARDWAREGAIN:
132 		return IIO_VAL_INT_PLUS_MICRO_DB;
133 	default:
134 		return -EINVAL;
135 	}
136 }
137 
138 static const struct iio_info admfm2000_info = {
139 	.read_raw = &admfm2000_read_raw,
140 	.write_raw = &admfm2000_write_raw,
141 	.write_raw_get_fmt = &admfm2000_write_raw_get_fmt,
142 };
143 
144 #define ADMFM2000_CHAN(_channel) {					\
145 	.type = IIO_VOLTAGE,						\
146 	.output = 1,							\
147 	.indexed = 1,							\
148 	.channel = _channel,						\
149 	.info_mask_separate = BIT(IIO_CHAN_INFO_HARDWAREGAIN),		\
150 }
151 
152 static const struct iio_chan_spec admfm2000_channels[] = {
153 	ADMFM2000_CHAN(0),
154 	ADMFM2000_CHAN(1),
155 };
156 
157 static int admfm2000_channel_config(struct admfm2000_state *st,
158 				    struct iio_dev *indio_dev)
159 {
160 	struct platform_device *pdev = to_platform_device(indio_dev->dev.parent);
161 	struct device *dev = &pdev->dev;
162 	struct gpio_desc **dsa;
163 	struct gpio_desc **sw;
164 	int ret, i;
165 	bool mode;
166 	u32 reg;
167 
168 	device_for_each_child_node_scoped(dev, child) {
169 		ret = fwnode_property_read_u32(child, "reg", &reg);
170 		if (ret)
171 			return dev_err_probe(dev, ret,
172 					     "Failed to get reg property\n");
173 
174 		if (reg >= indio_dev->num_channels)
175 			return dev_err_probe(dev, -EINVAL, "reg bigger than: %d\n",
176 					     indio_dev->num_channels);
177 
178 		if (fwnode_property_present(child, "adi,mixer-mode"))
179 			mode = ADMFM2000_MIXER_MODE;
180 		else
181 			mode = ADMFM2000_DIRECT_IF_MODE;
182 
183 		switch (reg) {
184 		case 0:
185 			sw = st->sw1_ch;
186 			dsa = st->dsa1_gpios;
187 			break;
188 		case 1:
189 			sw = st->sw2_ch;
190 			dsa = st->dsa2_gpios;
191 			break;
192 		default:
193 			return -EINVAL;
194 		}
195 
196 		for (i = 0; i < ADMFM2000_MODE_GPIOS; i++) {
197 			sw[i] = devm_fwnode_gpiod_get_index(dev, child, "switch",
198 							    i, GPIOD_OUT_LOW, NULL);
199 			if (IS_ERR(sw[i]))
200 				return dev_err_probe(dev, PTR_ERR(sw[i]),
201 						     "Failed to get gpios\n");
202 		}
203 
204 		for (i = 0; i < ADMFM2000_DSA_GPIOS; i++) {
205 			dsa[i] = devm_fwnode_gpiod_get_index(dev, child,
206 							     "attenuation", i,
207 							     GPIOD_OUT_LOW, NULL);
208 			if (IS_ERR(dsa[i]))
209 				return dev_err_probe(dev, PTR_ERR(dsa[i]),
210 						     "Failed to get gpios\n");
211 		}
212 
213 		ret = admfm2000_mode(indio_dev, reg, mode);
214 		if (ret)
215 			return ret;
216 	}
217 
218 	return 0;
219 }
220 
221 static int admfm2000_probe(struct platform_device *pdev)
222 {
223 	struct device *dev = &pdev->dev;
224 	struct admfm2000_state *st;
225 	struct iio_dev *indio_dev;
226 	int ret;
227 
228 	indio_dev = devm_iio_device_alloc(dev, sizeof(*st));
229 	if (!indio_dev)
230 		return -ENOMEM;
231 
232 	st = iio_priv(indio_dev);
233 
234 	indio_dev->name = "admfm2000";
235 	indio_dev->num_channels = ARRAY_SIZE(admfm2000_channels);
236 	indio_dev->channels = admfm2000_channels;
237 	indio_dev->info = &admfm2000_info;
238 	indio_dev->modes = INDIO_DIRECT_MODE;
239 
240 	st->gain[0] = ADMFM2000_DEFAULT_GAIN;
241 	st->gain[1] = ADMFM2000_DEFAULT_GAIN;
242 
243 	mutex_init(&st->lock);
244 
245 	ret = admfm2000_channel_config(st, indio_dev);
246 	if (ret)
247 		return ret;
248 
249 	return devm_iio_device_register(dev, indio_dev);
250 }
251 
252 static const struct of_device_id admfm2000_of_match[] = {
253 	{ .compatible = "adi,admfm2000" },
254 	{ }
255 };
256 MODULE_DEVICE_TABLE(of, admfm2000_of_match);
257 
258 static struct platform_driver admfm2000_driver = {
259 	.driver = {
260 		.name = "admfm2000",
261 		.of_match_table = admfm2000_of_match,
262 	},
263 	.probe = admfm2000_probe,
264 };
265 module_platform_driver(admfm2000_driver);
266 
267 MODULE_AUTHOR("Kim Seer Paller <kimseer.paller@analog.com>");
268 MODULE_DESCRIPTION("ADMFM2000 Dual Microwave Down Converter");
269 MODULE_LICENSE("GPL");
270