0001
0002
0003
0004
0005
0006
0007
0008 #include <linux/bitfield.h>
0009 #include <linux/iio/iio.h>
0010 #include <linux/iio/sysfs.h>
0011 #include <linux/module.h>
0012 #include <linux/regmap.h>
0013 #include <linux/spi/spi.h>
0014 #include <linux/units.h>
0015
0016 #include <asm/unaligned.h>
0017
0018
0019 #define ADMV4420_SPI_CONFIG_1 0x00
0020 #define ADMV4420_SPI_CONFIG_2 0x01
0021 #define ADMV4420_CHIPTYPE 0x03
0022 #define ADMV4420_PRODUCT_ID_L 0x04
0023 #define ADMV4420_PRODUCT_ID_H 0x05
0024 #define ADMV4420_SCRATCHPAD 0x0A
0025 #define ADMV4420_SPI_REV 0x0B
0026 #define ADMV4420_ENABLES 0x103
0027 #define ADMV4420_SDO_LEVEL 0x108
0028 #define ADMV4420_INT_L 0x200
0029 #define ADMV4420_INT_H 0x201
0030 #define ADMV4420_FRAC_L 0x202
0031 #define ADMV4420_FRAC_M 0x203
0032 #define ADMV4420_FRAC_H 0x204
0033 #define ADMV4420_MOD_L 0x208
0034 #define ADMV4420_MOD_M 0x209
0035 #define ADMV4420_MOD_H 0x20A
0036 #define ADMV4420_R_DIV_L 0x20C
0037 #define ADMV4420_R_DIV_H 0x20D
0038 #define ADMV4420_REFERENCE 0x20E
0039 #define ADMV4420_VCO_DATA_READBACK1 0x211
0040 #define ADMV4420_VCO_DATA_READBACK2 0x212
0041 #define ADMV4420_PLL_MUX_SEL 0x213
0042 #define ADMV4420_LOCK_DETECT 0x214
0043 #define ADMV4420_BAND_SELECT 0x215
0044 #define ADMV4420_VCO_ALC_TIMEOUT 0x216
0045 #define ADMV4420_VCO_MANUAL 0x217
0046 #define ADMV4420_ALC 0x219
0047 #define ADMV4420_VCO_TIMEOUT1 0x21C
0048 #define ADMV4420_VCO_TIMEOUT2 0x21D
0049 #define ADMV4420_VCO_BAND_DIV 0x21E
0050 #define ADMV4420_VCO_READBACK_SEL 0x21F
0051 #define ADMV4420_AUTOCAL 0x226
0052 #define ADMV4420_CP_STATE 0x22C
0053 #define ADMV4420_CP_BLEED_EN 0x22D
0054 #define ADMV4420_CP_CURRENT 0x22E
0055 #define ADMV4420_CP_BLEED 0x22F
0056
0057 #define ADMV4420_SPI_CONFIG_1_SDOACTIVE (BIT(4) | BIT(3))
0058 #define ADMV4420_SPI_CONFIG_1_ENDIAN (BIT(5) | BIT(2))
0059 #define ADMV4420_SPI_CONFIG_1_SOFTRESET (BIT(7) | BIT(1))
0060
0061 #define ADMV4420_REFERENCE_DIVIDE_BY_2_MASK BIT(0)
0062 #define ADMV4420_REFERENCE_MODE_MASK BIT(1)
0063 #define ADMV4420_REFERENCE_DOUBLER_MASK BIT(2)
0064
0065 #define ADMV4420_REF_DIVIDER_MAX_VAL GENMASK(9, 0)
0066 #define ADMV4420_N_COUNTER_INT_MAX GENMASK(15, 0)
0067 #define ADMV4420_N_COUNTER_FRAC_MAX GENMASK(23, 0)
0068 #define ADMV4420_N_COUNTER_MOD_MAX GENMASK(23, 0)
0069
0070 #define ENABLE_PLL BIT(6)
0071 #define ENABLE_LO BIT(5)
0072 #define ENABLE_VCO BIT(3)
0073 #define ENABLE_IFAMP BIT(2)
0074 #define ENABLE_MIXER BIT(1)
0075 #define ENABLE_LNA BIT(0)
0076
0077 #define ADMV4420_SCRATCH_PAD_VAL_1 0xAD
0078 #define ADMV4420_SCRATCH_PAD_VAL_2 0xEA
0079
0080 #define ADMV4420_REF_FREQ_HZ 50000000
0081 #define MAX_N_COUNTER 655360UL
0082 #define MAX_R_DIVIDER 1024
0083 #define ADMV4420_DEFAULT_LO_FREQ_HZ 16750000000ULL
0084
0085 enum admv4420_mux_sel {
0086 ADMV4420_LOW = 0,
0087 ADMV4420_LOCK_DTCT = 1,
0088 ADMV4420_R_COUNTER_PER_2 = 4,
0089 ADMV4420_N_CONUTER_PER_2 = 5,
0090 ADMV4420_HIGH = 8,
0091 };
0092
0093 struct admv4420_reference_block {
0094 bool doubler_en;
0095 bool divide_by_2_en;
0096 bool ref_single_ended;
0097 u32 divider;
0098 };
0099
0100 struct admv4420_n_counter {
0101 u32 int_val;
0102 u32 frac_val;
0103 u32 mod_val;
0104 u32 n_counter;
0105 };
0106
0107 struct admv4420_state {
0108 struct spi_device *spi;
0109 struct regmap *regmap;
0110 u64 vco_freq_hz;
0111 u64 lo_freq_hz;
0112 struct admv4420_reference_block ref_block;
0113 struct admv4420_n_counter n_counter;
0114 enum admv4420_mux_sel mux_sel;
0115 struct mutex lock;
0116 u8 transf_buf[4] __aligned(IIO_DMA_MINALIGN);
0117 };
0118
0119 static const struct regmap_config admv4420_regmap_config = {
0120 .reg_bits = 16,
0121 .val_bits = 8,
0122 .read_flag_mask = BIT(7),
0123 };
0124
0125 static int admv4420_reg_access(struct iio_dev *indio_dev,
0126 u32 reg, u32 writeval,
0127 u32 *readval)
0128 {
0129 struct admv4420_state *st = iio_priv(indio_dev);
0130
0131 if (readval)
0132 return regmap_read(st->regmap, reg, readval);
0133 else
0134 return regmap_write(st->regmap, reg, writeval);
0135 }
0136
0137 static int admv4420_set_n_counter(struct admv4420_state *st, u32 int_val,
0138 u32 frac_val, u32 mod_val)
0139 {
0140 int ret;
0141
0142 put_unaligned_le32(frac_val, st->transf_buf);
0143 ret = regmap_bulk_write(st->regmap, ADMV4420_FRAC_L, st->transf_buf, 3);
0144 if (ret)
0145 return ret;
0146
0147 put_unaligned_le32(mod_val, st->transf_buf);
0148 ret = regmap_bulk_write(st->regmap, ADMV4420_MOD_L, st->transf_buf, 3);
0149 if (ret)
0150 return ret;
0151
0152 put_unaligned_le32(int_val, st->transf_buf);
0153 return regmap_bulk_write(st->regmap, ADMV4420_INT_L, st->transf_buf, 2);
0154 }
0155
0156 static int admv4420_read_raw(struct iio_dev *indio_dev,
0157 struct iio_chan_spec const *chan,
0158 int *val, int *val2, long info)
0159 {
0160 struct admv4420_state *st = iio_priv(indio_dev);
0161
0162 switch (info) {
0163 case IIO_CHAN_INFO_FREQUENCY:
0164
0165 *val = div_u64_rem(st->lo_freq_hz, MICRO, val2);
0166
0167 return IIO_VAL_INT_PLUS_MICRO;
0168 default:
0169 return -EINVAL;
0170 }
0171 }
0172
0173 static const struct iio_info admv4420_info = {
0174 .read_raw = admv4420_read_raw,
0175 .debugfs_reg_access = &admv4420_reg_access,
0176 };
0177
0178 static const struct iio_chan_spec admv4420_channels[] = {
0179 {
0180 .type = IIO_ALTVOLTAGE,
0181 .output = 0,
0182 .indexed = 1,
0183 .channel = 0,
0184 .info_mask_separate = BIT(IIO_CHAN_INFO_FREQUENCY),
0185 },
0186 };
0187
0188 static void admv4420_fw_parse(struct admv4420_state *st)
0189 {
0190 struct device *dev = &st->spi->dev;
0191 u32 tmp;
0192 int ret;
0193
0194 ret = device_property_read_u32(dev, "adi,lo-freq-khz", &tmp);
0195 if (!ret)
0196 st->lo_freq_hz = (u64)tmp * KILO;
0197
0198 st->ref_block.ref_single_ended = device_property_read_bool(dev,
0199 "adi,ref-ext-single-ended-en");
0200 }
0201
0202 static inline uint64_t admv4420_calc_pfd_vco(struct admv4420_state *st)
0203 {
0204 return div_u64(st->vco_freq_hz * 10, st->n_counter.n_counter);
0205 }
0206
0207 static inline uint32_t admv4420_calc_pfd_ref(struct admv4420_state *st)
0208 {
0209 uint32_t tmp;
0210 u8 doubler, divide_by_2;
0211
0212 doubler = st->ref_block.doubler_en ? 2 : 1;
0213 divide_by_2 = st->ref_block.divide_by_2_en ? 2 : 1;
0214 tmp = ADMV4420_REF_FREQ_HZ * doubler;
0215
0216 return (tmp / (st->ref_block.divider * divide_by_2));
0217 }
0218
0219 static int admv4420_calc_parameters(struct admv4420_state *st)
0220 {
0221 u64 pfd_ref, pfd_vco;
0222 bool sol_found = false;
0223
0224 st->ref_block.doubler_en = false;
0225 st->ref_block.divide_by_2_en = false;
0226 st->vco_freq_hz = div_u64(st->lo_freq_hz, 2);
0227
0228 for (st->ref_block.divider = 1; st->ref_block.divider < MAX_R_DIVIDER;
0229 st->ref_block.divider++) {
0230 pfd_ref = admv4420_calc_pfd_ref(st);
0231 for (st->n_counter.n_counter = 1; st->n_counter.n_counter < MAX_N_COUNTER;
0232 st->n_counter.n_counter++) {
0233 pfd_vco = admv4420_calc_pfd_vco(st);
0234 if (pfd_ref == pfd_vco) {
0235 sol_found = true;
0236 break;
0237 }
0238 }
0239
0240 if (sol_found)
0241 break;
0242
0243 st->n_counter.n_counter = 1;
0244 }
0245 if (!sol_found)
0246 return -1;
0247
0248 st->n_counter.int_val = div_u64_rem(st->n_counter.n_counter, 10, &st->n_counter.frac_val);
0249 st->n_counter.mod_val = 10;
0250
0251 return 0;
0252 }
0253
0254 static int admv4420_setup(struct iio_dev *indio_dev)
0255 {
0256 struct admv4420_state *st = iio_priv(indio_dev);
0257 struct device *dev = indio_dev->dev.parent;
0258 u32 val;
0259 int ret;
0260
0261 ret = regmap_write(st->regmap, ADMV4420_SPI_CONFIG_1,
0262 ADMV4420_SPI_CONFIG_1_SOFTRESET);
0263 if (ret)
0264 return ret;
0265
0266 ret = regmap_write(st->regmap, ADMV4420_SPI_CONFIG_1,
0267 ADMV4420_SPI_CONFIG_1_SDOACTIVE |
0268 ADMV4420_SPI_CONFIG_1_ENDIAN);
0269 if (ret)
0270 return ret;
0271
0272 ret = regmap_write(st->regmap,
0273 ADMV4420_SCRATCHPAD,
0274 ADMV4420_SCRATCH_PAD_VAL_1);
0275 if (ret)
0276 return ret;
0277
0278 ret = regmap_read(st->regmap, ADMV4420_SCRATCHPAD, &val);
0279 if (ret)
0280 return ret;
0281
0282 if (val != ADMV4420_SCRATCH_PAD_VAL_1) {
0283 dev_err(dev, "Failed ADMV4420 to read/write scratchpad %x ", val);
0284 return -EIO;
0285 }
0286
0287 ret = regmap_write(st->regmap,
0288 ADMV4420_SCRATCHPAD,
0289 ADMV4420_SCRATCH_PAD_VAL_2);
0290 if (ret)
0291 return ret;
0292
0293 ret = regmap_read(st->regmap, ADMV4420_SCRATCHPAD, &val);
0294 if (ret)
0295 return ret;
0296
0297 if (val != ADMV4420_SCRATCH_PAD_VAL_2) {
0298 dev_err(dev, "Failed to read/write scratchpad %x ", val);
0299 return -EIO;
0300 }
0301
0302 st->mux_sel = ADMV4420_LOCK_DTCT;
0303 st->lo_freq_hz = ADMV4420_DEFAULT_LO_FREQ_HZ;
0304
0305 admv4420_fw_parse(st);
0306
0307 ret = admv4420_calc_parameters(st);
0308 if (ret) {
0309 dev_err(dev, "Failed calc parameters for %lld ", st->vco_freq_hz);
0310 return ret;
0311 }
0312
0313 ret = regmap_write(st->regmap, ADMV4420_R_DIV_L,
0314 FIELD_GET(0xFF, st->ref_block.divider));
0315 if (ret)
0316 return ret;
0317
0318 ret = regmap_write(st->regmap, ADMV4420_R_DIV_H,
0319 FIELD_GET(0xFF00, st->ref_block.divider));
0320 if (ret)
0321 return ret;
0322
0323 ret = regmap_write(st->regmap, ADMV4420_REFERENCE,
0324 st->ref_block.divide_by_2_en |
0325 FIELD_PREP(ADMV4420_REFERENCE_MODE_MASK, st->ref_block.ref_single_ended) |
0326 FIELD_PREP(ADMV4420_REFERENCE_DOUBLER_MASK, st->ref_block.doubler_en));
0327 if (ret)
0328 return ret;
0329
0330 ret = admv4420_set_n_counter(st, st->n_counter.int_val,
0331 st->n_counter.frac_val,
0332 st->n_counter.mod_val);
0333 if (ret)
0334 return ret;
0335
0336 ret = regmap_write(st->regmap, ADMV4420_PLL_MUX_SEL, st->mux_sel);
0337 if (ret)
0338 return ret;
0339
0340 return regmap_write(st->regmap, ADMV4420_ENABLES,
0341 ENABLE_PLL | ENABLE_LO | ENABLE_VCO |
0342 ENABLE_IFAMP | ENABLE_MIXER | ENABLE_LNA);
0343 }
0344
0345 static int admv4420_probe(struct spi_device *spi)
0346 {
0347 struct iio_dev *indio_dev;
0348 struct admv4420_state *st;
0349 struct regmap *regmap;
0350 int ret;
0351
0352 indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
0353 if (!indio_dev)
0354 return -ENOMEM;
0355
0356 regmap = devm_regmap_init_spi(spi, &admv4420_regmap_config);
0357 if (IS_ERR(regmap))
0358 return dev_err_probe(&spi->dev, PTR_ERR(regmap),
0359 "Failed to initializing spi regmap\n");
0360
0361 st = iio_priv(indio_dev);
0362 st->spi = spi;
0363 st->regmap = regmap;
0364
0365 indio_dev->name = "admv4420";
0366 indio_dev->info = &admv4420_info;
0367 indio_dev->channels = admv4420_channels;
0368 indio_dev->num_channels = ARRAY_SIZE(admv4420_channels);
0369
0370 ret = admv4420_setup(indio_dev);
0371 if (ret) {
0372 dev_err(&spi->dev, "Setup ADMV4420 failed (%d)\n", ret);
0373 return ret;
0374 }
0375
0376 return devm_iio_device_register(&spi->dev, indio_dev);
0377 }
0378
0379 static const struct of_device_id admv4420_of_match[] = {
0380 { .compatible = "adi,admv4420" },
0381 { }
0382 };
0383
0384 MODULE_DEVICE_TABLE(of, admv4420_of_match);
0385
0386 static struct spi_driver admv4420_driver = {
0387 .driver = {
0388 .name = "admv4420",
0389 .of_match_table = admv4420_of_match,
0390 },
0391 .probe = admv4420_probe,
0392 };
0393
0394 module_spi_driver(admv4420_driver);
0395
0396 MODULE_AUTHOR("Cristian Pop <cristian.pop@analog.com>");
0397 MODULE_DESCRIPTION("Analog Devices ADMV44200 K Band Downconverter");
0398 MODULE_LICENSE("Dual BSD/GPL");