1 // SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause
5 * Copyright 2021 Analog Devices Inc.
8 #include <linux/bitfield.h>
9 #include <linux/iio/iio.h>
10 #include <linux/iio/sysfs.h>
11 #include <linux/module.h>
12 #include <linux/regmap.h>
13 #include <linux/spi/spi.h>
14 #include <linux/units.h>
16 #include <linux/unaligned.h>
18 /* ADMV4420 Register Map */
19 #define ADMV4420_SPI_CONFIG_1 0x00
20 #define ADMV4420_SPI_CONFIG_2 0x01
21 #define ADMV4420_CHIPTYPE 0x03
22 #define ADMV4420_PRODUCT_ID_L 0x04
23 #define ADMV4420_PRODUCT_ID_H 0x05
24 #define ADMV4420_SCRATCHPAD 0x0A
25 #define ADMV4420_SPI_REV 0x0B
26 #define ADMV4420_ENABLES 0x103
27 #define ADMV4420_SDO_LEVEL 0x108
28 #define ADMV4420_INT_L 0x200
29 #define ADMV4420_INT_H 0x201
30 #define ADMV4420_FRAC_L 0x202
31 #define ADMV4420_FRAC_M 0x203
32 #define ADMV4420_FRAC_H 0x204
33 #define ADMV4420_MOD_L 0x208
34 #define ADMV4420_MOD_M 0x209
35 #define ADMV4420_MOD_H 0x20A
36 #define ADMV4420_R_DIV_L 0x20C
37 #define ADMV4420_R_DIV_H 0x20D
38 #define ADMV4420_REFERENCE 0x20E
39 #define ADMV4420_VCO_DATA_READBACK1 0x211
40 #define ADMV4420_VCO_DATA_READBACK2 0x212
41 #define ADMV4420_PLL_MUX_SEL 0x213
42 #define ADMV4420_LOCK_DETECT 0x214
43 #define ADMV4420_BAND_SELECT 0x215
44 #define ADMV4420_VCO_ALC_TIMEOUT 0x216
45 #define ADMV4420_VCO_MANUAL 0x217
46 #define ADMV4420_ALC 0x219
47 #define ADMV4420_VCO_TIMEOUT1 0x21C
48 #define ADMV4420_VCO_TIMEOUT2 0x21D
49 #define ADMV4420_VCO_BAND_DIV 0x21E
50 #define ADMV4420_VCO_READBACK_SEL 0x21F
51 #define ADMV4420_AUTOCAL 0x226
52 #define ADMV4420_CP_STATE 0x22C
53 #define ADMV4420_CP_BLEED_EN 0x22D
54 #define ADMV4420_CP_CURRENT 0x22E
55 #define ADMV4420_CP_BLEED 0x22F
57 #define ADMV4420_SPI_CONFIG_1_SDOACTIVE (BIT(4) | BIT(3))
58 #define ADMV4420_SPI_CONFIG_1_ENDIAN (BIT(5) | BIT(2))
59 #define ADMV4420_SPI_CONFIG_1_SOFTRESET (BIT(7) | BIT(1))
61 #define ADMV4420_REFERENCE_DIVIDE_BY_2_MASK BIT(0)
62 #define ADMV4420_REFERENCE_MODE_MASK BIT(1)
63 #define ADMV4420_REFERENCE_DOUBLER_MASK BIT(2)
65 #define ADMV4420_REF_DIVIDER_MAX_VAL GENMASK(9, 0)
66 #define ADMV4420_N_COUNTER_INT_MAX GENMASK(15, 0)
67 #define ADMV4420_N_COUNTER_FRAC_MAX GENMASK(23, 0)
68 #define ADMV4420_N_COUNTER_MOD_MAX GENMASK(23, 0)
70 #define ENABLE_PLL BIT(6)
71 #define ENABLE_LO BIT(5)
72 #define ENABLE_VCO BIT(3)
73 #define ENABLE_IFAMP BIT(2)
74 #define ENABLE_MIXER BIT(1)
75 #define ENABLE_LNA BIT(0)
77 #define ADMV4420_SCRATCH_PAD_VAL_1 0xAD
78 #define ADMV4420_SCRATCH_PAD_VAL_2 0xEA
80 #define ADMV4420_REF_FREQ_HZ 50000000
81 #define MAX_N_COUNTER 655360UL
82 #define MAX_R_DIVIDER 1024
83 #define ADMV4420_DEFAULT_LO_FREQ_HZ 16750000000ULL
85 enum admv4420_mux_sel
{
87 ADMV4420_LOCK_DTCT
= 1,
88 ADMV4420_R_COUNTER_PER_2
= 4,
89 ADMV4420_N_CONUTER_PER_2
= 5,
93 struct admv4420_reference_block
{
96 bool ref_single_ended
;
100 struct admv4420_n_counter
{
107 struct admv4420_state
{
108 struct spi_device
*spi
;
109 struct regmap
*regmap
;
112 struct admv4420_reference_block ref_block
;
113 struct admv4420_n_counter n_counter
;
114 enum admv4420_mux_sel mux_sel
;
116 u8 transf_buf
[4] __aligned(IIO_DMA_MINALIGN
);
119 static const struct regmap_config admv4420_regmap_config
= {
122 .read_flag_mask
= BIT(7),
125 static int admv4420_reg_access(struct iio_dev
*indio_dev
,
126 u32 reg
, u32 writeval
,
129 struct admv4420_state
*st
= iio_priv(indio_dev
);
132 return regmap_read(st
->regmap
, reg
, readval
);
134 return regmap_write(st
->regmap
, reg
, writeval
);
137 static int admv4420_set_n_counter(struct admv4420_state
*st
, u32 int_val
,
138 u32 frac_val
, u32 mod_val
)
142 put_unaligned_le32(frac_val
, st
->transf_buf
);
143 ret
= regmap_bulk_write(st
->regmap
, ADMV4420_FRAC_L
, st
->transf_buf
, 3);
147 put_unaligned_le32(mod_val
, st
->transf_buf
);
148 ret
= regmap_bulk_write(st
->regmap
, ADMV4420_MOD_L
, st
->transf_buf
, 3);
152 put_unaligned_le32(int_val
, st
->transf_buf
);
153 return regmap_bulk_write(st
->regmap
, ADMV4420_INT_L
, st
->transf_buf
, 2);
156 static int admv4420_read_raw(struct iio_dev
*indio_dev
,
157 struct iio_chan_spec
const *chan
,
158 int *val
, int *val2
, long info
)
160 struct admv4420_state
*st
= iio_priv(indio_dev
);
163 case IIO_CHAN_INFO_FREQUENCY
:
165 *val
= div_u64_rem(st
->lo_freq_hz
, MICRO
, val2
);
167 return IIO_VAL_INT_PLUS_MICRO
;
173 static const struct iio_info admv4420_info
= {
174 .read_raw
= admv4420_read_raw
,
175 .debugfs_reg_access
= &admv4420_reg_access
,
178 static const struct iio_chan_spec admv4420_channels
[] = {
180 .type
= IIO_ALTVOLTAGE
,
184 .info_mask_separate
= BIT(IIO_CHAN_INFO_FREQUENCY
),
188 static void admv4420_fw_parse(struct admv4420_state
*st
)
190 struct device
*dev
= &st
->spi
->dev
;
194 ret
= device_property_read_u32(dev
, "adi,lo-freq-khz", &tmp
);
196 st
->lo_freq_hz
= (u64
)tmp
* KILO
;
198 st
->ref_block
.ref_single_ended
= device_property_read_bool(dev
,
199 "adi,ref-ext-single-ended-en");
202 static inline uint64_t admv4420_calc_pfd_vco(struct admv4420_state
*st
)
204 return div_u64(st
->vco_freq_hz
* 10, st
->n_counter
.n_counter
);
207 static inline uint32_t admv4420_calc_pfd_ref(struct admv4420_state
*st
)
210 u8 doubler
, divide_by_2
;
212 doubler
= st
->ref_block
.doubler_en
? 2 : 1;
213 divide_by_2
= st
->ref_block
.divide_by_2_en
? 2 : 1;
214 tmp
= ADMV4420_REF_FREQ_HZ
* doubler
;
216 return (tmp
/ (st
->ref_block
.divider
* divide_by_2
));
219 static int admv4420_calc_parameters(struct admv4420_state
*st
)
221 u64 pfd_ref
, pfd_vco
;
222 bool sol_found
= false;
224 st
->ref_block
.doubler_en
= false;
225 st
->ref_block
.divide_by_2_en
= false;
226 st
->vco_freq_hz
= div_u64(st
->lo_freq_hz
, 2);
228 for (st
->ref_block
.divider
= 1; st
->ref_block
.divider
< MAX_R_DIVIDER
;
229 st
->ref_block
.divider
++) {
230 pfd_ref
= admv4420_calc_pfd_ref(st
);
231 for (st
->n_counter
.n_counter
= 1; st
->n_counter
.n_counter
< MAX_N_COUNTER
;
232 st
->n_counter
.n_counter
++) {
233 pfd_vco
= admv4420_calc_pfd_vco(st
);
234 if (pfd_ref
== pfd_vco
) {
243 st
->n_counter
.n_counter
= 1;
248 st
->n_counter
.int_val
= div_u64_rem(st
->n_counter
.n_counter
, 10, &st
->n_counter
.frac_val
);
249 st
->n_counter
.mod_val
= 10;
254 static int admv4420_setup(struct iio_dev
*indio_dev
)
256 struct admv4420_state
*st
= iio_priv(indio_dev
);
257 struct device
*dev
= indio_dev
->dev
.parent
;
261 ret
= regmap_write(st
->regmap
, ADMV4420_SPI_CONFIG_1
,
262 ADMV4420_SPI_CONFIG_1_SOFTRESET
);
266 ret
= regmap_write(st
->regmap
, ADMV4420_SPI_CONFIG_1
,
267 ADMV4420_SPI_CONFIG_1_SDOACTIVE
|
268 ADMV4420_SPI_CONFIG_1_ENDIAN
);
272 ret
= regmap_write(st
->regmap
,
274 ADMV4420_SCRATCH_PAD_VAL_1
);
278 ret
= regmap_read(st
->regmap
, ADMV4420_SCRATCHPAD
, &val
);
282 if (val
!= ADMV4420_SCRATCH_PAD_VAL_1
) {
283 dev_err(dev
, "Failed ADMV4420 to read/write scratchpad %x ", val
);
287 ret
= regmap_write(st
->regmap
,
289 ADMV4420_SCRATCH_PAD_VAL_2
);
293 ret
= regmap_read(st
->regmap
, ADMV4420_SCRATCHPAD
, &val
);
297 if (val
!= ADMV4420_SCRATCH_PAD_VAL_2
) {
298 dev_err(dev
, "Failed to read/write scratchpad %x ", val
);
302 st
->mux_sel
= ADMV4420_LOCK_DTCT
;
303 st
->lo_freq_hz
= ADMV4420_DEFAULT_LO_FREQ_HZ
;
305 admv4420_fw_parse(st
);
307 ret
= admv4420_calc_parameters(st
);
309 dev_err(dev
, "Failed calc parameters for %lld ", st
->vco_freq_hz
);
313 ret
= regmap_write(st
->regmap
, ADMV4420_R_DIV_L
,
314 FIELD_GET(0xFF, st
->ref_block
.divider
));
318 ret
= regmap_write(st
->regmap
, ADMV4420_R_DIV_H
,
319 FIELD_GET(0xFF00, st
->ref_block
.divider
));
323 ret
= regmap_write(st
->regmap
, ADMV4420_REFERENCE
,
324 st
->ref_block
.divide_by_2_en
|
325 FIELD_PREP(ADMV4420_REFERENCE_MODE_MASK
, st
->ref_block
.ref_single_ended
) |
326 FIELD_PREP(ADMV4420_REFERENCE_DOUBLER_MASK
, st
->ref_block
.doubler_en
));
330 ret
= admv4420_set_n_counter(st
, st
->n_counter
.int_val
,
331 st
->n_counter
.frac_val
,
332 st
->n_counter
.mod_val
);
336 ret
= regmap_write(st
->regmap
, ADMV4420_PLL_MUX_SEL
, st
->mux_sel
);
340 return regmap_write(st
->regmap
, ADMV4420_ENABLES
,
341 ENABLE_PLL
| ENABLE_LO
| ENABLE_VCO
|
342 ENABLE_IFAMP
| ENABLE_MIXER
| ENABLE_LNA
);
345 static int admv4420_probe(struct spi_device
*spi
)
347 struct iio_dev
*indio_dev
;
348 struct admv4420_state
*st
;
349 struct regmap
*regmap
;
352 indio_dev
= devm_iio_device_alloc(&spi
->dev
, sizeof(*st
));
356 regmap
= devm_regmap_init_spi(spi
, &admv4420_regmap_config
);
358 return dev_err_probe(&spi
->dev
, PTR_ERR(regmap
),
359 "Failed to initializing spi regmap\n");
361 st
= iio_priv(indio_dev
);
365 indio_dev
->name
= "admv4420";
366 indio_dev
->info
= &admv4420_info
;
367 indio_dev
->channels
= admv4420_channels
;
368 indio_dev
->num_channels
= ARRAY_SIZE(admv4420_channels
);
370 ret
= admv4420_setup(indio_dev
);
372 dev_err(&spi
->dev
, "Setup ADMV4420 failed (%d)\n", ret
);
376 return devm_iio_device_register(&spi
->dev
, indio_dev
);
379 static const struct of_device_id admv4420_of_match
[] = {
380 { .compatible
= "adi,admv4420" },
384 MODULE_DEVICE_TABLE(of
, admv4420_of_match
);
386 static struct spi_driver admv4420_driver
= {
389 .of_match_table
= admv4420_of_match
,
391 .probe
= admv4420_probe
,
394 module_spi_driver(admv4420_driver
);
396 MODULE_AUTHOR("Cristian Pop <cristian.pop@analog.com>");
397 MODULE_DESCRIPTION("Analog Devices ADMV44200 K Band Downconverter");
398 MODULE_LICENSE("Dual BSD/GPL");