All of lore.kernel.org
 help / color / mirror / Atom feed
From: Alexandru Ardelean <alexandru.ardelean@analog.com>
To: <linux-iio@vger.kernel.org>, <devicetree@vger.kernel.org>,
	<linux-kernel@vger.kernel.org>
Cc: <ekigwana@gmail.com>, <jic23@kernel.org>, <lars@metafoo.de>,
	<robh+dt@kernel.org>,
	Alexandru Ardelean <alexandru.ardelean@analog.com>
Subject: [PATCH v3 1/3] iio: frequency: adf4360: Add support for ADF4360 PLLs
Date: Tue, 28 Jan 2020 13:13:00 +0200	[thread overview]
Message-ID: <20200128111302.24359-1-alexandru.ardelean@analog.com> (raw)

From: Edward Kigwana <ekigwana@gmail.com>

The ADF4360-N (N is 0..9) are a family of integrated integer-N synthesizer
and voltage controlled oscillator (VCO).

Control of all the on-chip registers is through a simple 3-wire SPI
interface. The device operates with a power supply ranging from 3.0 V to
3.6 V and can be powered down when not in use.

The initial draft-work was done by Lars-Peter Clausen <lars@metafoo.de>
The current/latest/complete version was implemented by
Edward Kigwana <ekigwana@gmail.com>.

The ADF4360-9 is also present on the Analog Devices 'Advanced Active
Learning Module 2000' (ADALM-2000).

Link: https://www.analog.com/media/en/technical-documentation/data-sheets/ADF4360-0.pdf
Link: https://www.analog.com/media/en/technical-documentation/data-sheets/ADF4360-1.pdf
Link: https://www.analog.com/media/en/technical-documentation/data-sheets/ADF4360-2.pdf
Link: https://www.analog.com/media/en/technical-documentation/data-sheets/ADF4360-3.pdf
Link: https://www.analog.com/media/en/technical-documentation/data-sheets/ADF4360-4.pdf
Link: https://www.analog.com/media/en/technical-documentation/data-sheets/ADF4360-5.pdf
Link: https://www.analog.com/media/en/technical-documentation/data-sheets/ADF4360-6.pdf
Link: https://www.analog.com/media/en/technical-documentation/data-sheets/ADF4360-7.pdf
Link: https://www.analog.com/media/en/technical-documentation/data-sheets/ADF4360-8.pdf
Link: https://www.analog.com/media/en/technical-documentation/data-sheets/ADF4360-9.pdf
Link: https://www.analog.com/en/design-center/evaluation-hardware-and-software/evaluation-boards-kits/ADALM2000.html

Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
Signed-off-by: Edward Kigwana <ekigwana@gmail.com>
Signed-off-by: Alexandru Ardelean <alexandru.ardelean@analog.com>
---

Changelog v2 -> v3:
* dropped patch about adf4371.yaml; added by accident since it was used to
  compare against
* addressed Rob's review comments for DT schema

Changelog v1 -> v2:
* validate dt-bindings file with
  make dt_binding_check DT_SCHEMA_FILES=Documentation/devicetree/bindings/iio/frequency/adi,adf4360.yaml

 drivers/iio/frequency/Kconfig   |   11 +
 drivers/iio/frequency/Makefile  |    1 +
 drivers/iio/frequency/adf4360.c | 1263 +++++++++++++++++++++++++++++++
 3 files changed, 1275 insertions(+)
 create mode 100644 drivers/iio/frequency/adf4360.c

diff --git a/drivers/iio/frequency/Kconfig b/drivers/iio/frequency/Kconfig
index 240b81502512..781236496661 100644
--- a/drivers/iio/frequency/Kconfig
+++ b/drivers/iio/frequency/Kconfig
@@ -39,6 +39,17 @@ config ADF4350
 	  To compile this driver as a module, choose M here: the
 	  module will be called adf4350.
 
+config ADF4360
+	tristate "Analog Devices ADF4360 Wideband Synthesizers"
+	depends on SPI
+	depends on COMMON_CLK
+	help
+	  Say yes here to build support for Analog Devices ADF4360
+	  Wideband Synthesizers. The driver provides direct access via sysfs.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called adf4360.
+
 config ADF4371
 	tristate "Analog Devices ADF4371/ADF4372 Wideband Synthesizers"
 	depends on SPI
diff --git a/drivers/iio/frequency/Makefile b/drivers/iio/frequency/Makefile
index 518b1e50caef..85f2f81da662 100644
--- a/drivers/iio/frequency/Makefile
+++ b/drivers/iio/frequency/Makefile
@@ -6,4 +6,5 @@
 # When adding new entries keep the list in alphabetical order
 obj-$(CONFIG_AD9523) += ad9523.o
 obj-$(CONFIG_ADF4350) += adf4350.o
+obj-$(CONFIG_ADF4360) += adf4360.o
 obj-$(CONFIG_ADF4371) += adf4371.o
diff --git a/drivers/iio/frequency/adf4360.c b/drivers/iio/frequency/adf4360.c
new file mode 100644
index 000000000000..49ad58092171
--- /dev/null
+++ b/drivers/iio/frequency/adf4360.c
@@ -0,0 +1,1263 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * ADF4360 PLL with Integrated Synthesizer and VCO
+ *
+ * Copyright 2014-2019 Analog Devices Inc.
+ * Copyright 2019-2020 Edward Kigwana.
+ */
+
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/gpio/consumer.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/regulator/consumer.h>
+#include <linux/spi/spi.h>
+#include <linux/util_macros.h>
+
+#include <linux/iio/iio.h>
+
+/* Register address macro */
+#define ADF4360_REG(x)			(x)
+
+/* ADF4360_CTRL */
+#define ADF4360_ADDR_CPL_MSK		GENMASK(3, 2)
+#define ADF4360_CPL(x)			FIELD_PREP(ADF4360_ADDR_CPL_MSK, x)
+#define ADF4360_ADDR_MUXOUT_MSK		GENMASK(7, 5)
+#define ADF4360_MUXOUT(x)		FIELD_PREP(ADF4360_ADDR_MUXOUT_MSK, x)
+#define ADF4360_ADDR_PDP_MSK		BIT(8)
+#define ADF4360_PDP(x)			FIELD_PREP(ADF4360_ADDR_PDP_MSK, x)
+#define ADF4360_ADDR_MTLD_MSK		BIT(11)
+#define ADF4360_MTLD(x)			FIELD_PREP(ADF4360_ADDR_MTLD_MSK, x)
+#define ADF4360_ADDR_OPL_MSK		GENMASK(13, 12)
+#define ADF4360_OPL(x)			FIELD_PREP(ADF4360_ADDR_OPL_MSK, x)
+#define ADF4360_ADDR_CPI1_MSK		GENMASK(16, 14)
+#define ADF4360_CPI1(x)			FIELD_PREP(ADF4360_ADDR_CPI1_MSK, x)
+#define ADF4360_ADDR_CPI2_MSK		GENMASK(19, 17)
+#define ADF4360_CPI2(x)			FIELD_PREP(ADF4360_ADDR_CPI2_MSK, x)
+#define ADF4360_ADDR_PWR_DWN_MSK	GENMASK(21, 20)
+#define ADF4360_POWERDOWN(x)		FIELD_PREP(ADF4360_ADDR_PWR_DWN_MSK, x)
+#define ADF4360_ADDR_PRESCALER_MSK	GENMASK(23, 22)
+#define ADF4360_PRESCALER(x)		FIELD_PREP(ADF4360_ADDR_PRESCALER_MSK, x)
+
+/* ADF4360_NDIV */
+#define ADF4360_ADDR_A_CNTR_MSK		GENMASK(6, 2)
+#define ADF4360_A_COUNTER(x)		FIELD_PREP(ADF4360_ADDR_A_CNTR_MSK, x)
+#define ADF4360_ADDR_B_CNTR_MSK		GENMASK(20, 8)
+#define ADF4360_B_COUNTER(x)		FIELD_PREP(ADF4360_ADDR_B_CNTR_MSK, x)
+#define ADF4360_ADDR_OUT_DIV2_MSK	BIT(22)
+#define ADF4360_OUT_DIV2(x)		FIELD_PREP(ADF4360_ADDR_OUT_DIV2_MSK, x)
+#define ADF4360_ADDR_DIV2_SEL_MSK	BIT(23)
+#define ADF4360_PRESCALER_DIV2(x)	FIELD_PREP(ADF4360_ADDR_DIV2_SEL_MSK, x)
+
+/* ADF4360_RDIV */
+#define ADF4360_ADDR_R_CNTR_MSK		GENMASK(15, 2)
+#define ADF4360_R_COUNTER(x)		FIELD_PREP(ADF4360_ADDR_R_CNTR_MSK, x)
+#define ADF4360_ADDR_ABP_MSK		GENMASK(17, 16)
+#define ADF4360_ABP(x)			FIELD_PREP(ADF4360_ADDR_ABP_MSK, x)
+#define ADF4360_ADDR_BSC_MSK		GENMASK(21, 20)
+#define ADF4360_BSC(x)			FIELD_PREP(ADF4360_ADDR_BSC_MSK, x)
+
+/* Specifications */
+#define ADF4360_MAX_PFD_RATE		8000000 /* 8 MHz */
+#define ADF4360_MAX_COUNTER_RATE	300000000 /* 300 MHz */
+#define ADF4360_MAX_REFIN_RATE		250000000 /* 250 MHz */
+
+enum {
+	ADF4360_CTRL,
+	ADF4360_RDIV,
+	ADF4360_NDIV,
+	ADF4360_REG_NUM,
+};
+
+enum {
+	ADF4360_GEN1_PC_5,
+	ADF4360_GEN1_PC_10,
+	ADF4360_GEN1_PC_15,
+	ADF4360_GEN1_PC_20,
+};
+
+enum {
+	ADF4360_GEN2_PC_2_5,
+	ADF4360_GEN2_PC_5,
+	ADF4360_GEN2_PC_7_5,
+	ADF4360_GEN2_PC_10,
+};
+
+enum {
+	ADF4360_MUXOUT_THREE_STATE,
+	ADF4360_MUXOUT_LOCK_DETECT,
+	ADF4360_MUXOUT_NDIV,
+	ADF4360_MUXOUT_DVDD,
+	ADF4360_MUXOUT_RDIV,
+	ADF4360_MUXOUT_OD_LD,
+	ADF4360_MUXOUT_SDO,
+	ADF4360_MUXOUT_GND,
+};
+
+enum {
+	ADF4360_PL_3_5,
+	ADF4360_PL_5,
+	ADF4360_PL_7_5,
+	ADF4360_PL_11,
+};
+
+enum {
+	ADF4360_CPI_0_31,
+	ADF4360_CPI_0_62,
+	ADF4360_CPI_0_93,
+	ADF4360_CPI_1_25,
+	ADF4360_CPI_1_56,
+	ADF4360_CPI_1_87,
+	ADF4360_CPI_2_18,
+	ADF4360_CPI_2_50,
+};
+
+enum {
+	ADF4360_POWER_DOWN_NORMAL,
+	ADF4360_POWER_DOWN_SOFT_ASYNC,
+	ADF4360_POWER_DOWN_CE,
+	ADF4360_POWER_DOWN_SOFT_SYNC,
+	ADF4360_POWER_DOWN_REGULATOR,
+};
+
+enum {
+	ADF4360_PRESCALER_8,
+	ADF4360_PRESCALER_16,
+	ADF4360_PRESCALER_32,
+};
+
+enum {
+	ADF4360_ABP_3_0NS,
+	ADF4360_ABP_1_3NS,
+	ADF4360_ABP_6_0NS,
+};
+
+enum {
+	ADF4360_BSC_1,
+	ADF4360_BSC_2,
+	ADF4360_BSC_4,
+	ADF4360_BSC_8,
+};
+
+enum {
+	ID_ADF4360_0,
+	ID_ADF4360_1,
+	ID_ADF4360_2,
+	ID_ADF4360_3,
+	ID_ADF4360_4,
+	ID_ADF4360_5,
+	ID_ADF4360_6,
+	ID_ADF4360_7,
+	ID_ADF4360_8,
+	ID_ADF4360_9,
+};
+
+enum {
+	ADF4360_FREQ_REFIN,
+	ADF4360_MTLD,
+	ADF4360_FREQ_PFD,
+};
+
+static const char * const adf4360_power_level_modes[] = {
+	[ADF4360_PL_3_5] = "3500-uA",
+	[ADF4360_PL_5] = "5000-uA",
+	[ADF4360_PL_7_5] = "7500-uA",
+	[ADF4360_PL_11] = "11000-uA",
+};
+
+static const unsigned int adf4360_power_lvl_microamp[] = {
+	[ADF4360_PL_3_5] = 3500,
+	[ADF4360_PL_5] = 5000,
+	[ADF4360_PL_7_5] = 7500,
+	[ADF4360_PL_11] = 11000,
+};
+
+static const unsigned int adf4360_cpi_modes[] = {
+	[ADF4360_CPI_0_31] = 310,
+	[ADF4360_CPI_0_62] = 620,
+	[ADF4360_CPI_0_93] = 930,
+	[ADF4360_CPI_1_25] = 1250,
+	[ADF4360_CPI_1_56] = 1560,
+	[ADF4360_CPI_1_87] = 1870,
+	[ADF4360_CPI_2_18] = 2180,
+	[ADF4360_CPI_2_50] = 2500,
+};
+
+static const char * const adf4360_muxout_modes[] = {
+	[ADF4360_MUXOUT_THREE_STATE] = "three-state",
+	[ADF4360_MUXOUT_LOCK_DETECT] = "lock-detect",
+	[ADF4360_MUXOUT_NDIV] = "ndiv",
+	[ADF4360_MUXOUT_DVDD] = "dvdd",
+	[ADF4360_MUXOUT_RDIV] = "rdiv",
+	[ADF4360_MUXOUT_OD_LD] = "od-ld",
+	[ADF4360_MUXOUT_SDO] = "sdo",
+	[ADF4360_MUXOUT_GND] = "gnd",
+};
+
+static const char * const adf4360_power_down_modes[] = {
+	[ADF4360_POWER_DOWN_NORMAL] = "normal",
+	[ADF4360_POWER_DOWN_SOFT_ASYNC] = "soft-async",
+	[ADF4360_POWER_DOWN_CE] = "ce",
+	[ADF4360_POWER_DOWN_SOFT_SYNC] = "soft-sync",
+	[ADF4360_POWER_DOWN_REGULATOR] = "regulator",
+};
+
+struct adf4360_output {
+	struct clk_hw hw;
+	struct iio_dev *indio_dev;
+};
+
+#define to_output(_hw) container_of(_hw, struct adf4360_output, hw)
+
+struct adf4360_chip_info {
+	unsigned int vco_min;
+	unsigned int vco_max;
+	unsigned int default_cpl;
+};
+
+struct adf4360_state {
+	struct spi_device *spi;
+	const struct adf4360_chip_info *info;
+	struct adf4360_output output;
+	struct clk *clkin;
+	struct gpio_desc *muxout_gpio;
+	struct gpio_desc *chip_en_gpio;
+	struct regulator *vdd_reg;
+	struct mutex lock; /* Protect PLL state. */
+	unsigned int part_id;
+	unsigned long clkin_freq;
+	unsigned long freq_req;
+	unsigned long r;
+	unsigned long n;
+	unsigned int vco_min;
+	unsigned int vco_max;
+	unsigned int pfd_freq;
+	unsigned int cpi;
+	bool pdp;
+	bool mtld;
+	unsigned int power_level;
+	unsigned int muxout_mode;
+	unsigned int power_down_mode;
+	bool initial_reg_seq;
+	const char *clk_out_name;
+	unsigned int regs[ADF4360_REG_NUM];
+	u8 spi_data[3] ____cacheline_aligned;
+};
+
+static const struct adf4360_chip_info adf4360_chip_info_tbl[] = {
+	{	/* ADF4360-0 */
+		.vco_min = 2400000000U,
+		.vco_max = 2725000000U,
+		.default_cpl = ADF4360_GEN1_PC_10,
+	}, {	/* ADF4360-1 */
+		.vco_min = 2050000000U,
+		.vco_max = 2450000000U,
+		.default_cpl = ADF4360_GEN1_PC_15,
+	}, {	/* ADF4360-2 */
+		.vco_min = 1850000000U,
+		.vco_max = 2170000000U,
+		.default_cpl = ADF4360_GEN1_PC_15,
+	}, {	/* ADF4360-3 */
+		.vco_min = 1600000000U,
+		.vco_max = 1950000000U,
+		.default_cpl = ADF4360_GEN1_PC_15,
+	}, {	/* ADF4360-4 */
+		.vco_min = 1450000000U,
+		.vco_max = 1750000000U,
+		.default_cpl = ADF4360_GEN1_PC_15,
+	}, {	/* ADF4360-5 */
+		.vco_min = 1200000000U,
+		.vco_max = 1400000000U,
+		.default_cpl = ADF4360_GEN1_PC_10,
+	}, {	/* ADF4360-6 */
+		.vco_min = 1050000000U,
+		.vco_max = 1250000000U,
+		.default_cpl = ADF4360_GEN1_PC_10,
+	}, {	/* ADF4360-7 */
+		.vco_min = 350000000U,
+		.vco_max = 1800000000U,
+		.default_cpl = ADF4360_GEN1_PC_5,
+	}, {	/* ADF4360-8 */
+		.vco_min = 65000000U,
+		.vco_max = 400000000U,
+		.default_cpl = ADF4360_GEN2_PC_5,
+	}, {	/* ADF4360-9 */
+		.vco_min = 65000000U,
+		.vco_max = 400000000U,
+		.default_cpl = ADF4360_GEN2_PC_5,
+	}
+};
+
+static int adf4360_write_reg(struct adf4360_state *st, unsigned int reg,
+			     unsigned int val)
+{
+	int ret;
+
+	val |= reg;
+
+	st->spi_data[0] = (val >> 16) & 0xff;
+	st->spi_data[1] = (val >> 8) & 0xff;
+	st->spi_data[2] = val & 0xff;
+
+	ret = spi_write(st->spi, st->spi_data, ARRAY_SIZE(st->spi_data));
+	if (ret == 0)
+		st->regs[reg] = val;
+
+	return ret;
+}
+
+/* fVCO = B * fREFIN / R */
+
+static unsigned long adf4360_clk_recalc_rate(struct clk_hw *hw,
+					     unsigned long parent_rate)
+{
+	struct iio_dev *indio_dev = to_output(hw)->indio_dev;
+	struct adf4360_state *st = iio_priv(indio_dev);
+
+	if (st->r == 0)
+		return 0;
+
+	/*
+	 * The result is guaranteed to fit in 32-bit, but the intermediate
+	 * result might require 64-bit.
+	 */
+	return DIV_ROUND_CLOSEST_ULL((uint64_t)parent_rate * st->n, st->r);
+}
+
+static unsigned int adf4360_calc_prescaler(unsigned int pfd_freq,
+					   unsigned int n,
+					   unsigned int *out_p,
+					   unsigned int *out_a,
+					   unsigned int *out_b)
+{
+	unsigned int rate = pfd_freq * n;
+	unsigned int p, a, b;
+
+	/* Make sure divider counter input frequency is low enough */
+	p = 8;
+	while (p < 32 && rate / p > ADF4360_MAX_COUNTER_RATE)
+		p *= 2;
+
+	/*
+	 * The range of dividers that can be produced using the dual-modulus
+	 * pre-scaler is not continuous for values of n < p*(p-1). If we end up
+	 * with a non supported divider value, pick the next closest one.
+	 */
+	a = n % p;
+	b = n / p;
+
+	if (b < 3) {
+		b = 3;
+		a = 0;
+	} else if (a > b) {
+		if (a - b < p - a) {
+			a = b;
+		} else {
+			a = 0;
+			b++;
+		}
+	}
+
+	if (out_p)
+		*out_p = p;
+	if (out_a)
+		*out_a = a;
+	if (out_b)
+		*out_b = b;
+
+	return p * b + a;
+}
+
+static long adf4360_clk_round_rate(struct clk_hw *hw,
+				   unsigned long rate,
+				   unsigned long *parent_rate)
+{
+	struct iio_dev *indio_dev = to_output(hw)->indio_dev;
+	struct adf4360_state *st = iio_priv(indio_dev);
+	unsigned int r, n;
+	unsigned int pfd_freq;
+
+	if (*parent_rate == 0)
+		return 0;
+
+	if (st->part_id == ID_ADF4360_9)
+		return *parent_rate * st->n / st->r;
+
+	if (rate > st->vco_max)
+		return st->vco_max;
+
+	/* ADF4360-0 to AD4370-7 have an optional by two divider */
+	if (st->part_id <= ID_ADF4360_7) {
+		if (rate < st->vco_min / 2)
+			return st->vco_min / 2;
+		if (rate < st->vco_min && rate > st->vco_max / 2) {
+			if (st->vco_min - rate < rate - st->vco_max / 2)
+				return st->vco_min;
+			else
+				return st->vco_max / 2;
+		}
+	} else {
+		if (rate < st->vco_min)
+			return st->vco_min;
+	}
+
+	r = DIV_ROUND_CLOSEST(*parent_rate, st->pfd_freq);
+	pfd_freq = *parent_rate / r;
+	n = DIV_ROUND_CLOSEST(rate, pfd_freq);
+
+	if (st->part_id <= ID_ADF4360_7)
+		n = adf4360_calc_prescaler(pfd_freq, n, NULL, NULL, NULL);
+
+	return pfd_freq * n;
+}
+
+static inline bool adf4360_is_powerdown(struct adf4360_state *st)
+{
+	return (st->power_down_mode != ADF4360_POWER_DOWN_NORMAL);
+}
+
+static int adf4360_set_freq(struct adf4360_state *st, unsigned long rate)
+{
+	unsigned int val_r, val_n, val_ctrl;
+	unsigned int pfd_freq;
+	unsigned long r, n;
+	int ret;
+
+	if (!st->clkin_freq || (st->clkin_freq > ADF4360_MAX_REFIN_RATE))
+		return -EINVAL;
+
+	if ((rate < st->vco_min) || (rate > st->vco_max))
+		return -EINVAL;
+
+	if (adf4360_is_powerdown(st))
+		ret = -EBUSY;
+
+	r = DIV_ROUND_CLOSEST(st->clkin_freq, st->pfd_freq);
+	pfd_freq = st->clkin_freq / r;
+	n = DIV_ROUND_CLOSEST(rate, pfd_freq);
+
+	val_ctrl = ADF4360_CPL(st->info->default_cpl) |
+		   ADF4360_MUXOUT(st->muxout_mode) |
+		   ADF4360_PDP(!st->pdp) |
+		   ADF4360_MTLD(st->mtld) |
+		   ADF4360_OPL(st->power_level) |
+		   ADF4360_CPI1(st->cpi) |
+		   ADF4360_CPI2(st->cpi) |
+		   ADF4360_POWERDOWN(st->power_down_mode);
+
+	/* ADF4360-0 to ADF4360-7 have a dual-modulous prescaler */
+	if (st->part_id <= ID_ADF4360_7) {
+		unsigned int p, a, b;
+
+		n = adf4360_calc_prescaler(pfd_freq, n, &p, &a, &b);
+
+		switch (p) {
+		case 8:
+			val_ctrl |= ADF4360_PRESCALER(ADF4360_PRESCALER_8);
+			break;
+		case 16:
+			val_ctrl |= ADF4360_PRESCALER(ADF4360_PRESCALER_16);
+			break;
+		default:
+			val_ctrl |= ADF4360_PRESCALER(ADF4360_PRESCALER_32);
+			break;
+		}
+
+		val_n = ADF4360_A_COUNTER(a) |
+			ADF4360_B_COUNTER(b);
+
+		if (rate < st->vco_min)
+			val_n |= ADF4360_OUT_DIV2(true) |
+				 ADF4360_PRESCALER_DIV2(true);
+	} else {
+		val_n = ADF4360_B_COUNTER(n);
+	}
+
+	/*
+	 * Always use BSC divider of 8, see Analog Devices AN-1347.
+	 * http://www.analog.com/media/en/technical-documentation/application-notes/AN-1347.pdf
+	 */
+	val_r = ADF4360_R_COUNTER(r) |
+		ADF4360_ABP(ADF4360_ABP_3_0NS) |
+		ADF4360_BSC(ADF4360_BSC_8);
+
+	ret = adf4360_write_reg(st, ADF4360_REG(ADF4360_RDIV), val_r);
+	if (ret)
+		return ret;
+
+	ret = adf4360_write_reg(st, ADF4360_REG(ADF4360_CTRL), val_ctrl);
+	if (ret)
+		return ret;
+
+	/*
+	 * Allow the transient behavior of the ADF4360-7 during initial
+	 * power-up to settle.
+	 */
+	if (st->initial_reg_seq) {
+		usleep_range(15000, 20000);
+		st->initial_reg_seq = false;
+	}
+
+	ret = adf4360_write_reg(st, ADF4360_REG(ADF4360_NDIV), val_n);
+	if (ret)
+		return ret;
+
+	st->freq_req = rate;
+	st->n = n;
+	st->r = r;
+
+	return 0;
+}
+
+static int adf4360_clk_set_rate(struct clk_hw *hw,
+				unsigned long rate,
+				unsigned long parent_rate)
+{
+	struct iio_dev *indio_dev = to_output(hw)->indio_dev;
+	struct adf4360_state *st = iio_priv(indio_dev);
+	int ret;
+
+	if ((parent_rate == 0) || (parent_rate > ADF4360_MAX_REFIN_RATE))
+		return -EINVAL;
+
+	mutex_lock(&st->lock);
+	if (st->clkin_freq != parent_rate)
+		st->clkin_freq = parent_rate;
+
+	ret = adf4360_set_freq(st, rate);
+	mutex_unlock(&st->lock);
+
+	return ret;
+}
+
+static int __adf4360_power_down(struct adf4360_state *st, unsigned int mode)
+{
+	struct device *dev = &st->spi->dev;
+	unsigned int val;
+	int ret = 0;
+
+	switch (mode) {
+	case ADF4360_POWER_DOWN_NORMAL:
+		if (st->vdd_reg) {
+			ret = regulator_enable(st->vdd_reg);
+			if (ret) {
+				dev_err(dev, "Supply enable error: %d\n", ret);
+				return ret;
+			}
+		}
+
+		st->initial_reg_seq = true;
+		st->power_down_mode = mode;
+		ret = adf4360_set_freq(st, st->freq_req);
+		break;
+	case ADF4360_POWER_DOWN_SOFT_ASYNC: /* fallthrough */
+	case ADF4360_POWER_DOWN_SOFT_SYNC:
+		val = st->regs[ADF4360_CTRL] & ~ADF4360_ADDR_MUXOUT_MSK;
+		val |= ADF4360_POWERDOWN(mode);
+		ret = adf4360_write_reg(st, ADF4360_REG(ADF4360_CTRL), val);
+		break;
+	case ADF4360_POWER_DOWN_CE:
+		if (st->chip_en_gpio)
+			gpiod_set_value(st->chip_en_gpio, 0x0);
+		else
+			return -ENODEV;
+		break;
+	case ADF4360_POWER_DOWN_REGULATOR:
+		if (!st->vdd_reg)
+			return -ENODEV;
+
+		if (st->chip_en_gpio)
+			ret = __adf4360_power_down(st, ADF4360_POWER_DOWN_CE);
+		else
+			ret = __adf4360_power_down(st,
+						ADF4360_POWER_DOWN_SOFT_ASYNC);
+
+		ret = regulator_disable(st->vdd_reg);
+		if (ret)
+			dev_err(dev, "Supply disable error: %d\n", ret);
+		break;
+	}
+	if (ret == 0)
+		st->power_down_mode = mode;
+
+	return 0;
+}
+
+static int adf4360_power_down(struct adf4360_state *st, unsigned int mode)
+{
+	int ret;
+
+	mutex_lock(&st->lock);
+	ret = __adf4360_power_down(st, mode);
+	mutex_unlock(&st->lock);
+
+	return ret;
+}
+
+static int adf4360_clk_prepare(struct clk_hw *hw)
+{
+	struct iio_dev *indio_dev = to_output(hw)->indio_dev;
+	struct adf4360_state *st = iio_priv(indio_dev);
+
+	return adf4360_power_down(st, ADF4360_POWER_DOWN_NORMAL);
+}
+
+static void adf4360_clk_unprepare(struct clk_hw *hw)
+{
+	struct iio_dev *indio_dev = to_output(hw)->indio_dev;
+	struct adf4360_state *st = iio_priv(indio_dev);
+
+	adf4360_power_down(st, ADF4360_POWER_DOWN_SOFT_ASYNC);
+}
+
+static int adf4360_clk_enable(struct clk_hw *hw)
+{
+	struct iio_dev *indio_dev = to_output(hw)->indio_dev;
+	struct adf4360_state *st = iio_priv(indio_dev);
+
+	if (st->chip_en_gpio)
+		gpiod_set_value(st->chip_en_gpio, 0x1);
+
+	return 0;
+}
+
+static void adf4360_clk_disable(struct clk_hw *hw)
+{
+	struct iio_dev *indio_dev = to_output(hw)->indio_dev;
+	struct adf4360_state *st = iio_priv(indio_dev);
+
+	if (st->chip_en_gpio)
+		adf4360_power_down(st, ADF4360_POWER_DOWN_CE);
+}
+
+static int adf4360_clk_is_enabled(struct clk_hw *hw)
+{
+	struct iio_dev *indio_dev = to_output(hw)->indio_dev;
+	struct adf4360_state *st = iio_priv(indio_dev);
+
+	return adf4360_is_powerdown(st);
+}
+
+static const struct clk_ops adf4360_clk_ops = {
+	.recalc_rate = adf4360_clk_recalc_rate,
+	.round_rate = adf4360_clk_round_rate,
+	.set_rate = adf4360_clk_set_rate,
+	.prepare = adf4360_clk_prepare,
+	.unprepare = adf4360_clk_unprepare,
+	.enable = adf4360_clk_enable,
+	.disable = adf4360_clk_disable,
+	.is_enabled = adf4360_clk_is_enabled,
+};
+
+static ssize_t adf4360_read(struct iio_dev *indio_dev,
+			    uintptr_t private,
+			    const struct iio_chan_spec *chan,
+			    char *buf)
+{
+	struct adf4360_state *st = iio_priv(indio_dev);
+	unsigned long val;
+	int ret = 0;
+
+	switch ((u32)private) {
+	case ADF4360_FREQ_REFIN:
+		val = st->clkin_freq;
+		break;
+	case ADF4360_MTLD:
+		val = st->mtld;
+		break;
+	case ADF4360_FREQ_PFD:
+		val = st->pfd_freq;
+		break;
+	default:
+		ret = -EINVAL;
+		val = 0;
+	}
+
+	return ret < 0 ? ret : sprintf(buf, "%lu\n", val);
+}
+
+static ssize_t adf4360_write(struct iio_dev *indio_dev,
+			     uintptr_t private,
+			     const struct iio_chan_spec *chan,
+			     const char *buf, size_t len)
+{
+	struct adf4360_state *st = iio_priv(indio_dev);
+	unsigned long readin, tmp;
+	bool mtld;
+	int ret = 0;
+
+	mutex_lock(&st->lock);
+	switch ((u32)private) {
+	case ADF4360_FREQ_REFIN:
+		ret = kstrtoul(buf, 10, &readin);
+		if (ret)
+			break;
+
+		if ((readin > ADF4360_MAX_REFIN_RATE) || (readin == 0)) {
+			ret = -EINVAL;
+			break;
+		}
+
+		if (st->clkin) {
+			tmp = clk_round_rate(st->clkin, readin);
+			if (tmp != readin) {
+				ret = -EINVAL;
+				break;
+			}
+
+			ret = clk_set_rate(st->clkin, tmp);
+			if (ret)
+				break;
+		}
+
+		st->clkin_freq = readin;
+		break;
+	case ADF4360_MTLD:
+		ret = kstrtobool(buf, &mtld);
+		if (ret)
+			break;
+
+		st->mtld = mtld;
+		break;
+	case ADF4360_FREQ_PFD:
+		ret = kstrtoul(buf, 10, &readin);
+		if (ret)
+			break;
+
+		if ((readin > ADF4360_MAX_PFD_RATE) || (readin == 0)) {
+			ret = -EINVAL;
+			break;
+		}
+
+		st->pfd_freq = readin;
+		break;
+	default:
+		ret = -EINVAL;
+	}
+
+	if (ret == 0)
+		ret = adf4360_set_freq(st, st->freq_req);
+	mutex_unlock(&st->lock);
+
+	return ret ? ret : len;
+}
+
+static int adf4360_get_muxout_mode(struct iio_dev *indio_dev,
+				   const struct iio_chan_spec *chan)
+{
+	struct adf4360_state *st = iio_priv(indio_dev);
+
+	return st->muxout_mode;
+}
+
+static int adf4360_set_muxout_mode(struct iio_dev *indio_dev,
+				   const struct iio_chan_spec *chan,
+				   unsigned int mode)
+{
+	struct adf4360_state *st = iio_priv(indio_dev);
+	unsigned int writeval;
+	int ret = 0;
+
+	mutex_lock(&st->lock);
+	writeval = st->regs[ADF4360_CTRL] & ~ADF4360_ADDR_MUXOUT_MSK;
+	writeval |= ADF4360_MUXOUT(mode & 0x7);
+	ret = adf4360_write_reg(st, ADF4360_REG(ADF4360_CTRL), writeval);
+	if (ret == 0)
+		st->muxout_mode = mode & 0x7;
+	mutex_unlock(&st->lock);
+
+	return ret;
+}
+
+static const struct iio_enum adf4360_muxout_modes_available = {
+	.items = adf4360_muxout_modes,
+	.num_items = ARRAY_SIZE(adf4360_muxout_modes),
+	.get = adf4360_get_muxout_mode,
+	.set = adf4360_set_muxout_mode,
+};
+
+static int adf4360_get_power_down(struct iio_dev *indio_dev,
+				  const struct iio_chan_spec *chan)
+{
+	struct adf4360_state *st = iio_priv(indio_dev);
+
+	return st->power_down_mode;
+}
+
+static int adf4360_set_power_down(struct iio_dev *indio_dev,
+				  const struct iio_chan_spec *chan,
+				  unsigned int mode)
+{
+	struct adf4360_state *st = iio_priv(indio_dev);
+
+	return adf4360_power_down(st, mode);
+}
+
+static const struct iio_enum adf4360_pwr_dwn_modes_available = {
+	.items = adf4360_power_down_modes,
+	.num_items = ARRAY_SIZE(adf4360_power_down_modes),
+	.get = adf4360_get_power_down,
+	.set = adf4360_set_power_down,
+};
+
+static int adf4360_get_power_level(struct iio_dev *indio_dev,
+				   const struct iio_chan_spec *chan)
+{
+	struct adf4360_state *st = iio_priv(indio_dev);
+
+	return st->power_level;
+}
+
+static int adf4360_set_power_level(struct iio_dev *indio_dev,
+				   const struct iio_chan_spec *chan,
+				   unsigned int level)
+{
+	struct adf4360_state *st = iio_priv(indio_dev);
+	unsigned int val;
+	int ret;
+
+	if (adf4360_is_powerdown(st))
+		return -EBUSY;
+
+	mutex_lock(&st->lock);
+	val = st->regs[ADF4360_CTRL] & ~ADF4360_ADDR_OPL_MSK;
+	val |= ADF4360_OPL(level);
+	ret = adf4360_write_reg(st, ADF4360_REG(ADF4360_CTRL), val);
+	st->power_level = level;
+	mutex_unlock(&st->lock);
+
+	return ret;
+}
+
+static const struct iio_enum adf4360_pwr_lvl_modes_available = {
+	.items = adf4360_power_level_modes,
+	.num_items = ARRAY_SIZE(adf4360_power_level_modes),
+	.get = adf4360_get_power_level,
+	.set = adf4360_set_power_level,
+};
+
+#define _ADF4360_EXT_INFO(_name, _ident) { \
+	.name = _name, \
+	.read = adf4360_read, \
+	.write = adf4360_write, \
+	.private = _ident, \
+	.shared = IIO_SEPARATE, \
+}
+
+static const struct iio_chan_spec_ext_info adf4360_ext_info[] = {
+	_ADF4360_EXT_INFO("refin_frequency", ADF4360_FREQ_REFIN),
+	_ADF4360_EXT_INFO("mute_till_lock_detect", ADF4360_MTLD),
+	_ADF4360_EXT_INFO("pfd_frequency", ADF4360_FREQ_PFD),
+	IIO_ENUM_AVAILABLE("muxout_mode", &adf4360_muxout_modes_available),
+	IIO_ENUM("muxout_mode", false, &adf4360_muxout_modes_available),
+	IIO_ENUM_AVAILABLE("power_down", &adf4360_pwr_dwn_modes_available),
+	IIO_ENUM("power_down", false, &adf4360_pwr_dwn_modes_available),
+	IIO_ENUM_AVAILABLE("power_level", &adf4360_pwr_lvl_modes_available),
+	IIO_ENUM("power_level", false, &adf4360_pwr_lvl_modes_available),
+	{ },
+};
+
+static const struct iio_chan_spec adf4360_chan = {
+	.type = IIO_ALTVOLTAGE,
+	.indexed = 1,
+	.output = 1,
+	.info_mask_separate = BIT(IIO_CHAN_INFO_FREQUENCY),
+	.ext_info = adf4360_ext_info,
+};
+
+static int adf4360_read_raw(struct iio_dev *indio_dev,
+			    struct iio_chan_spec const *chan,
+			    int *val,
+			    int *val2,
+			    long mask)
+{
+	struct adf4360_state *st = iio_priv(indio_dev);
+	bool lk_det;
+
+	switch (mask) {
+	case IIO_CHAN_INFO_FREQUENCY:
+		if (adf4360_is_powerdown(st))
+			return -EBUSY;
+
+		lk_det = (ADF4360_MUXOUT_LOCK_DETECT | ADF4360_MUXOUT_OD_LD) &
+			 st->muxout_mode;
+		if (lk_det && st->muxout_gpio) {
+			if (!gpiod_get_value(st->muxout_gpio)) {
+				dev_dbg(&st->spi->dev, "PLL un-locked\n");
+				return -EBUSY;
+			}
+		}
+
+		*val = st->freq_req;
+		return IIO_VAL_INT;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+};
+
+static int adf4360_write_raw(struct iio_dev *indio_dev,
+			     struct iio_chan_spec const *chan,
+			     int val,
+			     int val2,
+			     long mask)
+{
+	struct adf4360_state *st = iio_priv(indio_dev);
+	int ret;
+
+	mutex_lock(&st->lock);
+	switch (mask) {
+	case IIO_CHAN_INFO_FREQUENCY:
+		ret = adf4360_set_freq(st, val);
+		break;
+	default:
+		ret = -EINVAL;
+	}
+	mutex_unlock(&st->lock);
+
+	return ret;
+}
+
+static int adf4360_reg_access(struct iio_dev *indio_dev,
+			      unsigned int reg,
+			      unsigned int writeval,
+			      unsigned int *readval)
+{
+	struct adf4360_state *st = iio_priv(indio_dev);
+	int ret = 0;
+
+	if (reg >= ADF4360_REG_NUM)
+		return -EFAULT;
+
+	mutex_lock(&st->lock);
+	if (readval) {
+		*readval = st->regs[reg];
+	} else {
+		writeval &= 0xFFFFFC;
+		ret = adf4360_write_reg(st, reg, writeval);
+	}
+	mutex_unlock(&st->lock);
+
+	return ret;
+}
+
+static const struct iio_info adf4360_iio_info = {
+	.read_raw = &adf4360_read_raw,
+	.write_raw = &adf4360_write_raw,
+	.debugfs_reg_access = &adf4360_reg_access,
+};
+
+static int adf4360_get_gpio(struct adf4360_state *st)
+{
+	struct device *dev = &st->spi->dev;
+	unsigned int val;
+	int ret, i;
+
+	st->chip_en_gpio = devm_gpiod_get_optional(dev, "enable",
+						   GPIOD_OUT_HIGH);
+	if (IS_ERR(st->chip_en_gpio)) {
+		dev_err(dev, "Chip enable GPIO error\n");
+		return PTR_ERR(st->chip_en_gpio);
+	}
+
+	if (st->chip_en_gpio)
+		st->power_down_mode = ADF4360_POWER_DOWN_CE;
+
+	st->muxout_gpio = devm_gpiod_get_optional(dev, "adi,muxout", GPIOD_IN);
+	if (IS_ERR(st->muxout_gpio)) {
+		dev_err(dev, "Muxout GPIO error\n");
+		return PTR_ERR(st->muxout_gpio);
+	}
+
+	if (!st->muxout_gpio)
+		return 0;
+
+	/* ADF4360 PLLs are write only devices, try to probe using GPIO. */
+	for (i = 0; i < 4; i++) {
+		if (i & 1)
+			val = ADF4360_MUXOUT(ADF4360_MUXOUT_DVDD);
+		else
+			val = ADF4360_MUXOUT(ADF4360_MUXOUT_GND);
+
+		ret = adf4360_write_reg(st, ADF4360_REG(ADF4360_CTRL), val);
+		if (ret)
+			return ret;
+
+		ret = gpiod_get_value(st->muxout_gpio);
+		if (ret ^ (i & 1)) {
+			dev_err(dev, "Probe failed (muxout)");
+			return -ENODEV;
+		}
+	}
+
+	return 0;
+}
+
+static void adf4360_clkin_disable(void *data)
+{
+	struct adf4360_state *st = data;
+
+	clk_disable_unprepare(st->clkin);
+}
+
+static int adf4360_get_clkin(struct adf4360_state *st)
+{
+	struct device *dev = &st->spi->dev;
+	struct clk *clk;
+	int ret;
+
+	clk = devm_clk_get(dev, "clkin");
+	if (IS_ERR(clk))
+		return PTR_ERR(clk);
+
+	ret = clk_prepare_enable(clk);
+	if (ret)
+		return ret;
+
+	ret = devm_add_action_or_reset(dev, adf4360_clkin_disable, st);
+	if (ret)
+		return ret;
+
+	st->clkin = clk;
+	st->clkin_freq = clk_get_rate(clk);
+
+	return 0;
+}
+
+static void adf4360_clk_del_provider(void *data)
+{
+	struct adf4360_state *st = data;
+
+	of_clk_del_provider(st->spi->dev.of_node);
+}
+
+static int adf4360_clk_register(struct adf4360_state *st)
+{
+	struct spi_device *spi = st->spi;
+	struct clk_init_data init;
+	struct clk *clk;
+	const char *parent_name;
+	int ret;
+
+	parent_name = of_clk_get_parent_name(spi->dev.of_node, 0);
+	if (!parent_name)
+		return -EINVAL;
+
+	init.name = st->clk_out_name;
+	init.ops = &adf4360_clk_ops;
+	init.flags = CLK_SET_RATE_GATE;
+	init.parent_names = &parent_name;
+	init.num_parents = 1;
+
+	st->output.hw.init = &init;
+
+	clk = devm_clk_register(&spi->dev, &st->output.hw);
+	if (IS_ERR(clk))
+		return PTR_ERR(clk);
+
+	ret = of_clk_add_provider(spi->dev.of_node, of_clk_src_simple_get, clk);
+	if (ret)
+		return ret;
+
+	return devm_add_action_or_reset(&spi->dev, adf4360_clk_del_provider, st);
+}
+
+static int adf4360_parse_dt(struct adf4360_state *st)
+{
+	struct device *dev = &st->spi->dev;
+	u32 tmp;
+	int ret;
+
+	ret = device_property_read_string(dev, "clock-output-names",
+					  &st->clk_out_name);
+	if ((ret < 0) && dev->of_node)
+		st->clk_out_name = dev->of_node->name;
+
+	if (st->part_id >= ID_ADF4360_7) {
+		/*
+		 * ADF4360-7 to ADF4360-9 have a VCO that is tuned to a specific
+		 * range using an external inductor. These properties describe
+		 * the range selected by the external inductor.
+		 */
+		ret = device_property_read_u32(dev,
+					       "adi,vco-minimum-frequency-hz",
+					       &tmp);
+		if (ret == 0)
+			st->vco_min = max(st->info->vco_min, tmp);
+		else
+			st->vco_min = st->info->vco_min;
+
+		ret = device_property_read_u32(dev,
+					       "adi,vco-maximum-frequency-hz",
+					       &tmp);
+		if (ret == 0)
+			st->vco_max = min(st->info->vco_max, tmp);
+		else
+			st->vco_max = st->info->vco_max;
+	} else {
+		st->vco_min = st->info->vco_min;
+		st->vco_max = st->info->vco_max;
+	}
+
+	st->pdp = device_property_read_bool(dev, "adi,loop-filter-inverting");
+
+	ret = device_property_read_u32(dev,
+				       "adi,loop-filter-pfd-frequency-hz",
+				       &tmp);
+	if (ret == 0) {
+		st->pfd_freq = tmp;
+	} else {
+		dev_err(dev, "PFD frequency property missing\n");
+		return ret;
+	}
+
+	ret = device_property_read_u32(dev,
+				"adi,loop-filter-charge-pump-current-microamp",
+				&tmp);
+	if (ret == 0) {
+		st->cpi = find_closest(tmp, adf4360_cpi_modes,
+				       ARRAY_SIZE(adf4360_cpi_modes));
+	} else {
+		dev_err(dev, "CPI property missing\n");
+		return ret;
+	}
+
+	ret = device_property_read_u32(dev, "adi,power-up-frequency-hz", &tmp);
+	if (ret == 0)
+		st->freq_req = tmp;
+	else
+		st->freq_req = st->vco_min;
+
+	ret = device_property_read_u32(dev, "adi,power-out-level-microamp",
+				       &tmp);
+	if (ret == 0)
+		st->power_level = find_closest(tmp, adf4360_power_lvl_microamp,
+					ARRAY_SIZE(adf4360_power_lvl_microamp));
+	else
+		st->power_level = ADF4360_PL_5;
+
+	return 0;
+}
+
+static int adf4360_probe(struct spi_device *spi)
+{
+	struct iio_dev *indio_dev;
+	const struct spi_device_id *id = spi_get_device_id(spi);
+	struct adf4360_state *st;
+	int ret;
+
+	indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
+	if (!indio_dev)
+		return -ENOMEM;
+
+	st = iio_priv(indio_dev);
+
+	mutex_init(&st->lock);
+
+	spi_set_drvdata(spi, indio_dev);
+
+	st->spi = spi;
+	st->info = &adf4360_chip_info_tbl[id->driver_data];
+	st->part_id = id->driver_data;
+	st->muxout_mode = ADF4360_MUXOUT_LOCK_DETECT;
+	st->mtld = true;
+
+	ret = adf4360_parse_dt(st);
+	if (ret) {
+		dev_err(&spi->dev, "Parsing properties failed (%d)\n", ret);
+		return -ENODEV;
+	}
+
+	indio_dev->dev.parent = &spi->dev;
+
+	if (spi->dev.of_node)
+		indio_dev->name = spi->dev.of_node->name;
+	else
+		indio_dev->name = spi_get_device_id(spi)->name;
+
+	indio_dev->info = &adf4360_iio_info;
+	indio_dev->modes = INDIO_DIRECT_MODE;
+	indio_dev->channels = &adf4360_chan;
+	indio_dev->num_channels = 1;
+	st->output.indio_dev = indio_dev;
+
+	ret = adf4360_get_gpio(st);
+	if (ret)
+		return ret;
+
+	ret = adf4360_get_clkin(st);
+	if (ret)
+		return ret;
+
+	st->vdd_reg = devm_regulator_get_optional(&spi->dev, "vdd");
+	if (IS_ERR(st->vdd_reg)) {
+		if (PTR_ERR(st->vdd_reg) != -ENODEV) {
+			dev_err(&spi->dev, "Regulator error\n");
+			return PTR_ERR(st->vdd_reg);
+		}
+
+		st->vdd_reg = NULL;
+	}
+
+	ret = adf4360_power_down(st, ADF4360_POWER_DOWN_NORMAL);
+	if (ret)
+		return ret;
+
+	ret = adf4360_clk_register(st);
+	if (ret)
+		return ret;
+
+	return devm_iio_device_register(&spi->dev, indio_dev);
+}
+
+static const struct of_device_id adf4360_of_match[] = {
+	{ .compatible = "adi,adf4360-0", },
+	{ .compatible = "adi,adf4360-1", },
+	{ .compatible = "adi,adf4360-2", },
+	{ .compatible = "adi,adf4360-3", },
+	{ .compatible = "adi,adf4360-4", },
+	{ .compatible = "adi,adf4360-5", },
+	{ .compatible = "adi,adf4360-6", },
+	{ .compatible = "adi,adf4360-7", },
+	{ .compatible = "adi,adf4360-8", },
+	{ .compatible = "adi,adf4360-9", },
+	{},
+};
+MODULE_DEVICE_TABLE(of, adf4360_of_match);
+
+static const struct spi_device_id adf4360_id[] = {
+	{"adf4360-0", ID_ADF4360_0},
+	{"adf4360-1", ID_ADF4360_1},
+	{"adf4360-2", ID_ADF4360_2},
+	{"adf4360-3", ID_ADF4360_3},
+	{"adf4360-4", ID_ADF4360_4},
+	{"adf4360-5", ID_ADF4360_5},
+	{"adf4360-6", ID_ADF4360_6},
+	{"adf4360-7", ID_ADF4360_7},
+	{"adf4360-8", ID_ADF4360_8},
+	{"adf4360-9", ID_ADF4360_9},
+	{}
+};
+
+static struct spi_driver adf4360_driver = {
+	.driver = {
+		.name = "adf4360",
+		.of_match_table = adf4360_of_match,
+		.owner = THIS_MODULE,
+	},
+	.probe = adf4360_probe,
+	.id_table = adf4360_id,
+};
+module_spi_driver(adf4360_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
+MODULE_AUTHOR("Edward Kigwana <ekigwana@gmail.com>");
+MODULE_DESCRIPTION("Analog Devices ADF4360 PLL");
-- 
2.20.1


             reply	other threads:[~2020-01-28 11:11 UTC|newest]

Thread overview: 10+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2020-01-28 11:13 Alexandru Ardelean [this message]
2020-01-28 11:13 ` [PATCH v3 2/3] dt-bindings: iio: frequency: Add docs for ADF4360 PLL Alexandru Ardelean
2020-02-02 14:47   ` Jonathan Cameron
2020-02-05 18:24   ` Rob Herring
2020-01-28 11:13 ` [PATCH v3 3/3] MAINTAINERS: add entry for ADF4360 PLL driver Alexandru Ardelean
2020-02-03 10:26   ` Andy Shevchenko
2020-02-02 14:45 ` [PATCH v3 1/3] iio: frequency: adf4360: Add support for ADF4360 PLLs Jonathan Cameron
2020-02-03 11:18   ` Ardelean, Alexandru
2020-02-03 11:27     ` Jonathan Cameron
2020-02-03 14:10     ` Andy Shevchenko

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20200128111302.24359-1-alexandru.ardelean@analog.com \
    --to=alexandru.ardelean@analog.com \
    --cc=devicetree@vger.kernel.org \
    --cc=ekigwana@gmail.com \
    --cc=jic23@kernel.org \
    --cc=lars@metafoo.de \
    --cc=linux-iio@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=robh+dt@kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.