1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * ADMV8818 driver |
4 | * |
5 | * Copyright 2021 Analog Devices Inc. |
6 | */ |
7 | |
8 | #include <linux/bitfield.h> |
9 | #include <linux/bits.h> |
10 | #include <linux/clk.h> |
11 | #include <linux/device.h> |
12 | #include <linux/iio/iio.h> |
13 | #include <linux/module.h> |
14 | #include <linux/mod_devicetable.h> |
15 | #include <linux/mutex.h> |
16 | #include <linux/notifier.h> |
17 | #include <linux/regmap.h> |
18 | #include <linux/spi/spi.h> |
19 | #include <linux/units.h> |
20 | |
21 | /* ADMV8818 Register Map */ |
22 | #define ADMV8818_REG_SPI_CONFIG_A 0x0 |
23 | #define ADMV8818_REG_SPI_CONFIG_B 0x1 |
24 | #define ADMV8818_REG_CHIPTYPE 0x3 |
25 | #define ADMV8818_REG_PRODUCT_ID_L 0x4 |
26 | #define ADMV8818_REG_PRODUCT_ID_H 0x5 |
27 | #define ADMV8818_REG_FAST_LATCH_POINTER 0x10 |
28 | #define ADMV8818_REG_FAST_LATCH_STOP 0x11 |
29 | #define ADMV8818_REG_FAST_LATCH_START 0x12 |
30 | #define ADMV8818_REG_FAST_LATCH_DIRECTION 0x13 |
31 | #define ADMV8818_REG_FAST_LATCH_STATE 0x14 |
32 | #define ADMV8818_REG_WR0_SW 0x20 |
33 | #define ADMV8818_REG_WR0_FILTER 0x21 |
34 | #define ADMV8818_REG_WR1_SW 0x22 |
35 | #define ADMV8818_REG_WR1_FILTER 0x23 |
36 | #define ADMV8818_REG_WR2_SW 0x24 |
37 | #define ADMV8818_REG_WR2_FILTER 0x25 |
38 | #define ADMV8818_REG_WR3_SW 0x26 |
39 | #define ADMV8818_REG_WR3_FILTER 0x27 |
40 | #define ADMV8818_REG_WR4_SW 0x28 |
41 | #define ADMV8818_REG_WR4_FILTER 0x29 |
42 | #define ADMV8818_REG_LUT0_SW 0x100 |
43 | #define ADMV8818_REG_LUT0_FILTER 0x101 |
44 | #define ADMV8818_REG_LUT127_SW 0x1FE |
45 | #define ADMV8818_REG_LUT127_FILTER 0x1FF |
46 | |
47 | /* ADMV8818_REG_SPI_CONFIG_A Map */ |
48 | #define ADMV8818_SOFTRESET_N_MSK BIT(7) |
49 | #define ADMV8818_LSB_FIRST_N_MSK BIT(6) |
50 | #define ADMV8818_ENDIAN_N_MSK BIT(5) |
51 | #define ADMV8818_SDOACTIVE_N_MSK BIT(4) |
52 | #define ADMV8818_SDOACTIVE_MSK BIT(3) |
53 | #define ADMV8818_ENDIAN_MSK BIT(2) |
54 | #define ADMV8818_LSBFIRST_MSK BIT(1) |
55 | #define ADMV8818_SOFTRESET_MSK BIT(0) |
56 | |
57 | /* ADMV8818_REG_SPI_CONFIG_B Map */ |
58 | #define ADMV8818_SINGLE_INSTRUCTION_MSK BIT(7) |
59 | #define ADMV8818_CSB_STALL_MSK BIT(6) |
60 | #define ADMV8818_MASTER_SLAVE_RB_MSK BIT(5) |
61 | #define ADMV8818_MASTER_SLAVE_TRANSFER_MSK BIT(0) |
62 | |
63 | /* ADMV8818_REG_WR0_SW Map */ |
64 | #define ADMV8818_SW_IN_SET_WR0_MSK BIT(7) |
65 | #define ADMV8818_SW_OUT_SET_WR0_MSK BIT(6) |
66 | #define ADMV8818_SW_IN_WR0_MSK GENMASK(5, 3) |
67 | #define ADMV8818_SW_OUT_WR0_MSK GENMASK(2, 0) |
68 | |
69 | /* ADMV8818_REG_WR0_FILTER Map */ |
70 | #define ADMV8818_HPF_WR0_MSK GENMASK(7, 4) |
71 | #define ADMV8818_LPF_WR0_MSK GENMASK(3, 0) |
72 | |
73 | enum { |
74 | ADMV8818_BW_FREQ, |
75 | ADMV8818_CENTER_FREQ |
76 | }; |
77 | |
78 | enum { |
79 | ADMV8818_AUTO_MODE, |
80 | ADMV8818_MANUAL_MODE, |
81 | ADMV8818_BYPASS_MODE, |
82 | }; |
83 | |
84 | struct admv8818_state { |
85 | struct spi_device *spi; |
86 | struct regmap *regmap; |
87 | struct clk *clkin; |
88 | struct notifier_block nb; |
89 | /* Protect against concurrent accesses to the device and data content*/ |
90 | struct mutex lock; |
91 | unsigned int filter_mode; |
92 | u64 cf_hz; |
93 | }; |
94 | |
95 | static const unsigned long long freq_range_hpf[4][2] = { |
96 | {1750000000ULL, 3550000000ULL}, |
97 | {3400000000ULL, 7250000000ULL}, |
98 | {6600000000, 12000000000}, |
99 | {12500000000, 19900000000} |
100 | }; |
101 | |
102 | static const unsigned long long freq_range_lpf[4][2] = { |
103 | {2050000000ULL, 3850000000ULL}, |
104 | {3350000000ULL, 7250000000ULL}, |
105 | {7000000000, 13000000000}, |
106 | {12550000000, 18500000000} |
107 | }; |
108 | |
109 | static const struct regmap_config admv8818_regmap_config = { |
110 | .reg_bits = 16, |
111 | .val_bits = 8, |
112 | .read_flag_mask = 0x80, |
113 | .max_register = 0x1FF, |
114 | }; |
115 | |
116 | static const char * const admv8818_modes[] = { |
117 | [0] = "auto" , |
118 | [1] = "manual" , |
119 | [2] = "bypass" |
120 | }; |
121 | |
122 | static int __admv8818_hpf_select(struct admv8818_state *st, u64 freq) |
123 | { |
124 | unsigned int hpf_step = 0, hpf_band = 0, i, j; |
125 | u64 freq_step; |
126 | int ret; |
127 | |
128 | if (freq < freq_range_hpf[0][0]) |
129 | goto hpf_write; |
130 | |
131 | if (freq > freq_range_hpf[3][1]) { |
132 | hpf_step = 15; |
133 | hpf_band = 4; |
134 | |
135 | goto hpf_write; |
136 | } |
137 | |
138 | for (i = 0; i < 4; i++) { |
139 | freq_step = div_u64(dividend: (freq_range_hpf[i][1] - |
140 | freq_range_hpf[i][0]), divisor: 15); |
141 | |
142 | if (freq > freq_range_hpf[i][0] && |
143 | (freq < freq_range_hpf[i][1] + freq_step)) { |
144 | hpf_band = i + 1; |
145 | |
146 | for (j = 1; j <= 16; j++) { |
147 | if (freq < (freq_range_hpf[i][0] + (freq_step * j))) { |
148 | hpf_step = j - 1; |
149 | break; |
150 | } |
151 | } |
152 | break; |
153 | } |
154 | } |
155 | |
156 | /* Close HPF frequency gap between 12 and 12.5 GHz */ |
157 | if (freq >= 12000 * HZ_PER_MHZ && freq <= 12500 * HZ_PER_MHZ) { |
158 | hpf_band = 3; |
159 | hpf_step = 15; |
160 | } |
161 | |
162 | hpf_write: |
163 | ret = regmap_update_bits(map: st->regmap, ADMV8818_REG_WR0_SW, |
164 | ADMV8818_SW_IN_SET_WR0_MSK | |
165 | ADMV8818_SW_IN_WR0_MSK, |
166 | FIELD_PREP(ADMV8818_SW_IN_SET_WR0_MSK, 1) | |
167 | FIELD_PREP(ADMV8818_SW_IN_WR0_MSK, hpf_band)); |
168 | if (ret) |
169 | return ret; |
170 | |
171 | return regmap_update_bits(map: st->regmap, ADMV8818_REG_WR0_FILTER, |
172 | ADMV8818_HPF_WR0_MSK, |
173 | FIELD_PREP(ADMV8818_HPF_WR0_MSK, hpf_step)); |
174 | } |
175 | |
176 | static int admv8818_hpf_select(struct admv8818_state *st, u64 freq) |
177 | { |
178 | int ret; |
179 | |
180 | mutex_lock(&st->lock); |
181 | ret = __admv8818_hpf_select(st, freq); |
182 | mutex_unlock(lock: &st->lock); |
183 | |
184 | return ret; |
185 | } |
186 | |
187 | static int __admv8818_lpf_select(struct admv8818_state *st, u64 freq) |
188 | { |
189 | unsigned int lpf_step = 0, lpf_band = 0, i, j; |
190 | u64 freq_step; |
191 | int ret; |
192 | |
193 | if (freq > freq_range_lpf[3][1]) |
194 | goto lpf_write; |
195 | |
196 | if (freq < freq_range_lpf[0][0]) { |
197 | lpf_band = 1; |
198 | |
199 | goto lpf_write; |
200 | } |
201 | |
202 | for (i = 0; i < 4; i++) { |
203 | if (freq > freq_range_lpf[i][0] && freq < freq_range_lpf[i][1]) { |
204 | lpf_band = i + 1; |
205 | freq_step = div_u64(dividend: (freq_range_lpf[i][1] - freq_range_lpf[i][0]), divisor: 15); |
206 | |
207 | for (j = 0; j <= 15; j++) { |
208 | if (freq < (freq_range_lpf[i][0] + (freq_step * j))) { |
209 | lpf_step = j; |
210 | break; |
211 | } |
212 | } |
213 | break; |
214 | } |
215 | } |
216 | |
217 | lpf_write: |
218 | ret = regmap_update_bits(map: st->regmap, ADMV8818_REG_WR0_SW, |
219 | ADMV8818_SW_OUT_SET_WR0_MSK | |
220 | ADMV8818_SW_OUT_WR0_MSK, |
221 | FIELD_PREP(ADMV8818_SW_OUT_SET_WR0_MSK, 1) | |
222 | FIELD_PREP(ADMV8818_SW_OUT_WR0_MSK, lpf_band)); |
223 | if (ret) |
224 | return ret; |
225 | |
226 | return regmap_update_bits(map: st->regmap, ADMV8818_REG_WR0_FILTER, |
227 | ADMV8818_LPF_WR0_MSK, |
228 | FIELD_PREP(ADMV8818_LPF_WR0_MSK, lpf_step)); |
229 | } |
230 | |
231 | static int admv8818_lpf_select(struct admv8818_state *st, u64 freq) |
232 | { |
233 | int ret; |
234 | |
235 | mutex_lock(&st->lock); |
236 | ret = __admv8818_lpf_select(st, freq); |
237 | mutex_unlock(lock: &st->lock); |
238 | |
239 | return ret; |
240 | } |
241 | |
242 | static int admv8818_rfin_band_select(struct admv8818_state *st) |
243 | { |
244 | int ret; |
245 | |
246 | st->cf_hz = clk_get_rate(clk: st->clkin); |
247 | |
248 | mutex_lock(&st->lock); |
249 | |
250 | ret = __admv8818_hpf_select(st, freq: st->cf_hz); |
251 | if (ret) |
252 | goto exit; |
253 | |
254 | ret = __admv8818_lpf_select(st, freq: st->cf_hz); |
255 | exit: |
256 | mutex_unlock(lock: &st->lock); |
257 | return ret; |
258 | } |
259 | |
260 | static int __admv8818_read_hpf_freq(struct admv8818_state *st, u64 *hpf_freq) |
261 | { |
262 | unsigned int data, hpf_band, hpf_state; |
263 | int ret; |
264 | |
265 | ret = regmap_read(map: st->regmap, ADMV8818_REG_WR0_SW, val: &data); |
266 | if (ret) |
267 | return ret; |
268 | |
269 | hpf_band = FIELD_GET(ADMV8818_SW_IN_WR0_MSK, data); |
270 | if (!hpf_band || hpf_band > 4) { |
271 | *hpf_freq = 0; |
272 | return ret; |
273 | } |
274 | |
275 | ret = regmap_read(map: st->regmap, ADMV8818_REG_WR0_FILTER, val: &data); |
276 | if (ret) |
277 | return ret; |
278 | |
279 | hpf_state = FIELD_GET(ADMV8818_HPF_WR0_MSK, data); |
280 | |
281 | *hpf_freq = div_u64(dividend: freq_range_hpf[hpf_band - 1][1] - freq_range_hpf[hpf_band - 1][0], divisor: 15); |
282 | *hpf_freq = freq_range_hpf[hpf_band - 1][0] + (*hpf_freq * hpf_state); |
283 | |
284 | return ret; |
285 | } |
286 | |
287 | static int admv8818_read_hpf_freq(struct admv8818_state *st, u64 *hpf_freq) |
288 | { |
289 | int ret; |
290 | |
291 | mutex_lock(&st->lock); |
292 | ret = __admv8818_read_hpf_freq(st, hpf_freq); |
293 | mutex_unlock(lock: &st->lock); |
294 | |
295 | return ret; |
296 | } |
297 | |
298 | static int __admv8818_read_lpf_freq(struct admv8818_state *st, u64 *lpf_freq) |
299 | { |
300 | unsigned int data, lpf_band, lpf_state; |
301 | int ret; |
302 | |
303 | ret = regmap_read(map: st->regmap, ADMV8818_REG_WR0_SW, val: &data); |
304 | if (ret) |
305 | return ret; |
306 | |
307 | lpf_band = FIELD_GET(ADMV8818_SW_OUT_WR0_MSK, data); |
308 | if (!lpf_band || lpf_band > 4) { |
309 | *lpf_freq = 0; |
310 | return ret; |
311 | } |
312 | |
313 | ret = regmap_read(map: st->regmap, ADMV8818_REG_WR0_FILTER, val: &data); |
314 | if (ret) |
315 | return ret; |
316 | |
317 | lpf_state = FIELD_GET(ADMV8818_LPF_WR0_MSK, data); |
318 | |
319 | *lpf_freq = div_u64(dividend: freq_range_lpf[lpf_band - 1][1] - freq_range_lpf[lpf_band - 1][0], divisor: 15); |
320 | *lpf_freq = freq_range_lpf[lpf_band - 1][0] + (*lpf_freq * lpf_state); |
321 | |
322 | return ret; |
323 | } |
324 | |
325 | static int admv8818_read_lpf_freq(struct admv8818_state *st, u64 *lpf_freq) |
326 | { |
327 | int ret; |
328 | |
329 | mutex_lock(&st->lock); |
330 | ret = __admv8818_read_lpf_freq(st, lpf_freq); |
331 | mutex_unlock(lock: &st->lock); |
332 | |
333 | return ret; |
334 | } |
335 | |
336 | static int admv8818_write_raw(struct iio_dev *indio_dev, |
337 | struct iio_chan_spec const *chan, |
338 | int val, int val2, long info) |
339 | { |
340 | struct admv8818_state *st = iio_priv(indio_dev); |
341 | |
342 | u64 freq = ((u64)val2 << 32 | (u32)val); |
343 | |
344 | switch (info) { |
345 | case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY: |
346 | return admv8818_lpf_select(st, freq); |
347 | case IIO_CHAN_INFO_HIGH_PASS_FILTER_3DB_FREQUENCY: |
348 | return admv8818_hpf_select(st, freq); |
349 | default: |
350 | return -EINVAL; |
351 | } |
352 | } |
353 | |
354 | static int admv8818_read_raw(struct iio_dev *indio_dev, |
355 | struct iio_chan_spec const *chan, |
356 | int *val, int *val2, long info) |
357 | { |
358 | struct admv8818_state *st = iio_priv(indio_dev); |
359 | int ret; |
360 | u64 freq; |
361 | |
362 | switch (info) { |
363 | case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY: |
364 | ret = admv8818_read_lpf_freq(st, lpf_freq: &freq); |
365 | if (ret) |
366 | return ret; |
367 | |
368 | *val = (u32)freq; |
369 | *val2 = (u32)(freq >> 32); |
370 | |
371 | return IIO_VAL_INT_64; |
372 | case IIO_CHAN_INFO_HIGH_PASS_FILTER_3DB_FREQUENCY: |
373 | ret = admv8818_read_hpf_freq(st, hpf_freq: &freq); |
374 | if (ret) |
375 | return ret; |
376 | |
377 | *val = (u32)freq; |
378 | *val2 = (u32)(freq >> 32); |
379 | |
380 | return IIO_VAL_INT_64; |
381 | default: |
382 | return -EINVAL; |
383 | } |
384 | } |
385 | |
386 | static int admv8818_reg_access(struct iio_dev *indio_dev, |
387 | unsigned int reg, |
388 | unsigned int write_val, |
389 | unsigned int *read_val) |
390 | { |
391 | struct admv8818_state *st = iio_priv(indio_dev); |
392 | |
393 | if (read_val) |
394 | return regmap_read(map: st->regmap, reg, val: read_val); |
395 | else |
396 | return regmap_write(map: st->regmap, reg, val: write_val); |
397 | } |
398 | |
399 | static int admv8818_filter_bypass(struct admv8818_state *st) |
400 | { |
401 | int ret; |
402 | |
403 | mutex_lock(&st->lock); |
404 | |
405 | ret = regmap_update_bits(map: st->regmap, ADMV8818_REG_WR0_SW, |
406 | ADMV8818_SW_IN_SET_WR0_MSK | |
407 | ADMV8818_SW_IN_WR0_MSK | |
408 | ADMV8818_SW_OUT_SET_WR0_MSK | |
409 | ADMV8818_SW_OUT_WR0_MSK, |
410 | FIELD_PREP(ADMV8818_SW_IN_SET_WR0_MSK, 1) | |
411 | FIELD_PREP(ADMV8818_SW_IN_WR0_MSK, 0) | |
412 | FIELD_PREP(ADMV8818_SW_OUT_SET_WR0_MSK, 1) | |
413 | FIELD_PREP(ADMV8818_SW_OUT_WR0_MSK, 0)); |
414 | if (ret) |
415 | goto exit; |
416 | |
417 | ret = regmap_update_bits(map: st->regmap, ADMV8818_REG_WR0_FILTER, |
418 | ADMV8818_HPF_WR0_MSK | |
419 | ADMV8818_LPF_WR0_MSK, |
420 | FIELD_PREP(ADMV8818_HPF_WR0_MSK, 0) | |
421 | FIELD_PREP(ADMV8818_LPF_WR0_MSK, 0)); |
422 | |
423 | exit: |
424 | mutex_unlock(lock: &st->lock); |
425 | |
426 | return ret; |
427 | } |
428 | |
429 | static int admv8818_get_mode(struct iio_dev *indio_dev, |
430 | const struct iio_chan_spec *chan) |
431 | { |
432 | struct admv8818_state *st = iio_priv(indio_dev); |
433 | |
434 | return st->filter_mode; |
435 | } |
436 | |
437 | static int admv8818_set_mode(struct iio_dev *indio_dev, |
438 | const struct iio_chan_spec *chan, |
439 | unsigned int mode) |
440 | { |
441 | struct admv8818_state *st = iio_priv(indio_dev); |
442 | int ret = 0; |
443 | |
444 | if (!st->clkin) { |
445 | if (mode == ADMV8818_MANUAL_MODE) |
446 | goto set_mode; |
447 | |
448 | if (mode == ADMV8818_BYPASS_MODE) { |
449 | ret = admv8818_filter_bypass(st); |
450 | if (ret) |
451 | return ret; |
452 | |
453 | goto set_mode; |
454 | } |
455 | |
456 | return -EINVAL; |
457 | } |
458 | |
459 | switch (mode) { |
460 | case ADMV8818_AUTO_MODE: |
461 | if (st->filter_mode == ADMV8818_AUTO_MODE) |
462 | return 0; |
463 | |
464 | ret = clk_prepare_enable(clk: st->clkin); |
465 | if (ret) |
466 | return ret; |
467 | |
468 | ret = clk_notifier_register(clk: st->clkin, nb: &st->nb); |
469 | if (ret) { |
470 | clk_disable_unprepare(clk: st->clkin); |
471 | |
472 | return ret; |
473 | } |
474 | |
475 | break; |
476 | case ADMV8818_MANUAL_MODE: |
477 | case ADMV8818_BYPASS_MODE: |
478 | if (st->filter_mode == ADMV8818_AUTO_MODE) { |
479 | clk_disable_unprepare(clk: st->clkin); |
480 | |
481 | ret = clk_notifier_unregister(clk: st->clkin, nb: &st->nb); |
482 | if (ret) |
483 | return ret; |
484 | } |
485 | |
486 | if (mode == ADMV8818_BYPASS_MODE) { |
487 | ret = admv8818_filter_bypass(st); |
488 | if (ret) |
489 | return ret; |
490 | } |
491 | |
492 | break; |
493 | default: |
494 | return -EINVAL; |
495 | } |
496 | |
497 | set_mode: |
498 | st->filter_mode = mode; |
499 | |
500 | return ret; |
501 | } |
502 | |
503 | static const struct iio_info admv8818_info = { |
504 | .write_raw = admv8818_write_raw, |
505 | .read_raw = admv8818_read_raw, |
506 | .debugfs_reg_access = &admv8818_reg_access, |
507 | }; |
508 | |
509 | static const struct iio_enum admv8818_mode_enum = { |
510 | .items = admv8818_modes, |
511 | .num_items = ARRAY_SIZE(admv8818_modes), |
512 | .get = admv8818_get_mode, |
513 | .set = admv8818_set_mode, |
514 | }; |
515 | |
516 | static const struct iio_chan_spec_ext_info admv8818_ext_info[] = { |
517 | IIO_ENUM("filter_mode" , IIO_SHARED_BY_ALL, &admv8818_mode_enum), |
518 | IIO_ENUM_AVAILABLE("filter_mode" , IIO_SHARED_BY_ALL, &admv8818_mode_enum), |
519 | { }, |
520 | }; |
521 | |
522 | #define ADMV8818_CHAN(_channel) { \ |
523 | .type = IIO_ALTVOLTAGE, \ |
524 | .output = 1, \ |
525 | .indexed = 1, \ |
526 | .channel = _channel, \ |
527 | .info_mask_separate = \ |
528 | BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY) | \ |
529 | BIT(IIO_CHAN_INFO_HIGH_PASS_FILTER_3DB_FREQUENCY) \ |
530 | } |
531 | |
532 | #define ADMV8818_CHAN_BW_CF(_channel, _admv8818_ext_info) { \ |
533 | .type = IIO_ALTVOLTAGE, \ |
534 | .output = 1, \ |
535 | .indexed = 1, \ |
536 | .channel = _channel, \ |
537 | .ext_info = _admv8818_ext_info, \ |
538 | } |
539 | |
540 | static const struct iio_chan_spec admv8818_channels[] = { |
541 | ADMV8818_CHAN(0), |
542 | ADMV8818_CHAN_BW_CF(0, admv8818_ext_info), |
543 | }; |
544 | |
545 | static int admv8818_freq_change(struct notifier_block *nb, unsigned long action, void *data) |
546 | { |
547 | struct admv8818_state *st = container_of(nb, struct admv8818_state, nb); |
548 | |
549 | if (action == POST_RATE_CHANGE) |
550 | return notifier_from_errno(err: admv8818_rfin_band_select(st)); |
551 | |
552 | return NOTIFY_OK; |
553 | } |
554 | |
555 | static void admv8818_clk_notifier_unreg(void *data) |
556 | { |
557 | struct admv8818_state *st = data; |
558 | |
559 | if (st->filter_mode == 0) |
560 | clk_notifier_unregister(clk: st->clkin, nb: &st->nb); |
561 | } |
562 | |
563 | static void admv8818_clk_disable(void *data) |
564 | { |
565 | struct admv8818_state *st = data; |
566 | |
567 | if (st->filter_mode == 0) |
568 | clk_disable_unprepare(clk: st->clkin); |
569 | } |
570 | |
571 | static int admv8818_init(struct admv8818_state *st) |
572 | { |
573 | int ret; |
574 | struct spi_device *spi = st->spi; |
575 | unsigned int chip_id; |
576 | |
577 | ret = regmap_update_bits(map: st->regmap, ADMV8818_REG_SPI_CONFIG_A, |
578 | ADMV8818_SOFTRESET_N_MSK | |
579 | ADMV8818_SOFTRESET_MSK, |
580 | FIELD_PREP(ADMV8818_SOFTRESET_N_MSK, 1) | |
581 | FIELD_PREP(ADMV8818_SOFTRESET_MSK, 1)); |
582 | if (ret) { |
583 | dev_err(&spi->dev, "ADMV8818 Soft Reset failed.\n" ); |
584 | return ret; |
585 | } |
586 | |
587 | ret = regmap_update_bits(map: st->regmap, ADMV8818_REG_SPI_CONFIG_A, |
588 | ADMV8818_SDOACTIVE_N_MSK | |
589 | ADMV8818_SDOACTIVE_MSK, |
590 | FIELD_PREP(ADMV8818_SDOACTIVE_N_MSK, 1) | |
591 | FIELD_PREP(ADMV8818_SDOACTIVE_MSK, 1)); |
592 | if (ret) { |
593 | dev_err(&spi->dev, "ADMV8818 SDO Enable failed.\n" ); |
594 | return ret; |
595 | } |
596 | |
597 | ret = regmap_read(map: st->regmap, ADMV8818_REG_CHIPTYPE, val: &chip_id); |
598 | if (ret) { |
599 | dev_err(&spi->dev, "ADMV8818 Chip ID read failed.\n" ); |
600 | return ret; |
601 | } |
602 | |
603 | if (chip_id != 0x1) { |
604 | dev_err(&spi->dev, "ADMV8818 Invalid Chip ID.\n" ); |
605 | return -EINVAL; |
606 | } |
607 | |
608 | ret = regmap_update_bits(map: st->regmap, ADMV8818_REG_SPI_CONFIG_B, |
609 | ADMV8818_SINGLE_INSTRUCTION_MSK, |
610 | FIELD_PREP(ADMV8818_SINGLE_INSTRUCTION_MSK, 1)); |
611 | if (ret) { |
612 | dev_err(&spi->dev, "ADMV8818 Single Instruction failed.\n" ); |
613 | return ret; |
614 | } |
615 | |
616 | if (st->clkin) |
617 | return admv8818_rfin_band_select(st); |
618 | else |
619 | return 0; |
620 | } |
621 | |
622 | static int admv8818_clk_setup(struct admv8818_state *st) |
623 | { |
624 | struct spi_device *spi = st->spi; |
625 | int ret; |
626 | |
627 | st->clkin = devm_clk_get_optional(dev: &spi->dev, id: "rf_in" ); |
628 | if (IS_ERR(ptr: st->clkin)) |
629 | return dev_err_probe(dev: &spi->dev, err: PTR_ERR(ptr: st->clkin), |
630 | fmt: "failed to get the input clock\n" ); |
631 | else if (!st->clkin) |
632 | return 0; |
633 | |
634 | ret = clk_prepare_enable(clk: st->clkin); |
635 | if (ret) |
636 | return ret; |
637 | |
638 | ret = devm_add_action_or_reset(&spi->dev, admv8818_clk_disable, st); |
639 | if (ret) |
640 | return ret; |
641 | |
642 | st->nb.notifier_call = admv8818_freq_change; |
643 | ret = clk_notifier_register(clk: st->clkin, nb: &st->nb); |
644 | if (ret < 0) |
645 | return ret; |
646 | |
647 | return devm_add_action_or_reset(&spi->dev, admv8818_clk_notifier_unreg, st); |
648 | } |
649 | |
650 | static int admv8818_probe(struct spi_device *spi) |
651 | { |
652 | struct iio_dev *indio_dev; |
653 | struct regmap *regmap; |
654 | struct admv8818_state *st; |
655 | int ret; |
656 | |
657 | indio_dev = devm_iio_device_alloc(parent: &spi->dev, sizeof_priv: sizeof(*st)); |
658 | if (!indio_dev) |
659 | return -ENOMEM; |
660 | |
661 | regmap = devm_regmap_init_spi(spi, &admv8818_regmap_config); |
662 | if (IS_ERR(ptr: regmap)) |
663 | return PTR_ERR(ptr: regmap); |
664 | |
665 | st = iio_priv(indio_dev); |
666 | st->regmap = regmap; |
667 | |
668 | indio_dev->info = &admv8818_info; |
669 | indio_dev->name = "admv8818" ; |
670 | indio_dev->channels = admv8818_channels; |
671 | indio_dev->num_channels = ARRAY_SIZE(admv8818_channels); |
672 | |
673 | st->spi = spi; |
674 | |
675 | ret = admv8818_clk_setup(st); |
676 | if (ret) |
677 | return ret; |
678 | |
679 | mutex_init(&st->lock); |
680 | |
681 | ret = admv8818_init(st); |
682 | if (ret) |
683 | return ret; |
684 | |
685 | return devm_iio_device_register(&spi->dev, indio_dev); |
686 | } |
687 | |
688 | static const struct spi_device_id admv8818_id[] = { |
689 | { "admv8818" , 0 }, |
690 | {} |
691 | }; |
692 | MODULE_DEVICE_TABLE(spi, admv8818_id); |
693 | |
694 | static const struct of_device_id admv8818_of_match[] = { |
695 | { .compatible = "adi,admv8818" }, |
696 | {} |
697 | }; |
698 | MODULE_DEVICE_TABLE(of, admv8818_of_match); |
699 | |
700 | static struct spi_driver admv8818_driver = { |
701 | .driver = { |
702 | .name = "admv8818" , |
703 | .of_match_table = admv8818_of_match, |
704 | }, |
705 | .probe = admv8818_probe, |
706 | .id_table = admv8818_id, |
707 | }; |
708 | module_spi_driver(admv8818_driver); |
709 | |
710 | MODULE_AUTHOR("Antoniu Miclaus <antoniu.miclaus@analog.com" ); |
711 | MODULE_DESCRIPTION("Analog Devices ADMV8818" ); |
712 | MODULE_LICENSE("GPL v2" ); |
713 | |