diff mbox series

[2/2] iio: frequency: Add ADF4382

Message ID 20241114130340.7354-3-ciprian.hegbeli@analog.com (mailing list archive)
State New
Headers show
Series Add support for ADF4382 | expand

Commit Message

Ciprian Hegbeli Nov. 14, 2024, 1:03 p.m. UTC
The ADF4382A is a high performance, ultralow jitter, Frac-N PLL
with integrated VCO ideally suited for LO generation for 5G applications
or data converter clock applications. The high performance
PLL has a figure of merit of -239 dBc/Hz, low 1/f Noise and
high PFD frequency of 625MHz in integer mode that can achieve
ultralow in-band noise and integrated jitter. The ADF4382A can
generate frequencies in a fundamental octave range of 11.5 GHz to
21 GHz, thereby eliminating the need for sub-harmonic filters. The
divide by 2 and 4 output dividers on the part allow frequencies to
be generated from 5.75GHz to 10.5GHz and 2.875GHz to 5.25GHz
respectively.

Signed-off-by: Ciprian Hegbeli <ciprian.hegbeli@analog.com>
---
 drivers/iio/frequency/Kconfig   |   11 +
 drivers/iio/frequency/Makefile  |    1 +
 drivers/iio/frequency/adf4382.c | 1825 +++++++++++++++++++++++++++++++
 3 files changed, 1837 insertions(+)
 create mode 100644 drivers/iio/frequency/adf4382.c

Comments

Javier Carrasco Nov. 14, 2024, 1:27 p.m. UTC | #1
On 14/11/2024 14:03, Ciprian Hegbeli wrote:
> The ADF4382A is a high performance, ultralow jitter, Frac-N PLL
> with integrated VCO ideally suited for LO generation for 5G applications
> or data converter clock applications. The high performance
> PLL has a figure of merit of -239 dBc/Hz, low 1/f Noise and
> high PFD frequency of 625MHz in integer mode that can achieve
> ultralow in-band noise and integrated jitter. The ADF4382A can
> generate frequencies in a fundamental octave range of 11.5 GHz to
> 21 GHz, thereby eliminating the need for sub-harmonic filters. The
> divide by 2 and 4 output dividers on the part allow frequencies to
> be generated from 5.75GHz to 10.5GHz and 2.875GHz to 5.25GHz
> respectively.
> 
> Signed-off-by: Ciprian Hegbeli <ciprian.hegbeli@analog.com>
> ---
>  drivers/iio/frequency/Kconfig   |   11 +
>  drivers/iio/frequency/Makefile  |    1 +
>  drivers/iio/frequency/adf4382.c | 1825 +++++++++++++++++++++++++++++++
>  3 files changed, 1837 insertions(+)
>  create mode 100644 drivers/iio/frequency/adf4382.c

...

> +static int _adf4382_set_freq(struct adf4382_state *st)
> +{
> +	u32 frac2_word = 0;
> +	u32 mod2_word = 0;
> +	u64 pfd_freq_hz;
> +	u32 frac1_word;
> +	u8 clkout_div;
> +	u32 read_val;
> +	u8 dclk_div1;
> +	u8 int_mode;
> +	u8 en_bleed;
> +	u8 ldwin_pw;
> +	u16 n_int;
> +	u8 div1;
> +	u64 tmp;
> +	u64 vco;
> +	int ret;
> +	u8 var;
> +
> +	ret = adf4382_pfd_compute(st, &pfd_freq_hz);
> +	if (ret) {
> +		dev_err(&st->spi->dev, "PFD frequency is out of range.\n");
> +		return ret;
> +	}
> +
> +	for (clkout_div = 0; clkout_div <= st->clkout_div_reg_val_max; clkout_div++) {
> +		tmp =  (1 << clkout_div) * st->freq;
> +		if (tmp < st->vco_min || tmp > st->vco_max)
> +			continue;
> +
> +		vco = tmp;
> +		break;
> +	}
> +

(At least) LLVM/Clang complains about vco for a good reason: you may use
it without proper initialization if the for loop does not assign any
value. I guess you meant it to be initialized to zero in the declaration.

> +	if (vco == 0) {
> +		dev_err(&st->spi->dev, "Output frequency is out of range.\n");
> +		ret = -EINVAL;
> +		return ret;
> +	}
> +

Best regards,
Javier Carrasco
diff mbox series

Patch

diff --git a/drivers/iio/frequency/Kconfig b/drivers/iio/frequency/Kconfig
index 583cbdf4e8cd..c4cf1ed1bc05 100644
--- a/drivers/iio/frequency/Kconfig
+++ b/drivers/iio/frequency/Kconfig
@@ -61,6 +61,17 @@  config ADF4377
 	  To compile this driver as a module, choose M here: the
 	  module will be called adf4377.
 
+config ADF4382
+	tristate "Analog Devices ADF4382 Microwave Wideband Synthesizer"
+	depends on SPI && COMMON_CLK
+	select REGMAP_SPI
+	help
+	  Say yes here to build support for Analog Devices ADF4382 Microwave
+	  Wideband Synthesizer.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called adf4382.
+
 config ADMFM2000
 	tristate "Analog Devices ADMFM2000 Dual Microwave Down Converter"
 	depends on GPIOLIB
diff --git a/drivers/iio/frequency/Makefile b/drivers/iio/frequency/Makefile
index 70d0e0b70e80..4852a8748ce4 100644
--- a/drivers/iio/frequency/Makefile
+++ b/drivers/iio/frequency/Makefile
@@ -8,6 +8,7 @@  obj-$(CONFIG_AD9523) += ad9523.o
 obj-$(CONFIG_ADF4350) += adf4350.o
 obj-$(CONFIG_ADF4371) += adf4371.o
 obj-$(CONFIG_ADF4377) += adf4377.o
+obj-$(CONFIG_ADF4382) += adf4382.o
 obj-$(CONFIG_ADMFM2000) += admfm2000.o
 obj-$(CONFIG_ADMV1013) += admv1013.o
 obj-$(CONFIG_ADMV1014) += admv1014.o
diff --git a/drivers/iio/frequency/adf4382.c b/drivers/iio/frequency/adf4382.c
new file mode 100644
index 000000000000..734b5c197328
--- /dev/null
+++ b/drivers/iio/frequency/adf4382.c
@@ -0,0 +1,1825 @@ 
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * ADF4382 Microwave Wideband Synthesizer with Integrated VCO
+ *
+ * Copyright 2022-2024 Analog Devices Inc.
+ */
+
+#include <linux/bitfield.h>
+#include <linux/bits.h>
+#include <linux/clk.h>
+#include <linux/clkdev.h>
+#include <linux/clk-provider.h>
+#include <linux/debugfs.h>
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/notifier.h>
+#include <linux/property.h>
+#include <linux/spi/spi.h>
+#include <linux/iio/iio.h>
+#include <linux/regmap.h>
+#include <linux/gcd.h>
+#include <linux/math64.h>
+#include <linux/units.h>
+#include <linux/util_macros.h>
+
+/* ADF4382 REG0000 Map */
+#define ADF4382_SOFT_RESET_R_MSK		BIT(7)
+#define ADF4382_LSB_FIRST_R_MSK			BIT(6)
+#define ADF4382_ADDRESS_ASC_R_MSK		BIT(5)
+#define ADF4382_SDO_ACTIVE_R_MSK		BIT(4)
+#define ADF4382_SDO_ACTIVE_MSK			BIT(3)
+#define ADF4382_ADDRESS_ASC_MSK			BIT(2)
+#define ADF4382_LSB_FIRST_MSK			BIT(1)
+#define ADF4382_SOFT_RESET_MSK			BIT(0)
+
+/* ADF4382_REG0 */
+#define ADF4382_ADDR_ASC_MSK			BIT(2)
+#define ADF4382_ADDR_ASC_R_MSK			BIT(5)
+#define ADF4382_SDO_ACT_MSK			BIT(3)
+#define ADF4382_SDO_ACT_R_MSK			BIT(4)
+#define ADF4382_RESET_CMD			0x81
+
+/* ADF4382 REG0000 Bit Definition */
+#define ADF4382_SDO_ACTIVE_SPI_3W		0x0
+#define ADF4382_SDO_ACTIVE_SPI_4W		0x1
+
+#define ADF4382_ADDR_ASC_AUTO_DECR		0x0
+#define ADF4382_ADDR_ASC_AUTO_INCR		0x1
+
+#define ADF4382_LSB_FIRST_MSB			0x0
+#define ADF4382_LSB_FIRST_LSB			0x1
+
+#define ADF4382_SOFT_RESET_N_OP			0x0
+#define ADF4382_SOFT_RESET_EN			0x1
+
+/* ADF4382 REG0001 Map */
+#define ADF4382_SINGLE_INSTR_MSK		BIT(7)
+#define ADF4382_MASTER_RB_CTRL_MSK		BIT(5)
+
+/* ADF4382 REG0001 Bit Definition */
+#define ADF4382_SPI_STREAM_EN			0x0
+#define ADF4382_SPI_STREAM_DIS			0x1
+
+#define ADF4382_RB_SLAVE_REG			0x0
+#define ADF4382_RB_MASTER_REG			0x1
+
+/* ADF4382 REG0003 Bit Definition */
+#define ADF4382_CHIP_TYPE			0x06
+
+/* ADF4382 REG0004 Bit Definition */
+#define ADF4382_PRODUCT_ID_LSB			0x0005
+
+/* ADF4382 REG0005 Bit Definition */
+#define ADF4382_PRODUCT_ID_MSB			0x0005
+
+/* ADF4382 REG000A Map */
+#define ADF4382_SCRATCHPAD_MSK			GENMASK(7, 0)
+
+/* ADF4382 REG000C Bit Definition */
+#define ADF4382_VENDOR_ID_LSB			0x56
+
+/* ADF4382 REG000D Bit Definition */
+#define ADF4382_VENDOR_ID_MSB			0x04
+
+/* ADF4382 REG000F Bit Definition */
+#define ADF4382_M_S_TRANSF_BIT_MSK		BIT(0)
+
+/* ADF4382 REG0010 Map*/
+#define ADF4382_N_INT_LSB_MSK			GENMASK(7, 0)
+
+/* ADF4382 REG0011 Map*/
+#define ADF4382_CLKOUT_DIV_MSK			GENMASK(7, 5)
+#define ADF4382_INV_CLK_OUT_MSK			BIT(4)
+#define ADF4382_N_INT_MSB_MSK			GENMASK(3, 0)
+
+/* ADF4382 REG0015 Map */
+#define ADF4382_M_VCO_BAND_LSB_MSK		BIT(7)
+#define ADF4382_M_VCO_CORE_MSK			BIT(6)
+#define ADF4382_BIAS_DEC_MODE_MSK		GENMASK(5, 3)
+#define ADF4382_INT_MODE_MSK			BIT(2)
+#define ADF4382_PFD_POL_MSK			BIT(1)
+#define ADF4382_FRAC1WORD_MSB			BIT(0)
+
+/* ADF4382 REG0016 Map */
+#define ADF4382_M_VCO_BAND_MSB_MSK		GENMASK(7, 0)
+
+/* ADF4382 REG001D Map */
+#define ADF4382_BLEED_I_LSB_MSK			GENMASK(7, 0)
+
+/* ADF4382 REG001E Map */
+#define ADF4382_EN_PHASE_RESYNC_MSK		BIT(7)
+#define ADF4382_EN_REF_RST_MSK			BIT(6)
+#define ADF4382_TIMED_SYNC_MSK			BIT(5)
+#define ADF4382_BLEED_I_MSB_MSK			GENMASK(4, 0)
+
+/* ADF4382 REG001F Map */
+#define ADF4382_SW_SYNC_MSK			BIT(7)
+#define ADF4382_SPARE_1F_MSK			BIT(6)
+#define ADF4382_BLEED_POL_MSK			BIT(5)
+#define ADF4382_EN_BLEED_MSK			BIT(4)
+#define ADF4382_CP_I_MSK			GENMASK(3, 0)
+
+/* ADF4382 REG0020 Map */
+#define ADF4382_EN_AUTOCAL_MSK			BIT(7)
+#define ADF4382_EN_RDBLR_MSK			BIT(6)
+#define ADF4382_R_DIV_MSK			GENMASK(5, 0)
+
+/* ADF4382 REG0021 Map */
+#define ADF4382_PHASE_WORD_LSB_MSK		GENMASK(7, 0)
+
+/* ADF4382 REG0022 Map */
+#define ADF4382_PHASE_WORD_MID_MSK		GENMASK(7, 0)
+
+/* ADF4382 REG0023 Map */
+#define ADF4382_PHASE_WORD_MSB_MSK		GENMASK(7, 0)
+
+/* ADF4382 REG0024 Map */
+#define ADF4382_SPARE_24_MSK			GENMASK(7, 5)
+#define ADF4382_DCLK_DIV_SEL_MSK		BIT(4)
+#define ADF4382_DNCLK_DIV1_MSK			GENMASK(3, 2)
+#define ADF4382_DCLK_DIV1_MSK			GENMASK(1, 0)
+
+/* ADF4382 REG0025 Map */
+#define ADF4382_RESYNC_WAIT_LSB_MSK		GENMASK(7, 0)
+
+/* ADF4382 REG0026 Map */
+#define ADF4382_RESYNC_WAIT_MSB_MSK		GENMASK(7, 0)
+
+/* ADF4382 REG0027 Map */
+#define ADF4382_CAL_BLEED_FINE_MIN_MSK		GENMASK(7, 4)
+#define ADF4382_BLEED_ADJ_SCALE_MSK		GENMASK(3, 0)
+
+/* ADF4382 REG0028 Map */
+#define ADF4382_PH_RESYNC_RB_SEL_MSK		BIT(7)
+#define ADF4382_LSB_P1_MSK			BIT(6)
+#define ADF4382_VAR_MOD_EN_MSK			BIT(5)
+#define ADF4382_DITHER1_SCALE_MSK		GENMASK(4, 2)
+#define ADF4382_EN_DITHER2_MSK			BIT(1)
+#define ADF4382_EN_DITHER1_MSK			BIT(0)
+
+/* ADF4382 REG0029 Map */
+#define ADF4382_CLK2_OPWR_MSK			GENMASK(7, 4)
+#define ADF4382_CLK1_OPWR_MSK			GENMASK(3, 0)
+
+/* ADF4382 REG002A Map */
+#define ADF4382_FN_DBL_MSK			BIT(7)
+#define ADF4382_PD_NDIV_TL_MSK	BIT(6)
+#define ADF4382_CLKOUT_BST_MSK			BIT(5)
+#define ADF4382_PD_SYNC_MSK			BIT(4)
+#define ADF4382_PD_CLK_MSK			BIT(3)
+#define ADF4382_PD_RDET_MSK			BIT(2)
+#define ADF4382_PD_ADC_MSK			BIT(1)
+#define ADF4382_PD_CALGEN_MSK			BIT(0)
+
+/* ADF4382 REG002B Map */
+#define ADF4382_PD_ALL_MSK			BIT(7)
+#define ADF4382_PD_RDIV_TL_MSK			BIT(6)
+#define ADF4382_PD_NDIV_MSK			BIT(5)
+#define ADF4382_PD_VCO_MSK			BIT(4)
+#define ADF4382_PD_LD_MSK			BIT(3)
+#define ADF4382_PD_PFDCP_MSK			BIT(2)
+#define ADF4382_PD_CLKOUT1_MSK			BIT(1)
+#define ADF4382_PD_CLKOUT2_MSK			BIT(0)
+
+/* ADF4382 REG002C Map */
+#define ADF4382_LDWIN_PW_MSK			GENMASK(7, 4)
+#define ADF4382_LD_COUNT_OPWR_MSK		GENMASK(3, 0)
+
+/* ADF4382 REG002D Map */
+#define ADF4382_EN_DNCLK_MSK			BIT(7)
+#define ADF4382_EN_DRCLK_MSK			BIT(6)
+#define ADF4382_EN_LOL_MSK			BIT(5)
+#define ADF4382_EN_LDWIN_MSK			BIT(4)
+#define ADF4382_PDET_POL_MSK			BIT(3)
+#define ADF4382_RST_LD_MSK			BIT(2)
+#define ADF4382_LD_O_CTRL_MSK			GENMASK(1, 0)
+
+/* ADF4382 REG002E Map */
+#define ADF4382_MUXOUT_MSK			GENMASK(7, 4)
+#define ADF4382_ABPW_WD_MSK			BIT(3)
+#define ADF4382_EN_CPTEST_MSK			BIT(2)
+#define ADF4382_CP_DOWN_MSK			BIT(1)
+#define ADF4382_CP_UP_MSK			BIT(0)
+
+/* ADF4382 REG002F Map*/
+#define ADF4382_BST_REF_MSK			BIT(7)
+#define ADF4382_FILT_REF_MSK			BIT(6)
+#define ADF4382_RDBLR_DC_MSK			GENMASK(5, 0)
+
+/* ADF4382 REG0030 Map */
+#define ADF4382_MUTE_NCLK_MSK			BIT(7)
+#define ADF4382_MUTE_RCLK_MSK			BIT(6)
+#define ADF4382_REF_SEL_MSK			BIT(5)
+#define ADF4382_INV_RDBLR_MSK			BIT(4)
+#define ADF4382_RDBLR_DEL_SEL_MSK		GENMASK(3, 0)
+
+/* ADF4382 REG0031 Map */
+#define ADF4382_SYNC_DEL_MSK			GENMASK(7, 5)
+#define ADF4382_RST_SYS_MSK			BIT(4)
+#define ADF4382_EN_ADC_CLK_MSK			BIT(3)
+#define ADF4382_EN_VCAL_MSK			BIT(2)
+#define ADF4382_CAL_CT_SEL_MSK			BIT(1)
+#define ADF4382_DCLK_MODE_MSK			BIT(0)
+
+/* ADF4382 REG0032 Map */
+#define ADF4382_SPARE_32_MSK			BIT(7)
+#define ADF4382_BLEED_ADJ_CAL_MSK		BIT(6)
+#define ADF4382_DEL_MODE_MSK			BIT(5)
+#define ADF4382_EN_AUTO_ALIGN_MSK		BIT(4)
+#define ADF4382_PHASE_ADJ_POL_MSK		BIT(3)
+#define ADF4382_EFM3_MODE_MSK			GENMASK(2, 0)
+
+/* ADF4382 REG0033 Map */
+#define ADF4382_PHASE_ADJUST_MSK		GENMASK(7, 0)
+
+/* ADF4382 REG0034 Map */
+#define ADF4382_PHASE_ADJ_MSK			BIT(7)
+#define ADF4382_DRCLK_DEL_MSK			GENMASK(6, 4)
+#define ADF4382_DNCLK_DEL_MSK			GENMASK(3, 1)
+#define ADF4382_RST_CNTR_MSK			BIT(0)
+
+/* ADF4382 REG0035 Map */
+#define ADF4382_SPARE_35_MSK			GENMASK(7, 6)
+#define ADF4382_M_VCO_BIAS_MSK			GENMASK(5, 0)
+
+/* ADF4382 REG0036 Map */
+#define ADF4382_CLKODIV_DB_MSK			BIT(7)
+#define ADF4382_DCLK_DIV_DB_MSK			BIT(6)
+#define ADF4382_SPARE_36_MSK			GENMASK(5, 2)
+#define ADF4382_EN_LUT_GEN_MSK			BIT(1)
+#define ADF4382_EN_LUT_CAL_MSK			BIT(0)
+
+/* ADF4382 REG0037 Map */
+#define ADF4382_CAL_COUNT_TO_MSK		GENMASK(7, 0)
+
+/* ADF4382 REG0038 Map */
+#define ADF4382_CAL_VTUNE_TO_LSB_MSK		GENMASK(7, 0)
+
+/* ADF4382 REG0039 Map */
+#define ADF4382_O_VCO_DB_MSK			BIT(7)
+#define ADF4382_CAL_VTUNE_TO_MSB_MSK		GENMASK(6, 0)
+
+/* ADF4382 REG003A Map */
+#define ADF4382_CAL_VCO_TO_LSB_MSK		GENMASK(7, 0)
+
+/* ADF4382 REG003B Map */
+#define ADF4382_DEL_CTRL_DB_MSK			BIT(7)
+#define ADF4382_CAL_VCO_TO_MSB_MSK		GENMASK(6, 0)
+
+/* ADF4382 REG003C Map */
+#define ADF4382_CNTR_DIV_WORD_MSK		GENMASK(7, 0)
+
+/* ADF4382 REG003D Map */
+#define ADF4382_SPARE_3D_MSK			BIT(7)
+#define ADF4382_SYNC_SP_DB_MSK			BIT(6)
+#define ADF4382_CMOS_OV_MSK			BIT(5)
+#define ADF4382_READ_MODE_MSK			BIT(4)
+#define ADF4382_CNTR_DIV_WORD_MSB_MSK		GENMASK(3, 0)
+
+/* ADF4382 REG003E Map */
+#define ADF4382_ADC_CLK_DIV_MSK			GENMASK(7, 0)
+
+/* ADF4382 REG003F Map */
+#define ADF4382_EN_ADC_CNV_MSK			BIT(7)
+#define ADF4382_EN_ADC_VTEST_MSK		BIT(6)
+#define ADF4382_ADC_VTEST_SEL_MSK		BIT(5)
+#define ADF4382_ADC_MUX_SEL_MSK			BIT(4)
+#define ADF4382_ADC_F_CONV_MSK			BIT(3)
+#define ADF4382_ADC_C_CONV_MSK			BIT(2)
+#define ADF4382_EN_ADC_MSK			BIT(1)
+#define ADF4382_SPARE_3F_MSK			BIT(0)
+
+/* ADF4382 REG0040 Map */
+#define ADF4382_EXT_DIV_DEC_SEL_MSK		BIT(7)
+#define ADF4382_ADC_CLK_TEST_SEL_MSK		BIT(6)
+#define ADF4382_MUTE_CLKOUT2_MSK		GENMASK(5, 3)
+#define ADF4382_MUTE_CLKOUT1_MSK		GENMASK(2, 0)
+
+/* ADF4382 REG0041 Map */
+#define ADF4382_EXT_DIV_MSK			GENMASK(7, 5)
+#define ADF4382_EN_VCO_CAP_TEST_MSK		BIT(4)
+#define ADF4382_EN_CALGEN_CAP_TEST_MSK		BIT(3)
+#define ADF4382_EN_CP_CAP_TEST_MSK		BIT(2)
+#define ADF4382_CAP_TEST_STATE_MSK		BIT(1)
+#define ADF4382_TRANS_LOOP_SEL_MSK		BIT(0)
+
+/* ADF4382 REG0042 Map */
+#define ADF4382_NDIV_PWRUP_TIMEOUT_MSK		GENMASK(7, 0)
+
+/* ADF4382 REG0043 Map */
+#define ADF4382_CAL_BLEED_FINE_MAX_MSK		GENMASK(7, 0)
+
+/* ADF4382 REG0044 Map */
+#define ADF4382_VCAL_ZERO_MSK			BIT(7)
+#define ADF4382_VPTAT_CALGEN_MSK		GENMASK(6, 0)
+
+/* ADF4382 REG0045 Map */
+#define ADF4382_SPARE_45_MSK			BIT(7)
+#define ADF4382_VCTAT_CALGEN_MSK		GENMASK(6, 0)
+
+/* ADF4382 REG0046 Map */
+#define ADF4382_NVMDIN_MSK			GENMASK(7, 0)
+
+/* ADF4382 REG0047 Map */
+#define ADF4382_SPARE_47_MSK			BIT(7)
+#define ADF4382_NVMADDR_MSK			GENMASK(6, 3)
+#define ADF4382_NVMBIT_SEL			GENMASK(2, 0)
+
+/* ADF4382 REG0048 Map */
+#define ADF4382_TRIM_LATCH_MSK			BIT(7)
+#define ADF4382_NVMTEST_MSK			BIT(6)
+#define ADF4382_NVMPROG_MSK			BIT(5)
+#define ADF4382_NVMRD_MSK			BIT(4)
+#define ADF4382_NVMSTART_MSK			BIT(3)
+#define ADF4382_NVMON_MSK			BIT(2)
+#define ADF4382_MARGIN_MSK			GENMASK(1, 0)
+
+/* ADF4382 REG0049 Map */
+#define ADF4382_NVMDOUT_MSK			GENMASK(7, 0)
+
+/* ADF4382 REG004A Map */
+#define ADF4382_SCAN_MODE_CODE_MSK		GENMASK(7, 0)
+
+/* ADF4382 REG004B Map */
+#define ADF4382_TEMP_OFFSET_MSK			GENMASK(7, 0)
+
+/* ADF4382 REG004C Map */
+#define ADF4382_SPARE_4C_MSK			GENMASK(7, 6)
+#define ADF4382_TEMP_SLOPE_MSK			GENMASK(5, 0)
+
+/* ADF4382 REG004D Map */
+#define ADF4382_VCO_FSM_TEST_MUX_MSK		GENMASK(7, 5)
+#define ADF4382_SPARE_4D_MSK			GENMASK(4, 3)
+#define ADF4382_O_VCO_BIAS_MSK			BIT(2)
+#define ADF4382_O_VCO_BAND_MSK			BIT(1)
+#define ADF4382_O_VCO_CORE_MSK			BIT(0)
+
+/* ADF4382 REG004E Map */
+#define ADF4382_EN_TWO_PASS_CALL_MSK		BIT(4)
+#define ADF4382_TWO_PASS_BAND_START_MSK		GENMASK(3, 0)
+
+/* ADF4382 REG004F Map */
+#define ADF4382_LUT_SCALE_MSK			GENMASK(7, 0)
+
+/* ADF4382 REG0050 Map */
+#define ADF4382_SPARE0_MSK			GENMASK(7, 0)
+
+/* ADF4382 REG0051 Map */
+#define ADF4382_SPARE1_MSK			GENMASK(7, 0)
+
+/* ADF4382 REG0052 Map */
+#define ADF4382_SYNC_REF_SPARE_MSK		GENMASK(7, 4)
+#define ADF4382_SYNC_MON_DEL_MSK		GENMASK(3, 0)
+
+/* ADF4382 REG0053 Map */
+#define ADF4382_PD_SYNC_MON_MSK			BIT(6)
+#define ADF4382_SYNC_SEL_MSK			BIT(5)
+#define ADF4382_RST_SYNC_MON_MSK		BIT(4)
+#define ADF4382_SYNC_SH_DEL_MSK			GENMASK(3, 0)
+
+/* ADF4382 REG0054 Map */
+#define ADF4382_ADC_ST_CNV_MSK			BIT(0)
+
+/* ADF4382 REG0058 Map */
+#define ADF4382_PLL_LOCK_MSK			BIT(0)
+
+#define ADF4382_MOD2WORD_LSB_MSK		GENMASK(7, 0)
+#define ADF4382_MOD2WORD_MID_MSK		GENMASK(15, 8)
+#define ADF4382_MOD2WORD_MSB_MSK		GENMASK(23, 16)
+
+#define ADF4382_FRAC1WORD_LSB_MSK		GENMASK(7, 0)
+#define ADF4382_FRAC1WORD_MID_MSK		GENMASK(15, 8)
+#define ADF4382_FRAC1WORD_MSB_MSK		GENMASK(23, 16)
+#define ADF4382_FRAC1WORD_MS_BIT_MSK		BIT(24)
+
+#define ADF4382_FRAC2WORD_LSB_MSK		GENMASK(7, 0)
+#define ADF4382_FRAC2WORD_MID_MSK		GENMASK(15, 8)
+#define ADF4382_FRAC2WORD_MSB_MSK		GENMASK(23, 16)
+
+#define ADF4382_DEL_CNT_LSB_MSK			GENMASK(7, 0)
+#define ADF4382_DEL_CNT_MSB_MSK			GENMASK(15, 8)
+
+#define ADF4382_DEL_CNT_FINE_MSK		GENMASK(8, 0)
+#define ADF4382_DEL_CNT_COARSE_MSK		GENMASK(12, 9)
+#define ADF4382_DEL_CNT_BLEED_POL_MSK		BIT(15)
+
+#define ADF4382_REF_MIN				10000000ULL	// 10MHz
+#define ADF4382_REF_MAX				5000000000ULL	// 5GHz
+#define ADF4382_VCO_FREQ_MIN			11000000000ULL	// 11GHz
+#define ADF4382_VCO_FREQ_MAX			22000000000ULL	// 22GHz
+#define ADF4382A_VCO_FREQ_MIN			11500000000ULL	// 11.5GHz
+#define ADF4382A_VCO_FREQ_MAX			21000000000ULL	// 21GHz
+#define ADF4382_PFD_FREQ_MAX			625000000ULL	// 625MHz
+#define ADF4382_PFD_FREQ_FRAC_MAX		250000000ULL	// 250MHz
+#define ADF4382_PFD_FREQ_MIN			5400000ULL	// 5.4MHz
+#define ADF4382_MOD1WORD			0x2000000ULL	// 2^25
+#define ADF4382_MOD2WORD_MAX			0xFFFFFFU	// 2^24 - 1
+#define ADF4382_PHASE_RESYNC_MOD2WORD_MAX	0x1FFFFU	// 2^17 - 1
+#define ADF4382_CHANNEL_SPACING_MAX		78125U
+#define ADF4382_DCLK_DIV1_0_MAX			160000000ULL	// 160MHz
+#define ADF4382_DCLK_DIV1_1_MAX			320000000ULL	// 320MHz
+#define ADF4382_OUT_PWR_MAX			15
+#define ADF4382_CLKOUT_DIV_REG_VAL_MAX		4
+#define ADF4382A_CLKOUT_DIV_REG_VAL_MAX		2
+
+#define ADF4382_CP_I_DEFAULT			15
+#define ADF4382_OPOWER_DEFAULT			11
+#define ADF4382_REF_DIV_DEFAULT			1
+#define ADF4382_RFOUT_DEFAULT			2875000000ULL	// 2.875GHz
+#define ADF4382_SCRATCHPAD_VAL			0xA5
+
+#define ADF4382_PHASE_BLEED_CNST_MUL		511
+#define ADF4382_PHASE_BLEED_CNST_DIV		285
+#define ADF4382_VCO_CAL_CNT			202
+#define ADF4382_VCO_CAL_VTUNE			124
+#define ADF4382_VCO_CAL_ALC			250
+
+#define FS_PER_NS				MICRO
+#define NS_PER_MS				MICRO
+#define MS_PER_NS				MICRO
+#define NS_PER_FS				MICRO
+#define PS_PER_NS				1000
+#define UA_PER_A				1000000
+
+#define PERIOD_IN_DEG				360
+#define PERIOD_IN_DEG_MS			360000
+
+#ifdef CONFIG_64BIT
+#define ADF4382_CLK_SCALE			1
+#else
+#define	ADF4382_CLK_SCALE			10ULL
+#endif
+
+enum {
+	ADF4382_FREQ,
+	ADF4382_EN_AUTO_ALIGN,
+};
+
+enum {
+	ADF4382,
+	ADF4382A,
+};
+
+struct adf4382_state {
+	struct spi_device	*spi;
+	struct regmap		*regmap;
+	struct clk		*clkin;
+	struct clk		*clkout;
+	struct clk_hw		clk_hw;
+	/* Protect against concurrent accesses to the device and data content */
+	struct mutex		lock;
+	struct notifier_block	nb;
+	unsigned int		ref_freq_hz;
+	u8			cp_i;
+	u8			opwr_a;
+	u64			freq;
+	bool			spi_3wire_en;
+	bool			ref_doubler_en;
+	bool			auto_align_en;
+	u8			ref_div;
+	u8			clkout_div_reg_val_max;
+	u16			bleed_word;
+	int			phase;
+	bool			cmos_3v3;
+	u64			vco_max;
+	u64			vco_min;
+};
+
+#define to_adf4382_state(_hw) container_of(_hw, struct adf4382_state, clk_hw)
+
+/* Charge pump current values expressed in uA */
+static const int adf4382_ci_ua[] = {
+	790, 990, 1190, 1380, 1590, 1980, 2390, 2790, 3180, 3970, 4770, 5570,
+	6330, 7910, 9510, 11100
+};
+
+static const struct reg_sequence adf4382_reg_default[] = {
+	{ 0x00a, 0xA5 }, { 0x200, 0x00 }, { 0x201, 0x00 }, { 0x202, 0x00 },
+	{ 0x203, 0x00 }, { 0x203, 0x00 }, { 0x203, 0x00 }, { 0x100, 0x25 },
+	{ 0x101, 0x3F }, { 0x102, 0x3F }, { 0x103, 0x3F }, { 0x104, 0x3F },
+	{ 0x105, 0x3F }, { 0x106, 0x3F }, { 0x107, 0x3F }, { 0x108, 0x3F },
+	{ 0x109, 0x25 }, { 0x10A, 0x25 }, { 0x10B, 0x3F }, { 0x10C, 0x3F },
+	{ 0x10D, 0x3F }, { 0x10E, 0x3F }, { 0x10F, 0x3F }, { 0x110, 0x3F },
+	{ 0x111, 0x3F }, { 0x054, 0x00 }, { 0x053, 0x45 }, { 0x052, 0x00 },
+	{ 0x051, 0x00 }, { 0x050, 0x00 }, { 0x04f, 0x08 }, { 0x04e, 0x06 },
+	{ 0x04d, 0x00 }, { 0x04c, 0x2B }, { 0x04b, 0x5D }, { 0x04a, 0x00 },
+	{ 0x048, 0x00 }, { 0x047, 0x00 }, { 0x046, 0x00 }, { 0x045, 0x62 },
+	{ 0x044, 0x3F }, { 0x043, 0xB8 }, { 0x042, 0x01 }, { 0x041, 0x00 },
+	{ 0x040, 0x00 }, { 0x03f, 0x82 }, { 0x03e, 0x4E }, { 0x03c, 0x00 },
+	{ 0x03b, 0x00 }, { 0x03a, 0xFA }, { 0x039, 0x00 }, { 0x038, 0x71 },
+	{ 0x037, 0x82 }, { 0x036, 0xC0 }, { 0x035, 0x00 }, { 0x034, 0x36 },
+	{ 0x033, 0x00 }, { 0x032, 0x40 }, { 0x031, 0x63 }, { 0x030, 0x0F },
+	{ 0x02f, 0x3F }, { 0x02e, 0x00 }, { 0x02d, 0xF1 }, { 0x02c, 0x0E },
+	{ 0x02b, 0x01 }, { 0x02a, 0x30 }, { 0x029, 0x09 }, { 0x028, 0x00 },
+	{ 0x027, 0xF0 }, { 0x026, 0x00 }, { 0x025, 0x01 }, { 0x024, 0x01 },
+	{ 0x023, 0x00 }, { 0x022, 0x00 }, { 0x021, 0x00 }, { 0x01e, 0x20 },
+	{ 0x01d, 0x00 }, { 0x01c, 0x00 }, { 0x01b, 0x00 }, { 0x01a, 0x00 },
+	{ 0x019, 0x00 }, { 0x018, 0x00 }, { 0x017, 0x00 }, { 0x016, 0x00 },
+	{ 0x015, 0x06 }, { 0x014, 0x00 }, { 0x013, 0x00 }, { 0x012, 0x00 },
+	{ 0x011, 0x00 }, { 0x010, 0x50 }
+};
+
+static const struct regmap_config adf4382_regmap_config = {
+	.reg_bits = 16,
+	.val_bits = 8,
+	.read_flag_mask = BIT(7),
+};
+
+static int adf4382_pfd_compute(struct adf4382_state *st, u64 *pfd_freq_hz)
+{
+	u64 tmp;
+
+	tmp = DIV_ROUND_CLOSEST(st->ref_freq_hz, st->ref_div);
+	if (st->ref_doubler_en)
+		tmp *= 2;
+
+	if (tmp < ADF4382_PFD_FREQ_MIN || tmp > ADF4382_PFD_FREQ_MAX)
+		return -EINVAL;
+
+	*pfd_freq_hz = tmp;
+
+	return 0;
+}
+
+static int adf4382_frac2_compute(struct adf4382_state *st, u64 res,
+				 unsigned int pfd_freq_hz, u32 *frac2_word,
+				 u32 *mod2_word)
+{
+	u32 channel_spacing;
+	u8 en_phase_resync;
+	u32 chsp_freq;
+	u32 mod2_tmp;
+	u32 mod2_max;
+	u32 mod2_wd;
+	u32 gcd_var;
+
+	channel_spacing = 1;
+	mod2_wd = 1;
+
+	en_phase_resync = regmap_test_bits(st->regmap, 0x1E,
+					   ADF4382_EN_PHASE_RESYNC_MSK);
+	if (en_phase_resync < 0)
+		return en_phase_resync;
+
+	if (en_phase_resync)
+		mod2_max = ADF4382_PHASE_RESYNC_MOD2WORD_MAX;
+	else
+		mod2_max = ADF4382_MOD2WORD_MAX;
+
+	do {
+		chsp_freq = channel_spacing * ADF4382_MOD1WORD;
+		gcd_var = gcd(chsp_freq, pfd_freq_hz);
+		mod2_tmp = DIV_ROUND_UP(pfd_freq_hz, gcd_var);
+
+		if (mod2_tmp > mod2_max) {
+			channel_spacing *= 5;
+		} else {
+			mod2_wd = mod2_tmp;
+			break;
+		}
+
+	} while (channel_spacing < ADF4382_CHANNEL_SPACING_MAX);
+
+	if (!en_phase_resync)
+		mod2_wd *= DIV_ROUND_DOWN_ULL(mod2_max, mod2_wd);
+
+	*frac2_word = DIV_ROUND_CLOSEST_ULL(res * mod2_wd, pfd_freq_hz);
+	*mod2_word = mod2_wd;
+
+	return 0;
+}
+
+static int adf4382_pll_fract_n_compute(struct adf4382_state *st, unsigned int pfd_freq_hz,
+				       u16 *n_int, u32 *frac1_word, u32 *frac2_word,
+				       u32 *mod2_word)
+{
+	u64 rem;
+	u64 res;
+
+	*n_int = div64_u64_rem(st->freq, pfd_freq_hz, &rem);
+
+	res = rem * ADF4382_MOD1WORD;
+	*frac1_word = (u32)div64_u64_rem(res, pfd_freq_hz, &rem);
+
+	*frac2_word = 0;
+	*mod2_word = 0;
+
+	if (pfd_freq_hz > ADF4382_PFD_FREQ_FRAC_MAX) {
+		dev_warn(&st->spi->dev, "PFD frequency exceeds 250MHz.");
+		dev_warn(&st->spi->dev, "Only integer mode available.");
+	}
+
+	if (rem > 0)
+		return adf4382_frac2_compute(st, rem, pfd_freq_hz, frac2_word,
+					     mod2_word);
+
+	return 0;
+}
+
+static int _adf4382_set_freq(struct adf4382_state *st)
+{
+	u32 frac2_word = 0;
+	u32 mod2_word = 0;
+	u64 pfd_freq_hz;
+	u32 frac1_word;
+	u8 clkout_div;
+	u32 read_val;
+	u8 dclk_div1;
+	u8 int_mode;
+	u8 en_bleed;
+	u8 ldwin_pw;
+	u16 n_int;
+	u8 div1;
+	u64 tmp;
+	u64 vco;
+	int ret;
+	u8 var;
+
+	ret = adf4382_pfd_compute(st, &pfd_freq_hz);
+	if (ret) {
+		dev_err(&st->spi->dev, "PFD frequency is out of range.\n");
+		return ret;
+	}
+
+	for (clkout_div = 0; clkout_div <= st->clkout_div_reg_val_max; clkout_div++) {
+		tmp =  (1 << clkout_div) * st->freq;
+		if (tmp < st->vco_min || tmp > st->vco_max)
+			continue;
+
+		vco = tmp;
+		break;
+	}
+
+	if (vco == 0) {
+		dev_err(&st->spi->dev, "Output frequency is out of range.\n");
+		ret = -EINVAL;
+		return ret;
+	}
+
+	ret = adf4382_pll_fract_n_compute(st, pfd_freq_hz, &n_int, &frac1_word,
+					  &frac2_word, &mod2_word);
+	if (ret)
+		return ret;
+
+	if (frac1_word || frac2_word) {
+		int_mode = 0;
+		en_bleed = 1;
+
+		if (pfd_freq_hz <= (40 * HZ_PER_MHZ)) {
+			ldwin_pw = 7;
+		} else if (pfd_freq_hz <= (50 * HZ_PER_MHZ)) {
+			ldwin_pw = 6;
+		} else if (pfd_freq_hz <= (100 * HZ_PER_MHZ)) {
+			ldwin_pw = 5;
+		} else if (pfd_freq_hz <= (200 * HZ_PER_MHZ)) {
+			ldwin_pw = 4;
+		} else if (pfd_freq_hz <= (250 * HZ_PER_MHZ)) {
+			if (st->freq >= (5000U * HZ_PER_MHZ) &&
+			    st->freq < (6400U * HZ_PER_MHZ)) {
+				ldwin_pw = 3;
+			} else {
+				ldwin_pw = 2;
+			}
+		}
+	} else {
+		int_mode = 1;
+		en_bleed = 0;
+
+		tmp = DIV_ROUND_UP_ULL(pfd_freq_hz, UA_PER_A);
+		tmp *= adf4382_ci_ua[st->cp_i];
+		tmp = DIV_ROUND_UP_ULL(st->bleed_word, tmp);
+		if (tmp <= 85)
+			ldwin_pw = 0;
+		else
+			ldwin_pw = 1;
+	}
+
+	dev_dbg(&st->spi->dev,
+		"VCO=%llu PFD=%llu RFout_div=%u N=%u FRAC1=%u FRAC2=%u MOD2=%u\n",
+		vco, pfd_freq_hz, 1 << clkout_div, n_int,
+		frac1_word, frac2_word, mod2_word);
+
+	ret = regmap_update_bits(st->regmap, 0x28, ADF4382_VAR_MOD_EN_MSK,
+				 frac2_word != 0 ? 0xff : 0);
+	if (ret)
+		return ret;
+
+	ret = regmap_update_bits(st->regmap, 0x15, ADF4382_INT_MODE_MSK,
+				 FIELD_PREP(ADF4382_INT_MODE_MSK, int_mode));
+	if (ret)
+		return ret;
+
+	ret = regmap_write(st->regmap, 0x1D,
+			   FIELD_GET(ADF4382_BLEED_I_LSB_MSK, st->bleed_word));
+	if (ret)
+		return ret;
+
+	var = (st->bleed_word >> 8) & ADF4382_BLEED_I_MSB_MSK;
+	ret = regmap_update_bits(st->regmap, 0x1E, ADF4382_BLEED_I_MSB_MSK, var);
+	if (ret)
+		return ret;
+	ret = regmap_update_bits(st->regmap, 0x1F, ADF4382_EN_BLEED_MSK,
+				 FIELD_PREP(ADF4382_EN_BLEED_MSK, en_bleed));
+	if (ret)
+		return ret;
+
+	ret = regmap_write(st->regmap, 0x1A,
+			   FIELD_GET(ADF4382_MOD2WORD_LSB_MSK, mod2_word));
+	if (ret)
+		return ret;
+
+	ret = regmap_write(st->regmap, 0x1B,
+			   FIELD_GET(ADF4382_MOD2WORD_MID_MSK, mod2_word));
+	if (ret)
+		return ret;
+
+	ret = regmap_write(st->regmap, 0x1C,
+			   FIELD_GET(ADF4382_MOD2WORD_MSB_MSK, mod2_word));
+	if (ret)
+		return ret;
+
+	ret = regmap_write(st->regmap, 0x12,
+			   FIELD_GET(ADF4382_FRAC1WORD_LSB_MSK, frac1_word));
+	if (ret)
+		return ret;
+
+	ret = regmap_write(st->regmap, 0x13,
+			   FIELD_GET(ADF4382_FRAC1WORD_MID_MSK, frac1_word));
+	if (ret)
+		return ret;
+
+	ret = regmap_write(st->regmap, 0x14,
+			   FIELD_GET(ADF4382_FRAC1WORD_MSB_MSK, frac1_word));
+	if (ret)
+		return ret;
+
+	ret = regmap_update_bits(st->regmap, 0x15, ADF4382_FRAC1WORD_MSB,
+				 FIELD_GET(ADF4382_FRAC1WORD_MS_BIT_MSK, frac1_word));
+	if (ret)
+		return ret;
+
+	ret = regmap_write(st->regmap, 0x17,
+			   FIELD_GET(ADF4382_FRAC2WORD_LSB_MSK, frac2_word));
+	if (ret)
+		return ret;
+
+	ret = regmap_write(st->regmap, 0x18,
+			   FIELD_GET(ADF4382_FRAC2WORD_MID_MSK, frac2_word));
+	if (ret)
+		return ret;
+
+	ret = regmap_write(st->regmap, 0x19,
+			   FIELD_GET(ADF4382_FRAC2WORD_MSB_MSK, frac2_word));
+	if (ret)
+		return ret;
+
+	dclk_div1 = 2;
+	div1 = 8;
+	if (pfd_freq_hz <= ADF4382_DCLK_DIV1_0_MAX) {
+		dclk_div1 = 0;
+		div1 = 1;
+	} else if (pfd_freq_hz <= ADF4382_DCLK_DIV1_1_MAX) {
+		dclk_div1 = 1;
+		div1 = 2;
+	}
+
+	ret = regmap_update_bits(st->regmap, 0x24, ADF4382_DCLK_DIV1_MSK,
+				 FIELD_PREP(ADF4382_DCLK_DIV1_MSK, dclk_div1));
+	if (ret)
+		return ret;
+
+	ret = regmap_update_bits(st->regmap, 0x31, ADF4382_DCLK_MODE_MSK, 0xff);
+	if (ret)
+		return ret;
+
+	ret = regmap_update_bits(st->regmap, 0x31, ADF4382_CAL_CT_SEL_MSK, 0xff);
+	if (ret)
+		return ret;
+
+	ret = regmap_write(st->regmap, 0x38, ADF4382_VCO_CAL_VTUNE);
+	if (ret)
+		return ret;
+
+	ret = regmap_write(st->regmap, 0x3a, ADF4382_VCO_CAL_ALC);
+	if (ret)
+		return ret;
+
+	ret = regmap_write(st->regmap, 0x37, ADF4382_VCO_CAL_CNT);
+	if (ret)
+		return ret;
+
+	var = DIV_ROUND_UP(div_u64(pfd_freq_hz, div1 * 400000) - 2, 4);
+	var = clamp_t(u8, var, 0U, 255U);
+	ret = regmap_write(st->regmap, 0x3e, var);
+	if (ret)
+		return ret;
+
+	ret = regmap_update_bits(st->regmap, 0x2c, ADF4382_LD_COUNT_OPWR_MSK,
+				 10);
+	if (ret)
+		return ret;
+
+	ret = regmap_update_bits(st->regmap, 0x2c, ADF4382_LDWIN_PW_MSK,
+				 FIELD_PREP(ADF4382_LDWIN_PW_MSK, ldwin_pw));
+	if (ret)
+		return ret;
+
+	ret = regmap_update_bits(st->regmap, 0x11, ADF4382_CLKOUT_DIV_MSK,
+				 FIELD_PREP(ADF4382_CLKOUT_DIV_MSK, clkout_div));
+	if (ret)
+		return ret;
+
+	var = (n_int >> 8) & ADF4382_N_INT_MSB_MSK;
+	ret = regmap_update_bits(st->regmap, 0x11, ADF4382_N_INT_MSB_MSK, var);
+	if (ret)
+		return ret;
+
+	ret  = regmap_write(st->regmap, 0x10,
+			    FIELD_PREP(ADF4382_N_INT_LSB_MSK, n_int));
+	if (ret)
+		return ret;
+
+	mdelay(1);
+
+	ret = regmap_read(st->regmap, 0x58, &read_val);
+	if (ret)
+		return ret;
+
+	if (!FIELD_GET(ADF4382_PLL_LOCK_MSK, read_val)) {
+		dev_err(&st->spi->dev, "PLL is not locked.\n");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int adf4382_set_freq(struct adf4382_state *st)
+{
+	int ret;
+
+	mutex_lock(&st->lock);
+	ret = _adf4382_set_freq(st);
+	mutex_unlock(&st->lock);
+
+	return ret;
+}
+
+static int adf4382_get_freq(struct adf4382_state *st, u64 *val)
+{
+	unsigned int tmp;
+	u32 frac1 = 0;
+	u32 frac2 = 0;
+	u32 mod2 = 0;
+	u64 freq;
+	u64 pfd;
+	u16 n;
+	int ret;
+
+	ret = adf4382_pfd_compute(st, &pfd);
+	if (ret)
+		return ret;
+
+	ret = regmap_read(st->regmap, 0x11, &tmp);
+	if  (ret)
+		return ret;
+
+	n = FIELD_PREP(ADF4382_N_INT_MSB_MSK, tmp);
+	n = n << 8;
+
+	ret = regmap_read(st->regmap, 0x10, &tmp);
+	if  (ret)
+		return ret;
+	n |= FIELD_PREP(ADF4382_N_INT_LSB_MSK, tmp);
+
+	ret = regmap_read(st->regmap, 0x15, &tmp);
+	if  (ret)
+		return ret;
+	frac1 |= FIELD_PREP(ADF4382_FRAC1WORD_MS_BIT_MSK, tmp);
+
+	ret = regmap_read(st->regmap, 0x14, &tmp);
+	if  (ret)
+		return ret;
+	frac1 |= FIELD_PREP(ADF4382_FRAC1WORD_MSB_MSK, tmp);
+
+	ret = regmap_read(st->regmap, 0x13, &tmp);
+	if  (ret)
+		return ret;
+	frac1 |= FIELD_PREP(ADF4382_FRAC1WORD_MID_MSK, tmp);
+
+	ret = regmap_read(st->regmap, 0x12, &tmp);
+	if  (ret)
+		return ret;
+	frac1 |= FIELD_PREP(ADF4382_FRAC1WORD_LSB_MSK, tmp);
+
+	ret = regmap_read(st->regmap, 0x19, &tmp);
+	if  (ret)
+		return ret;
+	frac2 |= FIELD_PREP(ADF4382_FRAC2WORD_MSB_MSK, tmp);
+
+	ret = regmap_read(st->regmap, 0x18, &tmp);
+	if  (ret)
+		return ret;
+	frac2 |= FIELD_PREP(ADF4382_FRAC2WORD_MID_MSK, tmp);
+
+	ret = regmap_read(st->regmap, 0x17, &tmp);
+	if  (ret)
+		return ret;
+	frac2 |= FIELD_PREP(ADF4382_FRAC2WORD_LSB_MSK, tmp);
+
+	ret = regmap_read(st->regmap, 0x1c, &tmp);
+	if  (ret)
+		return ret;
+	mod2 |= FIELD_PREP(ADF4382_MOD2WORD_MSB_MSK, tmp);
+
+	ret = regmap_read(st->regmap, 0x1b, &tmp);
+	if  (ret)
+		return ret;
+	mod2 |= FIELD_PREP(ADF4382_MOD2WORD_MID_MSK, tmp);
+
+	ret = regmap_read(st->regmap, 0x1a, &tmp);
+	if  (ret)
+		return ret;
+	mod2 |= FIELD_PREP(ADF4382_MOD2WORD_LSB_MSK, tmp);
+
+	if (mod2 == 0)
+		mod2 = 1;
+
+	freq = frac2 * pfd;
+	freq = div_u64(freq, mod2);
+	freq = freq + (frac1 * pfd);
+	freq = div_u64(freq, ADF4382_MOD1WORD);
+	freq = freq + (n * pfd);
+
+	*val = freq;
+	return 0;
+}
+
+static int adf4382_set_phase_adjust(struct adf4382_state *st, u32 phase_fs)
+{
+	u8 phase_reg_value;
+	u64 phase_deg_fs;
+	u64 phase_deg_ns;
+	u64 phase_deg_ms;
+	u64 phase_bleed;
+	u64 phase_value;
+	u64 pfd_freq_hz;
+	u64 phase_ci;
+	int ret;
+
+	ret = regmap_update_bits(st->regmap, 0x1E, ADF4382_EN_PHASE_RESYNC_MSK,
+				 0xff);
+	if (ret)
+		return ret;
+
+	ret = regmap_update_bits(st->regmap, 0x1F, ADF4382_EN_BLEED_MSK, 0xff);
+	if (ret)
+		return ret;
+
+	ret = regmap_update_bits(st->regmap, 0x32, ADF4382_DEL_MODE_MSK, 0x0);
+	if (ret)
+		return ret;
+
+	ret = adf4382_pfd_compute(st, &pfd_freq_hz);
+	if (ret) {
+		dev_err(&st->spi->dev, "PFD frequency is out of range.\n");
+		return ret;
+	}
+
+	// Determine the phase adjustment in degrees relative the output freq.
+	phase_deg_fs = phase_fs * st->freq;
+	phase_deg_ns = div_u64(phase_deg_fs, FS_PER_NS);
+	phase_deg_ns = PERIOD_IN_DEG * phase_deg_ns;
+	phase_deg_ms = div_u64(phase_deg_ns, NS_PER_MS);
+
+	if (phase_deg_ms > PERIOD_IN_DEG_MS) {
+		dev_err(&st->spi->dev, "Phase adjustment is out of range.\n");
+		return -EINVAL;
+	}
+
+	/*
+	 * The charge pump current will also need to be taken in to account
+	 * as well as the Bleed constant
+	 */
+	phase_ci = phase_deg_ms * adf4382_ci_ua[st->cp_i];
+	phase_bleed = phase_ci * ADF4382_PHASE_BLEED_CNST_MUL;
+	phase_bleed = div_u64(phase_bleed, ADF4382_PHASE_BLEED_CNST_DIV);
+
+	// Computation of the register value for the phase adjust
+	phase_value = phase_bleed * pfd_freq_hz;
+	phase_value = div64_u64(phase_value, st->freq);
+	phase_value = div_u64(phase_value, PERIOD_IN_DEG);
+	phase_value = DIV_ROUND_CLOSEST_ULL(phase_value, MILLI);
+
+	// Mask the value to 8 bits
+	phase_reg_value = phase_value & 0xff;
+
+	ret = regmap_write(st->regmap, 0x33, phase_reg_value);
+	if (ret)
+		return ret;
+
+	if (st->auto_align_en)
+		return regmap_update_bits(st->regmap, 0x32,
+					  ADF4382_EN_AUTO_ALIGN_MSK, 0xff);
+
+	ret = regmap_update_bits(st->regmap, 0x32, ADF4382_EN_AUTO_ALIGN_MSK, 0x0);
+	if (ret)
+		return ret;
+
+	return regmap_update_bits(st->regmap, 0x34, ADF4382_PHASE_ADJ_MSK, 0xff);
+}
+
+static int adf4382_get_phase_adjust(struct adf4382_state *st, u32 *val)
+{
+	unsigned int tmp;
+	u8 phase_reg_value;
+	u64 phase_value;
+	u64 pfd_freq_hz;
+	int ret;
+
+	ret = regmap_read(st->regmap, 0x33, &tmp);
+	if (ret)
+		return ret;
+
+	phase_reg_value = tmp;
+
+	ret = adf4382_pfd_compute(st, &pfd_freq_hz);
+	if (ret) {
+		dev_err(&st->spi->dev, "PFD frequency is out of range.\n");
+		return ret;
+	}
+
+	phase_value = phase_reg_value * PERIOD_IN_DEG;
+	phase_value = phase_value * st->freq;
+	phase_value = div64_u64(phase_value, pfd_freq_hz);
+
+	phase_value = phase_value * ADF4382_PHASE_BLEED_CNST_DIV;
+	phase_value = phase_value * MS_PER_NS;
+	phase_value = div_u64(phase_value, ADF4382_PHASE_BLEED_CNST_MUL);
+	phase_value = phase_value * MILLI;
+	phase_value = div_u64(phase_value, adf4382_ci_ua[st->cp_i]);
+
+	phase_value = phase_value * NS_PER_FS;
+	phase_value = div_u64(phase_value, PERIOD_IN_DEG);
+	phase_value = div64_u64(phase_value, st->freq);
+
+	*val = (u32)phase_value;
+
+	return 0;
+}
+
+static int adf4382_set_phase_pol(struct adf4382_state *st, bool ph_pol)
+{
+	return regmap_update_bits(st->regmap, 0x32, ADF4382_PHASE_ADJ_POL_MSK,
+				  FIELD_PREP(ADF4382_PHASE_ADJ_POL_MSK, ph_pol));
+}
+
+static int adf4382_get_phase_pol(struct adf4382_state *st, bool *ph_pol)
+{
+	unsigned int tmp;
+	int ret;
+
+	ret =  regmap_read(st->regmap, 0x32, &tmp);
+	if (ret)
+		return ret;
+
+	*ph_pol = FIELD_GET(ADF4382_PHASE_ADJ_POL_MSK, tmp);
+
+	return 0;
+}
+
+static int adf4382_set_out_power(struct adf4382_state *st, int ch, int pwr)
+{
+	if (pwr > ADF4382_OUT_PWR_MAX)
+		pwr = ADF4382_OUT_PWR_MAX;
+
+	if (!ch) {
+		return regmap_update_bits(st->regmap, 0x29, ADF4382_CLK1_OPWR_MSK,
+					  FIELD_PREP(ADF4382_CLK1_OPWR_MSK, pwr));
+	}
+
+	return regmap_update_bits(st->regmap, 0x29, ADF4382_CLK2_OPWR_MSK,
+				  FIELD_PREP(ADF4382_CLK2_OPWR_MSK, pwr));
+
+};
+
+static int adf4382_get_out_power(struct adf4382_state *st, int ch, int *pwr)
+{
+	unsigned int tmp;
+	int ret;
+
+	ret = regmap_read(st->regmap, 0x29, &tmp);
+	if (ret)
+		return ret;
+
+	if (!ch)
+		*pwr = FIELD_GET(ADF4382_CLK1_OPWR_MSK, tmp);
+	else
+		*pwr = FIELD_GET(ADF4382_CLK2_OPWR_MSK, tmp);
+
+	return 0;
+}
+
+static int adf4382_set_en_chan(struct adf4382_state *st, int ch, int en)
+{
+	if (!ch) {
+		return regmap_update_bits(st->regmap, 0x2B,
+					  ADF4382_PD_CLKOUT1_MSK,
+					  FIELD_PREP(ADF4382_PD_CLKOUT1_MSK, !en));
+	}
+
+	return regmap_update_bits(st->regmap, 0x2B, ADF4382_PD_CLKOUT2_MSK,
+				  FIELD_PREP(ADF4382_PD_CLKOUT2_MSK, !en));
+}
+
+static int adf4382_get_en_chan(struct adf4382_state *st, int ch, int *en)
+{
+	int enable;
+
+	if (!ch)
+		enable = regmap_test_bits(st->regmap, 0x2B,
+					  ADF4382_PD_CLKOUT1_MSK);
+	else
+		enable = regmap_test_bits(st->regmap, 0x2B,
+					  ADF4382_PD_CLKOUT2_MSK);
+	if (enable < 0)
+		return enable;
+
+	*en = !enable;
+	return 0;
+}
+
+static ssize_t adf4382_write(struct iio_dev *indio_dev, uintptr_t private,
+			     const struct iio_chan_spec *chan, const char *buf,
+			     size_t len)
+{
+	struct adf4382_state *st = iio_priv(indio_dev);
+	unsigned long long val;
+	int ret;
+
+	ret = kstrtoull(buf, 10, &val);
+	if (ret)
+		return ret;
+
+	switch ((u32)private) {
+	case ADF4382_FREQ:
+		st->freq = val;
+		ret = adf4382_set_freq(st);
+		break;
+	case ADF4382_EN_AUTO_ALIGN:
+		st->auto_align_en = !!val;
+		ret = adf4382_set_phase_adjust(st, 0);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return ret ? ret : len;
+}
+
+static ssize_t adf4382_read(struct iio_dev *indio_dev, uintptr_t private,
+			    const struct iio_chan_spec *chan, char *buf)
+{
+	struct adf4382_state *st = iio_priv(indio_dev);
+	unsigned int val = 0;
+	u64 val_u64 = 0;
+	int ret;
+
+	switch ((u32)private) {
+	case ADF4382_FREQ:
+		ret = adf4382_get_freq(st, &val_u64);
+		if (ret)
+			return ret;
+		return sysfs_emit(buf, "%llu\n", val_u64);
+	case ADF4382_EN_AUTO_ALIGN:
+		ret = regmap_read(st->regmap, 0x32, &val);
+		if (ret)
+			return ret;
+		return sysfs_emit(buf, "%lu\n",
+				  FIELD_GET(ADF4382_EN_AUTO_ALIGN_MSK, val));
+	default:
+		return -EINVAL;
+	}
+}
+
+#define _ADF4382_EXT_INFO(_name, _shared, _ident) { \
+		.name = _name, \
+		.read = adf4382_read, \
+		.write = adf4382_write, \
+		.private = _ident, \
+		.shared = _shared, \
+}
+
+static const struct iio_chan_spec_ext_info adf4382_ext_info[] = {
+	/*
+	 * Usually we use IIO_CHAN_INFO_FREQUENCY, but there are
+	 * values > 2^32 in order to support the entire frequency range
+	 * in Hz.
+	 */
+	_ADF4382_EXT_INFO("frequency", IIO_SHARED_BY_TYPE, ADF4382_FREQ),
+	_ADF4382_EXT_INFO("en_auto_align", IIO_SHARED_BY_TYPE, ADF4382_EN_AUTO_ALIGN),
+	{ },
+};
+
+static int adf4382_read_raw(struct iio_dev *indio_dev,
+			    struct iio_chan_spec const *chan,
+			    int *val,
+			    int *val2,
+			    long mask)
+{
+	struct adf4382_state *st = iio_priv(indio_dev);
+	bool pol;
+	u32 tmp;
+	int ret;
+
+	switch (mask) {
+	case IIO_CHAN_INFO_HARDWAREGAIN:
+		ret = adf4382_get_out_power(st, chan->channel, val);
+		if (ret)
+			return ret;
+		return IIO_VAL_INT;
+	case IIO_CHAN_INFO_ENABLE:
+		ret = adf4382_get_en_chan(st, chan->channel, val);
+		if (ret)
+			return ret;
+		return IIO_VAL_INT;
+	case IIO_CHAN_INFO_PHASE:
+		ret = adf4382_get_phase_adjust(st, &tmp);
+		if (ret)
+			return ret;
+		*val = tmp;
+
+		ret = adf4382_get_phase_pol(st, &pol);
+		if (ret)
+			return ret;
+
+		if (pol)
+			*val *= -1;
+
+		return IIO_VAL_INT;
+	default:
+		return -EINVAL;
+	}
+}
+
+static int adf4382_write_raw(struct iio_dev *indio_dev,
+			     struct iio_chan_spec const *chan,
+			     int val,
+			     int val2,
+			     long mask)
+{
+	struct adf4382_state *st = iio_priv(indio_dev);
+	int ret;
+
+	switch (mask) {
+	case IIO_CHAN_INFO_HARDWAREGAIN:
+		return adf4382_set_out_power(st, chan->channel, val);
+	case IIO_CHAN_INFO_ENABLE:
+		return adf4382_set_en_chan(st, chan->channel, val);
+	case IIO_CHAN_INFO_PHASE:
+		st->phase = val;
+
+		if (val < 0)
+			ret = adf4382_set_phase_pol(st, true);
+		else
+			ret = adf4382_set_phase_pol(st, false);
+		if (ret)
+			return ret;
+
+		return adf4382_set_phase_adjust(st, abs(val));
+	default:
+		return -EINVAL;
+	}
+}
+
+static int adf4382_reg_access(struct iio_dev *indio_dev,
+			      unsigned int reg,
+			      unsigned int write_val,
+			      unsigned int *read_val)
+{
+	struct adf4382_state *st = iio_priv(indio_dev);
+
+	if (read_val)
+		return regmap_read(st->regmap, reg, read_val);
+
+	return regmap_write(st->regmap, reg, write_val);
+}
+
+static const struct iio_info adf4382_info = {
+	.read_raw = &adf4382_read_raw,
+	.write_raw = &adf4382_write_raw,
+	.debugfs_reg_access = &adf4382_reg_access,
+};
+
+static const struct iio_chan_spec adf4382_channels[] = {
+	{
+		.type = IIO_ALTVOLTAGE,
+		.info_mask_separate = BIT(IIO_CHAN_INFO_ENABLE) |
+				      BIT(IIO_CHAN_INFO_HARDWAREGAIN),
+		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_PHASE),
+		.indexed = 1,
+		.output = 1,
+		.channel = 0,
+		.ext_info = adf4382_ext_info,
+	},
+	{
+		.type = IIO_ALTVOLTAGE,
+		.info_mask_separate = BIT(IIO_CHAN_INFO_ENABLE) |
+				      BIT(IIO_CHAN_INFO_HARDWAREGAIN),
+		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_PHASE),
+		.indexed = 1,
+		.output = 1,
+		.channel = 1,
+		.ext_info = adf4382_ext_info,
+	},
+};
+
+#ifdef CONFIG_DEBUG_FS
+static int adf4382_show_del_cnt_raw(void *arg, u64 *val)
+{
+	struct iio_dev *indio_dev = arg;
+	struct adf4382_state *st = iio_priv(indio_dev);
+	unsigned int tmp;
+	u16 del_cnt = 0;
+	int ret;
+
+	ret = regmap_read(st->regmap, 0x64, &tmp);
+	if (ret)
+		return ret;
+	del_cnt |= FIELD_PREP(ADF4382_DEL_CNT_LSB_MSK, tmp);
+
+	ret = regmap_read(st->regmap, 0x65, &tmp);
+	if (ret)
+		return ret;
+	del_cnt |= FIELD_PREP(ADF4382_DEL_CNT_MSB_MSK, tmp);
+
+	*val = del_cnt;
+
+	return 0;
+}
+DEFINE_DEBUGFS_ATTRIBUTE(adf4382_del_cnt_raw_fops,
+			 adf4382_show_del_cnt_raw, NULL, "%llu\n");
+
+static int adf4382_show_bleed_pol(void *arg, u64 *val)
+{
+	struct iio_dev *indio_dev = arg;
+	struct adf4382_state *st = iio_priv(indio_dev);
+	unsigned int tmp;
+	u16 del_cnt = 0;
+	u8 bleed_pol = 0;
+	int ret;
+
+	ret = regmap_read(st->regmap, 0x64, &tmp);
+	if (ret)
+		return ret;
+	del_cnt |= FIELD_PREP(ADF4382_DEL_CNT_LSB_MSK, tmp);
+
+	ret = regmap_read(st->regmap, 0x65, &tmp);
+	if (ret)
+		return ret;
+	del_cnt |= FIELD_PREP(ADF4382_DEL_CNT_MSB_MSK, tmp);
+
+	bleed_pol = FIELD_GET(ADF4382_DEL_CNT_BLEED_POL_MSK, del_cnt);
+
+	*val = bleed_pol;
+
+	return 0;
+}
+DEFINE_DEBUGFS_ATTRIBUTE(adf4382_bleed_pol_fops,
+			 adf4382_show_bleed_pol, NULL, "%llu\n");
+
+static int adf4382_show_fine_current(void *arg, u64 *val)
+{
+	struct iio_dev *indio_dev = arg;
+	struct adf4382_state *st = iio_priv(indio_dev);
+	u8 fine_current = 0;
+	unsigned int tmp;
+	u16 del_cnt = 0;
+	int ret;
+
+	ret = regmap_read(st->regmap, 0x64, &tmp);
+	if (ret)
+		return ret;
+	del_cnt |= FIELD_PREP(ADF4382_DEL_CNT_LSB_MSK, tmp);
+
+	ret = regmap_read(st->regmap, 0x65, &tmp);
+	if (ret)
+		return ret;
+	del_cnt |= FIELD_PREP(ADF4382_DEL_CNT_MSB_MSK, tmp);
+
+	fine_current = FIELD_GET(ADF4382_DEL_CNT_FINE_MSK, del_cnt);
+
+	*val = fine_current;
+
+	return 0;
+}
+DEFINE_DEBUGFS_ATTRIBUTE(adf4382_fine_current_fops,
+			 adf4382_show_fine_current, NULL, "%llu\n");
+
+static int adf4382_show_coarse_current(void *arg, u64 *val)
+{
+	struct iio_dev *indio_dev = arg;
+	struct adf4382_state *st = iio_priv(indio_dev);
+	u8 coarse_current = 0;
+	unsigned int tmp;
+	u16 del_cnt = 0;
+	int ret;
+
+	ret = regmap_read(st->regmap, 0x64, &tmp);
+	if (ret)
+		return ret;
+	del_cnt |= FIELD_PREP(ADF4382_DEL_CNT_LSB_MSK, tmp);
+
+	ret = regmap_read(st->regmap, 0x65, &tmp);
+	if (ret)
+		return ret;
+	del_cnt |= FIELD_PREP(ADF4382_DEL_CNT_MSB_MSK, tmp);
+
+	coarse_current = FIELD_GET(ADF4382_DEL_CNT_COARSE_MSK, del_cnt);
+
+	*val = coarse_current;
+
+	return 0;
+}
+DEFINE_DEBUGFS_ATTRIBUTE(adf4382_coarse_current_fops,
+			 adf4382_show_coarse_current, NULL, "%llu\n");
+
+static void adf4382_debugfs_init(struct iio_dev *indio_dev)
+{
+	struct dentry *d = iio_get_debugfs_dentry(indio_dev);
+
+	debugfs_create_file_unsafe("del_cnt_raw", 0400, d,
+				   indio_dev, &adf4382_del_cnt_raw_fops);
+
+	debugfs_create_file_unsafe("bleed_pol", 0400, d,
+				   indio_dev, &adf4382_bleed_pol_fops);
+
+	debugfs_create_file_unsafe("fine_current", 0400, d,
+				   indio_dev, &adf4382_fine_current_fops);
+
+	debugfs_create_file_unsafe("coarse_current", 0400, d,
+				   indio_dev, &adf4382_coarse_current_fops);
+}
+#else
+static void adf4382_debugfs_init(struct iio_dev *indio_dev)
+{
+}
+#endif
+
+static int adf4382_parse_device(struct adf4382_state *st)
+{
+	u32 tmp;
+	int ret;
+	int i;
+
+	ret = device_property_read_u64(&st->spi->dev, "adi,power-up-frequency",
+				       &st->freq);
+	if (ret)
+		st->freq = ADF4382_RFOUT_DEFAULT;
+
+	ret = device_property_read_u32(&st->spi->dev, "adi,bleed-word",
+				       &tmp);
+	if (ret)
+		st->bleed_word = 0;
+	else
+		st->bleed_word = (u16)tmp;
+
+	ret = device_property_read_u32(&st->spi->dev, "adi,charge-pump-microamp",
+				       &tmp);
+	if (ret) {
+		st->cp_i = ADF4382_CP_I_DEFAULT;
+	} else {
+		i = find_closest(tmp, adf4382_ci_ua, ARRAY_SIZE(adf4382_ci_ua));
+		st->cp_i = (u8)i;
+	}
+
+	ret = device_property_read_u32(&st->spi->dev, "adi,output-power-value",
+				       &tmp);
+	if (ret)
+		st->opwr_a = ADF4382_OPOWER_DEFAULT;
+	else
+		st->opwr_a = clamp_t(u8, tmp, 0, 15);
+
+	ret = device_property_read_u32(&st->spi->dev, "adi,ref-divider",
+				       &tmp);
+	if (ret || !tmp)
+		st->ref_div = ADF4382_REF_DIV_DEFAULT;
+	else
+		st->ref_div = (u8)tmp;
+
+	st->spi_3wire_en = device_property_read_bool(&st->spi->dev,
+						     "adi,spi-3wire-enable");
+	st->ref_doubler_en = device_property_read_bool(&st->spi->dev,
+						       "adi,ref-doubler-enable");
+	st->cmos_3v3 = device_property_read_bool(&st->spi->dev, "adi,cmos-3v3");
+
+	st->clkin = devm_clk_get_enabled(&st->spi->dev, "ref_clk");
+
+	return PTR_ERR_OR_ZERO(st->clkin);
+}
+
+static int adf4382_scratchpad_check(struct adf4382_state *st)
+{
+	unsigned int val;
+	int ret;
+
+	ret = regmap_write(st->regmap, 0x0A, ADF4382_SCRATCHPAD_VAL);
+	if (ret)
+		return ret;
+
+	ret = regmap_read(st->regmap, 0x0A, &val);
+	if (ret)
+		return ret;
+
+	if (val != ADF4382_SCRATCHPAD_VAL)
+		return dev_err_probe(&st->spi->dev, -EINVAL,
+				      "Scratch pad test failed please check SPI connection");
+
+	return 0;
+}
+
+static int adf4382_init(struct adf4382_state *st)
+{
+	int ret;
+	bool en = true;
+
+	ret = regmap_write(st->regmap, 0x00, ADF4382_RESET_CMD);
+	if (ret)
+		return ret;
+
+	if (st->spi->mode & SPI_3WIRE || st->spi_3wire_en)
+		en = false;
+
+	ret = regmap_write(st->regmap, 0x00,
+			   FIELD_PREP(ADF4382_SDO_ACT_MSK, en) |
+			   FIELD_PREP(ADF4382_SDO_ACT_R_MSK, en));
+	if (ret)
+		return ret;
+
+	ret = regmap_write(st->regmap, 0x3D,
+			   FIELD_PREP(ADF4382_CMOS_OV_MSK, st->cmos_3v3));
+	if (ret)
+		return ret;
+
+	ret = adf4382_scratchpad_check(st);
+	if (ret)
+		return ret;
+
+	ret = regmap_write(st->regmap, 0x20,
+			   ADF4382_EN_AUTOCAL_MSK |
+			   FIELD_PREP(ADF4382_EN_RDBLR_MSK, st->ref_doubler_en) |
+			   FIELD_PREP(ADF4382_R_DIV_MSK, st->ref_div));
+	if (ret)
+		return ret;
+
+	ret = regmap_write(st->regmap, 0x1f, st->cp_i);
+	if (ret)
+		return ret;
+
+	ret = regmap_multi_reg_write(st->regmap, adf4382_reg_default,
+				     ARRAY_SIZE(adf4382_reg_default));
+	if (ret)
+		return ret;
+
+	st->ref_freq_hz = clk_get_rate(st->clkin);
+
+	ret = adf4382_set_out_power(st, 0, st->opwr_a);
+	if (ret)
+		return ret;
+
+	ret = adf4382_set_out_power(st, 1, st->opwr_a);
+	if (ret)
+		return ret;
+
+	return _adf4382_set_freq(st);
+}
+
+static int adf4382_freq_change(struct notifier_block *nb, unsigned long action,
+			       void *data)
+{
+	struct adf4382_state *st = container_of(nb, struct adf4382_state, nb);
+	int ret;
+
+	if (action == POST_RATE_CHANGE) {
+		mutex_lock(&st->lock);
+		ret = notifier_from_errno(adf4382_init(st));
+		mutex_unlock(&st->lock);
+		return ret;
+	}
+
+	return NOTIFY_OK;
+}
+
+static int adf4382_clock_set_rate(struct clk_hw *hw, unsigned long rate,
+				  unsigned long parent_rate)
+{
+	struct adf4382_state *st = to_adf4382_state(hw);
+
+	st->ref_freq_hz = parent_rate;
+	st->freq = rate * ADF4382_CLK_SCALE;
+
+	return adf4382_set_freq(st);
+}
+
+static void adf4382_clk_notifier_unreg(void *data)
+{
+	struct adf4382_state *st = data;
+
+	clk_notifier_unregister(st->clkin, &st->nb);
+}
+
+static unsigned long adf4382_clock_recalc_rate(struct clk_hw *hw,
+					       unsigned long parent_rate)
+{
+	struct adf4382_state *st = to_adf4382_state(hw);
+	u64 freq = 0;
+	unsigned long rate;
+
+	adf4382_get_freq(st, &freq);
+	rate = DIV_ROUND_CLOSEST_ULL(freq, ADF4382_CLK_SCALE);
+
+	return rate;
+}
+
+static int adf4382_clock_enable(struct clk_hw *hw)
+{
+	struct adf4382_state *st = to_adf4382_state(hw);
+
+	return regmap_update_bits(st->regmap, 0x2B,
+				  ADF4382_PD_CLKOUT1_MSK | ADF4382_PD_CLKOUT2_MSK,
+				  0x00);
+}
+
+static void adf4382_clock_disable(struct clk_hw *hw)
+{
+	struct adf4382_state *st = to_adf4382_state(hw);
+
+	regmap_update_bits(st->regmap, 0x2B,
+			   ADF4382_PD_CLKOUT1_MSK | ADF4382_PD_CLKOUT2_MSK,
+			   0xff);
+}
+
+static long adf4382_clock_round_rate(struct clk_hw *hw, unsigned long rate,
+				     unsigned long *parent_rate)
+{
+	struct adf4382_state *st = to_adf4382_state(hw);
+	u64 freq = rate * ADF4382_CLK_SCALE;
+	u8 div_rate;
+	u64 tmp;
+
+	for (div_rate = 0; div_rate <= st->clkout_div_reg_val_max; div_rate++) {
+		tmp = (1 << div_rate) * freq;
+		if (tmp >= st->vco_min)
+			break;
+	}
+	div_rate = clamp_t(u8, div_rate, 0U, st->clkout_div_reg_val_max);
+	freq = clamp_t(u64, tmp, st->vco_min, st->vco_max);
+	freq = div_u64(freq, 1 << div_rate);
+
+	rate = DIV_ROUND_CLOSEST_ULL(freq, ADF4382_CLK_SCALE);
+	return rate;
+}
+
+static const struct clk_ops adf4382_clock_ops = {
+	.set_rate = adf4382_clock_set_rate,
+	.recalc_rate = adf4382_clock_recalc_rate,
+	.round_rate = adf4382_clock_round_rate,
+	.enable = adf4382_clock_enable,
+	.disable = adf4382_clock_disable,
+};
+
+static int adf4382_setup_clk(struct adf4382_state *st)
+{
+	struct device *dev = &st->spi->dev;
+	struct clk_init_data init;
+	struct clk *clk;
+	const char *parent_name;
+
+	if (!device_property_present(dev, "#clock-cells"))
+		return 0;
+
+	if (device_property_read_string(dev, "clock-output-names", &init.name)) {
+		init.name = devm_kasprintf(dev, GFP_KERNEL, "%s-clk",
+					   fwnode_get_name(dev_fwnode(dev)));
+		if (!init.name)
+			return -ENOMEM;
+	}
+
+	parent_name = of_clk_get_parent_name(dev->of_node, 0);
+	if (!parent_name)
+		return -EINVAL;
+
+	init.ops = &adf4382_clock_ops;
+	init.parent_names = (parent_name ? &parent_name : NULL);
+	init.num_parents = (parent_name ? 1 : 0);
+
+	st->clk_hw.init = &init;
+	clk = devm_clk_register(dev, &st->clk_hw);
+	if (IS_ERR(clk))
+		return PTR_ERR(clk);
+
+	st->clkout = clk;
+
+	return devm_of_clk_add_hw_provider(dev, of_clk_hw_simple_get, clk);
+}
+
+static int adf4382_probe(struct spi_device *spi)
+{
+	struct iio_dev *indio_dev;
+	struct regmap *regmap;
+	struct adf4382_state *st;
+	int ret;
+
+	indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
+	if (!indio_dev)
+		return -ENOMEM;
+
+	regmap = devm_regmap_init_spi(spi, &adf4382_regmap_config);
+	if (IS_ERR(regmap))
+		return PTR_ERR(regmap);
+
+	st = iio_priv(indio_dev);
+
+	indio_dev->info = &adf4382_info;
+	indio_dev->name = "adf4382";
+
+	st->regmap = regmap;
+	st->spi = spi;
+	st->phase = 0;
+
+	st->vco_max = ADF4382_VCO_FREQ_MAX;
+	st->vco_min = ADF4382_VCO_FREQ_MIN;
+	st->clkout_div_reg_val_max = ADF4382_CLKOUT_DIV_REG_VAL_MAX;
+	if (spi_get_device_id(spi)->driver_data == ADF4382A) {
+		indio_dev->name = "adf4382a";
+		st->vco_max = ADF4382A_VCO_FREQ_MAX;
+		st->vco_min = ADF4382A_VCO_FREQ_MIN;
+		st->clkout_div_reg_val_max = ADF4382A_CLKOUT_DIV_REG_VAL_MAX;
+	}
+
+	mutex_init(&st->lock);
+
+	ret = adf4382_parse_device(st);
+	if (ret)
+		return ret;
+
+	st->nb.notifier_call = adf4382_freq_change;
+	ret = clk_notifier_register(st->clkin, &st->nb);
+	if (ret)
+		return ret;
+
+	ret = devm_add_action_or_reset(&spi->dev, adf4382_clk_notifier_unreg, st);
+	if (ret)
+		return ret;
+
+	ret = adf4382_init(st);
+	if (ret)
+		return dev_err_probe(&spi->dev, ret, "adf4382 init failed\n");
+
+	ret = adf4382_setup_clk(st);
+	if (ret)
+		return ret;
+
+	if (!st->clkout) {
+		indio_dev->channels = adf4382_channels;
+		indio_dev->num_channels = ARRAY_SIZE(adf4382_channels);
+	}
+
+	ret = devm_iio_device_register(&spi->dev, indio_dev);
+	if (ret)
+		return ret;
+
+	if (IS_ENABLED(CONFIG_DEBUG_FS))
+		adf4382_debugfs_init(indio_dev);
+
+	return 0;
+}
+
+static const struct spi_device_id adf4382_id[] = {
+	{ "adf4382", ADF4382 },
+	{ "adf4382a", ADF4382A },
+	{},
+};
+MODULE_DEVICE_TABLE(spi, adf4382_id);
+
+static const struct of_device_id adf4382_of_match[] = {
+	{ .compatible = "adi,adf4382" },
+	{ .compatible = "adi,adf4382a" },
+	{},
+};
+MODULE_DEVICE_TABLE(of, adf4382_of_match);
+
+static struct spi_driver adf4382_driver = {
+	.driver = {
+		.name = "adf4382",
+		.of_match_table = adf4382_of_match,
+	},
+	.probe = adf4382_probe,
+	.id_table = adf4382_id,
+};
+module_spi_driver(adf4382_driver);
+
+MODULE_AUTHOR("Antoniu Miclaus <antoniu.miclaus@analog.com>");
+MODULE_AUTHOR("Ciprian Hegbeli <ciprian.hegbeli@analog.com>");
+MODULE_DESCRIPTION("Analog Devices ADF4382");
+MODULE_LICENSE("GPL");