Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause
0002 /*
0003  * ADMV4420
0004  *
0005  * Copyright 2021 Analog Devices Inc.
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 /* ADMV4420 Register Map */
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");