cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

admv4420.c (10206B)


      1// SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause
      2/*
      3 * ADMV4420
      4 *
      5 * Copyright 2021 Analog Devices Inc.
      6 */
      7
      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>
     15
     16#include <asm/unaligned.h>
     17
     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
     56
     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))
     60
     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)
     64
     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)
     69
     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)
     76
     77#define ADMV4420_SCRATCH_PAD_VAL_1              0xAD
     78#define ADMV4420_SCRATCH_PAD_VAL_2              0xEA
     79
     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
     84
     85enum admv4420_mux_sel {
     86	ADMV4420_LOW = 0,
     87	ADMV4420_LOCK_DTCT = 1,
     88	ADMV4420_R_COUNTER_PER_2 = 4,
     89	ADMV4420_N_CONUTER_PER_2 = 5,
     90	ADMV4420_HIGH = 8,
     91};
     92
     93struct admv4420_reference_block {
     94	bool doubler_en;
     95	bool divide_by_2_en;
     96	bool ref_single_ended;
     97	u32 divider;
     98};
     99
    100struct admv4420_n_counter {
    101	u32 int_val;
    102	u32 frac_val;
    103	u32 mod_val;
    104	u32 n_counter;
    105};
    106
    107struct admv4420_state {
    108	struct spi_device		*spi;
    109	struct regmap			*regmap;
    110	u64				vco_freq_hz;
    111	u64				lo_freq_hz;
    112	struct admv4420_reference_block ref_block;
    113	struct admv4420_n_counter	n_counter;
    114	enum admv4420_mux_sel		mux_sel;
    115	struct mutex			lock;
    116	u8				transf_buf[4] ____cacheline_aligned;
    117};
    118
    119static const struct regmap_config admv4420_regmap_config = {
    120	.reg_bits = 16,
    121	.val_bits = 8,
    122	.read_flag_mask = BIT(7),
    123};
    124
    125static int admv4420_reg_access(struct iio_dev *indio_dev,
    126			       u32 reg, u32 writeval,
    127			       u32 *readval)
    128{
    129	struct admv4420_state *st = iio_priv(indio_dev);
    130
    131	if (readval)
    132		return regmap_read(st->regmap, reg, readval);
    133	else
    134		return regmap_write(st->regmap, reg, writeval);
    135}
    136
    137static int admv4420_set_n_counter(struct admv4420_state *st, u32 int_val,
    138				  u32 frac_val, u32 mod_val)
    139{
    140	int ret;
    141
    142	put_unaligned_le32(frac_val, st->transf_buf);
    143	ret = regmap_bulk_write(st->regmap, ADMV4420_FRAC_L, st->transf_buf, 3);
    144	if (ret)
    145		return ret;
    146
    147	put_unaligned_le32(mod_val, st->transf_buf);
    148	ret = regmap_bulk_write(st->regmap, ADMV4420_MOD_L, st->transf_buf, 3);
    149	if (ret)
    150		return ret;
    151
    152	put_unaligned_le32(int_val, st->transf_buf);
    153	return regmap_bulk_write(st->regmap, ADMV4420_INT_L, st->transf_buf, 2);
    154}
    155
    156static int admv4420_read_raw(struct iio_dev *indio_dev,
    157			     struct iio_chan_spec const *chan,
    158			     int *val, int *val2, long info)
    159{
    160	struct admv4420_state *st = iio_priv(indio_dev);
    161
    162	switch (info) {
    163	case IIO_CHAN_INFO_FREQUENCY:
    164
    165		*val = div_u64_rem(st->lo_freq_hz, MICRO, val2);
    166
    167		return IIO_VAL_INT_PLUS_MICRO;
    168	default:
    169		return -EINVAL;
    170	}
    171}
    172
    173static const struct iio_info admv4420_info = {
    174	.read_raw = admv4420_read_raw,
    175	.debugfs_reg_access = &admv4420_reg_access,
    176};
    177
    178static const struct iio_chan_spec admv4420_channels[] = {
    179	{
    180		.type = IIO_ALTVOLTAGE,
    181		.output = 0,
    182		.indexed = 1,
    183		.channel = 0,
    184		.info_mask_separate = BIT(IIO_CHAN_INFO_FREQUENCY),
    185	},
    186};
    187
    188static void admv4420_fw_parse(struct admv4420_state *st)
    189{
    190	struct device *dev = &st->spi->dev;
    191	u32 tmp;
    192	int ret;
    193
    194	ret = device_property_read_u32(dev, "adi,lo-freq-khz", &tmp);
    195	if (!ret)
    196		st->lo_freq_hz = (u64)tmp * KILO;
    197
    198	st->ref_block.ref_single_ended = device_property_read_bool(dev,
    199								   "adi,ref-ext-single-ended-en");
    200}
    201
    202static inline uint64_t admv4420_calc_pfd_vco(struct admv4420_state *st)
    203{
    204	return div_u64(st->vco_freq_hz * 10, st->n_counter.n_counter);
    205}
    206
    207static inline uint32_t admv4420_calc_pfd_ref(struct admv4420_state *st)
    208{
    209	uint32_t tmp;
    210	u8 doubler, divide_by_2;
    211
    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;
    215
    216	return (tmp / (st->ref_block.divider * divide_by_2));
    217}
    218
    219static int admv4420_calc_parameters(struct admv4420_state *st)
    220{
    221	u64 pfd_ref, pfd_vco;
    222	bool sol_found = false;
    223
    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);
    227
    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) {
    235				sol_found = true;
    236				break;
    237			}
    238		}
    239
    240		if (sol_found)
    241			break;
    242
    243		st->n_counter.n_counter = 1;
    244	}
    245	if (!sol_found)
    246		return -1;
    247
    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;
    250
    251	return 0;
    252}
    253
    254static int admv4420_setup(struct iio_dev *indio_dev)
    255{
    256	struct admv4420_state *st = iio_priv(indio_dev);
    257	struct device *dev = indio_dev->dev.parent;
    258	u32 val;
    259	int ret;
    260
    261	ret = regmap_write(st->regmap, ADMV4420_SPI_CONFIG_1,
    262			   ADMV4420_SPI_CONFIG_1_SOFTRESET);
    263	if (ret)
    264		return ret;
    265
    266	ret = regmap_write(st->regmap, ADMV4420_SPI_CONFIG_1,
    267			   ADMV4420_SPI_CONFIG_1_SDOACTIVE |
    268			   ADMV4420_SPI_CONFIG_1_ENDIAN);
    269	if (ret)
    270		return ret;
    271
    272	ret = regmap_write(st->regmap,
    273			   ADMV4420_SCRATCHPAD,
    274			   ADMV4420_SCRATCH_PAD_VAL_1);
    275	if (ret)
    276		return ret;
    277
    278	ret = regmap_read(st->regmap, ADMV4420_SCRATCHPAD, &val);
    279	if (ret)
    280		return ret;
    281
    282	if (val != ADMV4420_SCRATCH_PAD_VAL_1) {
    283		dev_err(dev, "Failed ADMV4420 to read/write scratchpad %x ", val);
    284		return -EIO;
    285	}
    286
    287	ret = regmap_write(st->regmap,
    288			   ADMV4420_SCRATCHPAD,
    289			   ADMV4420_SCRATCH_PAD_VAL_2);
    290	if (ret)
    291		return ret;
    292
    293	ret = regmap_read(st->regmap, ADMV4420_SCRATCHPAD, &val);
    294	if (ret)
    295		return ret;
    296
    297	if (val != ADMV4420_SCRATCH_PAD_VAL_2) {
    298		dev_err(dev, "Failed to read/write scratchpad %x ", val);
    299		return -EIO;
    300	}
    301
    302	st->mux_sel = ADMV4420_LOCK_DTCT;
    303	st->lo_freq_hz = ADMV4420_DEFAULT_LO_FREQ_HZ;
    304
    305	admv4420_fw_parse(st);
    306
    307	ret = admv4420_calc_parameters(st);
    308	if (ret) {
    309		dev_err(dev, "Failed calc parameters for %lld ", st->vco_freq_hz);
    310		return ret;
    311	}
    312
    313	ret = regmap_write(st->regmap, ADMV4420_R_DIV_L,
    314			   FIELD_GET(0xFF, st->ref_block.divider));
    315	if (ret)
    316		return ret;
    317
    318	ret = regmap_write(st->regmap, ADMV4420_R_DIV_H,
    319			   FIELD_GET(0xFF00, st->ref_block.divider));
    320	if (ret)
    321		return ret;
    322
    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));
    327	if (ret)
    328		return ret;
    329
    330	ret = admv4420_set_n_counter(st, st->n_counter.int_val,
    331				     st->n_counter.frac_val,
    332				     st->n_counter.mod_val);
    333	if (ret)
    334		return ret;
    335
    336	ret = regmap_write(st->regmap, ADMV4420_PLL_MUX_SEL, st->mux_sel);
    337	if (ret)
    338		return ret;
    339
    340	return regmap_write(st->regmap, ADMV4420_ENABLES,
    341			    ENABLE_PLL | ENABLE_LO | ENABLE_VCO |
    342			    ENABLE_IFAMP | ENABLE_MIXER | ENABLE_LNA);
    343}
    344
    345static int admv4420_probe(struct spi_device *spi)
    346{
    347	struct iio_dev *indio_dev;
    348	struct admv4420_state *st;
    349	struct regmap *regmap;
    350	int ret;
    351
    352	indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
    353	if (!indio_dev)
    354		return -ENOMEM;
    355
    356	regmap = devm_regmap_init_spi(spi, &admv4420_regmap_config);
    357	if (IS_ERR(regmap))
    358		return dev_err_probe(&spi->dev, PTR_ERR(regmap),
    359				     "Failed to initializing spi regmap\n");
    360
    361	st = iio_priv(indio_dev);
    362	st->spi = spi;
    363	st->regmap = regmap;
    364
    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);
    369
    370	ret = admv4420_setup(indio_dev);
    371	if (ret) {
    372		dev_err(&spi->dev, "Setup ADMV4420 failed (%d)\n", ret);
    373		return ret;
    374	}
    375
    376	return devm_iio_device_register(&spi->dev, indio_dev);
    377}
    378
    379static const struct of_device_id admv4420_of_match[] = {
    380	{ .compatible = "adi,admv4420" },
    381	{ }
    382};
    383
    384MODULE_DEVICE_TABLE(of, admv4420_of_match);
    385
    386static struct spi_driver admv4420_driver = {
    387	.driver = {
    388		.name = "admv4420",
    389		.of_match_table = admv4420_of_match,
    390	},
    391	.probe = admv4420_probe,
    392};
    393
    394module_spi_driver(admv4420_driver);
    395
    396MODULE_AUTHOR("Cristian Pop <cristian.pop@analog.com>");
    397MODULE_DESCRIPTION("Analog Devices ADMV44200 K Band Downconverter");
    398MODULE_LICENSE("Dual BSD/GPL");