All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 1/2] iio: frequency: New driver for AD9523 SPI Low Jitter Clock Generator
@ 2012-05-21 14:58 michael.hennerich
  2012-05-21 14:58 ` [PATCH 2/2] iio: frequency: New driver for Analog Devices ADF4350/ADF4351 Wideband Synthesizers michael.hennerich
  2012-05-24 13:00 ` [PATCH 1/2] iio: frequency: New driver for AD9523 SPI Low Jitter Clock Generator Jonathan Cameron
  0 siblings, 2 replies; 6+ messages in thread
From: michael.hennerich @ 2012-05-21 14:58 UTC (permalink / raw)
  To: jic23; +Cc: linux-iio, device-drivers-devel, Michael Hennerich

From: Michael Hennerich <michael.hennerich@analog.com>

Changes since V1:

Apply Jonathan's review feedback:
Revise device status attribute names, and split documentation into two sections.
Add additional comments, and fix indention issues.
Remove pointless zero initializations.
Revise return value handling.
Simplify some code sections.
Split store_eeprom and sync handling into separate functions.
Use strtobool where applicable.
Document platform data structures using kernel-doc style.

Use dev_to_iio_dev
write_raw IIO_CHAN_INFO_FREQUENCY: Reject values <= 0
Make patch target drivers/iio

Signed-off-by: Michael Hennerich <michael.hennerich@analog.com>
---
 .../ABI/testing/sysfs-bus-iio-frequency-ad9523     |   37 +
 drivers/iio/Kconfig                                |    1 +
 drivers/iio/Makefile                               |    1 +
 drivers/iio/frequency/Kconfig                      |   23 +
 drivers/iio/frequency/Makefile                     |    5 +
 drivers/iio/frequency/ad9523.c                     | 1059 ++++++++++++++++++++
 include/linux/iio/frequency/ad9523.h               |  195 ++++
 7 files changed, 1321 insertions(+), 0 deletions(-)
 create mode 100644 Documentation/ABI/testing/sysfs-bus-iio-frequency-ad9523
 create mode 100644 drivers/iio/frequency/Kconfig
 create mode 100644 drivers/iio/frequency/Makefile
 create mode 100644 drivers/iio/frequency/ad9523.c
 create mode 100644 include/linux/iio/frequency/ad9523.h

diff --git a/Documentation/ABI/testing/sysfs-bus-iio-frequency-ad9523 b/Documentation/ABI/testing/sysfs-bus-iio-frequency-ad9523
new file mode 100644
index 0000000..2ce9c3f
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-bus-iio-frequency-ad9523
@@ -0,0 +1,37 @@
+What:		/sys/bus/iio/devices/iio:deviceX/pll2_feedback_clk_present
+What:		/sys/bus/iio/devices/iio:deviceX/pll2_reference_clk_present
+What:		/sys/bus/iio/devices/iio:deviceX/pll1_reference_clk_a_present
+What:		/sys/bus/iio/devices/iio:deviceX/pll1_reference_clk_b_present
+What:		/sys/bus/iio/devices/iio:deviceX/pll1_reference_clk_test_present
+What:		/sys/bus/iio/devices/iio:deviceX/vcxo_clk_present
+KernelVersion:	3.4.0
+Contact:	linux-iio@vger.kernel.org
+Description:
+		Reading returns either '1' or '0'.
+		'1' means that the clock in question is present.
+		'0' means that the clock is missing.
+
+What:		/sys/bus/iio/devices/iio:deviceX/pllY_locked
+KernelVersion:	3.4.0
+Contact:	linux-iio@vger.kernel.org
+Description:
+		Reading returns either '1' or '0'. '1' means that the
+		pllY is locked.
+
+What:		/sys/bus/iio/devices/iio:deviceX/store_eeprom
+KernelVersion:	3.4.0
+Contact:	linux-iio@vger.kernel.org
+Description:
+		Writing '1' stores the current device configuration into
+		on-chip EEPROM. After power-up or chip reset the device will
+		automatically load the saved configuration.
+
+What:		/sys/bus/iio/devices/iio:deviceX/sync_dividers
+KernelVersion:	3.4.0
+Contact:	linux-iio@vger.kernel.org
+Description:
+		Writing '1' triggers the clock distribution synchronization
+		functionality. All dividers are reset and the channels start
+		with their predefined phase offsets (out_altvoltageY_phase).
+		Writing this file has the effect as driving the external
+		/SYNC pin low.
diff --git a/drivers/iio/Kconfig b/drivers/iio/Kconfig
index 56eecef..ad09f67 100644
--- a/drivers/iio/Kconfig
+++ b/drivers/iio/Kconfig
@@ -50,5 +50,6 @@ config IIO_CONSUMERS_PER_TRIGGER
 
 source "drivers/iio/adc/Kconfig"
 source "drivers/iio/amplifiers/Kconfig"
+source "drivers/iio/frequency/Kconfig"
 
 endif # IIO
diff --git a/drivers/iio/Makefile b/drivers/iio/Makefile
index e425afd..014e0e4 100644
--- a/drivers/iio/Makefile
+++ b/drivers/iio/Makefile
@@ -11,3 +11,4 @@ obj-$(CONFIG_IIO_KFIFO_BUF) += kfifo_buf.o
 
 obj-y += adc/
 obj-y += amplifiers/
+obj-y += frequency/
diff --git a/drivers/iio/frequency/Kconfig b/drivers/iio/frequency/Kconfig
new file mode 100644
index 0000000..0458c92
--- /dev/null
+++ b/drivers/iio/frequency/Kconfig
@@ -0,0 +1,23 @@
+#
+# Frequency
+#	Direct Digital Synthesis drivers (DDS)
+#	Clock Distribution device drivers
+#	Phase-Locked Loop (PLL) frequency synthesizers
+#
+
+menu "Frequency Synthesizers DDS/PLL"
+
+menu "Clock Generator/Distribution"
+
+config AD9523
+	tristate "Analog Devices AD9523 Low Jitter Clock Generator"
+	depends on SPI
+	help
+	  Say yes here to build support for Analog Devices AD9523 Low Jitter
+	  Clock Generator. The driver provides direct access via sysfs.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called ad9523.
+
+endmenu
+endmenu
diff --git a/drivers/iio/frequency/Makefile b/drivers/iio/frequency/Makefile
new file mode 100644
index 0000000..1b5b224
--- /dev/null
+++ b/drivers/iio/frequency/Makefile
@@ -0,0 +1,5 @@
+#
+# Makefile iio/frequency
+#
+
+obj-$(CONFIG_AD9523) += ad9523.o
diff --git a/drivers/iio/frequency/ad9523.c b/drivers/iio/frequency/ad9523.c
new file mode 100644
index 0000000..77a17de
--- /dev/null
+++ b/drivers/iio/frequency/ad9523.c
@@ -0,0 +1,1059 @@
+/*
+ * AD9523 SPI Low Jitter Clock Generator
+ *
+ * Copyright 2012 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+#include <linux/spi/spi.h>
+#include <linux/regulator/consumer.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/frequency/ad9523.h>
+
+#define AD9523_READ	(1 << 15)
+#define AD9523_WRITE	(0 << 15)
+#define AD9523_CNT(x)	(((x) - 1) << 13)
+#define AD9523_ADDR(x)	((x) & 0xFFF)
+
+#define AD9523_R1B	(1 << 16)
+#define AD9523_R2B	(2 << 16)
+#define AD9523_R3B	(3 << 16)
+#define AD9523_TRANSF_LEN(x)			((x) >> 16)
+
+#define AD9523_SERIAL_PORT_CONFIG		(AD9523_R1B | 0x0)
+#define AD9523_VERSION_REGISTER			(AD9523_R1B | 0x2)
+#define AD9523_PART_REGISTER			(AD9523_R1B | 0x3)
+#define AD9523_READBACK_CTRL			(AD9523_R1B | 0x4)
+
+#define AD9523_EEPROM_CUSTOMER_VERSION_ID	(AD9523_R2B | 0x6)
+
+#define AD9523_PLL1_REF_A_DIVIDER		(AD9523_R2B | 0x11)
+#define AD9523_PLL1_REF_B_DIVIDER		(AD9523_R2B | 0x13)
+#define AD9523_PLL1_REF_TEST_DIVIDER		(AD9523_R1B | 0x14)
+#define AD9523_PLL1_FEEDBACK_DIVIDER		(AD9523_R2B | 0x17)
+#define AD9523_PLL1_CHARGE_PUMP_CTRL		(AD9523_R2B | 0x19)
+#define AD9523_PLL1_INPUT_RECEIVERS_CTRL	(AD9523_R1B | 0x1A)
+#define AD9523_PLL1_REF_CTRL			(AD9523_R1B | 0x1B)
+#define AD9523_PLL1_MISC_CTRL			(AD9523_R1B | 0x1C)
+#define AD9523_PLL1_LOOP_FILTER_CTRL		(AD9523_R1B | 0x1D)
+
+#define AD9523_PLL2_CHARGE_PUMP			(AD9523_R1B | 0xF0)
+#define AD9523_PLL2_FEEDBACK_DIVIDER_AB		(AD9523_R1B | 0xF1)
+#define AD9523_PLL2_CTRL			(AD9523_R1B | 0xF2)
+#define AD9523_PLL2_VCO_CTRL			(AD9523_R1B | 0xF3)
+#define AD9523_PLL2_VCO_DIVIDER			(AD9523_R1B | 0xF4)
+#define AD9523_PLL2_LOOP_FILTER_CTRL		(AD9523_R2B | 0xF6)
+#define AD9523_PLL2_R2_DIVIDER			(AD9523_R1B | 0xF7)
+
+#define AD9523_CHANNEL_CLOCK_DIST(ch)		(AD9523_R3B | (0x192 + 3 * ch))
+
+#define AD9523_PLL1_OUTPUT_CTRL			(AD9523_R1B | 0x1BA)
+#define AD9523_PLL1_OUTPUT_CHANNEL_CTRL		(AD9523_R1B | 0x1BB)
+
+#define AD9523_READBACK_0			(AD9523_R1B | 0x22C)
+#define AD9523_READBACK_1			(AD9523_R1B | 0x22D)
+
+#define AD9523_STATUS_SIGNALS			(AD9523_R3B | 0x232)
+#define AD9523_POWER_DOWN_CTRL			(AD9523_R1B | 0x233)
+#define AD9523_IO_UPDATE			(AD9523_R1B | 0x234)
+
+#define AD9523_EEPROM_DATA_XFER_STATUS		(AD9523_R1B | 0xB00)
+#define AD9523_EEPROM_ERROR_READBACK		(AD9523_R1B | 0xB01)
+#define AD9523_EEPROM_CTRL1			(AD9523_R1B | 0xB02)
+#define AD9523_EEPROM_CTRL2			(AD9523_R1B | 0xB03)
+
+/* AD9523_SERIAL_PORT_CONFIG */
+
+#define AD9523_SER_CONF_SDO_ACTIVE		(1 << 7)
+#define AD9523_SER_CONF_SOFT_RESET		(1 << 5)
+
+/* AD9523_READBACK_CTRL */
+#define AD9523_READBACK_CTRL_READ_BUFFERED	(1 << 0)
+
+/* AD9523_PLL1_CHARGE_PUMP_CTRL */
+#define AD9523_PLL1_CHARGE_PUMP_CURRENT_nA(x)	(((x) / 500) & 0x7F)
+#define AD9523_PLL1_CHARGE_PUMP_TRISTATE	(1 << 7)
+#define AD9523_PLL1_CHARGE_PUMP_MODE_NORMAL	(3 << 8)
+#define AD9523_PLL1_CHARGE_PUMP_MODE_PUMP_DOWN	(2 << 8)
+#define AD9523_PLL1_CHARGE_PUMP_MODE_PUMP_UP	(1 << 8)
+#define AD9523_PLL1_CHARGE_PUMP_MODE_TRISTATE	(0 << 8)
+#define AD9523_PLL1_BACKLASH_PW_MIN		(0 << 10)
+#define AD9523_PLL1_BACKLASH_PW_LOW		(1 << 10)
+#define AD9523_PLL1_BACKLASH_PW_HIGH		(2 << 10)
+#define AD9523_PLL1_BACKLASH_PW_MAX		(3 << 10)
+
+/* AD9523_PLL1_INPUT_RECEIVERS_CTRL */
+#define AD9523_PLL1_REF_TEST_RCV_EN		(1 << 7)
+#define AD9523_PLL1_REFB_DIFF_RCV_EN		(1 << 6)
+#define AD9523_PLL1_REFA_DIFF_RCV_EN		(1 << 5)
+#define AD9523_PLL1_REFB_RCV_EN			(1 << 4)
+#define AD9523_PLL1_REFA_RCV_EN			(1 << 3)
+#define AD9523_PLL1_REFA_REFB_PWR_CTRL_EN	(1 << 2)
+#define AD9523_PLL1_OSC_IN_CMOS_NEG_INP_EN	(1 << 1)
+#define AD9523_PLL1_OSC_IN_DIFF_EN		(1 << 0)
+
+/* AD9523_PLL1_REF_CTRL */
+#define AD9523_PLL1_BYPASS_REF_TEST_DIV_EN	(1 << 7)
+#define AD9523_PLL1_BYPASS_FEEDBACK_DIV_EN	(1 << 6)
+#define AD9523_PLL1_ZERO_DELAY_MODE_INT		(1 << 5)
+#define AD9523_PLL1_ZERO_DELAY_MODE_EXT		(0 << 5)
+#define AD9523_PLL1_OSC_IN_PLL_FEEDBACK_EN	(1 << 4)
+#define AD9523_PLL1_ZD_IN_CMOS_NEG_INP_EN	(1 << 3)
+#define AD9523_PLL1_ZD_IN_DIFF_EN		(1 << 2)
+#define AD9523_PLL1_REFB_CMOS_NEG_INP_EN	(1 << 1)
+#define AD9523_PLL1_REFA_CMOS_NEG_INP_EN	(1 << 0)
+
+/* AD9523_PLL1_MISC_CTRL */
+#define AD9523_PLL1_REFB_INDEP_DIV_CTRL_EN	(1 << 7)
+#define AD9523_PLL1_OSC_CTRL_FAIL_VCC_BY2_EN	(1 << 6)
+#define AD9523_PLL1_REF_MODE(x)			((x) << 2)
+#define AD9523_PLL1_BYPASS_REFB_DIV		(1 << 1)
+#define AD9523_PLL1_BYPASS_REFA_DIV		(1 << 0)
+
+/* AD9523_PLL1_LOOP_FILTER_CTRL */
+#define AD9523_PLL1_LOOP_FILTER_RZERO(x)	((x) & 0xF)
+
+/* AD9523_PLL2_CHARGE_PUMP */
+#define AD9523_PLL2_CHARGE_PUMP_CURRENT_nA(x)	((x) / 3500)
+
+/* AD9523_PLL2_FEEDBACK_DIVIDER_AB */
+#define AD9523_PLL2_FB_NDIV_A_CNT(x)		(((x) & 0x3) << 6)
+#define AD9523_PLL2_FB_NDIV_B_CNT(x)		(((x) & 0x3F) << 0)
+#define AD9523_PLL2_FB_NDIV(a, b)		(4 * (b) + (a))
+
+/* AD9523_PLL2_CTRL */
+#define AD9523_PLL2_CHARGE_PUMP_MODE_NORMAL	(3 << 0)
+#define AD9523_PLL2_CHARGE_PUMP_MODE_PUMP_DOWN	(2 << 0)
+#define AD9523_PLL2_CHARGE_PUMP_MODE_PUMP_UP	(1 << 0)
+#define AD9523_PLL2_CHARGE_PUMP_MODE_TRISTATE	(0 << 0)
+#define AD9523_PLL2_BACKLASH_PW_MIN		(0 << 2)
+#define AD9523_PLL2_BACKLASH_PW_LOW		(1 << 2)
+#define AD9523_PLL2_BACKLASH_PW_HIGH		(2 << 2)
+#define AD9523_PLL2_BACKLASH_PW_MAX		(3 << 1)
+#define AD9523_PLL2_BACKLASH_CTRL_EN		(1 << 4)
+#define AD9523_PLL2_FREQ_DOUBLER_EN		(1 << 5)
+#define AD9523_PLL2_LOCK_DETECT_PWR_DOWN_EN	(1 << 7)
+
+/* AD9523_PLL2_VCO_CTRL */
+#define AD9523_PLL2_VCO_CALIBRATE		(1 << 1)
+#define AD9523_PLL2_FORCE_VCO_MIDSCALE		(1 << 2)
+#define AD9523_PLL2_FORCE_REFERENCE_VALID	(1 << 3)
+#define AD9523_PLL2_FORCE_RELEASE_SYNC		(1 << 4)
+
+/* AD9523_PLL2_VCO_DIVIDER */
+#define AD9523_PLL2_VCO_DIV_M1(x)		((((x) - 3) & 0x3) << 0)
+#define AD9523_PLL2_VCO_DIV_M2(x)		((((x) - 3) & 0x3) << 4)
+#define AD9523_PLL2_VCO_DIV_M1_PWR_DOWN_EN	(1 << 2)
+#define AD9523_PLL2_VCO_DIV_M2_PWR_DOWN_EN	(1 << 6)
+
+/* AD9523_PLL2_LOOP_FILTER_CTRL */
+#define AD9523_PLL2_LOOP_FILTER_CPOLE1(x)	(((x) & 0x7) << 0)
+#define AD9523_PLL2_LOOP_FILTER_RZERO(x)	(((x) & 0x7) << 3)
+#define AD9523_PLL2_LOOP_FILTER_RPOLE2(x)	(((x) & 0x7) << 6)
+#define AD9523_PLL2_LOOP_FILTER_RZERO_BYPASS_EN	(1 << 8)
+
+/* AD9523_PLL2_R2_DIVIDER */
+#define AD9523_PLL2_R2_DIVIDER_VAL(x)		(((x) & 0x1F) << 0)
+
+/* AD9523_CHANNEL_CLOCK_DIST */
+#define AD9523_CLK_DIST_DIV_PHASE(x)		(((x) & 0x3F) << 18)
+#define AD9523_CLK_DIST_DIV_PHASE_REV(x)	((ret >> 18) & 0x3F)
+#define AD9523_CLK_DIST_DIV(x)			((((x) - 1) & 0x3FF) << 8)
+#define AD9523_CLK_DIST_DIV_REV(x)		(((ret >> 8) & 0x3FF) + 1)
+#define AD9523_CLK_DIST_INV_DIV_OUTPUT_EN	(1 << 7)
+#define AD9523_CLK_DIST_IGNORE_SYNC_EN		(1 << 6)
+#define AD9523_CLK_DIST_PWR_DOWN_EN		(1 << 5)
+#define AD9523_CLK_DIST_LOW_PWR_MODE_EN		(1 << 4)
+#define AD9523_CLK_DIST_DRIVER_MODE(x)		(((x) & 0xF) << 0)
+
+/* AD9523_PLL1_OUTPUT_CTRL */
+#define AD9523_PLL1_OUTP_CTRL_VCO_DIV_SEL_CH6_M2	(1 << 7)
+#define AD9523_PLL1_OUTP_CTRL_VCO_DIV_SEL_CH5_M2	(1 << 6)
+#define AD9523_PLL1_OUTP_CTRL_VCO_DIV_SEL_CH4_M2	(1 << 5)
+#define AD9523_PLL1_OUTP_CTRL_CMOS_DRV_WEAK		(1 << 4)
+#define AD9523_PLL1_OUTP_CTRL_OUTPUT_DIV_1		(0 << 0)
+#define AD9523_PLL1_OUTP_CTRL_OUTPUT_DIV_2		(1 << 0)
+#define AD9523_PLL1_OUTP_CTRL_OUTPUT_DIV_4		(2 << 0)
+#define AD9523_PLL1_OUTP_CTRL_OUTPUT_DIV_8		(4 << 0)
+#define AD9523_PLL1_OUTP_CTRL_OUTPUT_DIV_16		(8 << 0)
+
+/* AD9523_PLL1_OUTPUT_CHANNEL_CTRL */
+#define AD9523_PLL1_OUTP_CH_CTRL_OUTPUT_PWR_DOWN_EN	(1 << 7)
+#define AD9523_PLL1_OUTP_CH_CTRL_VCO_DIV_SEL_CH9_M2	(1 << 6)
+#define AD9523_PLL1_OUTP_CH_CTRL_VCO_DIV_SEL_CH8_M2	(1 << 5)
+#define AD9523_PLL1_OUTP_CH_CTRL_VCO_DIV_SEL_CH7_M2	(1 << 4)
+#define AD9523_PLL1_OUTP_CH_CTRL_VCXO_SRC_SEL_CH3	(1 << 3)
+#define AD9523_PLL1_OUTP_CH_CTRL_VCXO_SRC_SEL_CH2	(1 << 2)
+#define AD9523_PLL1_OUTP_CH_CTRL_VCXO_SRC_SEL_CH1	(1 << 1)
+#define AD9523_PLL1_OUTP_CH_CTRL_VCXO_SRC_SEL_CH0	(1 << 0)
+
+/* AD9523_READBACK_0 */
+#define AD9523_READBACK_0_STAT_PLL2_REF_CLK		(1 << 7)
+#define AD9523_READBACK_0_STAT_PLL2_FB_CLK		(1 << 6)
+#define AD9523_READBACK_0_STAT_VCXO			(1 << 5)
+#define AD9523_READBACK_0_STAT_REF_TEST			(1 << 4)
+#define AD9523_READBACK_0_STAT_REFB			(1 << 3)
+#define AD9523_READBACK_0_STAT_REFA			(1 << 2)
+#define AD9523_READBACK_0_STAT_PLL2_LD			(1 << 1)
+#define AD9523_READBACK_0_STAT_PLL1_LD			(1 << 0)
+
+/* AD9523_READBACK_1 */
+#define AD9523_READBACK_1_HOLDOVER_ACTIVE		(1 << 3)
+#define AD9523_READBACK_1_AUTOMODE_SEL_REFB		(1 << 2)
+#define AD9523_READBACK_1_VCO_CALIB_IN_PROGRESS		(1 << 0)
+
+/* AD9523_STATUS_SIGNALS */
+#define AD9523_STATUS_SIGNALS_SYNC_MAN_CTRL		(1 << 16)
+#define AD9523_STATUS_MONITOR_01_PLL12_LOCKED		(0x302)
+/* AD9523_POWER_DOWN_CTRL */
+#define AD9523_POWER_DOWN_CTRL_PLL1_PWR_DOWN		(1 << 2)
+#define AD9523_POWER_DOWN_CTRL_PLL2_PWR_DOWN		(1 << 1)
+#define AD9523_POWER_DOWN_CTRL_DIST_PWR_DOWN		(1 << 0)
+
+/* AD9523_IO_UPDATE */
+#define AD9523_IO_UPDATE_EN				(1 << 0)
+
+/* AD9523_EEPROM_DATA_XFER_STATUS */
+#define AD9523_EEPROM_DATA_XFER_IN_PROGRESS		(1 << 0)
+
+/* AD9523_EEPROM_ERROR_READBACK */
+#define AD9523_EEPROM_ERROR_READBACK_FAIL		(1 << 0)
+
+/* AD9523_EEPROM_CTRL1 */
+#define AD9523_EEPROM_CTRL1_SOFT_EEPROM			(1 << 1)
+#define AD9523_EEPROM_CTRL1_EEPROM_WRITE_PROT_DIS	(1 << 0)
+
+/* AD9523_EEPROM_CTRL2 */
+#define AD9523_EEPROM_CTRL2_REG2EEPROM			(1 << 0)
+
+#define AD9523_NUM_CHAN					14
+#define AD9523_NUM_CHAN_ALT_CLK_SRC			10
+
+/* Helpers to avoid excess line breaks */
+#define AD_IFE(_pde, _a, _b) ((pdata->_pde) ? _a : _b)
+#define AD_IF(_pde, _a) AD_IFE(_pde, _a, 0)
+
+enum {
+	AD9523_STAT_PLL1_LD,
+	AD9523_STAT_PLL2_LD,
+	AD9523_STAT_REFA,
+	AD9523_STAT_REFB,
+	AD9523_STAT_REF_TEST,
+	AD9523_STAT_VCXO,
+	AD9523_STAT_PLL2_FB_CLK,
+	AD9523_STAT_PLL2_REF_CLK,
+	AD9523_SYNC,
+	AD9523_EEPROM,
+};
+
+enum {
+	AD9523_VCO1,
+	AD9523_VCO2,
+	AD9523_VCXO,
+	AD9523_NUM_CLK_SRC,
+};
+
+struct ad9523_state {
+	struct spi_device		*spi;
+	struct regulator		*reg;
+	struct ad9523_platform_data	*pdata;
+	struct iio_chan_spec		ad9523_channels[AD9523_NUM_CHAN];
+
+	unsigned long		vcxo_freq;
+	unsigned long		vco_freq;
+	unsigned long		vco_out_freq[AD9523_NUM_CLK_SRC];
+	unsigned char		vco_out_map[AD9523_NUM_CHAN_ALT_CLK_SRC];
+
+	/*
+	 * DMA (thus cache coherency maintenance) requires the
+	 * transfer buffers to live in their own cache lines.
+	 */
+	union {
+		__be32 d32;
+		u8 d8[4];
+	} data[2] ____cacheline_aligned;
+};
+
+static int ad9523_read(struct iio_dev *indio_dev, unsigned addr)
+{
+	struct ad9523_state *st = iio_priv(indio_dev);
+	struct spi_message m;
+	int ret;
+
+	/* We encode the register size 1..3 bytes into the register address.
+	 * On transfer we get the size from the register datum, and make sure
+	 * the result is properly aligned.
+	 */
+
+	struct spi_transfer t[] = {
+		{
+			.tx_buf = &st->data[0].d8[2],
+			.len = 2,
+		}, {
+			.rx_buf = &st->data[1].d8[4 - AD9523_TRANSF_LEN(addr)],
+			.len = AD9523_TRANSF_LEN(addr),
+		},
+	};
+
+	spi_message_init(&m);
+	spi_message_add_tail(&t[0], &m);
+	spi_message_add_tail(&t[1], &m);
+
+	st->data[0].d32 = cpu_to_be32(AD9523_READ |
+				      AD9523_CNT(AD9523_TRANSF_LEN(addr)) |
+				      AD9523_ADDR(addr));
+
+	ret = spi_sync(st->spi, &m);
+	if (ret < 0)
+		dev_err(&indio_dev->dev, "read failed (%d)", ret);
+	else
+		ret = be32_to_cpu(st->data[1].d32) & (0xFFFFFF >>
+				  (8 * (3 - AD9523_TRANSF_LEN(addr))));
+
+	return ret;
+};
+
+static int ad9523_write(struct iio_dev *indio_dev, unsigned addr, unsigned val)
+{
+	struct ad9523_state *st = iio_priv(indio_dev);
+	struct spi_message m;
+	int ret;
+	struct spi_transfer t[] = {
+		{
+			.tx_buf = &st->data[0].d8[2],
+			.len = 2,
+		}, {
+			.tx_buf = &st->data[1].d8[4 - AD9523_TRANSF_LEN(addr)],
+			.len = AD9523_TRANSF_LEN(addr),
+		},
+	};
+
+	spi_message_init(&m);
+	spi_message_add_tail(&t[0], &m);
+	spi_message_add_tail(&t[1], &m);
+
+	st->data[0].d32 = cpu_to_be32(AD9523_WRITE |
+				      AD9523_CNT(AD9523_TRANSF_LEN(addr)) |
+				      AD9523_ADDR(addr));
+	st->data[1].d32 = cpu_to_be32(val);
+
+	ret = spi_sync(st->spi, &m);
+
+	if (ret < 0)
+		dev_err(&indio_dev->dev, "write failed (%d)", ret);
+
+	return ret;
+}
+
+static int ad9523_io_update(struct iio_dev *indio_dev)
+{
+	return ad9523_write(indio_dev, AD9523_IO_UPDATE, AD9523_IO_UPDATE_EN);
+}
+
+static int ad9523_vco_out_map(struct iio_dev *indio_dev,
+			      unsigned ch, bool out)
+{
+	struct ad9523_state *st = iio_priv(indio_dev);
+	int ret;
+	unsigned mask;
+
+	switch (ch) {
+	case 0 ... 3:
+		ret = ad9523_read(indio_dev, AD9523_PLL1_OUTPUT_CHANNEL_CTRL);
+		if (ret < 0)
+			break;
+		mask = AD9523_PLL1_OUTP_CH_CTRL_VCXO_SRC_SEL_CH0 << ch;
+		if (out) {
+			ret |= mask;
+			out = 2;
+		} else {
+			ret &= ~mask;
+		}
+		ret = ad9523_write(indio_dev,
+				   AD9523_PLL1_OUTPUT_CHANNEL_CTRL, ret);
+		break;
+	case 4 ... 6:
+		ret = ad9523_read(indio_dev, AD9523_PLL1_OUTPUT_CTRL);
+		if (ret < 0)
+			break;
+		mask = AD9523_PLL1_OUTP_CTRL_VCO_DIV_SEL_CH4_M2 << (ch - 4);
+		if (out)
+			ret |= mask;
+		else
+			ret &= ~mask;
+		ret = ad9523_write(indio_dev, AD9523_PLL1_OUTPUT_CTRL, ret);
+		break;
+	case 7 ... 9:
+		ret = ad9523_read(indio_dev, AD9523_PLL1_OUTPUT_CHANNEL_CTRL);
+		if (ret < 0)
+			break;
+		mask = AD9523_PLL1_OUTP_CH_CTRL_VCO_DIV_SEL_CH7_M2 << (ch - 7);
+		if (out)
+			ret |= mask;
+		else
+			ret &= ~mask;
+		ret = ad9523_write(indio_dev,
+				   AD9523_PLL1_OUTPUT_CHANNEL_CTRL, ret);
+		break;
+	default:
+		return 0;
+	}
+
+	st->vco_out_map[ch] = out;
+
+	return ret;
+}
+
+static int ad9523_set_clock_provider(struct iio_dev *indio_dev,
+			      unsigned ch, unsigned long freq)
+{
+	struct ad9523_state *st = iio_priv(indio_dev);
+	long tmp1, tmp2;
+	bool use_alt_clk_src;
+
+	switch (ch) {
+	case 0 ... 3:
+		use_alt_clk_src = (freq == st->vco_out_freq[AD9523_VCXO]);
+		break;
+	case 4 ... 9:
+		tmp1 = st->vco_out_freq[AD9523_VCO1] / freq;
+		tmp2 = st->vco_out_freq[AD9523_VCO2] / freq;
+		tmp1 *= freq;
+		tmp2 *= freq;
+		use_alt_clk_src = (abs(tmp1 - freq) > abs(tmp2 - freq));
+		break;
+	default:
+		/* Ch 10..14: No action required, return success */
+		return 0;
+	}
+
+	return ad9523_vco_out_map(indio_dev, ch, use_alt_clk_src);
+}
+
+static int ad9523_store_eeprom(struct iio_dev *indio_dev)
+{
+	int ret, tmp;
+
+	ret = ad9523_write(indio_dev, AD9523_EEPROM_CTRL1,
+			   AD9523_EEPROM_CTRL1_EEPROM_WRITE_PROT_DIS);
+	if (ret < 0)
+		return ret;
+	ret = ad9523_write(indio_dev, AD9523_EEPROM_CTRL2,
+			   AD9523_EEPROM_CTRL2_REG2EEPROM);
+	if (ret < 0)
+		return ret;
+
+	tmp = 4;
+	do {
+		msleep(16);
+		ret = ad9523_read(indio_dev,
+				  AD9523_EEPROM_DATA_XFER_STATUS);
+		if (ret < 0)
+			return ret;
+	} while ((ret & AD9523_EEPROM_DATA_XFER_IN_PROGRESS) && tmp--);
+
+	ret = ad9523_write(indio_dev, AD9523_EEPROM_CTRL1, 0);
+	if (ret < 0)
+		return ret;
+
+	ret = ad9523_read(indio_dev, AD9523_EEPROM_ERROR_READBACK);
+	if (ret < 0)
+		return ret;
+
+	if (ret & AD9523_EEPROM_ERROR_READBACK_FAIL) {
+		dev_err(&indio_dev->dev, "Verify EEPROM failed");
+		ret = -EIO;
+	}
+
+	return ret;
+}
+
+static int ad9523_sync(struct iio_dev *indio_dev)
+{
+	int ret, tmp;
+
+	ret = ad9523_read(indio_dev, AD9523_STATUS_SIGNALS);
+	if (ret < 0)
+		return ret;
+
+	tmp = ret;
+	tmp |= AD9523_STATUS_SIGNALS_SYNC_MAN_CTRL;
+
+	ret = ad9523_write(indio_dev, AD9523_STATUS_SIGNALS, tmp);
+	if (ret < 0)
+		return ret;
+
+	ad9523_io_update(indio_dev);
+	tmp &= ~AD9523_STATUS_SIGNALS_SYNC_MAN_CTRL;
+
+	ret = ad9523_write(indio_dev, AD9523_STATUS_SIGNALS, tmp);
+	if (ret < 0)
+		return ret;
+
+	return ad9523_io_update(indio_dev);
+}
+
+static ssize_t ad9523_store(struct device *dev,
+				struct device_attribute *attr,
+				const char *buf, size_t len)
+{
+	struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+	bool state;
+	int ret;
+
+	ret = strtobool(buf, &state);
+	if (ret < 0)
+		return ret;
+
+	if (!state)
+		return 0;
+
+	mutex_lock(&indio_dev->mlock);
+	switch ((u32)this_attr->address) {
+	case AD9523_SYNC:
+		ret = ad9523_sync(indio_dev);
+		break;
+	case AD9523_EEPROM:
+		ret = ad9523_store_eeprom(indio_dev);
+		break;
+	default:
+		ret = -ENODEV;
+	}
+	mutex_unlock(&indio_dev->mlock);
+
+	return ret ? ret : len;
+}
+
+static ssize_t ad9523_show(struct device *dev,
+			struct device_attribute *attr,
+			char *buf)
+{
+	struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+	int ret;
+
+	mutex_lock(&indio_dev->mlock);
+	ret = ad9523_read(indio_dev, AD9523_READBACK_0);
+	if (ret >= 0) {
+		ret = sprintf(buf, "%d\n", !!(ret & (1 <<
+			(u32)this_attr->address)));
+	}
+	mutex_unlock(&indio_dev->mlock);
+
+	return ret;
+}
+
+static IIO_DEVICE_ATTR(pll1_locked, S_IRUGO,
+			ad9523_show,
+			NULL,
+			AD9523_STAT_PLL1_LD);
+
+static IIO_DEVICE_ATTR(pll2_locked, S_IRUGO,
+			ad9523_show,
+			NULL,
+			AD9523_STAT_PLL2_LD);
+
+static IIO_DEVICE_ATTR(pll1_reference_clk_a_present, S_IRUGO,
+			ad9523_show,
+			NULL,
+			AD9523_STAT_REFA);
+
+static IIO_DEVICE_ATTR(pll1_reference_clk_b_present, S_IRUGO,
+			ad9523_show,
+			NULL,
+			AD9523_STAT_REFB);
+
+static IIO_DEVICE_ATTR(pll1_reference_clk_test_present, S_IRUGO,
+			ad9523_show,
+			NULL,
+			AD9523_STAT_REF_TEST);
+
+static IIO_DEVICE_ATTR(vcxo_clk_present, S_IRUGO,
+			ad9523_show,
+			NULL,
+			AD9523_STAT_VCXO);
+
+static IIO_DEVICE_ATTR(pll2_feedback_clk_present, S_IRUGO,
+			ad9523_show,
+			NULL,
+			AD9523_STAT_PLL2_FB_CLK);
+
+static IIO_DEVICE_ATTR(pll2_reference_clk_present, S_IRUGO,
+			ad9523_show,
+			NULL,
+			AD9523_STAT_PLL2_REF_CLK);
+
+static IIO_DEVICE_ATTR(sync_dividers, S_IWUSR,
+			NULL,
+			ad9523_store,
+			AD9523_SYNC);
+
+static IIO_DEVICE_ATTR(store_eeprom, S_IWUSR,
+			NULL,
+			ad9523_store,
+			AD9523_EEPROM);
+
+static struct attribute *ad9523_attributes[] = {
+	&iio_dev_attr_sync_dividers.dev_attr.attr,
+	&iio_dev_attr_store_eeprom.dev_attr.attr,
+	&iio_dev_attr_pll2_feedback_clk_present.dev_attr.attr,
+	&iio_dev_attr_pll2_reference_clk_present.dev_attr.attr,
+	&iio_dev_attr_pll1_reference_clk_a_present.dev_attr.attr,
+	&iio_dev_attr_pll1_reference_clk_b_present.dev_attr.attr,
+	&iio_dev_attr_pll1_reference_clk_test_present.dev_attr.attr,
+	&iio_dev_attr_vcxo_clk_present.dev_attr.attr,
+	&iio_dev_attr_pll1_locked.dev_attr.attr,
+	&iio_dev_attr_pll2_locked.dev_attr.attr,
+	NULL,
+};
+
+static const struct attribute_group ad9523_attribute_group = {
+	.attrs = ad9523_attributes,
+};
+
+static int ad9523_read_raw(struct iio_dev *indio_dev,
+			   struct iio_chan_spec const *chan,
+			   int *val,
+			   int *val2,
+			   long m)
+{
+	struct ad9523_state *st = iio_priv(indio_dev);
+	unsigned code;
+	int ret;
+
+	mutex_lock(&indio_dev->mlock);
+	ret = ad9523_read(indio_dev, AD9523_CHANNEL_CLOCK_DIST(chan->channel));
+	mutex_unlock(&indio_dev->mlock);
+
+	if (ret < 0)
+		return ret;
+
+	switch (m) {
+	case IIO_CHAN_INFO_RAW:
+		*val = !(ret & AD9523_CLK_DIST_PWR_DOWN_EN);
+		return IIO_VAL_INT;
+	case IIO_CHAN_INFO_FREQUENCY:
+		*val = st->vco_out_freq[st->vco_out_map[chan->channel]] /
+			AD9523_CLK_DIST_DIV_REV(ret);
+		return IIO_VAL_INT;
+	case IIO_CHAN_INFO_PHASE:
+		code = (AD9523_CLK_DIST_DIV_PHASE_REV(ret) * 3141592) /
+			AD9523_CLK_DIST_DIV_REV(ret);
+		*val = code / 1000000;
+		*val2 = (code % 1000000) * 10;
+		return IIO_VAL_INT_PLUS_MICRO;
+	default:
+		return -EINVAL;
+	}
+};
+
+static int ad9523_write_raw(struct iio_dev *indio_dev,
+			    struct iio_chan_spec const *chan,
+			    int val,
+			    int val2,
+			    long mask)
+{
+	struct ad9523_state *st = iio_priv(indio_dev);
+	unsigned reg;
+	int ret, tmp, code;
+
+	mutex_lock(&indio_dev->mlock);
+	ret = ad9523_read(indio_dev, AD9523_CHANNEL_CLOCK_DIST(chan->channel));
+	if (ret < 0)
+		goto out;
+
+	reg = ret;
+
+	switch (mask) {
+	case IIO_CHAN_INFO_RAW:
+		if (val)
+			reg &= ~AD9523_CLK_DIST_PWR_DOWN_EN;
+		else
+			reg |= AD9523_CLK_DIST_PWR_DOWN_EN;
+		break;
+	case IIO_CHAN_INFO_FREQUENCY:
+		if (val <= 0) {
+			ret = -EINVAL;
+			goto out;
+		}
+		ret = ad9523_set_clock_provider(indio_dev, chan->channel, val);
+		if (ret < 0)
+			goto out;
+		tmp = st->vco_out_freq[st->vco_out_map[chan->channel]] / val;
+		tmp = clamp(tmp, 1, 1024);
+		reg &= ~(0x3FF << 8);
+		reg |= AD9523_CLK_DIST_DIV(tmp);
+		break;
+	case IIO_CHAN_INFO_PHASE:
+		code = val * 1000000 + val2 % 1000000;
+		tmp = (code * AD9523_CLK_DIST_DIV_REV(ret)) / 3141592;
+		tmp = clamp(tmp, 0, 63);
+		reg &= ~AD9523_CLK_DIST_DIV_PHASE(~0);
+		reg |= AD9523_CLK_DIST_DIV_PHASE(tmp);
+		break;
+	default:
+		ret = -EINVAL;
+		goto out;
+	}
+
+	ret = ad9523_write(indio_dev, AD9523_CHANNEL_CLOCK_DIST(chan->channel),
+			   reg);
+	if (ret < 0)
+		goto out;
+
+	ad9523_io_update(indio_dev);
+out:
+	mutex_unlock(&indio_dev->mlock);
+	return ret;
+}
+
+static int ad9523_reg_access(struct iio_dev *indio_dev,
+			      unsigned reg, unsigned writeval,
+			      unsigned *readval)
+{
+	int ret;
+
+	mutex_lock(&indio_dev->mlock);
+	if (readval == NULL) {
+		ret = ad9523_write(indio_dev, reg | AD9523_R1B, writeval);
+		ad9523_io_update(indio_dev);
+	} else {
+		ret = ad9523_read(indio_dev, reg | AD9523_R1B);
+		if (ret < 0)
+			return ret;
+		*readval = ret;
+		ret = 0;
+	}
+	mutex_unlock(&indio_dev->mlock);
+
+	return ret;
+}
+
+static const struct iio_info ad9523_info = {
+	.read_raw = &ad9523_read_raw,
+	.write_raw = &ad9523_write_raw,
+	.debugfs_reg_access = &ad9523_reg_access,
+	.attrs = &ad9523_attribute_group,
+	.driver_module = THIS_MODULE,
+};
+
+static int ad9523_setup(struct iio_dev *indio_dev)
+{
+	struct ad9523_state *st = iio_priv(indio_dev);
+	struct ad9523_platform_data *pdata = st->pdata;
+	struct ad9523_channel_spec *chan;
+	unsigned active_mask = 0;
+	int ret, i;
+
+	ret = ad9523_write(indio_dev, AD9523_SERIAL_PORT_CONFIG,
+			   AD9523_SER_CONF_SOFT_RESET |
+			  (st->spi->mode & SPI_3WIRE ? 0 :
+			  AD9523_SER_CONF_SDO_ACTIVE));
+	if (ret < 0)
+		return ret;
+
+	ret = ad9523_write(indio_dev, AD9523_READBACK_CTRL,
+			  AD9523_READBACK_CTRL_READ_BUFFERED);
+	if (ret < 0)
+		return ret;
+
+	ret = ad9523_io_update(indio_dev);
+	if (ret < 0)
+		return ret;
+
+	/*
+	 * PLL1 Setup
+	 */
+	ret = ad9523_write(indio_dev, AD9523_PLL1_REF_A_DIVIDER,
+		pdata->refa_r_div);
+	if (ret < 0)
+		return ret;
+
+	ret = ad9523_write(indio_dev, AD9523_PLL1_REF_B_DIVIDER,
+		pdata->refb_r_div);
+	if (ret < 0)
+		return ret;
+
+	ret = ad9523_write(indio_dev, AD9523_PLL1_FEEDBACK_DIVIDER,
+		pdata->pll1_feedback_div);
+	if (ret < 0)
+		return ret;
+
+	ret = ad9523_write(indio_dev, AD9523_PLL1_CHARGE_PUMP_CTRL,
+		AD9523_PLL1_CHARGE_PUMP_CURRENT_nA(pdata->
+			pll1_charge_pump_current_nA) |
+		AD9523_PLL1_CHARGE_PUMP_MODE_NORMAL |
+		AD9523_PLL1_BACKLASH_PW_MIN);
+	if (ret < 0)
+		return ret;
+
+	ret = ad9523_write(indio_dev, AD9523_PLL1_INPUT_RECEIVERS_CTRL,
+		AD_IF(refa_diff_rcv_en, AD9523_PLL1_REFA_RCV_EN) |
+		AD_IF(refb_diff_rcv_en, AD9523_PLL1_REFB_RCV_EN) |
+		AD_IF(osc_in_diff_en, AD9523_PLL1_OSC_IN_DIFF_EN) |
+		AD_IF(osc_in_cmos_neg_inp_en,
+		      AD9523_PLL1_OSC_IN_CMOS_NEG_INP_EN) |
+		AD_IF(refa_diff_rcv_en, AD9523_PLL1_REFA_DIFF_RCV_EN) |
+		AD_IF(refb_diff_rcv_en, AD9523_PLL1_REFB_DIFF_RCV_EN));
+	if (ret < 0)
+		return ret;
+
+	ret = ad9523_write(indio_dev, AD9523_PLL1_REF_CTRL,
+		AD_IF(zd_in_diff_en, AD9523_PLL1_ZD_IN_DIFF_EN) |
+		AD_IF(zd_in_cmos_neg_inp_en,
+		      AD9523_PLL1_ZD_IN_CMOS_NEG_INP_EN) |
+		AD_IF(zero_delay_mode_internal_en,
+		      AD9523_PLL1_ZERO_DELAY_MODE_INT) |
+		AD_IF(osc_in_feedback_en, AD9523_PLL1_OSC_IN_PLL_FEEDBACK_EN) |
+		AD_IF(refa_cmos_neg_inp_en, AD9523_PLL1_REFA_CMOS_NEG_INP_EN) |
+		AD_IF(refb_cmos_neg_inp_en, AD9523_PLL1_REFB_CMOS_NEG_INP_EN));
+	if (ret < 0)
+		return ret;
+
+	ret = ad9523_write(indio_dev, AD9523_PLL1_MISC_CTRL,
+		AD9523_PLL1_REFB_INDEP_DIV_CTRL_EN |
+		AD9523_PLL1_REF_MODE(pdata->ref_mode));
+	if (ret < 0)
+		return ret;
+
+	ret = ad9523_write(indio_dev, AD9523_PLL1_LOOP_FILTER_CTRL,
+		AD9523_PLL1_LOOP_FILTER_RZERO(pdata->pll1_loop_filter_rzero));
+	if (ret < 0)
+		return ret;
+	/*
+	 * PLL2 Setup
+	 */
+
+	ret = ad9523_write(indio_dev, AD9523_PLL2_CHARGE_PUMP,
+		AD9523_PLL2_CHARGE_PUMP_CURRENT_nA(pdata->
+			pll2_charge_pump_current_nA));
+	if (ret < 0)
+		return ret;
+
+	ret = ad9523_write(indio_dev, AD9523_PLL2_FEEDBACK_DIVIDER_AB,
+		AD9523_PLL2_FB_NDIV_A_CNT(pdata->pll2_ndiv_a_cnt) |
+		AD9523_PLL2_FB_NDIV_B_CNT(pdata->pll2_ndiv_b_cnt));
+	if (ret < 0)
+		return ret;
+
+	ret = ad9523_write(indio_dev, AD9523_PLL2_CTRL,
+		AD9523_PLL2_CHARGE_PUMP_MODE_NORMAL |
+		AD9523_PLL2_BACKLASH_CTRL_EN |
+		AD_IF(pll2_freq_doubler_en, AD9523_PLL2_FREQ_DOUBLER_EN));
+	if (ret < 0)
+		return ret;
+
+	st->vco_freq = (pdata->vcxo_freq * (pdata->pll2_freq_doubler_en ? 2 : 1)
+			/ pdata->pll2_r2_div) * AD9523_PLL2_FB_NDIV(pdata->
+			pll2_ndiv_a_cnt, pdata->pll2_ndiv_b_cnt);
+
+	ret = ad9523_write(indio_dev, AD9523_PLL2_VCO_CTRL,
+		AD9523_PLL2_VCO_CALIBRATE);
+	if (ret < 0)
+		return ret;
+
+	ret = ad9523_write(indio_dev, AD9523_PLL2_VCO_DIVIDER,
+		AD9523_PLL2_VCO_DIV_M1(pdata->pll2_vco_diff_m1) |
+		AD9523_PLL2_VCO_DIV_M2(pdata->pll2_vco_diff_m2) |
+		AD_IFE(pll2_vco_diff_m1, 0,
+		       AD9523_PLL2_VCO_DIV_M1_PWR_DOWN_EN) |
+		AD_IFE(pll2_vco_diff_m2, 0,
+		       AD9523_PLL2_VCO_DIV_M2_PWR_DOWN_EN));
+	if (ret < 0)
+		return ret;
+
+	if (pdata->pll2_vco_diff_m1)
+		st->vco_out_freq[AD9523_VCO1] =
+			st->vco_freq / pdata->pll2_vco_diff_m1;
+
+	if (pdata->pll2_vco_diff_m2)
+		st->vco_out_freq[AD9523_VCO2] =
+			st->vco_freq / pdata->pll2_vco_diff_m2;
+
+	st->vco_out_freq[AD9523_VCXO] = pdata->vcxo_freq;
+
+	ret = ad9523_write(indio_dev, AD9523_PLL2_R2_DIVIDER,
+		AD9523_PLL2_R2_DIVIDER_VAL(pdata->pll2_r2_div));
+	if (ret < 0)
+		return ret;
+
+	ret = ad9523_write(indio_dev, AD9523_PLL2_LOOP_FILTER_CTRL,
+		AD9523_PLL2_LOOP_FILTER_CPOLE1(pdata->cpole1) |
+		AD9523_PLL2_LOOP_FILTER_RZERO(pdata->rzero) |
+		AD9523_PLL2_LOOP_FILTER_RPOLE2(pdata->rpole2) |
+		AD_IF(rzero_bypass_en,
+		      AD9523_PLL2_LOOP_FILTER_RZERO_BYPASS_EN));
+	if (ret < 0)
+		return ret;
+
+	for (i = 0; i < pdata->num_channels; i++) {
+		chan = &pdata->channels[i];
+		if (chan->channel_num < AD9523_NUM_CHAN) {
+			active_mask |= (1 << chan->channel_num);
+			ret = ad9523_write(indio_dev,
+				AD9523_CHANNEL_CLOCK_DIST(chan->channel_num),
+				AD9523_CLK_DIST_DRIVER_MODE(chan->driver_mode) |
+				AD9523_CLK_DIST_DIV(chan->channel_divider) |
+				AD9523_CLK_DIST_DIV_PHASE(chan->divider_phase) |
+				(chan->sync_ignore_en ?
+					AD9523_CLK_DIST_IGNORE_SYNC_EN : 0) |
+				(chan->divider_output_invert_en ?
+					AD9523_CLK_DIST_INV_DIV_OUTPUT_EN : 0) |
+				(chan->low_power_mode_en ?
+					AD9523_CLK_DIST_LOW_PWR_MODE_EN : 0) |
+				(chan->output_dis ?
+					AD9523_CLK_DIST_PWR_DOWN_EN : 0));
+			if (ret < 0)
+				return ret;
+
+			ret = ad9523_vco_out_map(indio_dev, chan->channel_num,
+					   chan->use_alt_clock_src);
+			if (ret < 0)
+				return ret;
+
+			st->ad9523_channels[i].type = IIO_ALTVOLTAGE;
+			st->ad9523_channels[i].output = 1;
+			st->ad9523_channels[i].indexed = 1;
+			st->ad9523_channels[i].channel = chan->channel_num;
+			st->ad9523_channels[i].extend_name =
+				chan->extended_name;
+			st->ad9523_channels[i].info_mask =
+				IIO_CHAN_INFO_RAW_SEPARATE_BIT |
+				IIO_CHAN_INFO_PHASE_SEPARATE_BIT |
+				IIO_CHAN_INFO_FREQUENCY_SEPARATE_BIT;
+		}
+	}
+
+	for (i = 0; i < AD9523_NUM_CHAN; i++)
+		if (!(active_mask & (1 << i)))
+			ad9523_write(indio_dev,
+				     AD9523_CHANNEL_CLOCK_DIST(i),
+				     AD9523_CLK_DIST_DRIVER_MODE(TRISTATE) |
+				     AD9523_CLK_DIST_PWR_DOWN_EN);
+
+	ret = ad9523_write(indio_dev, AD9523_POWER_DOWN_CTRL, 0);
+	if (ret < 0)
+		return ret;
+
+	ret = ad9523_write(indio_dev, AD9523_STATUS_SIGNALS,
+			   AD9523_STATUS_MONITOR_01_PLL12_LOCKED);
+	if (ret < 0)
+		return ret;
+
+	ret = ad9523_io_update(indio_dev);
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+
+static int __devinit ad9523_probe(struct spi_device *spi)
+{
+	struct ad9523_platform_data *pdata = spi->dev.platform_data;
+	struct iio_dev *indio_dev;
+	struct ad9523_state *st;
+	int ret;
+
+	if (!pdata) {
+		dev_err(&spi->dev, "no platform data?\n");
+		return -EINVAL;
+	}
+
+	indio_dev = iio_device_alloc(sizeof(*st));
+	if (indio_dev == NULL)
+		return -ENOMEM;
+
+	st = iio_priv(indio_dev);
+
+	st->reg = regulator_get(&spi->dev, "vcc");
+	if (!IS_ERR(st->reg)) {
+		ret = regulator_enable(st->reg);
+		if (ret)
+			goto error_put_reg;
+	}
+
+	spi_set_drvdata(spi, indio_dev);
+	st->spi = spi;
+	st->pdata = pdata;
+
+	indio_dev->dev.parent = &spi->dev;
+	indio_dev->name = (pdata->name[0] != 0) ? pdata->name :
+			  spi_get_device_id(spi)->name;
+	indio_dev->info = &ad9523_info;
+	indio_dev->modes = INDIO_DIRECT_MODE;
+	indio_dev->channels = st->ad9523_channels;
+	indio_dev->num_channels = pdata->num_channels;
+
+	ret = ad9523_setup(indio_dev);
+	if (ret < 0)
+		goto error_disable_reg;
+
+	ret = iio_device_register(indio_dev);
+	if (ret)
+		goto error_disable_reg;
+
+	dev_info(&spi->dev, "probed %s\n", indio_dev->name);
+
+	return 0;
+
+error_disable_reg:
+	if (!IS_ERR(st->reg))
+		regulator_disable(st->reg);
+error_put_reg:
+	if (!IS_ERR(st->reg))
+		regulator_put(st->reg);
+
+	iio_device_free(indio_dev);
+
+	return ret;
+}
+
+static int __devexit ad9523_remove(struct spi_device *spi)
+{
+	struct iio_dev *indio_dev = spi_get_drvdata(spi);
+	struct ad9523_state *st = iio_priv(indio_dev);
+	struct regulator *reg = st->reg;
+
+	iio_device_unregister(indio_dev);
+
+	if (!IS_ERR(reg)) {
+		regulator_disable(reg);
+		regulator_put(reg);
+	}
+
+	iio_device_free(indio_dev);
+
+	return 0;
+}
+
+static const struct spi_device_id ad9523_id[] = {
+	{"ad9523", 9523},
+	{}
+};
+MODULE_DEVICE_TABLE(spi, ad9523_id);
+
+static struct spi_driver ad9523_driver = {
+	.driver = {
+		.name	= "ad9523",
+		.owner	= THIS_MODULE,
+	},
+	.probe		= ad9523_probe,
+	.remove		= __devexit_p(ad9523_remove),
+	.id_table	= ad9523_id,
+};
+module_spi_driver(ad9523_driver);
+
+MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
+MODULE_DESCRIPTION("Analog Devices AD9523 CLOCKDIST/PLL");
+MODULE_LICENSE("GPL v2");
diff --git a/include/linux/iio/frequency/ad9523.h b/include/linux/iio/frequency/ad9523.h
new file mode 100644
index 0000000..e878f09
--- /dev/null
+++ b/include/linux/iio/frequency/ad9523.h
@@ -0,0 +1,195 @@
+/*
+ * AD9523 SPI Low Jitter Clock Generator
+ *
+ * Copyright 2012 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2.
+ */
+
+#ifndef IIO_FREQUENCY_AD9523_H_
+#define IIO_FREQUENCY_AD9523_H_
+
+enum outp_drv_mode {
+	TRISTATE,
+	LVPECL_8mA,
+	LVDS_4mA,
+	LVDS_7mA,
+	HSTL0_16mA,
+	HSTL1_8mA,
+	CMOS_CONF1,
+	CMOS_CONF2,
+	CMOS_CONF3,
+	CMOS_CONF4,
+	CMOS_CONF5,
+	CMOS_CONF6,
+	CMOS_CONF7,
+	CMOS_CONF8,
+	CMOS_CONF9
+};
+
+enum ref_sel_mode {
+	NONEREVERTIVE_STAY_ON_REFB,
+	REVERT_TO_REFA,
+	SELECT_REFA,
+	SELECT_REFB,
+	EXT_REF_SEL
+};
+
+/**
+ * struct ad9523_channel_spec - Output channel configuration
+ *
+ * @channel_num: Output channel number.
+ * @divider_output_invert_en: Invert the polarity of the output clock.
+ * @sync_ignore_en: Ignore chip-level SYNC signal.
+ * @low_power_mode_en: Reduce power used in the differential output modes.
+ * @use_alt_clock_src: Channel divider uses alternative clk source.
+ * @output_dis: Disables, powers down the entire channel.
+ * @driver_mode: Output driver mode (logic level family).
+ * @divider_phase: Divider initial phase after a SYNC. Range 0..63
+		   LSB = 1/2 of a period of the divider input clock.
+ * @channel_divider: 10-bit channel divider.
+ * @extended_name: Optional descriptive channel name.
+ */
+
+struct ad9523_channel_spec {
+	unsigned		channel_num;
+	bool			divider_output_invert_en;
+	bool			sync_ignore_en;
+	bool			low_power_mode_en;
+				 /* CH0..CH3 VCXO, CH4..CH9 VCO2 */
+	bool			use_alt_clock_src;
+	bool			output_dis;
+	enum outp_drv_mode	driver_mode;
+	unsigned char		divider_phase;
+	unsigned short		channel_divider;
+	char			extended_name[16];
+};
+
+enum pll1_rzero_resistor {
+	RZERO_883_OHM,
+	RZERO_677_OHM,
+	RZERO_341_OHM,
+	RZERO_135_OHM,
+	RZERO_10_OHM,
+	RZERO_USE_EXT_RES = 8,
+};
+
+enum rpole2_resistor {
+	RPOLE2_900_OHM,
+	RPOLE2_450_OHM,
+	RPOLE2_300_OHM,
+	RPOLE2_225_OHM,
+};
+
+enum rzero_resistor {
+	RZERO_3250_OHM,
+	RZERO_2750_OHM,
+	RZERO_2250_OHM,
+	RZERO_2100_OHM,
+	RZERO_3000_OHM,
+	RZERO_2500_OHM,
+	RZERO_2000_OHM,
+	RZERO_1850_OHM,
+};
+
+enum cpole1_capacitor {
+	CPOLE1_0_PF,
+	CPOLE1_8_PF,
+	CPOLE1_16_PF,
+	CPOLE1_24_PF,
+	_CPOLE1_24_PF,
+	CPOLE1_32_PF,
+	CPOLE1_40_PF,
+	CPOLE1_48_PF,
+};
+
+/**
+ * struct ad9523_platform_data - platform specific information
+ *
+ * @vcxo_freq: External VCXO frequency in Hz
+ * @refa_diff_rcv_en: REFA differential/single-ended input selection.
+ * @refb_diff_rcv_en: REFB differential/single-ended input selection.
+ * @zd_in_diff_en: Zero Delay differential/single-ended input selection.
+ * @osc_in_diff_en: OSC differential/ single-ended input selection.
+ * @refa_cmos_neg_inp_en: REFA single-ended neg./pos. input enable.
+ * @refb_cmos_neg_inp_en: REFB single-ended neg./pos. input enable.
+ * @zd_in_cmos_neg_inp_en: Zero Delay single-ended neg./pos. input enable.
+ * @osc_in_cmos_neg_inp_en: OSC single-ended neg./pos. input enable.
+ * @refa_r_div: PLL1 10-bit REFA R divider.
+ * @refb_r_div: PLL1 10-bit REFB R divider.
+ * @pll1_feedback_div: PLL1 10-bit Feedback N divider.
+ * @pll1_charge_pump_current_nA: Magnitude of PLL1 charge pump current (nA).
+ * @zero_delay_mode_internal_en: Internal, external Zero Delay mode selection.
+ * @osc_in_feedback_en: PLL1 feedback path, local feedback from
+ *			the OSC_IN receiver or zero delay mode
+ * @pll1_loop_filter_rzero: PLL1 Loop Filter Zero Resistor selection.
+ * @ref_mode: Reference selection mode.
+ * @pll2_charge_pump_current_nA: Magnitude of PLL2 charge pump current (nA).
+ * @pll2_ndiv_a_cnt: PLL2 Feedback N-divider, A Counter, range 0..4.
+ * @pll2_ndiv_b_cnt: PLL2 Feedback N-divider, B Counter, range 0..63.
+ * @pll2_freq_doubler_en: PLL2 frequency doubler enable.
+ * @pll2_r2_div: PLL2 R2 divider, range 0..31.
+ * @pll2_vco_diff_m1: VCO1 divider, range 3..5.
+ * @pll2_vco_diff_m2: VCO2 divider, range 3..5.
+ * @rpole2: PLL2 loop filter Rpole resistor value.
+ * @rzero: PLL2 loop filter Rzero resistor value.
+ * @cpole1: PLL2 loop filter Cpole capacitor value.
+ * @rzero_bypass_en: PLL2 loop filter Rzero bypass enable.
+ * @num_channels: Array size of struct ad9523_channel_spec.
+ * @channels: Pointer to channel array.
+ * @name: Optional alternative iio device name.
+ */
+
+struct ad9523_platform_data {
+	unsigned long vcxo_freq;
+
+	/* Differential/ Single-Ended Input Configuration */
+	bool				refa_diff_rcv_en;
+	bool				refb_diff_rcv_en;
+	bool				zd_in_diff_en;
+	bool				osc_in_diff_en;
+
+	/*
+	 * Valid if differential input disabled
+	 * if false defaults to pos input
+	 */
+	bool				refa_cmos_neg_inp_en;
+	bool				refb_cmos_neg_inp_en;
+	bool				zd_in_cmos_neg_inp_en;
+	bool				osc_in_cmos_neg_inp_en;
+
+	/* PLL1 Setting */
+	unsigned short			refa_r_div;
+	unsigned short			refb_r_div;
+	unsigned short			pll1_feedback_div;
+	unsigned short			pll1_charge_pump_current_nA;
+	bool				zero_delay_mode_internal_en;
+	bool				osc_in_feedback_en;
+	enum pll1_rzero_resistor	pll1_loop_filter_rzero;
+
+	/* Reference */
+	enum ref_sel_mode		ref_mode;
+
+	/* PLL2 Setting */
+	unsigned int			pll2_charge_pump_current_nA;
+	unsigned char			pll2_ndiv_a_cnt;
+	unsigned char			pll2_ndiv_b_cnt;
+	bool				pll2_freq_doubler_en;
+	unsigned char			pll2_r2_div;
+	unsigned char			pll2_vco_diff_m1; /* 3..5 */
+	unsigned char			pll2_vco_diff_m2; /* 3..5 */
+
+	/* Loop Filter PLL2 */
+	enum rpole2_resistor		rpole2;
+	enum rzero_resistor		rzero;
+	enum cpole1_capacitor		cpole1;
+	bool				rzero_bypass_en;
+
+	/* Output Channel Configuration */
+	int				num_channels;
+	struct ad9523_channel_spec	*channels;
+
+	char				name[SPI_NAME_SIZE];
+};
+
+#endif /* IIO_FREQUENCY_AD9523_H_ */
-- 
1.7.0.4



^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [PATCH 2/2] iio: frequency: New driver for Analog Devices ADF4350/ADF4351 Wideband Synthesizers
  2012-05-21 14:58 [PATCH 1/2] iio: frequency: New driver for AD9523 SPI Low Jitter Clock Generator michael.hennerich
@ 2012-05-21 14:58 ` michael.hennerich
  2012-05-24 13:19   ` Jonathan Cameron
  2012-05-24 13:00 ` [PATCH 1/2] iio: frequency: New driver for AD9523 SPI Low Jitter Clock Generator Jonathan Cameron
  1 sibling, 1 reply; 6+ messages in thread
From: michael.hennerich @ 2012-05-21 14:58 UTC (permalink / raw)
  To: jic23; +Cc: linux-iio, device-drivers-devel, Michael Hennerich

From: Michael Hennerich <michael.hennerich@analog.com>

Changes since V1:
Apply Jonathan's review feedback:
    Introduce and use IIO_ALTVOLTAGE.
    Fix up comments and documentation.
    Remove dead code.
    Reorder some code fragments.
    Add missing iio_device_free.

Convert to new API.
Fix-up out of staging includes.
Removed pll_locked attribute.

Changes since V2:
Use module_spi_driver.
adf4350_remove: move gpio_free after regulator.
target patch to drivers/iio

Signed-off-by: Michael Hennerich <michael.hennerich@analog.com>
---
 .../ABI/testing/sysfs-bus-iio-frequency-adf4350    |   21 +
 drivers/iio/frequency/Kconfig                      |   18 +
 drivers/iio/frequency/Makefile                     |    1 +
 drivers/iio/frequency/adf4350.c                    |  478 ++++++++++++++++++++
 include/linux/iio/frequency/adf4350.h              |  130 ++++++
 5 files changed, 648 insertions(+), 0 deletions(-)
 create mode 100644 Documentation/ABI/testing/sysfs-bus-iio-frequency-adf4350
 create mode 100644 drivers/iio/frequency/adf4350.c
 create mode 100644 include/linux/iio/frequency/adf4350.h

diff --git a/Documentation/ABI/testing/sysfs-bus-iio-frequency-adf4350 b/Documentation/ABI/testing/sysfs-bus-iio-frequency-adf4350
new file mode 100644
index 0000000..d89aded
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-bus-iio-frequency-adf4350
@@ -0,0 +1,21 @@
+What:		/sys/bus/iio/devices/iio:deviceX/out_altvoltageY_frequency_resolution
+KernelVersion:	3.4.0
+Contact:	linux-iio@vger.kernel.org
+Description:
+		Stores channel Y frequency resolution/channel spacing in Hz.
+		The value given directly influences the MODULUS used by
+		the fractional-N PLL. It is assumed that the algorithm
+		that is used to compute the various dividers, is able to
+		generate proper values for multiples of channel spacing.
+
+What:		/sys/bus/iio/devices/iio:deviceX/out_altvoltageY_refin_frequency
+KernelVersion:	3.4.0
+Contact:	linux-iio@vger.kernel.org
+Description:
+		Sets channel Y REFin frequency in Hz. In some clock chained
+		applications, the reference frequency used by the PLL may
+		change during runtime. This attribute allows the user to
+		adjust the reference frequency accordingly.
+		The value written has no effect until out_altvoltageY_frequency
+		is updated. Consider to use out_altvoltageY_powerdown to power
+		down the PLL and it's RFOut buffers during REFin changes.
diff --git a/drivers/iio/frequency/Kconfig b/drivers/iio/frequency/Kconfig
index 0458c92..6aaa33e 100644
--- a/drivers/iio/frequency/Kconfig
+++ b/drivers/iio/frequency/Kconfig
@@ -20,4 +20,22 @@ config AD9523
 	  module will be called ad9523.
 
 endmenu
+
+#
+# Phase-Locked Loop (PLL) frequency synthesizers
+#
+
+menu "Phase-Locked Loop (PLL) frequency synthesizers"
+
+config ADF4350
+	tristate "Analog Devices ADF4350/ADF4351 Wideband Synthesizers"
+	depends on SPI
+	help
+	  Say yes here to build support for Analog Devices  ADF4350/ADF4351
+	  Wideband Synthesizers. The driver provides direct access via sysfs.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called adf4350.
+
+endmenu
 endmenu
diff --git a/drivers/iio/frequency/Makefile b/drivers/iio/frequency/Makefile
index 1b5b224..00d26e5 100644
--- a/drivers/iio/frequency/Makefile
+++ b/drivers/iio/frequency/Makefile
@@ -3,3 +3,4 @@
 #
 
 obj-$(CONFIG_AD9523) += ad9523.o
+obj-$(CONFIG_ADF4350) += adf4350.o
diff --git a/drivers/iio/frequency/adf4350.c b/drivers/iio/frequency/adf4350.c
new file mode 100644
index 0000000..fd4c850
--- /dev/null
+++ b/drivers/iio/frequency/adf4350.c
@@ -0,0 +1,478 @@
+/*
+ * ADF4350/ADF4351 SPI Wideband Synthesizer driver
+ *
+ * Copyright 2012 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+#include <linux/spi/spi.h>
+#include <linux/regulator/consumer.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/gcd.h>
+#include <linux/gpio.h>
+#include <asm/div64.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/frequency/adf4350.h>
+
+enum {
+	ADF4350_FREQ,
+	ADF4350_FREQ_REFIN,
+	ADF4350_FREQ_RESOLUTION,
+	ADF4350_PWRDOWN,
+};
+
+struct adf4350_state {
+	struct spi_device		*spi;
+	struct regulator		*reg;
+	struct adf4350_platform_data	*pdata;
+	unsigned long			clkin;
+	unsigned long			chspc; /* Channel Spacing */
+	unsigned long			fpfd; /* Phase Frequency Detector */
+	unsigned long			min_out_freq;
+	unsigned			r0_fract;
+	unsigned			r0_int;
+	unsigned			r1_mod;
+	unsigned			r4_rf_div_sel;
+	unsigned long			regs[6];
+	unsigned long			regs_hw[6];
+
+	/*
+	 * DMA (thus cache coherency maintenance) requires the
+	 * transfer buffers to live in their own cache lines.
+	 */
+	__be32				val ____cacheline_aligned;
+};
+
+static struct adf4350_platform_data default_pdata = {
+	.clkin = 122880000,
+	.channel_spacing = 10000,
+	.r2_user_settings = ADF4350_REG2_PD_POLARITY_POS,
+			    ADF4350_REG2_CHARGE_PUMP_CURR_uA(2500),
+	.r3_user_settings = ADF4350_REG3_12BIT_CLKDIV_MODE(0),
+	.r4_user_settings = ADF4350_REG4_OUTPUT_PWR(3) |
+			    ADF4350_REG4_MUTE_TILL_LOCK_EN,
+	.gpio_lock_detect = -1,
+};
+
+static int adf4350_sync_config(struct adf4350_state *st)
+{
+	int ret, i, doublebuf = 0;
+
+	for (i = ADF4350_REG5; i >= ADF4350_REG0; i--) {
+		if ((st->regs_hw[i] != st->regs[i]) ||
+			((i == ADF4350_REG0) && doublebuf)) {
+
+			switch (i) {
+			case ADF4350_REG1:
+			case ADF4350_REG4:
+				doublebuf = 1;
+				break;
+			}
+
+			st->val  = cpu_to_be32(st->regs[i] | i);
+			ret = spi_write(st->spi, &st->val, 4);
+			if (ret < 0)
+				return ret;
+			st->regs_hw[i] = st->regs[i];
+			dev_dbg(&st->spi->dev, "[%d] 0x%X\n",
+				i, (u32)st->regs[i] | i);
+		}
+	}
+	return 0;
+}
+
+static int adf4350_reg_access(struct iio_dev *indio_dev,
+			      unsigned reg, unsigned writeval,
+			      unsigned *readval)
+{
+	struct adf4350_state *st = iio_priv(indio_dev);
+	int ret;
+
+	if (reg > ADF4350_REG5)
+		return -EINVAL;
+
+	mutex_lock(&indio_dev->mlock);
+	if (readval == NULL) {
+		st->regs[reg] = writeval & ~(BIT(0) | BIT(1) | BIT(2));
+		ret = adf4350_sync_config(st);
+	} else {
+		*readval =  st->regs_hw[reg];
+		ret = 0;
+	}
+	mutex_unlock(&indio_dev->mlock);
+
+	return ret;
+}
+
+static int adf4350_tune_r_cnt(struct adf4350_state *st, unsigned short r_cnt)
+{
+	struct adf4350_platform_data *pdata = st->pdata;
+
+	do {
+		r_cnt++;
+		st->fpfd = (st->clkin * (pdata->ref_doubler_en ? 2 : 1)) /
+			   (r_cnt * (pdata->ref_div2_en ? 2 : 1));
+	} while (st->fpfd > ADF4350_MAX_FREQ_PFD);
+
+	return r_cnt;
+}
+
+static int adf4350_set_freq(struct adf4350_state *st, unsigned long long freq)
+{
+	struct adf4350_platform_data *pdata = st->pdata;
+	u64 tmp;
+	u32 div_gcd, prescaler;
+	u16 mdiv, r_cnt = 0;
+	u8 band_sel_div;
+
+	if (freq > ADF4350_MAX_OUT_FREQ || freq < st->min_out_freq)
+		return -EINVAL;
+
+	if (freq > ADF4350_MAX_FREQ_45_PRESC) {
+		prescaler = ADF4350_REG1_PRESCALER;
+		mdiv = 75;
+	} else {
+		prescaler = 0;
+		mdiv = 23;
+	}
+
+	st->r4_rf_div_sel = 0;
+
+	while (freq < ADF4350_MIN_VCO_FREQ) {
+		freq <<= 1;
+		st->r4_rf_div_sel++;
+	}
+
+	/*
+	 * Allow a predefined reference division factor
+	 * if not set, compute our own
+	 */
+	if (pdata->ref_div_factor)
+		r_cnt = pdata->ref_div_factor - 1;
+
+	do  {
+		r_cnt = adf4350_tune_r_cnt(st, r_cnt);
+
+		st->r1_mod = st->fpfd / st->chspc;
+		while (st->r1_mod > ADF4350_MAX_MODULUS) {
+			r_cnt = adf4350_tune_r_cnt(st, r_cnt);
+			st->r1_mod = st->fpfd / st->chspc;
+		}
+
+		tmp = freq * (u64)st->r1_mod + (st->fpfd > 1);
+		do_div(tmp, st->fpfd); /* Div round closest (n + d/2)/d */
+		st->r0_fract = do_div(tmp, st->r1_mod);
+		st->r0_int = tmp;
+	} while (mdiv > st->r0_int);
+
+	band_sel_div = DIV_ROUND_UP(st->fpfd, ADF4350_MAX_BANDSEL_CLK);
+
+	if (st->r0_fract && st->r1_mod) {
+		div_gcd = gcd(st->r1_mod, st->r0_fract);
+		st->r1_mod /= div_gcd;
+		st->r0_fract /= div_gcd;
+	} else {
+		st->r0_fract = 0;
+		st->r1_mod = 1;
+	}
+
+	dev_dbg(&st->spi->dev, "VCO: %llu Hz, PFD %lu Hz\n"
+		"REF_DIV %d, R0_INT %d, R0_FRACT %d\n"
+		"R1_MOD %d, RF_DIV %d\nPRESCALER %s, BAND_SEL_DIV %d\n",
+		freq, st->fpfd, r_cnt, st->r0_int, st->r0_fract, st->r1_mod,
+		1 << st->r4_rf_div_sel, prescaler ? "8/9" : "4/5",
+		band_sel_div);
+
+	st->regs[ADF4350_REG0] = ADF4350_REG0_INT(st->r0_int) |
+				 ADF4350_REG0_FRACT(st->r0_fract);
+
+	st->regs[ADF4350_REG1] = ADF4350_REG1_PHASE(0) |
+				 ADF4350_REG1_MOD(st->r1_mod) |
+				 prescaler;
+
+	st->regs[ADF4350_REG2] =
+		ADF4350_REG2_10BIT_R_CNT(r_cnt) |
+		ADF4350_REG2_DOUBLE_BUFF_EN |
+		(pdata->ref_doubler_en ? ADF4350_REG2_RMULT2_EN : 0) |
+		(pdata->ref_div2_en ? ADF4350_REG2_RDIV2_EN : 0) |
+		(pdata->r2_user_settings & (ADF4350_REG2_PD_POLARITY_POS |
+		ADF4350_REG2_LDP_6ns | ADF4350_REG2_LDF_INT_N |
+		ADF4350_REG2_CHARGE_PUMP_CURR_uA(5000) |
+		ADF4350_REG2_MUXOUT(0x7) | ADF4350_REG2_NOISE_MODE(0x9)));
+
+	st->regs[ADF4350_REG3] = pdata->r3_user_settings &
+				 (ADF4350_REG3_12BIT_CLKDIV(0xFFF) |
+				 ADF4350_REG3_12BIT_CLKDIV_MODE(0x3) |
+				 ADF4350_REG3_12BIT_CSR_EN |
+				 ADF4351_REG3_CHARGE_CANCELLATION_EN |
+				 ADF4351_REG3_ANTI_BACKLASH_3ns_EN |
+				 ADF4351_REG3_BAND_SEL_CLOCK_MODE_HIGH);
+
+	st->regs[ADF4350_REG4] =
+		ADF4350_REG4_FEEDBACK_FUND |
+		ADF4350_REG4_RF_DIV_SEL(st->r4_rf_div_sel) |
+		ADF4350_REG4_8BIT_BAND_SEL_CLKDIV(band_sel_div) |
+		ADF4350_REG4_RF_OUT_EN |
+		(pdata->r4_user_settings &
+		(ADF4350_REG4_OUTPUT_PWR(0x3) |
+		ADF4350_REG4_AUX_OUTPUT_PWR(0x3) |
+		ADF4350_REG4_AUX_OUTPUT_EN |
+		ADF4350_REG4_AUX_OUTPUT_FUND |
+		ADF4350_REG4_MUTE_TILL_LOCK_EN));
+
+	st->regs[ADF4350_REG5] = ADF4350_REG5_LD_PIN_MODE_DIGITAL;
+
+	return adf4350_sync_config(st);
+}
+
+static ssize_t adf4350_write(struct iio_dev *indio_dev,
+				    uintptr_t private,
+				    const struct iio_chan_spec *chan,
+				    const char *buf, size_t len)
+{
+	struct adf4350_state *st = iio_priv(indio_dev);
+	unsigned long long readin;
+	int ret;
+
+	ret = kstrtoull(buf, 10, &readin);
+	if (ret)
+		return ret;
+
+	mutex_lock(&indio_dev->mlock);
+	switch ((u32)private) {
+	case ADF4350_FREQ:
+		ret = adf4350_set_freq(st, readin);
+		break;
+	case ADF4350_FREQ_REFIN:
+		if (readin > ADF4350_MAX_FREQ_REFIN)
+			ret = -EINVAL;
+		else
+			st->clkin = readin;
+		break;
+	case ADF4350_FREQ_RESOLUTION:
+		if (readin == 0)
+			ret = -EINVAL;
+		else
+			st->chspc = readin;
+		break;
+	case ADF4350_PWRDOWN:
+		if (readin)
+			st->regs[ADF4350_REG2] |= ADF4350_REG2_POWER_DOWN_EN;
+		else
+			st->regs[ADF4350_REG2] &= ~ADF4350_REG2_POWER_DOWN_EN;
+
+		adf4350_sync_config(st);
+		break;
+	default:
+		ret = -ENODEV;
+	}
+	mutex_unlock(&indio_dev->mlock);
+
+	return ret ? ret : len;
+}
+
+static ssize_t adf4350_read(struct iio_dev *indio_dev,
+				   uintptr_t private,
+				   const struct iio_chan_spec *chan,
+				   char *buf)
+{
+	struct adf4350_state *st = iio_priv(indio_dev);
+	unsigned long long val;
+	int ret = 0;
+
+	mutex_lock(&indio_dev->mlock);
+	switch ((u32)private) {
+	case ADF4350_FREQ:
+		val = (u64)((st->r0_int * st->r1_mod) + st->r0_fract) *
+			(u64)st->fpfd;
+		do_div(val, st->r1_mod * (1 << st->r4_rf_div_sel));
+		/* PLL unlocked? return error */
+		if (gpio_is_valid(st->pdata->gpio_lock_detect))
+			if (!gpio_get_value(st->pdata->gpio_lock_detect)) {
+				dev_dbg(&st->spi->dev, "PLL un-locked\n");
+				ret = -EBUSY;
+			}
+		break;
+	case ADF4350_FREQ_REFIN:
+		val = st->clkin;
+		break;
+	case ADF4350_FREQ_RESOLUTION:
+		val = st->chspc;
+		break;
+	case ADF4350_PWRDOWN:
+		val = !!(st->regs[ADF4350_REG2] & ADF4350_REG2_POWER_DOWN_EN);
+		break;
+	}
+	mutex_unlock(&indio_dev->mlock);
+
+	return ret < 0 ? ret : sprintf(buf, "%llu\n", val);
+}
+
+#define _ADF4350_EXT_INFO(_name, _ident) { \
+	.name = _name, \
+	.read = adf4350_read, \
+	.write = adf4350_write, \
+	.private = _ident, \
+}
+
+static const struct iio_chan_spec_ext_info adf4350_ext_info[] = {
+	/* Ideally we use IIO_CHAN_INFO_FREQUENCY, but there are
+	 * values > 2^32 in order to support the entire frequency range
+	 * in Hz. Using scale is a bit ugly.
+	 */
+	_ADF4350_EXT_INFO("frequency", ADF4350_FREQ),
+	_ADF4350_EXT_INFO("frequency_resolution", ADF4350_FREQ_RESOLUTION),
+	_ADF4350_EXT_INFO("refin_frequency", ADF4350_FREQ_REFIN),
+	_ADF4350_EXT_INFO("powerdown", ADF4350_PWRDOWN),
+	{ },
+};
+
+static const struct iio_chan_spec adf4350_chan = {
+	.type = IIO_ALTVOLTAGE,
+	.indexed = 1,
+	.output = 1,
+	.ext_info = adf4350_ext_info,
+};
+
+static const struct iio_info adf4350_info = {
+	.debugfs_reg_access = &adf4350_reg_access,
+	.driver_module = THIS_MODULE,
+};
+
+static int __devinit adf4350_probe(struct spi_device *spi)
+{
+	struct adf4350_platform_data *pdata = spi->dev.platform_data;
+	struct iio_dev *indio_dev;
+	struct adf4350_state *st;
+	int ret;
+
+	if (!pdata) {
+		dev_warn(&spi->dev, "no platform data? using default\n");
+
+		pdata = &default_pdata;
+	}
+
+	indio_dev = iio_device_alloc(sizeof(*st));
+	if (indio_dev == NULL)
+		return -ENOMEM;
+
+	st = iio_priv(indio_dev);
+
+	st->reg = regulator_get(&spi->dev, "vcc");
+	if (!IS_ERR(st->reg)) {
+		ret = regulator_enable(st->reg);
+		if (ret)
+			goto error_put_reg;
+	}
+
+	spi_set_drvdata(spi, indio_dev);
+	st->spi = spi;
+	st->pdata = pdata;
+
+	indio_dev->dev.parent = &spi->dev;
+	indio_dev->name = (pdata->name[0] != 0) ? pdata->name :
+		spi_get_device_id(spi)->name;
+
+	indio_dev->info = &adf4350_info;
+	indio_dev->modes = INDIO_DIRECT_MODE;
+	indio_dev->channels = &adf4350_chan;
+	indio_dev->num_channels = 1;
+
+	st->chspc = pdata->channel_spacing;
+	st->clkin = pdata->clkin;
+
+	st->min_out_freq = spi_get_device_id(spi)->driver_data == 4351 ?
+		ADF4351_MIN_OUT_FREQ : ADF4350_MIN_OUT_FREQ;
+
+	memset(st->regs_hw, 0xFF, sizeof(st->regs_hw));
+
+	if (gpio_is_valid(pdata->gpio_lock_detect)) {
+		ret = gpio_request(pdata->gpio_lock_detect, indio_dev->name);
+		if (ret) {
+			dev_err(&spi->dev, "fail to request lock detect GPIO-%d",
+				pdata->gpio_lock_detect);
+			goto error_disable_reg;
+		}
+		gpio_direction_input(pdata->gpio_lock_detect);
+	}
+
+	if (pdata->power_up_frequency) {
+		ret = adf4350_set_freq(st, pdata->power_up_frequency);
+		if (ret)
+			goto error_free_gpio;
+	}
+
+	ret = iio_device_register(indio_dev);
+	if (ret)
+		goto error_free_gpio;
+
+	return 0;
+
+error_free_gpio:
+	if (gpio_is_valid(pdata->gpio_lock_detect))
+		gpio_free(pdata->gpio_lock_detect);
+
+error_disable_reg:
+	if (!IS_ERR(st->reg))
+		regulator_disable(st->reg);
+error_put_reg:
+	if (!IS_ERR(st->reg))
+		regulator_put(st->reg);
+
+	iio_device_free(indio_dev);
+
+	return ret;
+}
+
+static int __devexit adf4350_remove(struct spi_device *spi)
+{
+	struct iio_dev *indio_dev = spi_get_drvdata(spi);
+	struct adf4350_state *st = iio_priv(indio_dev);
+	struct regulator *reg = st->reg;
+
+	st->regs[ADF4350_REG2] |= ADF4350_REG2_POWER_DOWN_EN;
+	adf4350_sync_config(st);
+
+	iio_device_unregister(indio_dev);
+
+	if (!IS_ERR(reg)) {
+		regulator_disable(reg);
+		regulator_put(reg);
+	}
+
+	if (gpio_is_valid(st->pdata->gpio_lock_detect))
+		gpio_free(st->pdata->gpio_lock_detect);
+
+	iio_device_free(indio_dev);
+
+	return 0;
+}
+
+static const struct spi_device_id adf4350_id[] = {
+	{"adf4350", 4350},
+	{"adf4351", 4351},
+	{}
+};
+
+static struct spi_driver adf4350_driver = {
+	.driver = {
+		.name	= "adf4350",
+		.owner	= THIS_MODULE,
+	},
+	.probe		= adf4350_probe,
+	.remove		= __devexit_p(adf4350_remove),
+	.id_table	= adf4350_id,
+};
+module_spi_driver(adf4350_driver);
+
+MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
+MODULE_DESCRIPTION("Analog Devices ADF4350/ADF4351 PLL");
+MODULE_LICENSE("GPL v2");
diff --git a/include/linux/iio/frequency/adf4350.h b/include/linux/iio/frequency/adf4350.h
new file mode 100644
index 0000000..1420015
--- /dev/null
+++ b/include/linux/iio/frequency/adf4350.h
@@ -0,0 +1,130 @@
+/*
+ * ADF4350/ADF4351 SPI PLL driver
+ *
+ * Copyright 2012 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2.
+ */
+
+#ifndef IIO_PLL_ADF4350_H_
+#define IIO_PLL_ADF4350_H_
+
+/* Registers */
+#define ADF4350_REG0	0
+#define ADF4350_REG1	1
+#define ADF4350_REG2	2
+#define ADF4350_REG3	3
+#define ADF4350_REG4	4
+#define ADF4350_REG5	5
+
+/* REG0 Bit Definitions */
+#define ADF4350_REG0_FRACT(x)			(((x) & 0xFFF) << 3)
+#define ADF4350_REG0_INT(x)			(((x) & 0xFFFF) << 15)
+
+/* REG1 Bit Definitions */
+#define ADF4350_REG1_MOD(x)			(((x) & 0xFFF) << 3)
+#define ADF4350_REG1_PHASE(x)			(((x) & 0xFFF) << 15)
+#define ADF4350_REG1_PRESCALER			(1 << 27)
+
+/* REG2 Bit Definitions */
+#define ADF4350_REG2_COUNTER_RESET_EN		(1 << 3)
+#define ADF4350_REG2_CP_THREESTATE_EN		(1 << 4)
+#define ADF4350_REG2_POWER_DOWN_EN		(1 << 5)
+#define ADF4350_REG2_PD_POLARITY_POS		(1 << 6)
+#define ADF4350_REG2_LDP_6ns			(1 << 7)
+#define ADF4350_REG2_LDP_10ns			(0 << 7)
+#define ADF4350_REG2_LDF_FRACT_N		(0 << 8)
+#define ADF4350_REG2_LDF_INT_N			(1 << 8)
+#define ADF4350_REG2_CHARGE_PUMP_CURR_uA(x)	(((((x)-312) / 312) & 0xF) << 9)
+#define ADF4350_REG2_DOUBLE_BUFF_EN		(1 << 13)
+#define ADF4350_REG2_10BIT_R_CNT(x)		((x) << 14)
+#define ADF4350_REG2_RDIV2_EN			(1 << 24)
+#define ADF4350_REG2_RMULT2_EN			(1 << 25)
+#define ADF4350_REG2_MUXOUT(x)			((x) << 26)
+#define ADF4350_REG2_NOISE_MODE(x)		((x) << 29)
+#define ADF4350_MUXOUT_THREESTATE		0
+#define ADF4350_MUXOUT_DVDD			1
+#define ADF4350_MUXOUT_GND			2
+#define ADF4350_MUXOUT_R_DIV_OUT		3
+#define ADF4350_MUXOUT_N_DIV_OUT		4
+#define ADF4350_MUXOUT_ANALOG_LOCK_DETECT	5
+#define ADF4350_MUXOUT_DIGITAL_LOCK_DETECT	6
+
+/* REG3 Bit Definitions */
+#define ADF4350_REG3_12BIT_CLKDIV(x)		((x) << 3)
+#define ADF4350_REG3_12BIT_CLKDIV_MODE(x)	((x) << 16)
+#define ADF4350_REG3_12BIT_CSR_EN		(1 << 18)
+#define ADF4351_REG3_CHARGE_CANCELLATION_EN	(1 << 21)
+#define ADF4351_REG3_ANTI_BACKLASH_3ns_EN	(1 << 22)
+#define ADF4351_REG3_BAND_SEL_CLOCK_MODE_HIGH	(1 << 23)
+
+/* REG4 Bit Definitions */
+#define ADF4350_REG4_OUTPUT_PWR(x)		((x) << 3)
+#define ADF4350_REG4_RF_OUT_EN			(1 << 5)
+#define ADF4350_REG4_AUX_OUTPUT_PWR(x)		((x) << 6)
+#define ADF4350_REG4_AUX_OUTPUT_EN		(1 << 8)
+#define ADF4350_REG4_AUX_OUTPUT_FUND		(1 << 9)
+#define ADF4350_REG4_AUX_OUTPUT_DIV		(0 << 9)
+#define ADF4350_REG4_MUTE_TILL_LOCK_EN		(1 << 10)
+#define ADF4350_REG4_VCO_PWRDOWN_EN		(1 << 11)
+#define ADF4350_REG4_8BIT_BAND_SEL_CLKDIV(x)	((x) << 12)
+#define ADF4350_REG4_RF_DIV_SEL(x)		((x) << 20)
+#define ADF4350_REG4_FEEDBACK_DIVIDED		(0 << 23)
+#define ADF4350_REG4_FEEDBACK_FUND		(1 << 23)
+
+/* REG5 Bit Definitions */
+#define ADF4350_REG5_LD_PIN_MODE_LOW		(0 << 22)
+#define ADF4350_REG5_LD_PIN_MODE_DIGITAL	(1 << 22)
+#define ADF4350_REG5_LD_PIN_MODE_HIGH		(3 << 22)
+
+/* Specifications */
+#define ADF4350_MAX_OUT_FREQ		4400000000ULL /* Hz */
+#define ADF4350_MIN_OUT_FREQ		137500000 /* Hz */
+#define ADF4351_MIN_OUT_FREQ		34375000 /* Hz */
+#define ADF4350_MIN_VCO_FREQ		2200000000ULL /* Hz */
+#define ADF4350_MAX_FREQ_45_PRESC	3000000000ULL /* Hz */
+#define ADF4350_MAX_FREQ_PFD		32000000 /* Hz */
+#define ADF4350_MAX_BANDSEL_CLK		125000 /* Hz */
+#define ADF4350_MAX_FREQ_REFIN		250000000 /* Hz */
+#define ADF4350_MAX_MODULUS		4095
+
+/*
+ * TODO: struct adf4350_platform_data needs to go into include/linux/iio
+ */
+
+/**
+ * struct adf4350_platform_data - platform specific information
+ * @name:		Optional device name.
+ * @clkin:		REFin frequency in Hz.
+ * @channel_spacing:	Channel spacing in Hz (influences MODULUS).
+ * @power_up_frequency:	Optional, If set in Hz the PLL tunes to the desired
+ *			frequency on probe.
+ * @ref_div_factor:	Optional, if set the driver skips dynamic calculation
+ *			and uses this default value instead.
+ * @ref_doubler_en:	Enables reference doubler.
+ * @ref_div2_en:	Enables reference divider.
+ * @r2_user_settings:	User defined settings for ADF4350/1 REGISTER_2.
+ * @r3_user_settings:	User defined settings for ADF4350/1 REGISTER_3.
+ * @r4_user_settings:	User defined settings for ADF4350/1 REGISTER_4.
+ * @gpio_lock_detect:	Optional, if set with a valid GPIO number,
+ *			pll lock state is tested upon read.
+ *			If not used - set to -1.
+ */
+
+struct adf4350_platform_data {
+	char			name[32];
+	unsigned long		clkin;
+	unsigned long		channel_spacing;
+	unsigned long long	power_up_frequency;
+
+	unsigned short		ref_div_factor; /* 10-bit R counter */
+	bool			ref_doubler_en;
+	bool			ref_div2_en;
+
+	unsigned		r2_user_settings;
+	unsigned		r3_user_settings;
+	unsigned		r4_user_settings;
+	int			gpio_lock_detect;
+};
+
+#endif /* IIO_PLL_ADF4350_H_ */
-- 
1.7.0.4



^ permalink raw reply related	[flat|nested] 6+ messages in thread

* Re: [PATCH 1/2] iio: frequency: New driver for AD9523 SPI Low Jitter Clock Generator
  2012-05-21 14:58 [PATCH 1/2] iio: frequency: New driver for AD9523 SPI Low Jitter Clock Generator michael.hennerich
  2012-05-21 14:58 ` [PATCH 2/2] iio: frequency: New driver for Analog Devices ADF4350/ADF4351 Wideband Synthesizers michael.hennerich
@ 2012-05-24 13:00 ` Jonathan Cameron
  2012-05-29  9:08   ` Michael Hennerich
  1 sibling, 1 reply; 6+ messages in thread
From: Jonathan Cameron @ 2012-05-24 13:00 UTC (permalink / raw)
  To: michael.hennerich; +Cc: linux-iio, device-drivers-devel

On 5/21/2012 3:58 PM, michael.hennerich@analog.com wrote:
> From: Michael Hennerich<michael.hennerich@analog.com>
>
> Changes since V1:
>
> Apply Jonathan's review feedback:
> Revise device status attribute names, and split documentation into two sections.
> Add additional comments, and fix indention issues.
> Remove pointless zero initializations.
> Revise return value handling.
> Simplify some code sections.
> Split store_eeprom and sync handling into separate functions.
> Use strtobool where applicable.
> Document platform data structures using kernel-doc style.
>
> Use dev_to_iio_dev
> write_raw IIO_CHAN_INFO_FREQUENCY: Reject values<= 0
> Make patch target drivers/iio

Hi Michael,

Couple of trivial bits inline.  Plenty of time for this so I'll
take one last look when you've addressed those.

I still don't like the immense opaque platform data needed for this.
Feels like there ought to be something clearer and more generic, but
I guess that's a job for another day.

Jonathan
>
> Signed-off-by: Michael Hennerich<michael.hennerich@analog.com>
> ---
>   .../ABI/testing/sysfs-bus-iio-frequency-ad9523     |   37 +
>   drivers/iio/Kconfig                                |    1 +
>   drivers/iio/Makefile                               |    1 +
>   drivers/iio/frequency/Kconfig                      |   23 +
>   drivers/iio/frequency/Makefile                     |    5 +
>   drivers/iio/frequency/ad9523.c                     | 1059 ++++++++++++++++++++
>   include/linux/iio/frequency/ad9523.h               |  195 ++++
>   7 files changed, 1321 insertions(+), 0 deletions(-)
>   create mode 100644 Documentation/ABI/testing/sysfs-bus-iio-frequency-ad9523
>   create mode 100644 drivers/iio/frequency/Kconfig
>   create mode 100644 drivers/iio/frequency/Makefile
>   create mode 100644 drivers/iio/frequency/ad9523.c
>   create mode 100644 include/linux/iio/frequency/ad9523.h
>
> diff --git a/Documentation/ABI/testing/sysfs-bus-iio-frequency-ad9523 b/Documentation/ABI/testing/sysfs-bus-iio-frequency-ad9523
> new file mode 100644
> index 0000000..2ce9c3f
> --- /dev/null
> +++ b/Documentation/ABI/testing/sysfs-bus-iio-frequency-ad9523
> @@ -0,0 +1,37 @@
> +What:		/sys/bus/iio/devices/iio:deviceX/pll2_feedback_clk_present
> +What:		/sys/bus/iio/devices/iio:deviceX/pll2_reference_clk_present
> +What:		/sys/bus/iio/devices/iio:deviceX/pll1_reference_clk_a_present
> +What:		/sys/bus/iio/devices/iio:deviceX/pll1_reference_clk_b_present
> +What:		/sys/bus/iio/devices/iio:deviceX/pll1_reference_clk_test_present
> +What:		/sys/bus/iio/devices/iio:deviceX/vcxo_clk_present
> +KernelVersion:	3.4.0
> +Contact:	linux-iio@vger.kernel.org
> +Description:
> +		Reading returns either '1' or '0'.
> +		'1' means that the clock in question is present.
> +		'0' means that the clock is missing.
> +
> +What:		/sys/bus/iio/devices/iio:deviceX/pllY_locked
> +KernelVersion:	3.4.0
> +Contact:	linux-iio@vger.kernel.org
> +Description:
> +		Reading returns either '1' or '0'. '1' means that the
> +		pllY is locked.
> +
> +What:		/sys/bus/iio/devices/iio:deviceX/store_eeprom
> +KernelVersion:	3.4.0
> +Contact:	linux-iio@vger.kernel.org
> +Description:
> +		Writing '1' stores the current device configuration into
> +		on-chip EEPROM. After power-up or chip reset the device will
> +		automatically load the saved configuration.
> +
> +What:		/sys/bus/iio/devices/iio:deviceX/sync_dividers
> +KernelVersion:	3.4.0
> +Contact:	linux-iio@vger.kernel.org
> +Description:
> +		Writing '1' triggers the clock distribution synchronization
> +		functionality. All dividers are reset and the channels start
> +		with their predefined phase offsets (out_altvoltageY_phase).
> +		Writing this file has the effect as driving the external
> +		/SYNC pin low.
> diff --git a/drivers/iio/Kconfig b/drivers/iio/Kconfig
> index 56eecef..ad09f67 100644
> --- a/drivers/iio/Kconfig
> +++ b/drivers/iio/Kconfig
> @@ -50,5 +50,6 @@ config IIO_CONSUMERS_PER_TRIGGER
>
>   source "drivers/iio/adc/Kconfig"
>   source "drivers/iio/amplifiers/Kconfig"
> +source "drivers/iio/frequency/Kconfig"
>
>   endif # IIO
> diff --git a/drivers/iio/Makefile b/drivers/iio/Makefile
> index e425afd..014e0e4 100644
> --- a/drivers/iio/Makefile
> +++ b/drivers/iio/Makefile
> @@ -11,3 +11,4 @@ obj-$(CONFIG_IIO_KFIFO_BUF) += kfifo_buf.o
>
>   obj-y += adc/
>   obj-y += amplifiers/
> +obj-y += frequency/
> diff --git a/drivers/iio/frequency/Kconfig b/drivers/iio/frequency/Kconfig
> new file mode 100644
> index 0000000..0458c92
> --- /dev/null
> +++ b/drivers/iio/frequency/Kconfig
> @@ -0,0 +1,23 @@
> +#
> +# Frequency
> +#	Direct Digital Synthesis drivers (DDS)
> +#	Clock Distribution device drivers
> +#	Phase-Locked Loop (PLL) frequency synthesizers
> +#
> +
> +menu "Frequency Synthesizers DDS/PLL"
> +
> +menu "Clock Generator/Distribution"
> +
> +config AD9523
> +	tristate "Analog Devices AD9523 Low Jitter Clock Generator"
> +	depends on SPI
> +	help
> +	  Say yes here to build support for Analog Devices AD9523 Low Jitter
> +	  Clock Generator. The driver provides direct access via sysfs.
> +
> +	  To compile this driver as a module, choose M here: the
> +	  module will be called ad9523.
> +
> +endmenu
> +endmenu
> diff --git a/drivers/iio/frequency/Makefile b/drivers/iio/frequency/Makefile
> new file mode 100644
> index 0000000..1b5b224
> --- /dev/null
> +++ b/drivers/iio/frequency/Makefile
> @@ -0,0 +1,5 @@
> +#
> +# Makefile iio/frequency
> +#
> +
> +obj-$(CONFIG_AD9523) += ad9523.o
> diff --git a/drivers/iio/frequency/ad9523.c b/drivers/iio/frequency/ad9523.c
> new file mode 100644
> index 0000000..77a17de
> --- /dev/null
> +++ b/drivers/iio/frequency/ad9523.c
> @@ -0,0 +1,1059 @@
> +/*
> + * AD9523 SPI Low Jitter Clock Generator
> + *
> + * Copyright 2012 Analog Devices Inc.
> + *
> + * Licensed under the GPL-2.
> + */
> +
> +#include<linux/device.h>
> +#include<linux/kernel.h>
> +#include<linux/slab.h>
> +#include<linux/sysfs.h>
> +#include<linux/spi/spi.h>
> +#include<linux/regulator/consumer.h>
> +#include<linux/err.h>
> +#include<linux/module.h>
> +#include<linux/delay.h>
> +
> +#include<linux/iio/iio.h>
> +#include<linux/iio/sysfs.h>
> +#include<linux/iio/frequency/ad9523.h>
> +
> +#define AD9523_READ	(1<<  15)
> +#define AD9523_WRITE	(0<<  15)
> +#define AD9523_CNT(x)	(((x) - 1)<<  13)
> +#define AD9523_ADDR(x)	((x)&  0xFFF)
> +
> +#define AD9523_R1B	(1<<  16)
> +#define AD9523_R2B	(2<<  16)
> +#define AD9523_R3B	(3<<  16)
> +#define AD9523_TRANSF_LEN(x)			((x)>>  16)
> +
> +#define AD9523_SERIAL_PORT_CONFIG		(AD9523_R1B | 0x0)
> +#define AD9523_VERSION_REGISTER			(AD9523_R1B | 0x2)
> +#define AD9523_PART_REGISTER			(AD9523_R1B | 0x3)
> +#define AD9523_READBACK_CTRL			(AD9523_R1B | 0x4)
> +
> +#define AD9523_EEPROM_CUSTOMER_VERSION_ID	(AD9523_R2B | 0x6)
> +
> +#define AD9523_PLL1_REF_A_DIVIDER		(AD9523_R2B | 0x11)
> +#define AD9523_PLL1_REF_B_DIVIDER		(AD9523_R2B | 0x13)
> +#define AD9523_PLL1_REF_TEST_DIVIDER		(AD9523_R1B | 0x14)
> +#define AD9523_PLL1_FEEDBACK_DIVIDER		(AD9523_R2B | 0x17)
> +#define AD9523_PLL1_CHARGE_PUMP_CTRL		(AD9523_R2B | 0x19)
> +#define AD9523_PLL1_INPUT_RECEIVERS_CTRL	(AD9523_R1B | 0x1A)
> +#define AD9523_PLL1_REF_CTRL			(AD9523_R1B | 0x1B)
> +#define AD9523_PLL1_MISC_CTRL			(AD9523_R1B | 0x1C)
> +#define AD9523_PLL1_LOOP_FILTER_CTRL		(AD9523_R1B | 0x1D)
> +
> +#define AD9523_PLL2_CHARGE_PUMP			(AD9523_R1B | 0xF0)
> +#define AD9523_PLL2_FEEDBACK_DIVIDER_AB		(AD9523_R1B | 0xF1)
> +#define AD9523_PLL2_CTRL			(AD9523_R1B | 0xF2)
> +#define AD9523_PLL2_VCO_CTRL			(AD9523_R1B | 0xF3)
> +#define AD9523_PLL2_VCO_DIVIDER			(AD9523_R1B | 0xF4)
> +#define AD9523_PLL2_LOOP_FILTER_CTRL		(AD9523_R2B | 0xF6)
> +#define AD9523_PLL2_R2_DIVIDER			(AD9523_R1B | 0xF7)
> +
> +#define AD9523_CHANNEL_CLOCK_DIST(ch)		(AD9523_R3B | (0x192 + 3 * ch))
> +
> +#define AD9523_PLL1_OUTPUT_CTRL			(AD9523_R1B | 0x1BA)
> +#define AD9523_PLL1_OUTPUT_CHANNEL_CTRL		(AD9523_R1B | 0x1BB)
> +
> +#define AD9523_READBACK_0			(AD9523_R1B | 0x22C)
> +#define AD9523_READBACK_1			(AD9523_R1B | 0x22D)
> +
> +#define AD9523_STATUS_SIGNALS			(AD9523_R3B | 0x232)
> +#define AD9523_POWER_DOWN_CTRL			(AD9523_R1B | 0x233)
> +#define AD9523_IO_UPDATE			(AD9523_R1B | 0x234)
> +
> +#define AD9523_EEPROM_DATA_XFER_STATUS		(AD9523_R1B | 0xB00)
> +#define AD9523_EEPROM_ERROR_READBACK		(AD9523_R1B | 0xB01)
> +#define AD9523_EEPROM_CTRL1			(AD9523_R1B | 0xB02)
> +#define AD9523_EEPROM_CTRL2			(AD9523_R1B | 0xB03)
> +
> +/* AD9523_SERIAL_PORT_CONFIG */
> +
> +#define AD9523_SER_CONF_SDO_ACTIVE		(1<<  7)
> +#define AD9523_SER_CONF_SOFT_RESET		(1<<  5)
> +
> +/* AD9523_READBACK_CTRL */
> +#define AD9523_READBACK_CTRL_READ_BUFFERED	(1<<  0)
> +
> +/* AD9523_PLL1_CHARGE_PUMP_CTRL */
> +#define AD9523_PLL1_CHARGE_PUMP_CURRENT_nA(x)	(((x) / 500)&  0x7F)
> +#define AD9523_PLL1_CHARGE_PUMP_TRISTATE	(1<<  7)
> +#define AD9523_PLL1_CHARGE_PUMP_MODE_NORMAL	(3<<  8)
> +#define AD9523_PLL1_CHARGE_PUMP_MODE_PUMP_DOWN	(2<<  8)
> +#define AD9523_PLL1_CHARGE_PUMP_MODE_PUMP_UP	(1<<  8)
> +#define AD9523_PLL1_CHARGE_PUMP_MODE_TRISTATE	(0<<  8)
> +#define AD9523_PLL1_BACKLASH_PW_MIN		(0<<  10)
> +#define AD9523_PLL1_BACKLASH_PW_LOW		(1<<  10)
> +#define AD9523_PLL1_BACKLASH_PW_HIGH		(2<<  10)
> +#define AD9523_PLL1_BACKLASH_PW_MAX		(3<<  10)
> +
> +/* AD9523_PLL1_INPUT_RECEIVERS_CTRL */
> +#define AD9523_PLL1_REF_TEST_RCV_EN		(1<<  7)
> +#define AD9523_PLL1_REFB_DIFF_RCV_EN		(1<<  6)
> +#define AD9523_PLL1_REFA_DIFF_RCV_EN		(1<<  5)
> +#define AD9523_PLL1_REFB_RCV_EN			(1<<  4)
> +#define AD9523_PLL1_REFA_RCV_EN			(1<<  3)
> +#define AD9523_PLL1_REFA_REFB_PWR_CTRL_EN	(1<<  2)
> +#define AD9523_PLL1_OSC_IN_CMOS_NEG_INP_EN	(1<<  1)
> +#define AD9523_PLL1_OSC_IN_DIFF_EN		(1<<  0)
> +
> +/* AD9523_PLL1_REF_CTRL */
> +#define AD9523_PLL1_BYPASS_REF_TEST_DIV_EN	(1<<  7)
> +#define AD9523_PLL1_BYPASS_FEEDBACK_DIV_EN	(1<<  6)
> +#define AD9523_PLL1_ZERO_DELAY_MODE_INT		(1<<  5)
> +#define AD9523_PLL1_ZERO_DELAY_MODE_EXT		(0<<  5)
> +#define AD9523_PLL1_OSC_IN_PLL_FEEDBACK_EN	(1<<  4)
> +#define AD9523_PLL1_ZD_IN_CMOS_NEG_INP_EN	(1<<  3)
> +#define AD9523_PLL1_ZD_IN_DIFF_EN		(1<<  2)
> +#define AD9523_PLL1_REFB_CMOS_NEG_INP_EN	(1<<  1)
> +#define AD9523_PLL1_REFA_CMOS_NEG_INP_EN	(1<<  0)
> +
> +/* AD9523_PLL1_MISC_CTRL */
> +#define AD9523_PLL1_REFB_INDEP_DIV_CTRL_EN	(1<<  7)
> +#define AD9523_PLL1_OSC_CTRL_FAIL_VCC_BY2_EN	(1<<  6)
> +#define AD9523_PLL1_REF_MODE(x)			((x)<<  2)
> +#define AD9523_PLL1_BYPASS_REFB_DIV		(1<<  1)
> +#define AD9523_PLL1_BYPASS_REFA_DIV		(1<<  0)
> +
> +/* AD9523_PLL1_LOOP_FILTER_CTRL */
> +#define AD9523_PLL1_LOOP_FILTER_RZERO(x)	((x)&  0xF)
> +
> +/* AD9523_PLL2_CHARGE_PUMP */
> +#define AD9523_PLL2_CHARGE_PUMP_CURRENT_nA(x)	((x) / 3500)
> +
> +/* AD9523_PLL2_FEEDBACK_DIVIDER_AB */
> +#define AD9523_PLL2_FB_NDIV_A_CNT(x)		(((x)&  0x3)<<  6)
> +#define AD9523_PLL2_FB_NDIV_B_CNT(x)		(((x)&  0x3F)<<  0)
> +#define AD9523_PLL2_FB_NDIV(a, b)		(4 * (b) + (a))
> +
> +/* AD9523_PLL2_CTRL */
> +#define AD9523_PLL2_CHARGE_PUMP_MODE_NORMAL	(3<<  0)
> +#define AD9523_PLL2_CHARGE_PUMP_MODE_PUMP_DOWN	(2<<  0)
> +#define AD9523_PLL2_CHARGE_PUMP_MODE_PUMP_UP	(1<<  0)
> +#define AD9523_PLL2_CHARGE_PUMP_MODE_TRISTATE	(0<<  0)
> +#define AD9523_PLL2_BACKLASH_PW_MIN		(0<<  2)
> +#define AD9523_PLL2_BACKLASH_PW_LOW		(1<<  2)
> +#define AD9523_PLL2_BACKLASH_PW_HIGH		(2<<  2)
> +#define AD9523_PLL2_BACKLASH_PW_MAX		(3<<  1)
> +#define AD9523_PLL2_BACKLASH_CTRL_EN		(1<<  4)
> +#define AD9523_PLL2_FREQ_DOUBLER_EN		(1<<  5)
> +#define AD9523_PLL2_LOCK_DETECT_PWR_DOWN_EN	(1<<  7)
> +
> +/* AD9523_PLL2_VCO_CTRL */
> +#define AD9523_PLL2_VCO_CALIBRATE		(1<<  1)
> +#define AD9523_PLL2_FORCE_VCO_MIDSCALE		(1<<  2)
> +#define AD9523_PLL2_FORCE_REFERENCE_VALID	(1<<  3)
> +#define AD9523_PLL2_FORCE_RELEASE_SYNC		(1<<  4)
> +
> +/* AD9523_PLL2_VCO_DIVIDER */
> +#define AD9523_PLL2_VCO_DIV_M1(x)		((((x) - 3)&  0x3)<<  0)
> +#define AD9523_PLL2_VCO_DIV_M2(x)		((((x) - 3)&  0x3)<<  4)
> +#define AD9523_PLL2_VCO_DIV_M1_PWR_DOWN_EN	(1<<  2)
> +#define AD9523_PLL2_VCO_DIV_M2_PWR_DOWN_EN	(1<<  6)
> +
> +/* AD9523_PLL2_LOOP_FILTER_CTRL */
> +#define AD9523_PLL2_LOOP_FILTER_CPOLE1(x)	(((x)&  0x7)<<  0)
> +#define AD9523_PLL2_LOOP_FILTER_RZERO(x)	(((x)&  0x7)<<  3)
> +#define AD9523_PLL2_LOOP_FILTER_RPOLE2(x)	(((x)&  0x7)<<  6)
> +#define AD9523_PLL2_LOOP_FILTER_RZERO_BYPASS_EN	(1<<  8)
> +
> +/* AD9523_PLL2_R2_DIVIDER */
> +#define AD9523_PLL2_R2_DIVIDER_VAL(x)		(((x)&  0x1F)<<  0)
> +
> +/* AD9523_CHANNEL_CLOCK_DIST */
> +#define AD9523_CLK_DIST_DIV_PHASE(x)		(((x)&  0x3F)<<  18)
> +#define AD9523_CLK_DIST_DIV_PHASE_REV(x)	((ret>>  18)&  0x3F)
> +#define AD9523_CLK_DIST_DIV(x)			((((x) - 1)&  0x3FF)<<  8)
> +#define AD9523_CLK_DIST_DIV_REV(x)		(((ret>>  8)&  0x3FF) + 1)
> +#define AD9523_CLK_DIST_INV_DIV_OUTPUT_EN	(1<<  7)
> +#define AD9523_CLK_DIST_IGNORE_SYNC_EN		(1<<  6)
> +#define AD9523_CLK_DIST_PWR_DOWN_EN		(1<<  5)
> +#define AD9523_CLK_DIST_LOW_PWR_MODE_EN		(1<<  4)
> +#define AD9523_CLK_DIST_DRIVER_MODE(x)		(((x)&  0xF)<<  0)
> +
> +/* AD9523_PLL1_OUTPUT_CTRL */
> +#define AD9523_PLL1_OUTP_CTRL_VCO_DIV_SEL_CH6_M2	(1<<  7)
> +#define AD9523_PLL1_OUTP_CTRL_VCO_DIV_SEL_CH5_M2	(1<<  6)
> +#define AD9523_PLL1_OUTP_CTRL_VCO_DIV_SEL_CH4_M2	(1<<  5)
> +#define AD9523_PLL1_OUTP_CTRL_CMOS_DRV_WEAK		(1<<  4)
> +#define AD9523_PLL1_OUTP_CTRL_OUTPUT_DIV_1		(0<<  0)
> +#define AD9523_PLL1_OUTP_CTRL_OUTPUT_DIV_2		(1<<  0)
> +#define AD9523_PLL1_OUTP_CTRL_OUTPUT_DIV_4		(2<<  0)
> +#define AD9523_PLL1_OUTP_CTRL_OUTPUT_DIV_8		(4<<  0)
> +#define AD9523_PLL1_OUTP_CTRL_OUTPUT_DIV_16		(8<<  0)
> +
> +/* AD9523_PLL1_OUTPUT_CHANNEL_CTRL */
> +#define AD9523_PLL1_OUTP_CH_CTRL_OUTPUT_PWR_DOWN_EN	(1<<  7)
> +#define AD9523_PLL1_OUTP_CH_CTRL_VCO_DIV_SEL_CH9_M2	(1<<  6)
> +#define AD9523_PLL1_OUTP_CH_CTRL_VCO_DIV_SEL_CH8_M2	(1<<  5)
> +#define AD9523_PLL1_OUTP_CH_CTRL_VCO_DIV_SEL_CH7_M2	(1<<  4)
> +#define AD9523_PLL1_OUTP_CH_CTRL_VCXO_SRC_SEL_CH3	(1<<  3)
> +#define AD9523_PLL1_OUTP_CH_CTRL_VCXO_SRC_SEL_CH2	(1<<  2)
> +#define AD9523_PLL1_OUTP_CH_CTRL_VCXO_SRC_SEL_CH1	(1<<  1)
> +#define AD9523_PLL1_OUTP_CH_CTRL_VCXO_SRC_SEL_CH0	(1<<  0)
> +
> +/* AD9523_READBACK_0 */
> +#define AD9523_READBACK_0_STAT_PLL2_REF_CLK		(1<<  7)
> +#define AD9523_READBACK_0_STAT_PLL2_FB_CLK		(1<<  6)
> +#define AD9523_READBACK_0_STAT_VCXO			(1<<  5)
> +#define AD9523_READBACK_0_STAT_REF_TEST			(1<<  4)
> +#define AD9523_READBACK_0_STAT_REFB			(1<<  3)
> +#define AD9523_READBACK_0_STAT_REFA			(1<<  2)
> +#define AD9523_READBACK_0_STAT_PLL2_LD			(1<<  1)
> +#define AD9523_READBACK_0_STAT_PLL1_LD			(1<<  0)
> +
> +/* AD9523_READBACK_1 */
> +#define AD9523_READBACK_1_HOLDOVER_ACTIVE		(1<<  3)
> +#define AD9523_READBACK_1_AUTOMODE_SEL_REFB		(1<<  2)
> +#define AD9523_READBACK_1_VCO_CALIB_IN_PROGRESS		(1<<  0)
> +
> +/* AD9523_STATUS_SIGNALS */
> +#define AD9523_STATUS_SIGNALS_SYNC_MAN_CTRL		(1<<  16)
> +#define AD9523_STATUS_MONITOR_01_PLL12_LOCKED		(0x302)
> +/* AD9523_POWER_DOWN_CTRL */
> +#define AD9523_POWER_DOWN_CTRL_PLL1_PWR_DOWN		(1<<  2)
> +#define AD9523_POWER_DOWN_CTRL_PLL2_PWR_DOWN		(1<<  1)
> +#define AD9523_POWER_DOWN_CTRL_DIST_PWR_DOWN		(1<<  0)
> +
> +/* AD9523_IO_UPDATE */
> +#define AD9523_IO_UPDATE_EN				(1<<  0)
> +
> +/* AD9523_EEPROM_DATA_XFER_STATUS */
> +#define AD9523_EEPROM_DATA_XFER_IN_PROGRESS		(1<<  0)
> +
> +/* AD9523_EEPROM_ERROR_READBACK */
> +#define AD9523_EEPROM_ERROR_READBACK_FAIL		(1<<  0)
> +
> +/* AD9523_EEPROM_CTRL1 */
> +#define AD9523_EEPROM_CTRL1_SOFT_EEPROM			(1<<  1)
> +#define AD9523_EEPROM_CTRL1_EEPROM_WRITE_PROT_DIS	(1<<  0)
> +
> +/* AD9523_EEPROM_CTRL2 */
> +#define AD9523_EEPROM_CTRL2_REG2EEPROM			(1<<  0)
> +
> +#define AD9523_NUM_CHAN					14
> +#define AD9523_NUM_CHAN_ALT_CLK_SRC			10
> +
> +/* Helpers to avoid excess line breaks */
> +#define AD_IFE(_pde, _a, _b) ((pdata->_pde) ? _a : _b)
> +#define AD_IF(_pde, _a) AD_IFE(_pde, _a, 0)
> +
> +enum {
> +	AD9523_STAT_PLL1_LD,
> +	AD9523_STAT_PLL2_LD,
> +	AD9523_STAT_REFA,
> +	AD9523_STAT_REFB,
> +	AD9523_STAT_REF_TEST,
> +	AD9523_STAT_VCXO,
> +	AD9523_STAT_PLL2_FB_CLK,
> +	AD9523_STAT_PLL2_REF_CLK,
> +	AD9523_SYNC,
> +	AD9523_EEPROM,
> +};
> +
> +enum {
> +	AD9523_VCO1,
> +	AD9523_VCO2,
> +	AD9523_VCXO,
> +	AD9523_NUM_CLK_SRC,
> +};
> +
> +struct ad9523_state {
> +	struct spi_device		*spi;
> +	struct regulator		*reg;
> +	struct ad9523_platform_data	*pdata;
> +	struct iio_chan_spec		ad9523_channels[AD9523_NUM_CHAN];
> +
> +	unsigned long		vcxo_freq;
> +	unsigned long		vco_freq;
> +	unsigned long		vco_out_freq[AD9523_NUM_CLK_SRC];
> +	unsigned char		vco_out_map[AD9523_NUM_CHAN_ALT_CLK_SRC];
> +
> +	/*
> +	 * DMA (thus cache coherency maintenance) requires the
> +	 * transfer buffers to live in their own cache lines.
> +	 */
> +	union {
> +		__be32 d32;
> +		u8 d8[4];
> +	} data[2] ____cacheline_aligned;
> +};
> +
> +static int ad9523_read(struct iio_dev *indio_dev, unsigned addr)
> +{
> +	struct ad9523_state *st = iio_priv(indio_dev);
> +	struct spi_message m;
> +	int ret;
> +
> +	/* We encode the register size 1..3 bytes into the register address.
> +	 * On transfer we get the size from the register datum, and make sure
> +	 * the result is properly aligned.
> +	 */
> +
> +	struct spi_transfer t[] = {
> +		{
> +			.tx_buf =&st->data[0].d8[2],
> +			.len = 2,
> +		}, {
> +			.rx_buf =&st->data[1].d8[4 - AD9523_TRANSF_LEN(addr)],
> +			.len = AD9523_TRANSF_LEN(addr),
> +		},
> +	};
> +
> +	spi_message_init(&m);
> +	spi_message_add_tail(&t[0],&m);
> +	spi_message_add_tail(&t[1],&m);
> +
> +	st->data[0].d32 = cpu_to_be32(AD9523_READ |
> +				      AD9523_CNT(AD9523_TRANSF_LEN(addr)) |
> +				      AD9523_ADDR(addr));
> +
> +	ret = spi_sync(st->spi,&m);
> +	if (ret<  0)
> +		dev_err(&indio_dev->dev, "read failed (%d)", ret);
> +	else
> +		ret = be32_to_cpu(st->data[1].d32)&  (0xFFFFFF>>
> +				  (8 * (3 - AD9523_TRANSF_LEN(addr))));
> +
> +	return ret;
> +};
> +
> +static int ad9523_write(struct iio_dev *indio_dev, unsigned addr, unsigned val)
> +{
> +	struct ad9523_state *st = iio_priv(indio_dev);
> +	struct spi_message m;
> +	int ret;
> +	struct spi_transfer t[] = {
> +		{
> +			.tx_buf =&st->data[0].d8[2],
> +			.len = 2,
> +		}, {
> +			.tx_buf =&st->data[1].d8[4 - AD9523_TRANSF_LEN(addr)],
> +			.len = AD9523_TRANSF_LEN(addr),
> +		},
> +	};
> +
> +	spi_message_init(&m);
> +	spi_message_add_tail(&t[0],&m);
> +	spi_message_add_tail(&t[1],&m);
> +
> +	st->data[0].d32 = cpu_to_be32(AD9523_WRITE |
> +				      AD9523_CNT(AD9523_TRANSF_LEN(addr)) |
> +				      AD9523_ADDR(addr));
> +	st->data[1].d32 = cpu_to_be32(val);
> +
> +	ret = spi_sync(st->spi,&m);
> +
> +	if (ret<  0)
> +		dev_err(&indio_dev->dev, "write failed (%d)", ret);
> +
> +	return ret;
> +}
> +
> +static int ad9523_io_update(struct iio_dev *indio_dev)
> +{
> +	return ad9523_write(indio_dev, AD9523_IO_UPDATE, AD9523_IO_UPDATE_EN);
> +}
> +
> +static int ad9523_vco_out_map(struct iio_dev *indio_dev,
> +			      unsigned ch, bool out)
> +{
> +	struct ad9523_state *st = iio_priv(indio_dev);
> +	int ret;
> +	unsigned mask;
> +
> +	switch (ch) {
> +	case 0 ... 3:
> +		ret = ad9523_read(indio_dev, AD9523_PLL1_OUTPUT_CHANNEL_CTRL);
> +		if (ret<  0)
> +			break;
> +		mask = AD9523_PLL1_OUTP_CH_CTRL_VCXO_SRC_SEL_CH0<<  ch;
> +		if (out) {
> +			ret |= mask;
> +			out = 2;
> +		} else {
> +			ret&= ~mask;
> +		}
> +		ret = ad9523_write(indio_dev,
> +				   AD9523_PLL1_OUTPUT_CHANNEL_CTRL, ret);
> +		break;
> +	case 4 ... 6:
> +		ret = ad9523_read(indio_dev, AD9523_PLL1_OUTPUT_CTRL);
> +		if (ret<  0)
> +			break;
> +		mask = AD9523_PLL1_OUTP_CTRL_VCO_DIV_SEL_CH4_M2<<  (ch - 4);
> +		if (out)
> +			ret |= mask;
> +		else
> +			ret&= ~mask;
> +		ret = ad9523_write(indio_dev, AD9523_PLL1_OUTPUT_CTRL, ret);
> +		break;
> +	case 7 ... 9:
> +		ret = ad9523_read(indio_dev, AD9523_PLL1_OUTPUT_CHANNEL_CTRL);
> +		if (ret<  0)
> +			break;
> +		mask = AD9523_PLL1_OUTP_CH_CTRL_VCO_DIV_SEL_CH7_M2<<  (ch - 7);
> +		if (out)
> +			ret |= mask;
> +		else
> +			ret&= ~mask;
> +		ret = ad9523_write(indio_dev,
> +				   AD9523_PLL1_OUTPUT_CHANNEL_CTRL, ret);
> +		break;
> +	default:
> +		return 0;
> +	}
> +
> +	st->vco_out_map[ch] = out;
> +
> +	return ret;
> +}
> +
> +static int ad9523_set_clock_provider(struct iio_dev *indio_dev,
> +			      unsigned ch, unsigned long freq)
> +{
> +	struct ad9523_state *st = iio_priv(indio_dev);
> +	long tmp1, tmp2;
> +	bool use_alt_clk_src;
> +
> +	switch (ch) {
> +	case 0 ... 3:
> +		use_alt_clk_src = (freq == st->vco_out_freq[AD9523_VCXO]);
> +		break;
> +	case 4 ... 9:
> +		tmp1 = st->vco_out_freq[AD9523_VCO1] / freq;
> +		tmp2 = st->vco_out_freq[AD9523_VCO2] / freq;
> +		tmp1 *= freq;
> +		tmp2 *= freq;
> +		use_alt_clk_src = (abs(tmp1 - freq)>  abs(tmp2 - freq));
> +		break;
> +	default:
> +		/* Ch 10..14: No action required, return success */
> +		return 0;
> +	}
> +
> +	return ad9523_vco_out_map(indio_dev, ch, use_alt_clk_src);
> +}
> +
> +static int ad9523_store_eeprom(struct iio_dev *indio_dev)
> +{
> +	int ret, tmp;
> +
> +	ret = ad9523_write(indio_dev, AD9523_EEPROM_CTRL1,
> +			   AD9523_EEPROM_CTRL1_EEPROM_WRITE_PROT_DIS);
> +	if (ret<  0)
> +		return ret;
> +	ret = ad9523_write(indio_dev, AD9523_EEPROM_CTRL2,
> +			   AD9523_EEPROM_CTRL2_REG2EEPROM);
> +	if (ret<  0)
> +		return ret;
> +
> +	tmp = 4;
> +	do {
> +		msleep(16);
> +		ret = ad9523_read(indio_dev,
> +				  AD9523_EEPROM_DATA_XFER_STATUS);
> +		if (ret<  0)
> +			return ret;
> +	} while ((ret&  AD9523_EEPROM_DATA_XFER_IN_PROGRESS)&&  tmp--);
> +
> +	ret = ad9523_write(indio_dev, AD9523_EEPROM_CTRL1, 0);
> +	if (ret<  0)
> +		return ret;
> +
> +	ret = ad9523_read(indio_dev, AD9523_EEPROM_ERROR_READBACK);
> +	if (ret<  0)
> +		return ret;
> +
> +	if (ret&  AD9523_EEPROM_ERROR_READBACK_FAIL) {
> +		dev_err(&indio_dev->dev, "Verify EEPROM failed");
> +		ret = -EIO;
> +	}
> +
> +	return ret;
> +}
> +
> +static int ad9523_sync(struct iio_dev *indio_dev)
> +{
> +	int ret, tmp;
> +
> +	ret = ad9523_read(indio_dev, AD9523_STATUS_SIGNALS);
> +	if (ret<  0)
> +		return ret;
> +
> +	tmp = ret;
> +	tmp |= AD9523_STATUS_SIGNALS_SYNC_MAN_CTRL;
> +
> +	ret = ad9523_write(indio_dev, AD9523_STATUS_SIGNALS, tmp);
> +	if (ret<  0)
> +		return ret;
> +
> +	ad9523_io_update(indio_dev);
> +	tmp&= ~AD9523_STATUS_SIGNALS_SYNC_MAN_CTRL;
> +
> +	ret = ad9523_write(indio_dev, AD9523_STATUS_SIGNALS, tmp);
> +	if (ret<  0)
> +		return ret;
> +
> +	return ad9523_io_update(indio_dev);
> +}
> +
> +static ssize_t ad9523_store(struct device *dev,
> +				struct device_attribute *attr,
> +				const char *buf, size_t len)
> +{
> +	struct iio_dev *indio_dev = dev_to_iio_dev(dev);
> +	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
> +	bool state;
> +	int ret;
> +
> +	ret = strtobool(buf,&state);
> +	if (ret<  0)
> +		return ret;
> +
> +	if (!state)
> +		return 0;
> +
> +	mutex_lock(&indio_dev->mlock);
> +	switch ((u32)this_attr->address) {
> +	case AD9523_SYNC:
> +		ret = ad9523_sync(indio_dev);
> +		break;
> +	case AD9523_EEPROM:
> +		ret = ad9523_store_eeprom(indio_dev);
> +		break;
> +	default:
> +		ret = -ENODEV;
> +	}
> +	mutex_unlock(&indio_dev->mlock);
> +
> +	return ret ? ret : len;
> +}
> +
> +static ssize_t ad9523_show(struct device *dev,
> +			struct device_attribute *attr,
> +			char *buf)
> +{
> +	struct iio_dev *indio_dev = dev_to_iio_dev(dev);
> +	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
> +	int ret;
> +
> +	mutex_lock(&indio_dev->mlock);
> +	ret = ad9523_read(indio_dev, AD9523_READBACK_0);
> +	if (ret>= 0) {
> +		ret = sprintf(buf, "%d\n", !!(ret&  (1<<
> +			(u32)this_attr->address)));
> +	}
> +	mutex_unlock(&indio_dev->mlock);
> +
> +	return ret;
> +}
> +
> +static IIO_DEVICE_ATTR(pll1_locked, S_IRUGO,
> +			ad9523_show,
> +			NULL,
> +			AD9523_STAT_PLL1_LD);
> +
> +static IIO_DEVICE_ATTR(pll2_locked, S_IRUGO,
> +			ad9523_show,
> +			NULL,
> +			AD9523_STAT_PLL2_LD);
> +
> +static IIO_DEVICE_ATTR(pll1_reference_clk_a_present, S_IRUGO,
> +			ad9523_show,
> +			NULL,
> +			AD9523_STAT_REFA);
> +
> +static IIO_DEVICE_ATTR(pll1_reference_clk_b_present, S_IRUGO,
> +			ad9523_show,
> +			NULL,
> +			AD9523_STAT_REFB);
> +
> +static IIO_DEVICE_ATTR(pll1_reference_clk_test_present, S_IRUGO,
> +			ad9523_show,
> +			NULL,
> +			AD9523_STAT_REF_TEST);
> +
> +static IIO_DEVICE_ATTR(vcxo_clk_present, S_IRUGO,
> +			ad9523_show,
> +			NULL,
> +			AD9523_STAT_VCXO);
> +
> +static IIO_DEVICE_ATTR(pll2_feedback_clk_present, S_IRUGO,
> +			ad9523_show,
> +			NULL,
> +			AD9523_STAT_PLL2_FB_CLK);
> +
> +static IIO_DEVICE_ATTR(pll2_reference_clk_present, S_IRUGO,
> +			ad9523_show,
> +			NULL,
> +			AD9523_STAT_PLL2_REF_CLK);
> +
> +static IIO_DEVICE_ATTR(sync_dividers, S_IWUSR,
> +			NULL,
> +			ad9523_store,
> +			AD9523_SYNC);
> +
> +static IIO_DEVICE_ATTR(store_eeprom, S_IWUSR,
> +			NULL,
> +			ad9523_store,
> +			AD9523_EEPROM);
> +
> +static struct attribute *ad9523_attributes[] = {
> +	&iio_dev_attr_sync_dividers.dev_attr.attr,
> +	&iio_dev_attr_store_eeprom.dev_attr.attr,
> +	&iio_dev_attr_pll2_feedback_clk_present.dev_attr.attr,
> +	&iio_dev_attr_pll2_reference_clk_present.dev_attr.attr,
> +	&iio_dev_attr_pll1_reference_clk_a_present.dev_attr.attr,
> +	&iio_dev_attr_pll1_reference_clk_b_present.dev_attr.attr,
> +	&iio_dev_attr_pll1_reference_clk_test_present.dev_attr.attr,
> +	&iio_dev_attr_vcxo_clk_present.dev_attr.attr,
> +	&iio_dev_attr_pll1_locked.dev_attr.attr,
> +	&iio_dev_attr_pll2_locked.dev_attr.attr,
> +	NULL,
> +};
> +
> +static const struct attribute_group ad9523_attribute_group = {
> +	.attrs = ad9523_attributes,
> +};
> +
> +static int ad9523_read_raw(struct iio_dev *indio_dev,
> +			   struct iio_chan_spec const *chan,
> +			   int *val,
> +			   int *val2,
> +			   long m)
> +{
> +	struct ad9523_state *st = iio_priv(indio_dev);
> +	unsigned code;
> +	int ret;
> +
> +	mutex_lock(&indio_dev->mlock);
> +	ret = ad9523_read(indio_dev, AD9523_CHANNEL_CLOCK_DIST(chan->channel));
> +	mutex_unlock(&indio_dev->mlock);
> +
> +	if (ret<  0)
> +		return ret;
> +
> +	switch (m) {
> +	case IIO_CHAN_INFO_RAW:
> +		*val = !(ret&  AD9523_CLK_DIST_PWR_DOWN_EN);
> +		return IIO_VAL_INT;
> +	case IIO_CHAN_INFO_FREQUENCY:
> +		*val = st->vco_out_freq[st->vco_out_map[chan->channel]] /
> +			AD9523_CLK_DIST_DIV_REV(ret);
> +		return IIO_VAL_INT;
> +	case IIO_CHAN_INFO_PHASE:
> +		code = (AD9523_CLK_DIST_DIV_PHASE_REV(ret) * 3141592) /
> +			AD9523_CLK_DIST_DIV_REV(ret);
> +		*val = code / 1000000;
> +		*val2 = (code % 1000000) * 10;
> +		return IIO_VAL_INT_PLUS_MICRO;
> +	default:
> +		return -EINVAL;
> +	}
> +};
> +
> +static int ad9523_write_raw(struct iio_dev *indio_dev,
> +			    struct iio_chan_spec const *chan,
> +			    int val,
> +			    int val2,
> +			    long mask)
> +{
> +	struct ad9523_state *st = iio_priv(indio_dev);
> +	unsigned reg;
> +	int ret, tmp, code;
> +
> +	mutex_lock(&indio_dev->mlock);
> +	ret = ad9523_read(indio_dev, AD9523_CHANNEL_CLOCK_DIST(chan->channel));
> +	if (ret<  0)
> +		goto out;
> +
> +	reg = ret;
> +
> +	switch (mask) {
> +	case IIO_CHAN_INFO_RAW:
> +		if (val)
> +			reg&= ~AD9523_CLK_DIST_PWR_DOWN_EN;
> +		else
> +			reg |= AD9523_CLK_DIST_PWR_DOWN_EN;
> +		break;
> +	case IIO_CHAN_INFO_FREQUENCY:
> +		if (val<= 0) {
> +			ret = -EINVAL;
> +			goto out;
> +		}
> +		ret = ad9523_set_clock_provider(indio_dev, chan->channel, val);
> +		if (ret<  0)
> +			goto out;
> +		tmp = st->vco_out_freq[st->vco_out_map[chan->channel]] / val;
> +		tmp = clamp(tmp, 1, 1024);
> +		reg&= ~(0x3FF<<  8);
> +		reg |= AD9523_CLK_DIST_DIV(tmp);
> +		break;
> +	case IIO_CHAN_INFO_PHASE:
> +		code = val * 1000000 + val2 % 1000000;
> +		tmp = (code * AD9523_CLK_DIST_DIV_REV(ret)) / 3141592;
> +		tmp = clamp(tmp, 0, 63);
> +		reg&= ~AD9523_CLK_DIST_DIV_PHASE(~0);
> +		reg |= AD9523_CLK_DIST_DIV_PHASE(tmp);
> +		break;
> +	default:
> +		ret = -EINVAL;
> +		goto out;
> +	}
> +
> +	ret = ad9523_write(indio_dev, AD9523_CHANNEL_CLOCK_DIST(chan->channel),
> +			   reg);
> +	if (ret<  0)
> +		goto out;
> +
> +	ad9523_io_update(indio_dev);
> +out:
> +	mutex_unlock(&indio_dev->mlock);
> +	return ret;
> +}
> +
> +static int ad9523_reg_access(struct iio_dev *indio_dev,
> +			      unsigned reg, unsigned writeval,
> +			      unsigned *readval)
> +{
> +	int ret;
> +
> +	mutex_lock(&indio_dev->mlock);
> +	if (readval == NULL) {
> +		ret = ad9523_write(indio_dev, reg | AD9523_R1B, writeval);
> +		ad9523_io_update(indio_dev);
> +	} else {
> +		ret = ad9523_read(indio_dev, reg | AD9523_R1B);
> +		if (ret<  0)
> +			return ret;
> +		*readval = ret;
> +		ret = 0;
> +	}
> +	mutex_unlock(&indio_dev->mlock);
> +
> +	return ret;
> +}
> +
> +static const struct iio_info ad9523_info = {
> +	.read_raw =&ad9523_read_raw,
> +	.write_raw =&ad9523_write_raw,
> +	.debugfs_reg_access =&ad9523_reg_access,
> +	.attrs =&ad9523_attribute_group,
> +	.driver_module = THIS_MODULE,
> +};
> +
> +static int ad9523_setup(struct iio_dev *indio_dev)
> +{
> +	struct ad9523_state *st = iio_priv(indio_dev);
> +	struct ad9523_platform_data *pdata = st->pdata;
> +	struct ad9523_channel_spec *chan;
> +	unsigned active_mask = 0;
> +	int ret, i;
> +
> +	ret = ad9523_write(indio_dev, AD9523_SERIAL_PORT_CONFIG,
> +			   AD9523_SER_CONF_SOFT_RESET |
> +			  (st->spi->mode&  SPI_3WIRE ? 0 :
> +			  AD9523_SER_CONF_SDO_ACTIVE));
> +	if (ret<  0)
> +		return ret;
> +
> +	ret = ad9523_write(indio_dev, AD9523_READBACK_CTRL,
> +			  AD9523_READBACK_CTRL_READ_BUFFERED);
> +	if (ret<  0)
> +		return ret;
> +
> +	ret = ad9523_io_update(indio_dev);
> +	if (ret<  0)
> +		return ret;
> +
> +	/*
> +	 * PLL1 Setup
> +	 */
> +	ret = ad9523_write(indio_dev, AD9523_PLL1_REF_A_DIVIDER,
> +		pdata->refa_r_div);
> +	if (ret<  0)
> +		return ret;
> +
> +	ret = ad9523_write(indio_dev, AD9523_PLL1_REF_B_DIVIDER,
> +		pdata->refb_r_div);
> +	if (ret<  0)
> +		return ret;
> +
> +	ret = ad9523_write(indio_dev, AD9523_PLL1_FEEDBACK_DIVIDER,
> +		pdata->pll1_feedback_div);
> +	if (ret<  0)
> +		return ret;
> +
> +	ret = ad9523_write(indio_dev, AD9523_PLL1_CHARGE_PUMP_CTRL,
> +		AD9523_PLL1_CHARGE_PUMP_CURRENT_nA(pdata->
> +			pll1_charge_pump_current_nA) |
> +		AD9523_PLL1_CHARGE_PUMP_MODE_NORMAL |
> +		AD9523_PLL1_BACKLASH_PW_MIN);
> +	if (ret<  0)
> +		return ret;
> +
> +	ret = ad9523_write(indio_dev, AD9523_PLL1_INPUT_RECEIVERS_CTRL,
> +		AD_IF(refa_diff_rcv_en, AD9523_PLL1_REFA_RCV_EN) |
> +		AD_IF(refb_diff_rcv_en, AD9523_PLL1_REFB_RCV_EN) |
> +		AD_IF(osc_in_diff_en, AD9523_PLL1_OSC_IN_DIFF_EN) |
> +		AD_IF(osc_in_cmos_neg_inp_en,
> +		      AD9523_PLL1_OSC_IN_CMOS_NEG_INP_EN) |
> +		AD_IF(refa_diff_rcv_en, AD9523_PLL1_REFA_DIFF_RCV_EN) |
> +		AD_IF(refb_diff_rcv_en, AD9523_PLL1_REFB_DIFF_RCV_EN));
> +	if (ret<  0)
> +		return ret;
> +
> +	ret = ad9523_write(indio_dev, AD9523_PLL1_REF_CTRL,
> +		AD_IF(zd_in_diff_en, AD9523_PLL1_ZD_IN_DIFF_EN) |
> +		AD_IF(zd_in_cmos_neg_inp_en,
> +		      AD9523_PLL1_ZD_IN_CMOS_NEG_INP_EN) |
> +		AD_IF(zero_delay_mode_internal_en,
> +		      AD9523_PLL1_ZERO_DELAY_MODE_INT) |
> +		AD_IF(osc_in_feedback_en, AD9523_PLL1_OSC_IN_PLL_FEEDBACK_EN) |
> +		AD_IF(refa_cmos_neg_inp_en, AD9523_PLL1_REFA_CMOS_NEG_INP_EN) |
> +		AD_IF(refb_cmos_neg_inp_en, AD9523_PLL1_REFB_CMOS_NEG_INP_EN));
> +	if (ret<  0)
> +		return ret;
> +
> +	ret = ad9523_write(indio_dev, AD9523_PLL1_MISC_CTRL,
> +		AD9523_PLL1_REFB_INDEP_DIV_CTRL_EN |
> +		AD9523_PLL1_REF_MODE(pdata->ref_mode));
> +	if (ret<  0)
> +		return ret;
> +
> +	ret = ad9523_write(indio_dev, AD9523_PLL1_LOOP_FILTER_CTRL,
> +		AD9523_PLL1_LOOP_FILTER_RZERO(pdata->pll1_loop_filter_rzero));
> +	if (ret<  0)
> +		return ret;
> +	/*
> +	 * PLL2 Setup
> +	 */
> +
> +	ret = ad9523_write(indio_dev, AD9523_PLL2_CHARGE_PUMP,
> +		AD9523_PLL2_CHARGE_PUMP_CURRENT_nA(pdata->
> +			pll2_charge_pump_current_nA));
> +	if (ret<  0)
> +		return ret;
> +
> +	ret = ad9523_write(indio_dev, AD9523_PLL2_FEEDBACK_DIVIDER_AB,
> +		AD9523_PLL2_FB_NDIV_A_CNT(pdata->pll2_ndiv_a_cnt) |
> +		AD9523_PLL2_FB_NDIV_B_CNT(pdata->pll2_ndiv_b_cnt));
> +	if (ret<  0)
> +		return ret;
> +
> +	ret = ad9523_write(indio_dev, AD9523_PLL2_CTRL,
> +		AD9523_PLL2_CHARGE_PUMP_MODE_NORMAL |
> +		AD9523_PLL2_BACKLASH_CTRL_EN |
> +		AD_IF(pll2_freq_doubler_en, AD9523_PLL2_FREQ_DOUBLER_EN));
> +	if (ret<  0)
> +		return ret;
> +
> +	st->vco_freq = (pdata->vcxo_freq * (pdata->pll2_freq_doubler_en ? 2 : 1)
> +			/ pdata->pll2_r2_div) * AD9523_PLL2_FB_NDIV(pdata->
> +			pll2_ndiv_a_cnt, pdata->pll2_ndiv_b_cnt);
> +
> +	ret = ad9523_write(indio_dev, AD9523_PLL2_VCO_CTRL,
> +		AD9523_PLL2_VCO_CALIBRATE);
> +	if (ret<  0)
> +		return ret;
> +
> +	ret = ad9523_write(indio_dev, AD9523_PLL2_VCO_DIVIDER,
> +		AD9523_PLL2_VCO_DIV_M1(pdata->pll2_vco_diff_m1) |
> +		AD9523_PLL2_VCO_DIV_M2(pdata->pll2_vco_diff_m2) |
> +		AD_IFE(pll2_vco_diff_m1, 0,
> +		       AD9523_PLL2_VCO_DIV_M1_PWR_DOWN_EN) |
> +		AD_IFE(pll2_vco_diff_m2, 0,
> +		       AD9523_PLL2_VCO_DIV_M2_PWR_DOWN_EN));
> +	if (ret<  0)
> +		return ret;
> +
> +	if (pdata->pll2_vco_diff_m1)
> +		st->vco_out_freq[AD9523_VCO1] =
> +			st->vco_freq / pdata->pll2_vco_diff_m1;
> +
> +	if (pdata->pll2_vco_diff_m2)
> +		st->vco_out_freq[AD9523_VCO2] =
> +			st->vco_freq / pdata->pll2_vco_diff_m2;
> +
> +	st->vco_out_freq[AD9523_VCXO] = pdata->vcxo_freq;
> +
> +	ret = ad9523_write(indio_dev, AD9523_PLL2_R2_DIVIDER,
> +		AD9523_PLL2_R2_DIVIDER_VAL(pdata->pll2_r2_div));
> +	if (ret<  0)
> +		return ret;
> +
> +	ret = ad9523_write(indio_dev, AD9523_PLL2_LOOP_FILTER_CTRL,
> +		AD9523_PLL2_LOOP_FILTER_CPOLE1(pdata->cpole1) |
> +		AD9523_PLL2_LOOP_FILTER_RZERO(pdata->rzero) |
> +		AD9523_PLL2_LOOP_FILTER_RPOLE2(pdata->rpole2) |
> +		AD_IF(rzero_bypass_en,
> +		      AD9523_PLL2_LOOP_FILTER_RZERO_BYPASS_EN));
> +	if (ret<  0)
> +		return ret;
> +
> +	for (i = 0; i<  pdata->num_channels; i++) {
> +		chan =&pdata->channels[i];
> +		if (chan->channel_num<  AD9523_NUM_CHAN) {
> +			active_mask |= (1<<  chan->channel_num);
> +			ret = ad9523_write(indio_dev,
> +				AD9523_CHANNEL_CLOCK_DIST(chan->channel_num),
> +				AD9523_CLK_DIST_DRIVER_MODE(chan->driver_mode) |
> +				AD9523_CLK_DIST_DIV(chan->channel_divider) |
> +				AD9523_CLK_DIST_DIV_PHASE(chan->divider_phase) |
> +				(chan->sync_ignore_en ?
> +					AD9523_CLK_DIST_IGNORE_SYNC_EN : 0) |
> +				(chan->divider_output_invert_en ?
> +					AD9523_CLK_DIST_INV_DIV_OUTPUT_EN : 0) |
> +				(chan->low_power_mode_en ?
> +					AD9523_CLK_DIST_LOW_PWR_MODE_EN : 0) |
> +				(chan->output_dis ?
> +					AD9523_CLK_DIST_PWR_DOWN_EN : 0));
> +			if (ret<  0)
> +				return ret;
> +
> +			ret = ad9523_vco_out_map(indio_dev, chan->channel_num,
> +					   chan->use_alt_clock_src);
> +			if (ret<  0)
> +				return ret;
> +
> +			st->ad9523_channels[i].type = IIO_ALTVOLTAGE;
> +			st->ad9523_channels[i].output = 1;
> +			st->ad9523_channels[i].indexed = 1;
> +			st->ad9523_channels[i].channel = chan->channel_num;
> +			st->ad9523_channels[i].extend_name =
> +				chan->extended_name;
> +			st->ad9523_channels[i].info_mask =
> +				IIO_CHAN_INFO_RAW_SEPARATE_BIT |
> +				IIO_CHAN_INFO_PHASE_SEPARATE_BIT |
> +				IIO_CHAN_INFO_FREQUENCY_SEPARATE_BIT;
> +		}
> +	}
> +
looks rather like a for_each_clear_bit.
> +	for (i = 0; i<  AD9523_NUM_CHAN; i++)
> +		if (!(active_mask&  (1<<  i)))
> +			ad9523_write(indio_dev,
> +				     AD9523_CHANNEL_CLOCK_DIST(i),
> +				     AD9523_CLK_DIST_DRIVER_MODE(TRISTATE) |
> +				     AD9523_CLK_DIST_PWR_DOWN_EN);
> +
> +	ret = ad9523_write(indio_dev, AD9523_POWER_DOWN_CTRL, 0);
> +	if (ret<  0)
> +		return ret;
> +
> +	ret = ad9523_write(indio_dev, AD9523_STATUS_SIGNALS,
> +			   AD9523_STATUS_MONITOR_01_PLL12_LOCKED);
> +	if (ret<  0)
> +		return ret;
> +
> +	ret = ad9523_io_update(indio_dev);
> +	if (ret<  0)
> +		return ret;
> +
> +	return 0;
> +}
> +
> +static int __devinit ad9523_probe(struct spi_device *spi)
> +{
> +	struct ad9523_platform_data *pdata = spi->dev.platform_data;
> +	struct iio_dev *indio_dev;
> +	struct ad9523_state *st;
> +	int ret;
> +
> +	if (!pdata) {
> +		dev_err(&spi->dev, "no platform data?\n");
> +		return -EINVAL;
> +	}
> +
> +	indio_dev = iio_device_alloc(sizeof(*st));
> +	if (indio_dev == NULL)
> +		return -ENOMEM;
> +
> +	st = iio_priv(indio_dev);
> +
> +	st->reg = regulator_get(&spi->dev, "vcc");
> +	if (!IS_ERR(st->reg)) {
> +		ret = regulator_enable(st->reg);
> +		if (ret)
> +			goto error_put_reg;
> +	}
> +
> +	spi_set_drvdata(spi, indio_dev);
> +	st->spi = spi;
> +	st->pdata = pdata;
> +
> +	indio_dev->dev.parent =&spi->dev;
> +	indio_dev->name = (pdata->name[0] != 0) ? pdata->name :
> +			  spi_get_device_id(spi)->name;
> +	indio_dev->info =&ad9523_info;
> +	indio_dev->modes = INDIO_DIRECT_MODE;
> +	indio_dev->channels = st->ad9523_channels;
> +	indio_dev->num_channels = pdata->num_channels;
> +
> +	ret = ad9523_setup(indio_dev);
> +	if (ret<  0)
> +		goto error_disable_reg;
> +
> +	ret = iio_device_register(indio_dev);
> +	if (ret)
> +		goto error_disable_reg;
> +
> +	dev_info(&spi->dev, "probed %s\n", indio_dev->name);
> +
> +	return 0;
> +
> +error_disable_reg:
> +	if (!IS_ERR(st->reg))
> +		regulator_disable(st->reg);
> +error_put_reg:
> +	if (!IS_ERR(st->reg))
> +		regulator_put(st->reg);
> +
> +	iio_device_free(indio_dev);
> +
> +	return ret;
> +}
> +
> +static int __devexit ad9523_remove(struct spi_device *spi)
> +{
> +	struct iio_dev *indio_dev = spi_get_drvdata(spi);
> +	struct ad9523_state *st = iio_priv(indio_dev);
> +	struct regulator *reg = st->reg;
> +
> +	iio_device_unregister(indio_dev);
> +
st is still available so I'd not bother with the temporary for reg.
Personal preference though.
> +	if (!IS_ERR(reg)) {
> +		regulator_disable(reg);
> +		regulator_put(reg);
> +	}
> +
> +	iio_device_free(indio_dev);
> +
> +	return 0;
> +}
> +
> +static const struct spi_device_id ad9523_id[] = {
> +	{"ad9523", 9523},
> +	{}
> +};
> +MODULE_DEVICE_TABLE(spi, ad9523_id);
> +
> +static struct spi_driver ad9523_driver = {
> +	.driver = {
> +		.name	= "ad9523",
> +		.owner	= THIS_MODULE,
> +	},
> +	.probe		= ad9523_probe,
> +	.remove		= __devexit_p(ad9523_remove),
> +	.id_table	= ad9523_id,
> +};
> +module_spi_driver(ad9523_driver);
> +
> +MODULE_AUTHOR("Michael Hennerich<hennerich@blackfin.uclinux.org>");
> +MODULE_DESCRIPTION("Analog Devices AD9523 CLOCKDIST/PLL");
> +MODULE_LICENSE("GPL v2");
> diff --git a/include/linux/iio/frequency/ad9523.h b/include/linux/iio/frequency/ad9523.h
> new file mode 100644
> index 0000000..e878f09
> --- /dev/null
> +++ b/include/linux/iio/frequency/ad9523.h
> @@ -0,0 +1,195 @@
> +/*
> + * AD9523 SPI Low Jitter Clock Generator
> + *
> + * Copyright 2012 Analog Devices Inc.
> + *
> + * Licensed under the GPL-2.
> + */
> +
> +#ifndef IIO_FREQUENCY_AD9523_H_
> +#define IIO_FREQUENCY_AD9523_H_
> +
> +enum outp_drv_mode {
> +	TRISTATE,
> +	LVPECL_8mA,
> +	LVDS_4mA,
> +	LVDS_7mA,
> +	HSTL0_16mA,
> +	HSTL1_8mA,
> +	CMOS_CONF1,
> +	CMOS_CONF2,
> +	CMOS_CONF3,
> +	CMOS_CONF4,
> +	CMOS_CONF5,
> +	CMOS_CONF6,
> +	CMOS_CONF7,
> +	CMOS_CONF8,
> +	CMOS_CONF9
> +};
> +
> +enum ref_sel_mode {
> +	NONEREVERTIVE_STAY_ON_REFB,
> +	REVERT_TO_REFA,
> +	SELECT_REFA,
> +	SELECT_REFB,
> +	EXT_REF_SEL
> +};
> +
> +/**
> + * struct ad9523_channel_spec - Output channel configuration
> + *
> + * @channel_num: Output channel number.
> + * @divider_output_invert_en: Invert the polarity of the output clock.
> + * @sync_ignore_en: Ignore chip-level SYNC signal.
> + * @low_power_mode_en: Reduce power used in the differential output modes.
> + * @use_alt_clock_src: Channel divider uses alternative clk source.
> + * @output_dis: Disables, powers down the entire channel.
> + * @driver_mode: Output driver mode (logic level family).
> + * @divider_phase: Divider initial phase after a SYNC. Range 0..63
> +		   LSB = 1/2 of a period of the divider input clock.
> + * @channel_divider: 10-bit channel divider.
> + * @extended_name: Optional descriptive channel name.
> + */
> +
> +struct ad9523_channel_spec {
> +	unsigned		channel_num;
> +	bool			divider_output_invert_en;
> +	bool			sync_ignore_en;
> +	bool			low_power_mode_en;
> +				 /* CH0..CH3 VCXO, CH4..CH9 VCO2 */
> +	bool			use_alt_clock_src;
> +	bool			output_dis;
> +	enum outp_drv_mode	driver_mode;
> +	unsigned char		divider_phase;
> +	unsigned short		channel_divider;
> +	char			extended_name[16];
> +};
> +
> +enum pll1_rzero_resistor {
> +	RZERO_883_OHM,
> +	RZERO_677_OHM,
> +	RZERO_341_OHM,
> +	RZERO_135_OHM,
> +	RZERO_10_OHM,
> +	RZERO_USE_EXT_RES = 8,
> +};
> +
> +enum rpole2_resistor {
> +	RPOLE2_900_OHM,
> +	RPOLE2_450_OHM,
> +	RPOLE2_300_OHM,
> +	RPOLE2_225_OHM,
> +};
> +
> +enum rzero_resistor {
> +	RZERO_3250_OHM,
> +	RZERO_2750_OHM,
> +	RZERO_2250_OHM,
> +	RZERO_2100_OHM,
> +	RZERO_3000_OHM,
> +	RZERO_2500_OHM,
> +	RZERO_2000_OHM,
> +	RZERO_1850_OHM,
> +};
> +
> +enum cpole1_capacitor {
> +	CPOLE1_0_PF,
> +	CPOLE1_8_PF,
> +	CPOLE1_16_PF,
> +	CPOLE1_24_PF,
This underscore version would benefit from and explanatory comment!
> +	_CPOLE1_24_PF,
> +	CPOLE1_32_PF,
> +	CPOLE1_40_PF,
> +	CPOLE1_48_PF,
> +};
> +
> +/**
> + * struct ad9523_platform_data - platform specific information
> + *
> + * @vcxo_freq: External VCXO frequency in Hz
> + * @refa_diff_rcv_en: REFA differential/single-ended input selection.
> + * @refb_diff_rcv_en: REFB differential/single-ended input selection.
> + * @zd_in_diff_en: Zero Delay differential/single-ended input selection.
> + * @osc_in_diff_en: OSC differential/ single-ended input selection.
> + * @refa_cmos_neg_inp_en: REFA single-ended neg./pos. input enable.
> + * @refb_cmos_neg_inp_en: REFB single-ended neg./pos. input enable.
> + * @zd_in_cmos_neg_inp_en: Zero Delay single-ended neg./pos. input enable.
> + * @osc_in_cmos_neg_inp_en: OSC single-ended neg./pos. input enable.
> + * @refa_r_div: PLL1 10-bit REFA R divider.
> + * @refb_r_div: PLL1 10-bit REFB R divider.
> + * @pll1_feedback_div: PLL1 10-bit Feedback N divider.
> + * @pll1_charge_pump_current_nA: Magnitude of PLL1 charge pump current (nA).
> + * @zero_delay_mode_internal_en: Internal, external Zero Delay mode selection.
> + * @osc_in_feedback_en: PLL1 feedback path, local feedback from
> + *			the OSC_IN receiver or zero delay mode
> + * @pll1_loop_filter_rzero: PLL1 Loop Filter Zero Resistor selection.
> + * @ref_mode: Reference selection mode.
> + * @pll2_charge_pump_current_nA: Magnitude of PLL2 charge pump current (nA).
> + * @pll2_ndiv_a_cnt: PLL2 Feedback N-divider, A Counter, range 0..4.
> + * @pll2_ndiv_b_cnt: PLL2 Feedback N-divider, B Counter, range 0..63.
> + * @pll2_freq_doubler_en: PLL2 frequency doubler enable.
> + * @pll2_r2_div: PLL2 R2 divider, range 0..31.
> + * @pll2_vco_diff_m1: VCO1 divider, range 3..5.
> + * @pll2_vco_diff_m2: VCO2 divider, range 3..5.
> + * @rpole2: PLL2 loop filter Rpole resistor value.
> + * @rzero: PLL2 loop filter Rzero resistor value.
> + * @cpole1: PLL2 loop filter Cpole capacitor value.
> + * @rzero_bypass_en: PLL2 loop filter Rzero bypass enable.
> + * @num_channels: Array size of struct ad9523_channel_spec.
> + * @channels: Pointer to channel array.
> + * @name: Optional alternative iio device name.
> + */
> +
> +struct ad9523_platform_data {
> +	unsigned long vcxo_freq;
> +
> +	/* Differential/ Single-Ended Input Configuration */
> +	bool				refa_diff_rcv_en;
> +	bool				refb_diff_rcv_en;
> +	bool				zd_in_diff_en;
> +	bool				osc_in_diff_en;
> +
> +	/*
> +	 * Valid if differential input disabled
> +	 * if false defaults to pos input
> +	 */
> +	bool				refa_cmos_neg_inp_en;
> +	bool				refb_cmos_neg_inp_en;
> +	bool				zd_in_cmos_neg_inp_en;
> +	bool				osc_in_cmos_neg_inp_en;
> +
> +	/* PLL1 Setting */
> +	unsigned short			refa_r_div;
> +	unsigned short			refb_r_div;
> +	unsigned short			pll1_feedback_div;
> +	unsigned short			pll1_charge_pump_current_nA;
> +	bool				zero_delay_mode_internal_en;
> +	bool				osc_in_feedback_en;
> +	enum pll1_rzero_resistor	pll1_loop_filter_rzero;
> +
> +	/* Reference */
> +	enum ref_sel_mode		ref_mode;
> +
> +	/* PLL2 Setting */
> +	unsigned int			pll2_charge_pump_current_nA;
> +	unsigned char			pll2_ndiv_a_cnt;
> +	unsigned char			pll2_ndiv_b_cnt;
> +	bool				pll2_freq_doubler_en;
> +	unsigned char			pll2_r2_div;
> +	unsigned char			pll2_vco_diff_m1; /* 3..5 */
> +	unsigned char			pll2_vco_diff_m2; /* 3..5 */
> +
> +	/* Loop Filter PLL2 */
> +	enum rpole2_resistor		rpole2;
> +	enum rzero_resistor		rzero;
> +	enum cpole1_capacitor		cpole1;
> +	bool				rzero_bypass_en;
> +
> +	/* Output Channel Configuration */
> +	int				num_channels;
> +	struct ad9523_channel_spec	*channels;
> +
> +	char				name[SPI_NAME_SIZE];
> +};
> +
> +#endif /* IIO_FREQUENCY_AD9523_H_ */


^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH 2/2] iio: frequency: New driver for Analog Devices ADF4350/ADF4351 Wideband Synthesizers
  2012-05-21 14:58 ` [PATCH 2/2] iio: frequency: New driver for Analog Devices ADF4350/ADF4351 Wideband Synthesizers michael.hennerich
@ 2012-05-24 13:19   ` Jonathan Cameron
  0 siblings, 0 replies; 6+ messages in thread
From: Jonathan Cameron @ 2012-05-24 13:19 UTC (permalink / raw)
  To: michael.hennerich; +Cc: linux-iio, device-drivers-devel

On 5/21/2012 3:58 PM, michael.hennerich@analog.com wrote:
> From: Michael Hennerich<michael.hennerich@analog.com>
>
> Changes since V1:
> Apply Jonathan's review feedback:
>      Introduce and use IIO_ALTVOLTAGE.
>      Fix up comments and documentation.
>      Remove dead code.
>      Reorder some code fragments.
>      Add missing iio_device_free.
>
> Convert to new API.
> Fix-up out of staging includes.
> Removed pll_locked attribute.
>
> Changes since V2:
> Use module_spi_driver.
> adf4350_remove: move gpio_free after regulator.
> target patch to drivers/iio
Looking good.
>
> Signed-off-by: Michael Hennerich<michael.hennerich@analog.com>
Acked-by: Jonathan Cameron <jic23@kernel.org>

> ---
>   .../ABI/testing/sysfs-bus-iio-frequency-adf4350    |   21 +
>   drivers/iio/frequency/Kconfig                      |   18 +
>   drivers/iio/frequency/Makefile                     |    1 +
>   drivers/iio/frequency/adf4350.c                    |  478 ++++++++++++++++++++
>   include/linux/iio/frequency/adf4350.h              |  130 ++++++
>   5 files changed, 648 insertions(+), 0 deletions(-)
>   create mode 100644 Documentation/ABI/testing/sysfs-bus-iio-frequency-adf4350
>   create mode 100644 drivers/iio/frequency/adf4350.c
>   create mode 100644 include/linux/iio/frequency/adf4350.h
>
> diff --git a/Documentation/ABI/testing/sysfs-bus-iio-frequency-adf4350 b/Documentation/ABI/testing/sysfs-bus-iio-frequency-adf4350
> new file mode 100644
> index 0000000..d89aded
> --- /dev/null
> +++ b/Documentation/ABI/testing/sysfs-bus-iio-frequency-adf4350
> @@ -0,0 +1,21 @@
> +What:		/sys/bus/iio/devices/iio:deviceX/out_altvoltageY_frequency_resolution
> +KernelVersion:	3.4.0
> +Contact:	linux-iio@vger.kernel.org
> +Description:
> +		Stores channel Y frequency resolution/channel spacing in Hz.
> +		The value given directly influences the MODULUS used by
> +		the fractional-N PLL. It is assumed that the algorithm
> +		that is used to compute the various dividers, is able to
> +		generate proper values for multiples of channel spacing.
> +
> +What:		/sys/bus/iio/devices/iio:deviceX/out_altvoltageY_refin_frequency
> +KernelVersion:	3.4.0
> +Contact:	linux-iio@vger.kernel.org
> +Description:
> +		Sets channel Y REFin frequency in Hz. In some clock chained
> +		applications, the reference frequency used by the PLL may
> +		change during runtime. This attribute allows the user to
> +		adjust the reference frequency accordingly.
> +		The value written has no effect until out_altvoltageY_frequency
> +		is updated. Consider to use out_altvoltageY_powerdown to power
> +		down the PLL and it's RFOut buffers during REFin changes.
> diff --git a/drivers/iio/frequency/Kconfig b/drivers/iio/frequency/Kconfig
> index 0458c92..6aaa33e 100644
> --- a/drivers/iio/frequency/Kconfig
> +++ b/drivers/iio/frequency/Kconfig
> @@ -20,4 +20,22 @@ config AD9523
>   	  module will be called ad9523.
>
>   endmenu
> +
> +#
> +# Phase-Locked Loop (PLL) frequency synthesizers
> +#
> +
> +menu "Phase-Locked Loop (PLL) frequency synthesizers"
> +
> +config ADF4350
> +	tristate "Analog Devices ADF4350/ADF4351 Wideband Synthesizers"
> +	depends on SPI
> +	help
> +	  Say yes here to build support for Analog Devices  ADF4350/ADF4351
> +	  Wideband Synthesizers. The driver provides direct access via sysfs.
> +
> +	  To compile this driver as a module, choose M here: the
> +	  module will be called adf4350.
> +
> +endmenu
>   endmenu
> diff --git a/drivers/iio/frequency/Makefile b/drivers/iio/frequency/Makefile
> index 1b5b224..00d26e5 100644
> --- a/drivers/iio/frequency/Makefile
> +++ b/drivers/iio/frequency/Makefile
> @@ -3,3 +3,4 @@
>   #
>
>   obj-$(CONFIG_AD9523) += ad9523.o
> +obj-$(CONFIG_ADF4350) += adf4350.o
> diff --git a/drivers/iio/frequency/adf4350.c b/drivers/iio/frequency/adf4350.c
> new file mode 100644
> index 0000000..fd4c850
> --- /dev/null
> +++ b/drivers/iio/frequency/adf4350.c
> @@ -0,0 +1,478 @@
> +/*
> + * ADF4350/ADF4351 SPI Wideband Synthesizer driver
> + *
> + * Copyright 2012 Analog Devices Inc.
> + *
> + * Licensed under the GPL-2.
> + */
> +
> +#include<linux/device.h>
> +#include<linux/kernel.h>
> +#include<linux/slab.h>
> +#include<linux/sysfs.h>
> +#include<linux/spi/spi.h>
> +#include<linux/regulator/consumer.h>
> +#include<linux/err.h>
> +#include<linux/module.h>
> +#include<linux/gcd.h>
> +#include<linux/gpio.h>
> +#include<asm/div64.h>
> +
> +#include<linux/iio/iio.h>
> +#include<linux/iio/sysfs.h>
> +#include<linux/iio/frequency/adf4350.h>
> +
> +enum {
> +	ADF4350_FREQ,
> +	ADF4350_FREQ_REFIN,
> +	ADF4350_FREQ_RESOLUTION,
> +	ADF4350_PWRDOWN,
> +};
> +
> +struct adf4350_state {
> +	struct spi_device		*spi;
> +	struct regulator		*reg;
> +	struct adf4350_platform_data	*pdata;
> +	unsigned long			clkin;
> +	unsigned long			chspc; /* Channel Spacing */
> +	unsigned long			fpfd; /* Phase Frequency Detector */
> +	unsigned long			min_out_freq;
> +	unsigned			r0_fract;
> +	unsigned			r0_int;
> +	unsigned			r1_mod;
> +	unsigned			r4_rf_div_sel;
> +	unsigned long			regs[6];
> +	unsigned long			regs_hw[6];
> +
> +	/*
> +	 * DMA (thus cache coherency maintenance) requires the
> +	 * transfer buffers to live in their own cache lines.
> +	 */
> +	__be32				val ____cacheline_aligned;
> +};
> +
> +static struct adf4350_platform_data default_pdata = {
> +	.clkin = 122880000,
> +	.channel_spacing = 10000,
> +	.r2_user_settings = ADF4350_REG2_PD_POLARITY_POS,
> +			    ADF4350_REG2_CHARGE_PUMP_CURR_uA(2500),
> +	.r3_user_settings = ADF4350_REG3_12BIT_CLKDIV_MODE(0),
> +	.r4_user_settings = ADF4350_REG4_OUTPUT_PWR(3) |
> +			    ADF4350_REG4_MUTE_TILL_LOCK_EN,
> +	.gpio_lock_detect = -1,
> +};
> +
> +static int adf4350_sync_config(struct adf4350_state *st)
> +{
> +	int ret, i, doublebuf = 0;
> +
> +	for (i = ADF4350_REG5; i>= ADF4350_REG0; i--) {
> +		if ((st->regs_hw[i] != st->regs[i]) ||
> +			((i == ADF4350_REG0)&&  doublebuf)) {
> +
> +			switch (i) {
> +			case ADF4350_REG1:
> +			case ADF4350_REG4:
> +				doublebuf = 1;
> +				break;
> +			}
> +
> +			st->val  = cpu_to_be32(st->regs[i] | i);
> +			ret = spi_write(st->spi,&st->val, 4);
> +			if (ret<  0)
> +				return ret;
> +			st->regs_hw[i] = st->regs[i];
> +			dev_dbg(&st->spi->dev, "[%d] 0x%X\n",
> +				i, (u32)st->regs[i] | i);
> +		}
> +	}
> +	return 0;
> +}
> +
> +static int adf4350_reg_access(struct iio_dev *indio_dev,
> +			      unsigned reg, unsigned writeval,
> +			      unsigned *readval)
> +{
> +	struct adf4350_state *st = iio_priv(indio_dev);
> +	int ret;
> +
> +	if (reg>  ADF4350_REG5)
> +		return -EINVAL;
> +
> +	mutex_lock(&indio_dev->mlock);
> +	if (readval == NULL) {
> +		st->regs[reg] = writeval&  ~(BIT(0) | BIT(1) | BIT(2));
> +		ret = adf4350_sync_config(st);
> +	} else {
> +		*readval =  st->regs_hw[reg];
> +		ret = 0;
> +	}
> +	mutex_unlock(&indio_dev->mlock);
> +
> +	return ret;
> +}
> +
> +static int adf4350_tune_r_cnt(struct adf4350_state *st, unsigned short r_cnt)
> +{
> +	struct adf4350_platform_data *pdata = st->pdata;
> +
> +	do {
> +		r_cnt++;
> +		st->fpfd = (st->clkin * (pdata->ref_doubler_en ? 2 : 1)) /
> +			   (r_cnt * (pdata->ref_div2_en ? 2 : 1));
> +	} while (st->fpfd>  ADF4350_MAX_FREQ_PFD);
> +
> +	return r_cnt;
> +}
> +
> +static int adf4350_set_freq(struct adf4350_state *st, unsigned long long freq)
> +{
> +	struct adf4350_platform_data *pdata = st->pdata;
> +	u64 tmp;
> +	u32 div_gcd, prescaler;
> +	u16 mdiv, r_cnt = 0;
> +	u8 band_sel_div;
> +
> +	if (freq>  ADF4350_MAX_OUT_FREQ || freq<  st->min_out_freq)
> +		return -EINVAL;
> +
> +	if (freq>  ADF4350_MAX_FREQ_45_PRESC) {
> +		prescaler = ADF4350_REG1_PRESCALER;
> +		mdiv = 75;
> +	} else {
> +		prescaler = 0;
> +		mdiv = 23;
> +	}
> +
> +	st->r4_rf_div_sel = 0;
> +
> +	while (freq<  ADF4350_MIN_VCO_FREQ) {
> +		freq<<= 1;
> +		st->r4_rf_div_sel++;
> +	}
> +
> +	/*
> +	 * Allow a predefined reference division factor
> +	 * if not set, compute our own
> +	 */
> +	if (pdata->ref_div_factor)
> +		r_cnt = pdata->ref_div_factor - 1;
> +
> +	do  {
> +		r_cnt = adf4350_tune_r_cnt(st, r_cnt);
> +
> +		st->r1_mod = st->fpfd / st->chspc;
> +		while (st->r1_mod>  ADF4350_MAX_MODULUS) {
> +			r_cnt = adf4350_tune_r_cnt(st, r_cnt);
> +			st->r1_mod = st->fpfd / st->chspc;
> +		}
> +
> +		tmp = freq * (u64)st->r1_mod + (st->fpfd>  1);
> +		do_div(tmp, st->fpfd); /* Div round closest (n + d/2)/d */
> +		st->r0_fract = do_div(tmp, st->r1_mod);
> +		st->r0_int = tmp;
> +	} while (mdiv>  st->r0_int);
> +
> +	band_sel_div = DIV_ROUND_UP(st->fpfd, ADF4350_MAX_BANDSEL_CLK);
> +
> +	if (st->r0_fract&&  st->r1_mod) {
> +		div_gcd = gcd(st->r1_mod, st->r0_fract);
> +		st->r1_mod /= div_gcd;
> +		st->r0_fract /= div_gcd;
> +	} else {
> +		st->r0_fract = 0;
> +		st->r1_mod = 1;
> +	}
> +
> +	dev_dbg(&st->spi->dev, "VCO: %llu Hz, PFD %lu Hz\n"
> +		"REF_DIV %d, R0_INT %d, R0_FRACT %d\n"
> +		"R1_MOD %d, RF_DIV %d\nPRESCALER %s, BAND_SEL_DIV %d\n",
> +		freq, st->fpfd, r_cnt, st->r0_int, st->r0_fract, st->r1_mod,
> +		1<<  st->r4_rf_div_sel, prescaler ? "8/9" : "4/5",
> +		band_sel_div);
> +
> +	st->regs[ADF4350_REG0] = ADF4350_REG0_INT(st->r0_int) |
> +				 ADF4350_REG0_FRACT(st->r0_fract);
> +
> +	st->regs[ADF4350_REG1] = ADF4350_REG1_PHASE(0) |
> +				 ADF4350_REG1_MOD(st->r1_mod) |
> +				 prescaler;
> +
> +	st->regs[ADF4350_REG2] =
> +		ADF4350_REG2_10BIT_R_CNT(r_cnt) |
> +		ADF4350_REG2_DOUBLE_BUFF_EN |
> +		(pdata->ref_doubler_en ? ADF4350_REG2_RMULT2_EN : 0) |
> +		(pdata->ref_div2_en ? ADF4350_REG2_RDIV2_EN : 0) |
> +		(pdata->r2_user_settings&  (ADF4350_REG2_PD_POLARITY_POS |
> +		ADF4350_REG2_LDP_6ns | ADF4350_REG2_LDF_INT_N |
> +		ADF4350_REG2_CHARGE_PUMP_CURR_uA(5000) |
> +		ADF4350_REG2_MUXOUT(0x7) | ADF4350_REG2_NOISE_MODE(0x9)));
> +
> +	st->regs[ADF4350_REG3] = pdata->r3_user_settings&
> +				 (ADF4350_REG3_12BIT_CLKDIV(0xFFF) |
> +				 ADF4350_REG3_12BIT_CLKDIV_MODE(0x3) |
> +				 ADF4350_REG3_12BIT_CSR_EN |
> +				 ADF4351_REG3_CHARGE_CANCELLATION_EN |
> +				 ADF4351_REG3_ANTI_BACKLASH_3ns_EN |
> +				 ADF4351_REG3_BAND_SEL_CLOCK_MODE_HIGH);
> +
> +	st->regs[ADF4350_REG4] =
> +		ADF4350_REG4_FEEDBACK_FUND |
> +		ADF4350_REG4_RF_DIV_SEL(st->r4_rf_div_sel) |
> +		ADF4350_REG4_8BIT_BAND_SEL_CLKDIV(band_sel_div) |
> +		ADF4350_REG4_RF_OUT_EN |
> +		(pdata->r4_user_settings&
> +		(ADF4350_REG4_OUTPUT_PWR(0x3) |
> +		ADF4350_REG4_AUX_OUTPUT_PWR(0x3) |
> +		ADF4350_REG4_AUX_OUTPUT_EN |
> +		ADF4350_REG4_AUX_OUTPUT_FUND |
> +		ADF4350_REG4_MUTE_TILL_LOCK_EN));
> +
> +	st->regs[ADF4350_REG5] = ADF4350_REG5_LD_PIN_MODE_DIGITAL;
> +
> +	return adf4350_sync_config(st);
> +}
> +
> +static ssize_t adf4350_write(struct iio_dev *indio_dev,
> +				    uintptr_t private,
> +				    const struct iio_chan_spec *chan,
> +				    const char *buf, size_t len)
> +{
> +	struct adf4350_state *st = iio_priv(indio_dev);
> +	unsigned long long readin;
> +	int ret;
> +
> +	ret = kstrtoull(buf, 10,&readin);
> +	if (ret)
> +		return ret;
> +
> +	mutex_lock(&indio_dev->mlock);
> +	switch ((u32)private) {
> +	case ADF4350_FREQ:
> +		ret = adf4350_set_freq(st, readin);
> +		break;
> +	case ADF4350_FREQ_REFIN:
> +		if (readin>  ADF4350_MAX_FREQ_REFIN)
> +			ret = -EINVAL;
> +		else
> +			st->clkin = readin;
> +		break;
> +	case ADF4350_FREQ_RESOLUTION:
> +		if (readin == 0)
> +			ret = -EINVAL;
> +		else
> +			st->chspc = readin;
> +		break;
> +	case ADF4350_PWRDOWN:
> +		if (readin)
> +			st->regs[ADF4350_REG2] |= ADF4350_REG2_POWER_DOWN_EN;
> +		else
> +			st->regs[ADF4350_REG2]&= ~ADF4350_REG2_POWER_DOWN_EN;
> +
> +		adf4350_sync_config(st);
> +		break;
> +	default:
> +		ret = -ENODEV;
> +	}
> +	mutex_unlock(&indio_dev->mlock);
> +
> +	return ret ? ret : len;
> +}
> +
> +static ssize_t adf4350_read(struct iio_dev *indio_dev,
> +				   uintptr_t private,
> +				   const struct iio_chan_spec *chan,
> +				   char *buf)
> +{
> +	struct adf4350_state *st = iio_priv(indio_dev);
> +	unsigned long long val;
> +	int ret = 0;
> +
> +	mutex_lock(&indio_dev->mlock);
> +	switch ((u32)private) {
> +	case ADF4350_FREQ:
> +		val = (u64)((st->r0_int * st->r1_mod) + st->r0_fract) *
> +			(u64)st->fpfd;
> +		do_div(val, st->r1_mod * (1<<  st->r4_rf_div_sel));
> +		/* PLL unlocked? return error */
> +		if (gpio_is_valid(st->pdata->gpio_lock_detect))
> +			if (!gpio_get_value(st->pdata->gpio_lock_detect)) {
> +				dev_dbg(&st->spi->dev, "PLL un-locked\n");
> +				ret = -EBUSY;
> +			}
> +		break;
> +	case ADF4350_FREQ_REFIN:
> +		val = st->clkin;
> +		break;
> +	case ADF4350_FREQ_RESOLUTION:
> +		val = st->chspc;
> +		break;
> +	case ADF4350_PWRDOWN:
> +		val = !!(st->regs[ADF4350_REG2]&  ADF4350_REG2_POWER_DOWN_EN);
> +		break;
> +	}
> +	mutex_unlock(&indio_dev->mlock);
> +
> +	return ret<  0 ? ret : sprintf(buf, "%llu\n", val);
> +}
> +
> +#define _ADF4350_EXT_INFO(_name, _ident) { \
> +	.name = _name, \
> +	.read = adf4350_read, \
> +	.write = adf4350_write, \
> +	.private = _ident, \
> +}
> +
> +static const struct iio_chan_spec_ext_info adf4350_ext_info[] = {
> +	/* Ideally we use IIO_CHAN_INFO_FREQUENCY, but there are
> +	 * values>  2^32 in order to support the entire frequency range
> +	 * in Hz. Using scale is a bit ugly.
> +	 */
> +	_ADF4350_EXT_INFO("frequency", ADF4350_FREQ),
> +	_ADF4350_EXT_INFO("frequency_resolution", ADF4350_FREQ_RESOLUTION),
> +	_ADF4350_EXT_INFO("refin_frequency", ADF4350_FREQ_REFIN),
> +	_ADF4350_EXT_INFO("powerdown", ADF4350_PWRDOWN),
> +	{ },
> +};
> +
> +static const struct iio_chan_spec adf4350_chan = {
> +	.type = IIO_ALTVOLTAGE,
> +	.indexed = 1,
> +	.output = 1,
> +	.ext_info = adf4350_ext_info,
> +};
> +
> +static const struct iio_info adf4350_info = {
> +	.debugfs_reg_access =&adf4350_reg_access,
> +	.driver_module = THIS_MODULE,
> +};
> +
> +static int __devinit adf4350_probe(struct spi_device *spi)
> +{
> +	struct adf4350_platform_data *pdata = spi->dev.platform_data;
> +	struct iio_dev *indio_dev;
> +	struct adf4350_state *st;
> +	int ret;
> +
> +	if (!pdata) {
> +		dev_warn(&spi->dev, "no platform data? using default\n");
> +
> +		pdata =&default_pdata;
> +	}
> +
> +	indio_dev = iio_device_alloc(sizeof(*st));
> +	if (indio_dev == NULL)
> +		return -ENOMEM;
> +
> +	st = iio_priv(indio_dev);
> +
> +	st->reg = regulator_get(&spi->dev, "vcc");
> +	if (!IS_ERR(st->reg)) {
> +		ret = regulator_enable(st->reg);
> +		if (ret)
> +			goto error_put_reg;
> +	}
> +
> +	spi_set_drvdata(spi, indio_dev);
> +	st->spi = spi;
> +	st->pdata = pdata;
> +
> +	indio_dev->dev.parent =&spi->dev;
> +	indio_dev->name = (pdata->name[0] != 0) ? pdata->name :
> +		spi_get_device_id(spi)->name;
> +
> +	indio_dev->info =&adf4350_info;
> +	indio_dev->modes = INDIO_DIRECT_MODE;
> +	indio_dev->channels =&adf4350_chan;
> +	indio_dev->num_channels = 1;
> +
> +	st->chspc = pdata->channel_spacing;
> +	st->clkin = pdata->clkin;
> +
> +	st->min_out_freq = spi_get_device_id(spi)->driver_data == 4351 ?
> +		ADF4351_MIN_OUT_FREQ : ADF4350_MIN_OUT_FREQ;
> +
> +	memset(st->regs_hw, 0xFF, sizeof(st->regs_hw));
> +
> +	if (gpio_is_valid(pdata->gpio_lock_detect)) {
> +		ret = gpio_request(pdata->gpio_lock_detect, indio_dev->name);
> +		if (ret) {
> +			dev_err(&spi->dev, "fail to request lock detect GPIO-%d",
> +				pdata->gpio_lock_detect);
> +			goto error_disable_reg;
> +		}
> +		gpio_direction_input(pdata->gpio_lock_detect);
> +	}
> +
> +	if (pdata->power_up_frequency) {
> +		ret = adf4350_set_freq(st, pdata->power_up_frequency);
> +		if (ret)
> +			goto error_free_gpio;
> +	}
> +
> +	ret = iio_device_register(indio_dev);
> +	if (ret)
> +		goto error_free_gpio;
> +
> +	return 0;
> +
> +error_free_gpio:
> +	if (gpio_is_valid(pdata->gpio_lock_detect))
> +		gpio_free(pdata->gpio_lock_detect);
> +
> +error_disable_reg:
> +	if (!IS_ERR(st->reg))
> +		regulator_disable(st->reg);
> +error_put_reg:
> +	if (!IS_ERR(st->reg))
> +		regulator_put(st->reg);
> +
> +	iio_device_free(indio_dev);
> +
> +	return ret;
> +}
> +
> +static int __devexit adf4350_remove(struct spi_device *spi)
> +{
> +	struct iio_dev *indio_dev = spi_get_drvdata(spi);
> +	struct adf4350_state *st = iio_priv(indio_dev);
> +	struct regulator *reg = st->reg;
> +
> +	st->regs[ADF4350_REG2] |= ADF4350_REG2_POWER_DOWN_EN;
> +	adf4350_sync_config(st);
> +
> +	iio_device_unregister(indio_dev);
> +
> +	if (!IS_ERR(reg)) {
> +		regulator_disable(reg);
> +		regulator_put(reg);
> +	}
> +
> +	if (gpio_is_valid(st->pdata->gpio_lock_detect))
> +		gpio_free(st->pdata->gpio_lock_detect);
> +
> +	iio_device_free(indio_dev);
> +
> +	return 0;
> +}
> +
> +static const struct spi_device_id adf4350_id[] = {
> +	{"adf4350", 4350},
> +	{"adf4351", 4351},
> +	{}
> +};
> +
> +static struct spi_driver adf4350_driver = {
> +	.driver = {
> +		.name	= "adf4350",
> +		.owner	= THIS_MODULE,
> +	},
> +	.probe		= adf4350_probe,
> +	.remove		= __devexit_p(adf4350_remove),
> +	.id_table	= adf4350_id,
> +};
> +module_spi_driver(adf4350_driver);
> +
> +MODULE_AUTHOR("Michael Hennerich<hennerich@blackfin.uclinux.org>");
> +MODULE_DESCRIPTION("Analog Devices ADF4350/ADF4351 PLL");
> +MODULE_LICENSE("GPL v2");
> diff --git a/include/linux/iio/frequency/adf4350.h b/include/linux/iio/frequency/adf4350.h
> new file mode 100644
> index 0000000..1420015
> --- /dev/null
> +++ b/include/linux/iio/frequency/adf4350.h
> @@ -0,0 +1,130 @@
> +/*
> + * ADF4350/ADF4351 SPI PLL driver
> + *
> + * Copyright 2012 Analog Devices Inc.
> + *
> + * Licensed under the GPL-2.
> + */
> +
> +#ifndef IIO_PLL_ADF4350_H_
> +#define IIO_PLL_ADF4350_H_
> +
> +/* Registers */
> +#define ADF4350_REG0	0
> +#define ADF4350_REG1	1
> +#define ADF4350_REG2	2
> +#define ADF4350_REG3	3
> +#define ADF4350_REG4	4
> +#define ADF4350_REG5	5
> +
> +/* REG0 Bit Definitions */
> +#define ADF4350_REG0_FRACT(x)			(((x)&  0xFFF)<<  3)
> +#define ADF4350_REG0_INT(x)			(((x)&  0xFFFF)<<  15)
> +
> +/* REG1 Bit Definitions */
> +#define ADF4350_REG1_MOD(x)			(((x)&  0xFFF)<<  3)
> +#define ADF4350_REG1_PHASE(x)			(((x)&  0xFFF)<<  15)
> +#define ADF4350_REG1_PRESCALER			(1<<  27)
> +
> +/* REG2 Bit Definitions */
> +#define ADF4350_REG2_COUNTER_RESET_EN		(1<<  3)
> +#define ADF4350_REG2_CP_THREESTATE_EN		(1<<  4)
> +#define ADF4350_REG2_POWER_DOWN_EN		(1<<  5)
> +#define ADF4350_REG2_PD_POLARITY_POS		(1<<  6)
> +#define ADF4350_REG2_LDP_6ns			(1<<  7)
> +#define ADF4350_REG2_LDP_10ns			(0<<  7)
> +#define ADF4350_REG2_LDF_FRACT_N		(0<<  8)
> +#define ADF4350_REG2_LDF_INT_N			(1<<  8)
> +#define ADF4350_REG2_CHARGE_PUMP_CURR_uA(x)	(((((x)-312) / 312)&  0xF)<<  9)
> +#define ADF4350_REG2_DOUBLE_BUFF_EN		(1<<  13)
> +#define ADF4350_REG2_10BIT_R_CNT(x)		((x)<<  14)
> +#define ADF4350_REG2_RDIV2_EN			(1<<  24)
> +#define ADF4350_REG2_RMULT2_EN			(1<<  25)
> +#define ADF4350_REG2_MUXOUT(x)			((x)<<  26)
> +#define ADF4350_REG2_NOISE_MODE(x)		((x)<<  29)
> +#define ADF4350_MUXOUT_THREESTATE		0
> +#define ADF4350_MUXOUT_DVDD			1
> +#define ADF4350_MUXOUT_GND			2
> +#define ADF4350_MUXOUT_R_DIV_OUT		3
> +#define ADF4350_MUXOUT_N_DIV_OUT		4
> +#define ADF4350_MUXOUT_ANALOG_LOCK_DETECT	5
> +#define ADF4350_MUXOUT_DIGITAL_LOCK_DETECT	6
> +
> +/* REG3 Bit Definitions */
> +#define ADF4350_REG3_12BIT_CLKDIV(x)		((x)<<  3)
> +#define ADF4350_REG3_12BIT_CLKDIV_MODE(x)	((x)<<  16)
> +#define ADF4350_REG3_12BIT_CSR_EN		(1<<  18)
> +#define ADF4351_REG3_CHARGE_CANCELLATION_EN	(1<<  21)
> +#define ADF4351_REG3_ANTI_BACKLASH_3ns_EN	(1<<  22)
> +#define ADF4351_REG3_BAND_SEL_CLOCK_MODE_HIGH	(1<<  23)
> +
> +/* REG4 Bit Definitions */
> +#define ADF4350_REG4_OUTPUT_PWR(x)		((x)<<  3)
> +#define ADF4350_REG4_RF_OUT_EN			(1<<  5)
> +#define ADF4350_REG4_AUX_OUTPUT_PWR(x)		((x)<<  6)
> +#define ADF4350_REG4_AUX_OUTPUT_EN		(1<<  8)
> +#define ADF4350_REG4_AUX_OUTPUT_FUND		(1<<  9)
> +#define ADF4350_REG4_AUX_OUTPUT_DIV		(0<<  9)
> +#define ADF4350_REG4_MUTE_TILL_LOCK_EN		(1<<  10)
> +#define ADF4350_REG4_VCO_PWRDOWN_EN		(1<<  11)
> +#define ADF4350_REG4_8BIT_BAND_SEL_CLKDIV(x)	((x)<<  12)
> +#define ADF4350_REG4_RF_DIV_SEL(x)		((x)<<  20)
> +#define ADF4350_REG4_FEEDBACK_DIVIDED		(0<<  23)
> +#define ADF4350_REG4_FEEDBACK_FUND		(1<<  23)
> +
> +/* REG5 Bit Definitions */
> +#define ADF4350_REG5_LD_PIN_MODE_LOW		(0<<  22)
> +#define ADF4350_REG5_LD_PIN_MODE_DIGITAL	(1<<  22)
> +#define ADF4350_REG5_LD_PIN_MODE_HIGH		(3<<  22)
> +
> +/* Specifications */
> +#define ADF4350_MAX_OUT_FREQ		4400000000ULL /* Hz */
> +#define ADF4350_MIN_OUT_FREQ		137500000 /* Hz */
> +#define ADF4351_MIN_OUT_FREQ		34375000 /* Hz */
> +#define ADF4350_MIN_VCO_FREQ		2200000000ULL /* Hz */
> +#define ADF4350_MAX_FREQ_45_PRESC	3000000000ULL /* Hz */
> +#define ADF4350_MAX_FREQ_PFD		32000000 /* Hz */
> +#define ADF4350_MAX_BANDSEL_CLK		125000 /* Hz */
> +#define ADF4350_MAX_FREQ_REFIN		250000000 /* Hz */
> +#define ADF4350_MAX_MODULUS		4095
> +
> +/*
> + * TODO: struct adf4350_platform_data needs to go into include/linux/iio
> + */

Err.. It is!
> +
> +/**
> + * struct adf4350_platform_data - platform specific information
> + * @name:		Optional device name.
> + * @clkin:		REFin frequency in Hz.
> + * @channel_spacing:	Channel spacing in Hz (influences MODULUS).
> + * @power_up_frequency:	Optional, If set in Hz the PLL tunes to the desired
> + *			frequency on probe.
> + * @ref_div_factor:	Optional, if set the driver skips dynamic calculation
> + *			and uses this default value instead.
> + * @ref_doubler_en:	Enables reference doubler.
> + * @ref_div2_en:	Enables reference divider.
> + * @r2_user_settings:	User defined settings for ADF4350/1 REGISTER_2.
> + * @r3_user_settings:	User defined settings for ADF4350/1 REGISTER_3.
> + * @r4_user_settings:	User defined settings for ADF4350/1 REGISTER_4.
> + * @gpio_lock_detect:	Optional, if set with a valid GPIO number,
> + *			pll lock state is tested upon read.
> + *			If not used - set to -1.
> + */
> +
> +struct adf4350_platform_data {
> +	char			name[32];
> +	unsigned long		clkin;
> +	unsigned long		channel_spacing;
> +	unsigned long long	power_up_frequency;
> +
> +	unsigned short		ref_div_factor; /* 10-bit R counter */
> +	bool			ref_doubler_en;
> +	bool			ref_div2_en;
> +
> +	unsigned		r2_user_settings;
> +	unsigned		r3_user_settings;
> +	unsigned		r4_user_settings;
> +	int			gpio_lock_detect;
> +};
> +
> +#endif /* IIO_PLL_ADF4350_H_ */


^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH 1/2] iio: frequency: New driver for AD9523 SPI Low Jitter Clock Generator
  2012-05-24 13:00 ` [PATCH 1/2] iio: frequency: New driver for AD9523 SPI Low Jitter Clock Generator Jonathan Cameron
@ 2012-05-29  9:08   ` Michael Hennerich
  0 siblings, 0 replies; 6+ messages in thread
From: Michael Hennerich @ 2012-05-29  9:08 UTC (permalink / raw)
  To: Jonathan Cameron; +Cc: linux-iio, device-drivers-devel

On 05/24/2012 03:00 PM, Jonathan Cameron wrote:
> On 5/21/2012 3:58 PM, michael.hennerich@analog.com wrote:
>> From: Michael Hennerich<michael.hennerich@analog.com>
>>
>> Changes since V1:
>>
>> Apply Jonathan's review feedback:
>> Revise device status attribute names, and split documentation into two sections.
>> Add additional comments, and fix indention issues.
>> Remove pointless zero initializations.
>> Revise return value handling.
>> Simplify some code sections.
>> Split store_eeprom and sync handling into separate functions.
>> Use strtobool where applicable.
>> Document platform data structures using kernel-doc style.
>>
>> Use dev_to_iio_dev
>> write_raw IIO_CHAN_INFO_FREQUENCY: Reject values<= 0
>> Make patch target drivers/iio
> Hi Michael,
>
> Couple of trivial bits inline.  Plenty of time for this so I'll
> take one last look when you've addressed those.
>
> I still don't like the immense opaque platform data needed for this.
> Feels like there ought to be something clearer and more generic, but
> I guess that's a job for another day.
>
> Jonathan
>> Signed-off-by: Michael Hennerich<michael.hennerich@analog.com>
>> ---
>>    .../ABI/testing/sysfs-bus-iio-frequency-ad9523     |   37 +
>>    drivers/iio/Kconfig                                |    1 +
>>    drivers/iio/Makefile                               |    1 +
>>    drivers/iio/frequency/Kconfig                      |   23 +
>>    drivers/iio/frequency/Makefile                     |    5 +
>>    drivers/iio/frequency/ad9523.c                     | 1059 ++++++++++++++++++++
>>    include/linux/iio/frequency/ad9523.h               |  195 ++++
>>    7 files changed, 1321 insertions(+), 0 deletions(-)
>>    create mode 100644 Documentation/ABI/testing/sysfs-bus-iio-frequency-ad9523
>>    create mode 100644 drivers/iio/frequency/Kconfig
>>    create mode 100644 drivers/iio/frequency/Makefile
>>    create mode 100644 drivers/iio/frequency/ad9523.c
>>    create mode 100644 include/linux/iio/frequency/ad9523.h
>>
>> diff --git a/Documentation/ABI/testing/sysfs-bus-iio-frequency-ad9523 b/Documentation/ABI/testing/sysfs-bus-iio-frequency-ad9523
>> new file mode 100644
>> index 0000000..2ce9c3f
>> --- /dev/null
>> +++ b/Documentation/ABI/testing/sysfs-bus-iio-frequency-ad9523
>> @@ -0,0 +1,37 @@
>> +What:                /sys/bus/iio/devices/iio:deviceX/pll2_feedback_clk_present
>> +What:                /sys/bus/iio/devices/iio:deviceX/pll2_reference_clk_present
>> +What:                /sys/bus/iio/devices/iio:deviceX/pll1_reference_clk_a_present
>> +What:                /sys/bus/iio/devices/iio:deviceX/pll1_reference_clk_b_present
>> +What:                /sys/bus/iio/devices/iio:deviceX/pll1_reference_clk_test_present
>> +What:                /sys/bus/iio/devices/iio:deviceX/vcxo_clk_present
>> +KernelVersion:       3.4.0
>> +Contact:     linux-iio@vger.kernel.org
>> +Description:
>> +             Reading returns either '1' or '0'.
>> +             '1' means that the clock in question is present.
>> +             '0' means that the clock is missing.
>> +
>> +What:                /sys/bus/iio/devices/iio:deviceX/pllY_locked
>> +KernelVersion:       3.4.0
>> +Contact:     linux-iio@vger.kernel.org
>> +Description:
>> +             Reading returns either '1' or '0'. '1' means that the
>> +             pllY is locked.
>> +
>> +What:                /sys/bus/iio/devices/iio:deviceX/store_eeprom
>> +KernelVersion:       3.4.0
>> +Contact:     linux-iio@vger.kernel.org
>> +Description:
>> +             Writing '1' stores the current device configuration into
>> +             on-chip EEPROM. After power-up or chip reset the device will
>> +             automatically load the saved configuration.
>> +
>> +What:                /sys/bus/iio/devices/iio:deviceX/sync_dividers
>> +KernelVersion:       3.4.0
>> +Contact:     linux-iio@vger.kernel.org
>> +Description:
>> +             Writing '1' triggers the clock distribution synchronization
>> +             functionality. All dividers are reset and the channels start
>> +             with their predefined phase offsets (out_altvoltageY_phase).
>> +             Writing this file has the effect as driving the external
>> +             /SYNC pin low.
>> diff --git a/drivers/iio/Kconfig b/drivers/iio/Kconfig
>> index 56eecef..ad09f67 100644
>> --- a/drivers/iio/Kconfig
>> +++ b/drivers/iio/Kconfig
>> @@ -50,5 +50,6 @@ config IIO_CONSUMERS_PER_TRIGGER
>>
>>    source "drivers/iio/adc/Kconfig"
>>    source "drivers/iio/amplifiers/Kconfig"
>> +source "drivers/iio/frequency/Kconfig"
>>
>>    endif # IIO
>> diff --git a/drivers/iio/Makefile b/drivers/iio/Makefile
>> index e425afd..014e0e4 100644
>> --- a/drivers/iio/Makefile
>> +++ b/drivers/iio/Makefile
>> @@ -11,3 +11,4 @@ obj-$(CONFIG_IIO_KFIFO_BUF) += kfifo_buf.o
>>
>>    obj-y += adc/
>>    obj-y += amplifiers/
>> +obj-y += frequency/
>> diff --git a/drivers/iio/frequency/Kconfig b/drivers/iio/frequency/Kconfig
>> new file mode 100644
>> index 0000000..0458c92
>> --- /dev/null
>> +++ b/drivers/iio/frequency/Kconfig
>> @@ -0,0 +1,23 @@
>> +#
>> +# Frequency
>> +#    Direct Digital Synthesis drivers (DDS)
>> +#    Clock Distribution device drivers
>> +#    Phase-Locked Loop (PLL) frequency synthesizers
>> +#
>> +
>> +menu "Frequency Synthesizers DDS/PLL"
>> +
>> +menu "Clock Generator/Distribution"
>> +
>> +config AD9523
>> +     tristate "Analog Devices AD9523 Low Jitter Clock Generator"
>> +     depends on SPI
>> +     help
>> +       Say yes here to build support for Analog Devices AD9523 Low Jitter
>> +       Clock Generator. The driver provides direct access via sysfs.
>> +
>> +       To compile this driver as a module, choose M here: the
>> +       module will be called ad9523.
>> +
>> +endmenu
>> +endmenu
>> diff --git a/drivers/iio/frequency/Makefile b/drivers/iio/frequency/Makefile
>> new file mode 100644
>> index 0000000..1b5b224
>> --- /dev/null
>> +++ b/drivers/iio/frequency/Makefile
>> @@ -0,0 +1,5 @@
>> +#
>> +# Makefile iio/frequency
>> +#
>> +
>> +obj-$(CONFIG_AD9523) += ad9523.o
>> diff --git a/drivers/iio/frequency/ad9523.c b/drivers/iio/frequency/ad9523.c
>> new file mode 100644
>> index 0000000..77a17de
>> --- /dev/null
>> +++ b/drivers/iio/frequency/ad9523.c
>> @@ -0,0 +1,1059 @@
>> +/*
>> + * AD9523 SPI Low Jitter Clock Generator
>> + *
>> + * Copyright 2012 Analog Devices Inc.
>> + *
>> + * Licensed under the GPL-2.
>> + */
>> +
>> +#include<linux/device.h>
>> +#include<linux/kernel.h>
>> +#include<linux/slab.h>
>> +#include<linux/sysfs.h>
>> +#include<linux/spi/spi.h>
>> +#include<linux/regulator/consumer.h>
>> +#include<linux/err.h>
>> +#include<linux/module.h>
>> +#include<linux/delay.h>
>> +
>> +#include<linux/iio/iio.h>
>> +#include<linux/iio/sysfs.h>
>> +#include<linux/iio/frequency/ad9523.h>
>> +
>> +#define AD9523_READ  (1<<   15)
>> +#define AD9523_WRITE (0<<   15)
>> +#define AD9523_CNT(x)        (((x) - 1)<<   13)
>> +#define AD9523_ADDR(x)       ((x)&   0xFFF)
>> +
>> +#define AD9523_R1B   (1<<   16)
>> +#define AD9523_R2B   (2<<   16)
>> +#define AD9523_R3B   (3<<   16)
>> +#define AD9523_TRANSF_LEN(x)                 ((x)>>   16)
>> +
>> +#define AD9523_SERIAL_PORT_CONFIG            (AD9523_R1B | 0x0)
>> +#define AD9523_VERSION_REGISTER                      (AD9523_R1B | 0x2)
>> +#define AD9523_PART_REGISTER                 (AD9523_R1B | 0x3)
>> +#define AD9523_READBACK_CTRL                 (AD9523_R1B | 0x4)
>> +
>> +#define AD9523_EEPROM_CUSTOMER_VERSION_ID    (AD9523_R2B | 0x6)
>> +
>> +#define AD9523_PLL1_REF_A_DIVIDER            (AD9523_R2B | 0x11)
>> +#define AD9523_PLL1_REF_B_DIVIDER            (AD9523_R2B | 0x13)
>> +#define AD9523_PLL1_REF_TEST_DIVIDER         (AD9523_R1B | 0x14)
>> +#define AD9523_PLL1_FEEDBACK_DIVIDER         (AD9523_R2B | 0x17)
>> +#define AD9523_PLL1_CHARGE_PUMP_CTRL         (AD9523_R2B | 0x19)
>> +#define AD9523_PLL1_INPUT_RECEIVERS_CTRL     (AD9523_R1B | 0x1A)
>> +#define AD9523_PLL1_REF_CTRL                 (AD9523_R1B | 0x1B)
>> +#define AD9523_PLL1_MISC_CTRL                        (AD9523_R1B | 0x1C)
>> +#define AD9523_PLL1_LOOP_FILTER_CTRL         (AD9523_R1B | 0x1D)
>> +
>> +#define AD9523_PLL2_CHARGE_PUMP                      (AD9523_R1B | 0xF0)
>> +#define AD9523_PLL2_FEEDBACK_DIVIDER_AB              (AD9523_R1B | 0xF1)
>> +#define AD9523_PLL2_CTRL                     (AD9523_R1B | 0xF2)
>> +#define AD9523_PLL2_VCO_CTRL                 (AD9523_R1B | 0xF3)
>> +#define AD9523_PLL2_VCO_DIVIDER                      (AD9523_R1B | 0xF4)
>> +#define AD9523_PLL2_LOOP_FILTER_CTRL         (AD9523_R2B | 0xF6)
>> +#define AD9523_PLL2_R2_DIVIDER                       (AD9523_R1B | 0xF7)
>> +
>> +#define AD9523_CHANNEL_CLOCK_DIST(ch)                (AD9523_R3B | (0x192 + 3 * ch))
>> +
>> +#define AD9523_PLL1_OUTPUT_CTRL                      (AD9523_R1B | 0x1BA)
>> +#define AD9523_PLL1_OUTPUT_CHANNEL_CTRL              (AD9523_R1B | 0x1BB)
>> +
>> +#define AD9523_READBACK_0                    (AD9523_R1B | 0x22C)
>> +#define AD9523_READBACK_1                    (AD9523_R1B | 0x22D)
>> +
>> +#define AD9523_STATUS_SIGNALS                        (AD9523_R3B | 0x232)
>> +#define AD9523_POWER_DOWN_CTRL                       (AD9523_R1B | 0x233)
>> +#define AD9523_IO_UPDATE                     (AD9523_R1B | 0x234)
>> +
>> +#define AD9523_EEPROM_DATA_XFER_STATUS               (AD9523_R1B | 0xB00)
>> +#define AD9523_EEPROM_ERROR_READBACK         (AD9523_R1B | 0xB01)
>> +#define AD9523_EEPROM_CTRL1                  (AD9523_R1B | 0xB02)
>> +#define AD9523_EEPROM_CTRL2                  (AD9523_R1B | 0xB03)
>> +
>> +/* AD9523_SERIAL_PORT_CONFIG */
>> +
>> +#define AD9523_SER_CONF_SDO_ACTIVE           (1<<   7)
>> +#define AD9523_SER_CONF_SOFT_RESET           (1<<   5)
>> +
>> +/* AD9523_READBACK_CTRL */
>> +#define AD9523_READBACK_CTRL_READ_BUFFERED   (1<<   0)
>> +
>> +/* AD9523_PLL1_CHARGE_PUMP_CTRL */
>> +#define AD9523_PLL1_CHARGE_PUMP_CURRENT_nA(x)        (((x) / 500)&   0x7F)
>> +#define AD9523_PLL1_CHARGE_PUMP_TRISTATE     (1<<   7)
>> +#define AD9523_PLL1_CHARGE_PUMP_MODE_NORMAL  (3<<   8)
>> +#define AD9523_PLL1_CHARGE_PUMP_MODE_PUMP_DOWN       (2<<   8)
>> +#define AD9523_PLL1_CHARGE_PUMP_MODE_PUMP_UP (1<<   8)
>> +#define AD9523_PLL1_CHARGE_PUMP_MODE_TRISTATE        (0<<   8)
>> +#define AD9523_PLL1_BACKLASH_PW_MIN          (0<<   10)
>> +#define AD9523_PLL1_BACKLASH_PW_LOW          (1<<   10)
>> +#define AD9523_PLL1_BACKLASH_PW_HIGH         (2<<   10)
>> +#define AD9523_PLL1_BACKLASH_PW_MAX          (3<<   10)
>> +
>> +/* AD9523_PLL1_INPUT_RECEIVERS_CTRL */
>> +#define AD9523_PLL1_REF_TEST_RCV_EN          (1<<   7)
>> +#define AD9523_PLL1_REFB_DIFF_RCV_EN         (1<<   6)
>> +#define AD9523_PLL1_REFA_DIFF_RCV_EN         (1<<   5)
>> +#define AD9523_PLL1_REFB_RCV_EN                      (1<<   4)
>> +#define AD9523_PLL1_REFA_RCV_EN                      (1<<   3)
>> +#define AD9523_PLL1_REFA_REFB_PWR_CTRL_EN    (1<<   2)
>> +#define AD9523_PLL1_OSC_IN_CMOS_NEG_INP_EN   (1<<   1)
>> +#define AD9523_PLL1_OSC_IN_DIFF_EN           (1<<   0)
>> +
>> +/* AD9523_PLL1_REF_CTRL */
>> +#define AD9523_PLL1_BYPASS_REF_TEST_DIV_EN   (1<<   7)
>> +#define AD9523_PLL1_BYPASS_FEEDBACK_DIV_EN   (1<<   6)
>> +#define AD9523_PLL1_ZERO_DELAY_MODE_INT              (1<<   5)
>> +#define AD9523_PLL1_ZERO_DELAY_MODE_EXT              (0<<   5)
>> +#define AD9523_PLL1_OSC_IN_PLL_FEEDBACK_EN   (1<<   4)
>> +#define AD9523_PLL1_ZD_IN_CMOS_NEG_INP_EN    (1<<   3)
>> +#define AD9523_PLL1_ZD_IN_DIFF_EN            (1<<   2)
>> +#define AD9523_PLL1_REFB_CMOS_NEG_INP_EN     (1<<   1)
>> +#define AD9523_PLL1_REFA_CMOS_NEG_INP_EN     (1<<   0)
>> +
>> +/* AD9523_PLL1_MISC_CTRL */
>> +#define AD9523_PLL1_REFB_INDEP_DIV_CTRL_EN   (1<<   7)
>> +#define AD9523_PLL1_OSC_CTRL_FAIL_VCC_BY2_EN (1<<   6)
>> +#define AD9523_PLL1_REF_MODE(x)                      ((x)<<   2)
>> +#define AD9523_PLL1_BYPASS_REFB_DIV          (1<<   1)
>> +#define AD9523_PLL1_BYPASS_REFA_DIV          (1<<   0)
>> +
>> +/* AD9523_PLL1_LOOP_FILTER_CTRL */
>> +#define AD9523_PLL1_LOOP_FILTER_RZERO(x)     ((x)&   0xF)
>> +
>> +/* AD9523_PLL2_CHARGE_PUMP */
>> +#define AD9523_PLL2_CHARGE_PUMP_CURRENT_nA(x)        ((x) / 3500)
>> +
>> +/* AD9523_PLL2_FEEDBACK_DIVIDER_AB */
>> +#define AD9523_PLL2_FB_NDIV_A_CNT(x)         (((x)&   0x3)<<   6)
>> +#define AD9523_PLL2_FB_NDIV_B_CNT(x)         (((x)&   0x3F)<<   0)
>> +#define AD9523_PLL2_FB_NDIV(a, b)            (4 * (b) + (a))
>> +
>> +/* AD9523_PLL2_CTRL */
>> +#define AD9523_PLL2_CHARGE_PUMP_MODE_NORMAL  (3<<   0)
>> +#define AD9523_PLL2_CHARGE_PUMP_MODE_PUMP_DOWN       (2<<   0)
>> +#define AD9523_PLL2_CHARGE_PUMP_MODE_PUMP_UP (1<<   0)
>> +#define AD9523_PLL2_CHARGE_PUMP_MODE_TRISTATE        (0<<   0)
>> +#define AD9523_PLL2_BACKLASH_PW_MIN          (0<<   2)
>> +#define AD9523_PLL2_BACKLASH_PW_LOW          (1<<   2)
>> +#define AD9523_PLL2_BACKLASH_PW_HIGH         (2<<   2)
>> +#define AD9523_PLL2_BACKLASH_PW_MAX          (3<<   1)
>> +#define AD9523_PLL2_BACKLASH_CTRL_EN         (1<<   4)
>> +#define AD9523_PLL2_FREQ_DOUBLER_EN          (1<<   5)
>> +#define AD9523_PLL2_LOCK_DETECT_PWR_DOWN_EN  (1<<   7)
>> +
>> +/* AD9523_PLL2_VCO_CTRL */
>> +#define AD9523_PLL2_VCO_CALIBRATE            (1<<   1)
>> +#define AD9523_PLL2_FORCE_VCO_MIDSCALE               (1<<   2)
>> +#define AD9523_PLL2_FORCE_REFERENCE_VALID    (1<<   3)
>> +#define AD9523_PLL2_FORCE_RELEASE_SYNC               (1<<   4)
>> +
>> +/* AD9523_PLL2_VCO_DIVIDER */
>> +#define AD9523_PLL2_VCO_DIV_M1(x)            ((((x) - 3)&   0x3)<<   0)
>> +#define AD9523_PLL2_VCO_DIV_M2(x)            ((((x) - 3)&   0x3)<<   4)
>> +#define AD9523_PLL2_VCO_DIV_M1_PWR_DOWN_EN   (1<<   2)
>> +#define AD9523_PLL2_VCO_DIV_M2_PWR_DOWN_EN   (1<<   6)
>> +
>> +/* AD9523_PLL2_LOOP_FILTER_CTRL */
>> +#define AD9523_PLL2_LOOP_FILTER_CPOLE1(x)    (((x)&   0x7)<<   0)
>> +#define AD9523_PLL2_LOOP_FILTER_RZERO(x)     (((x)&   0x7)<<   3)
>> +#define AD9523_PLL2_LOOP_FILTER_RPOLE2(x)    (((x)&   0x7)<<   6)
>> +#define AD9523_PLL2_LOOP_FILTER_RZERO_BYPASS_EN      (1<<   8)
>> +
>> +/* AD9523_PLL2_R2_DIVIDER */
>> +#define AD9523_PLL2_R2_DIVIDER_VAL(x)                (((x)&   0x1F)<<   0)
>> +
>> +/* AD9523_CHANNEL_CLOCK_DIST */
>> +#define AD9523_CLK_DIST_DIV_PHASE(x)         (((x)&   0x3F)<<   18)
>> +#define AD9523_CLK_DIST_DIV_PHASE_REV(x)     ((ret>>   18)&   0x3F)
>> +#define AD9523_CLK_DIST_DIV(x)                       ((((x) - 1)&   0x3FF)<<   8)
>> +#define AD9523_CLK_DIST_DIV_REV(x)           (((ret>>   8)&   0x3FF) + 1)
>> +#define AD9523_CLK_DIST_INV_DIV_OUTPUT_EN    (1<<   7)
>> +#define AD9523_CLK_DIST_IGNORE_SYNC_EN               (1<<   6)
>> +#define AD9523_CLK_DIST_PWR_DOWN_EN          (1<<   5)
>> +#define AD9523_CLK_DIST_LOW_PWR_MODE_EN              (1<<   4)
>> +#define AD9523_CLK_DIST_DRIVER_MODE(x)               (((x)&   0xF)<<   0)
>> +
>> +/* AD9523_PLL1_OUTPUT_CTRL */
>> +#define AD9523_PLL1_OUTP_CTRL_VCO_DIV_SEL_CH6_M2     (1<<   7)
>> +#define AD9523_PLL1_OUTP_CTRL_VCO_DIV_SEL_CH5_M2     (1<<   6)
>> +#define AD9523_PLL1_OUTP_CTRL_VCO_DIV_SEL_CH4_M2     (1<<   5)
>> +#define AD9523_PLL1_OUTP_CTRL_CMOS_DRV_WEAK          (1<<   4)
>> +#define AD9523_PLL1_OUTP_CTRL_OUTPUT_DIV_1           (0<<   0)
>> +#define AD9523_PLL1_OUTP_CTRL_OUTPUT_DIV_2           (1<<   0)
>> +#define AD9523_PLL1_OUTP_CTRL_OUTPUT_DIV_4           (2<<   0)
>> +#define AD9523_PLL1_OUTP_CTRL_OUTPUT_DIV_8           (4<<   0)
>> +#define AD9523_PLL1_OUTP_CTRL_OUTPUT_DIV_16          (8<<   0)
>> +
>> +/* AD9523_PLL1_OUTPUT_CHANNEL_CTRL */
>> +#define AD9523_PLL1_OUTP_CH_CTRL_OUTPUT_PWR_DOWN_EN  (1<<   7)
>> +#define AD9523_PLL1_OUTP_CH_CTRL_VCO_DIV_SEL_CH9_M2  (1<<   6)
>> +#define AD9523_PLL1_OUTP_CH_CTRL_VCO_DIV_SEL_CH8_M2  (1<<   5)
>> +#define AD9523_PLL1_OUTP_CH_CTRL_VCO_DIV_SEL_CH7_M2  (1<<   4)
>> +#define AD9523_PLL1_OUTP_CH_CTRL_VCXO_SRC_SEL_CH3    (1<<   3)
>> +#define AD9523_PLL1_OUTP_CH_CTRL_VCXO_SRC_SEL_CH2    (1<<   2)
>> +#define AD9523_PLL1_OUTP_CH_CTRL_VCXO_SRC_SEL_CH1    (1<<   1)
>> +#define AD9523_PLL1_OUTP_CH_CTRL_VCXO_SRC_SEL_CH0    (1<<   0)
>> +
>> +/* AD9523_READBACK_0 */
>> +#define AD9523_READBACK_0_STAT_PLL2_REF_CLK          (1<<   7)
>> +#define AD9523_READBACK_0_STAT_PLL2_FB_CLK           (1<<   6)
>> +#define AD9523_READBACK_0_STAT_VCXO                  (1<<   5)
>> +#define AD9523_READBACK_0_STAT_REF_TEST                      (1<<   4)
>> +#define AD9523_READBACK_0_STAT_REFB                  (1<<   3)
>> +#define AD9523_READBACK_0_STAT_REFA                  (1<<   2)
>> +#define AD9523_READBACK_0_STAT_PLL2_LD                       (1<<   1)
>> +#define AD9523_READBACK_0_STAT_PLL1_LD                       (1<<   0)
>> +
>> +/* AD9523_READBACK_1 */
>> +#define AD9523_READBACK_1_HOLDOVER_ACTIVE            (1<<   3)
>> +#define AD9523_READBACK_1_AUTOMODE_SEL_REFB          (1<<   2)
>> +#define AD9523_READBACK_1_VCO_CALIB_IN_PROGRESS              (1<<   0)
>> +
>> +/* AD9523_STATUS_SIGNALS */
>> +#define AD9523_STATUS_SIGNALS_SYNC_MAN_CTRL          (1<<   16)
>> +#define AD9523_STATUS_MONITOR_01_PLL12_LOCKED                (0x302)
>> +/* AD9523_POWER_DOWN_CTRL */
>> +#define AD9523_POWER_DOWN_CTRL_PLL1_PWR_DOWN         (1<<   2)
>> +#define AD9523_POWER_DOWN_CTRL_PLL2_PWR_DOWN         (1<<   1)
>> +#define AD9523_POWER_DOWN_CTRL_DIST_PWR_DOWN         (1<<   0)
>> +
>> +/* AD9523_IO_UPDATE */
>> +#define AD9523_IO_UPDATE_EN                          (1<<   0)
>> +
>> +/* AD9523_EEPROM_DATA_XFER_STATUS */
>> +#define AD9523_EEPROM_DATA_XFER_IN_PROGRESS          (1<<   0)
>> +
>> +/* AD9523_EEPROM_ERROR_READBACK */
>> +#define AD9523_EEPROM_ERROR_READBACK_FAIL            (1<<   0)
>> +
>> +/* AD9523_EEPROM_CTRL1 */
>> +#define AD9523_EEPROM_CTRL1_SOFT_EEPROM                      (1<<   1)
>> +#define AD9523_EEPROM_CTRL1_EEPROM_WRITE_PROT_DIS    (1<<   0)
>> +
>> +/* AD9523_EEPROM_CTRL2 */
>> +#define AD9523_EEPROM_CTRL2_REG2EEPROM                       (1<<   0)
>> +
>> +#define AD9523_NUM_CHAN                                      14
>> +#define AD9523_NUM_CHAN_ALT_CLK_SRC                  10
>> +
>> +/* Helpers to avoid excess line breaks */
>> +#define AD_IFE(_pde, _a, _b) ((pdata->_pde) ? _a : _b)
>> +#define AD_IF(_pde, _a) AD_IFE(_pde, _a, 0)
>> +
>> +enum {
>> +     AD9523_STAT_PLL1_LD,
>> +     AD9523_STAT_PLL2_LD,
>> +     AD9523_STAT_REFA,
>> +     AD9523_STAT_REFB,
>> +     AD9523_STAT_REF_TEST,
>> +     AD9523_STAT_VCXO,
>> +     AD9523_STAT_PLL2_FB_CLK,
>> +     AD9523_STAT_PLL2_REF_CLK,
>> +     AD9523_SYNC,
>> +     AD9523_EEPROM,
>> +};
>> +
>> +enum {
>> +     AD9523_VCO1,
>> +     AD9523_VCO2,
>> +     AD9523_VCXO,
>> +     AD9523_NUM_CLK_SRC,
>> +};
>> +
>> +struct ad9523_state {
>> +     struct spi_device               *spi;
>> +     struct regulator                *reg;
>> +     struct ad9523_platform_data     *pdata;
>> +     struct iio_chan_spec            ad9523_channels[AD9523_NUM_CHAN];
>> +
>> +     unsigned long           vcxo_freq;
>> +     unsigned long           vco_freq;
>> +     unsigned long           vco_out_freq[AD9523_NUM_CLK_SRC];
>> +     unsigned char           vco_out_map[AD9523_NUM_CHAN_ALT_CLK_SRC];
>> +
>> +     /*
>> +      * DMA (thus cache coherency maintenance) requires the
>> +      * transfer buffers to live in their own cache lines.
>> +      */
>> +     union {
>> +             __be32 d32;
>> +             u8 d8[4];
>> +     } data[2] ____cacheline_aligned;
>> +};
>> +
>> +static int ad9523_read(struct iio_dev *indio_dev, unsigned addr)
>> +{
>> +     struct ad9523_state *st = iio_priv(indio_dev);
>> +     struct spi_message m;
>> +     int ret;
>> +
>> +     /* We encode the register size 1..3 bytes into the register address.
>> +      * On transfer we get the size from the register datum, and make sure
>> +      * the result is properly aligned.
>> +      */
>> +
>> +     struct spi_transfer t[] = {
>> +             {
>> +                     .tx_buf =&st->data[0].d8[2],
>> +                     .len = 2,
>> +             }, {
>> +                     .rx_buf =&st->data[1].d8[4 - AD9523_TRANSF_LEN(addr)],
>> +                     .len = AD9523_TRANSF_LEN(addr),
>> +             },
>> +     };
>> +
>> +     spi_message_init(&m);
>> +     spi_message_add_tail(&t[0],&m);
>> +     spi_message_add_tail(&t[1],&m);
>> +
>> +     st->data[0].d32 = cpu_to_be32(AD9523_READ |
>> +                                   AD9523_CNT(AD9523_TRANSF_LEN(addr)) |
>> +                                   AD9523_ADDR(addr));
>> +
>> +     ret = spi_sync(st->spi,&m);
>> +     if (ret<   0)
>> +             dev_err(&indio_dev->dev, "read failed (%d)", ret);
>> +     else
>> +             ret = be32_to_cpu(st->data[1].d32)&   (0xFFFFFF>>
>> +                               (8 * (3 - AD9523_TRANSF_LEN(addr))));
>> +
>> +     return ret;
>> +};
>> +
>> +static int ad9523_write(struct iio_dev *indio_dev, unsigned addr, unsigned val)
>> +{
>> +     struct ad9523_state *st = iio_priv(indio_dev);
>> +     struct spi_message m;
>> +     int ret;
>> +     struct spi_transfer t[] = {
>> +             {
>> +                     .tx_buf =&st->data[0].d8[2],
>> +                     .len = 2,
>> +             }, {
>> +                     .tx_buf =&st->data[1].d8[4 - AD9523_TRANSF_LEN(addr)],
>> +                     .len = AD9523_TRANSF_LEN(addr),
>> +             },
>> +     };
>> +
>> +     spi_message_init(&m);
>> +     spi_message_add_tail(&t[0],&m);
>> +     spi_message_add_tail(&t[1],&m);
>> +
>> +     st->data[0].d32 = cpu_to_be32(AD9523_WRITE |
>> +                                   AD9523_CNT(AD9523_TRANSF_LEN(addr)) |
>> +                                   AD9523_ADDR(addr));
>> +     st->data[1].d32 = cpu_to_be32(val);
>> +
>> +     ret = spi_sync(st->spi,&m);
>> +
>> +     if (ret<   0)
>> +             dev_err(&indio_dev->dev, "write failed (%d)", ret);
>> +
>> +     return ret;
>> +}
>> +
>> +static int ad9523_io_update(struct iio_dev *indio_dev)
>> +{
>> +     return ad9523_write(indio_dev, AD9523_IO_UPDATE, AD9523_IO_UPDATE_EN);
>> +}
>> +
>> +static int ad9523_vco_out_map(struct iio_dev *indio_dev,
>> +                           unsigned ch, bool out)
>> +{
>> +     struct ad9523_state *st = iio_priv(indio_dev);
>> +     int ret;
>> +     unsigned mask;
>> +
>> +     switch (ch) {
>> +     case 0 ... 3:
>> +             ret = ad9523_read(indio_dev, AD9523_PLL1_OUTPUT_CHANNEL_CTRL);
>> +             if (ret<   0)
>> +                     break;
>> +             mask = AD9523_PLL1_OUTP_CH_CTRL_VCXO_SRC_SEL_CH0<<   ch;
>> +             if (out) {
>> +                     ret |= mask;
>> +                     out = 2;
>> +             } else {
>> +                     ret&= ~mask;
>> +             }
>> +             ret = ad9523_write(indio_dev,
>> +                                AD9523_PLL1_OUTPUT_CHANNEL_CTRL, ret);
>> +             break;
>> +     case 4 ... 6:
>> +             ret = ad9523_read(indio_dev, AD9523_PLL1_OUTPUT_CTRL);
>> +             if (ret<   0)
>> +                     break;
>> +             mask = AD9523_PLL1_OUTP_CTRL_VCO_DIV_SEL_CH4_M2<<   (ch - 4);
>> +             if (out)
>> +                     ret |= mask;
>> +             else
>> +                     ret&= ~mask;
>> +             ret = ad9523_write(indio_dev, AD9523_PLL1_OUTPUT_CTRL, ret);
>> +             break;
>> +     case 7 ... 9:
>> +             ret = ad9523_read(indio_dev, AD9523_PLL1_OUTPUT_CHANNEL_CTRL);
>> +             if (ret<   0)
>> +                     break;
>> +             mask = AD9523_PLL1_OUTP_CH_CTRL_VCO_DIV_SEL_CH7_M2<<   (ch - 7);
>> +             if (out)
>> +                     ret |= mask;
>> +             else
>> +                     ret&= ~mask;
>> +             ret = ad9523_write(indio_dev,
>> +                                AD9523_PLL1_OUTPUT_CHANNEL_CTRL, ret);
>> +             break;
>> +     default:
>> +             return 0;
>> +     }
>> +
>> +     st->vco_out_map[ch] = out;
>> +
>> +     return ret;
>> +}
>> +
>> +static int ad9523_set_clock_provider(struct iio_dev *indio_dev,
>> +                           unsigned ch, unsigned long freq)
>> +{
>> +     struct ad9523_state *st = iio_priv(indio_dev);
>> +     long tmp1, tmp2;
>> +     bool use_alt_clk_src;
>> +
>> +     switch (ch) {
>> +     case 0 ... 3:
>> +             use_alt_clk_src = (freq == st->vco_out_freq[AD9523_VCXO]);
>> +             break;
>> +     case 4 ... 9:
>> +             tmp1 = st->vco_out_freq[AD9523_VCO1] / freq;
>> +             tmp2 = st->vco_out_freq[AD9523_VCO2] / freq;
>> +             tmp1 *= freq;
>> +             tmp2 *= freq;
>> +             use_alt_clk_src = (abs(tmp1 - freq)>   abs(tmp2 - freq));
>> +             break;
>> +     default:
>> +             /* Ch 10..14: No action required, return success */
>> +             return 0;
>> +     }
>> +
>> +     return ad9523_vco_out_map(indio_dev, ch, use_alt_clk_src);
>> +}
>> +
>> +static int ad9523_store_eeprom(struct iio_dev *indio_dev)
>> +{
>> +     int ret, tmp;
>> +
>> +     ret = ad9523_write(indio_dev, AD9523_EEPROM_CTRL1,
>> +                        AD9523_EEPROM_CTRL1_EEPROM_WRITE_PROT_DIS);
>> +     if (ret<   0)
>> +             return ret;
>> +     ret = ad9523_write(indio_dev, AD9523_EEPROM_CTRL2,
>> +                        AD9523_EEPROM_CTRL2_REG2EEPROM);
>> +     if (ret<   0)
>> +             return ret;
>> +
>> +     tmp = 4;
>> +     do {
>> +             msleep(16);
>> +             ret = ad9523_read(indio_dev,
>> +                               AD9523_EEPROM_DATA_XFER_STATUS);
>> +             if (ret<   0)
>> +                     return ret;
>> +     } while ((ret&   AD9523_EEPROM_DATA_XFER_IN_PROGRESS)&&   tmp--);
>> +
>> +     ret = ad9523_write(indio_dev, AD9523_EEPROM_CTRL1, 0);
>> +     if (ret<   0)
>> +             return ret;
>> +
>> +     ret = ad9523_read(indio_dev, AD9523_EEPROM_ERROR_READBACK);
>> +     if (ret<   0)
>> +             return ret;
>> +
>> +     if (ret&   AD9523_EEPROM_ERROR_READBACK_FAIL) {
>> +             dev_err(&indio_dev->dev, "Verify EEPROM failed");
>> +             ret = -EIO;
>> +     }
>> +
>> +     return ret;
>> +}
>> +
>> +static int ad9523_sync(struct iio_dev *indio_dev)
>> +{
>> +     int ret, tmp;
>> +
>> +     ret = ad9523_read(indio_dev, AD9523_STATUS_SIGNALS);
>> +     if (ret<   0)
>> +             return ret;
>> +
>> +     tmp = ret;
>> +     tmp |= AD9523_STATUS_SIGNALS_SYNC_MAN_CTRL;
>> +
>> +     ret = ad9523_write(indio_dev, AD9523_STATUS_SIGNALS, tmp);
>> +     if (ret<   0)
>> +             return ret;
>> +
>> +     ad9523_io_update(indio_dev);
>> +     tmp&= ~AD9523_STATUS_SIGNALS_SYNC_MAN_CTRL;
>> +
>> +     ret = ad9523_write(indio_dev, AD9523_STATUS_SIGNALS, tmp);
>> +     if (ret<   0)
>> +             return ret;
>> +
>> +     return ad9523_io_update(indio_dev);
>> +}
>> +
>> +static ssize_t ad9523_store(struct device *dev,
>> +                             struct device_attribute *attr,
>> +                             const char *buf, size_t len)
>> +{
>> +     struct iio_dev *indio_dev = dev_to_iio_dev(dev);
>> +     struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
>> +     bool state;
>> +     int ret;
>> +
>> +     ret = strtobool(buf,&state);
>> +     if (ret<   0)
>> +             return ret;
>> +
>> +     if (!state)
>> +             return 0;
>> +
>> +     mutex_lock(&indio_dev->mlock);
>> +     switch ((u32)this_attr->address) {
>> +     case AD9523_SYNC:
>> +             ret = ad9523_sync(indio_dev);
>> +             break;
>> +     case AD9523_EEPROM:
>> +             ret = ad9523_store_eeprom(indio_dev);
>> +             break;
>> +     default:
>> +             ret = -ENODEV;
>> +     }
>> +     mutex_unlock(&indio_dev->mlock);
>> +
>> +     return ret ? ret : len;
>> +}
>> +
>> +static ssize_t ad9523_show(struct device *dev,
>> +                     struct device_attribute *attr,
>> +                     char *buf)
>> +{
>> +     struct iio_dev *indio_dev = dev_to_iio_dev(dev);
>> +     struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
>> +     int ret;
>> +
>> +     mutex_lock(&indio_dev->mlock);
>> +     ret = ad9523_read(indio_dev, AD9523_READBACK_0);
>> +     if (ret>= 0) {
>> +             ret = sprintf(buf, "%d\n", !!(ret&   (1<<
>> +                     (u32)this_attr->address)));
>> +     }
>> +     mutex_unlock(&indio_dev->mlock);
>> +
>> +     return ret;
>> +}
>> +
>> +static IIO_DEVICE_ATTR(pll1_locked, S_IRUGO,
>> +                     ad9523_show,
>> +                     NULL,
>> +                     AD9523_STAT_PLL1_LD);
>> +
>> +static IIO_DEVICE_ATTR(pll2_locked, S_IRUGO,
>> +                     ad9523_show,
>> +                     NULL,
>> +                     AD9523_STAT_PLL2_LD);
>> +
>> +static IIO_DEVICE_ATTR(pll1_reference_clk_a_present, S_IRUGO,
>> +                     ad9523_show,
>> +                     NULL,
>> +                     AD9523_STAT_REFA);
>> +
>> +static IIO_DEVICE_ATTR(pll1_reference_clk_b_present, S_IRUGO,
>> +                     ad9523_show,
>> +                     NULL,
>> +                     AD9523_STAT_REFB);
>> +
>> +static IIO_DEVICE_ATTR(pll1_reference_clk_test_present, S_IRUGO,
>> +                     ad9523_show,
>> +                     NULL,
>> +                     AD9523_STAT_REF_TEST);
>> +
>> +static IIO_DEVICE_ATTR(vcxo_clk_present, S_IRUGO,
>> +                     ad9523_show,
>> +                     NULL,
>> +                     AD9523_STAT_VCXO);
>> +
>> +static IIO_DEVICE_ATTR(pll2_feedback_clk_present, S_IRUGO,
>> +                     ad9523_show,
>> +                     NULL,
>> +                     AD9523_STAT_PLL2_FB_CLK);
>> +
>> +static IIO_DEVICE_ATTR(pll2_reference_clk_present, S_IRUGO,
>> +                     ad9523_show,
>> +                     NULL,
>> +                     AD9523_STAT_PLL2_REF_CLK);
>> +
>> +static IIO_DEVICE_ATTR(sync_dividers, S_IWUSR,
>> +                     NULL,
>> +                     ad9523_store,
>> +                     AD9523_SYNC);
>> +
>> +static IIO_DEVICE_ATTR(store_eeprom, S_IWUSR,
>> +                     NULL,
>> +                     ad9523_store,
>> +                     AD9523_EEPROM);
>> +
>> +static struct attribute *ad9523_attributes[] = {
>> +&iio_dev_attr_sync_dividers.dev_attr.attr,
>> +&iio_dev_attr_store_eeprom.dev_attr.attr,
>> +&iio_dev_attr_pll2_feedback_clk_present.dev_attr.attr,
>> +&iio_dev_attr_pll2_reference_clk_present.dev_attr.attr,
>> +&iio_dev_attr_pll1_reference_clk_a_present.dev_attr.attr,
>> +&iio_dev_attr_pll1_reference_clk_b_present.dev_attr.attr,
>> +&iio_dev_attr_pll1_reference_clk_test_present.dev_attr.attr,
>> +&iio_dev_attr_vcxo_clk_present.dev_attr.attr,
>> +&iio_dev_attr_pll1_locked.dev_attr.attr,
>> +&iio_dev_attr_pll2_locked.dev_attr.attr,
>> +     NULL,
>> +};
>> +
>> +static const struct attribute_group ad9523_attribute_group = {
>> +     .attrs = ad9523_attributes,
>> +};
>> +
>> +static int ad9523_read_raw(struct iio_dev *indio_dev,
>> +                        struct iio_chan_spec const *chan,
>> +                        int *val,
>> +                        int *val2,
>> +                        long m)
>> +{
>> +     struct ad9523_state *st = iio_priv(indio_dev);
>> +     unsigned code;
>> +     int ret;
>> +
>> +     mutex_lock(&indio_dev->mlock);
>> +     ret = ad9523_read(indio_dev, AD9523_CHANNEL_CLOCK_DIST(chan->channel));
>> +     mutex_unlock(&indio_dev->mlock);
>> +
>> +     if (ret<   0)
>> +             return ret;
>> +
>> +     switch (m) {
>> +     case IIO_CHAN_INFO_RAW:
>> +             *val = !(ret&   AD9523_CLK_DIST_PWR_DOWN_EN);
>> +             return IIO_VAL_INT;
>> +     case IIO_CHAN_INFO_FREQUENCY:
>> +             *val = st->vco_out_freq[st->vco_out_map[chan->channel]] /
>> +                     AD9523_CLK_DIST_DIV_REV(ret);
>> +             return IIO_VAL_INT;
>> +     case IIO_CHAN_INFO_PHASE:
>> +             code = (AD9523_CLK_DIST_DIV_PHASE_REV(ret) * 3141592) /
>> +                     AD9523_CLK_DIST_DIV_REV(ret);
>> +             *val = code / 1000000;
>> +             *val2 = (code % 1000000) * 10;
>> +             return IIO_VAL_INT_PLUS_MICRO;
>> +     default:
>> +             return -EINVAL;
>> +     }
>> +};
>> +
>> +static int ad9523_write_raw(struct iio_dev *indio_dev,
>> +                         struct iio_chan_spec const *chan,
>> +                         int val,
>> +                         int val2,
>> +                         long mask)
>> +{
>> +     struct ad9523_state *st = iio_priv(indio_dev);
>> +     unsigned reg;
>> +     int ret, tmp, code;
>> +
>> +     mutex_lock(&indio_dev->mlock);
>> +     ret = ad9523_read(indio_dev, AD9523_CHANNEL_CLOCK_DIST(chan->channel));
>> +     if (ret<   0)
>> +             goto out;
>> +
>> +     reg = ret;
>> +
>> +     switch (mask) {
>> +     case IIO_CHAN_INFO_RAW:
>> +             if (val)
>> +                     reg&= ~AD9523_CLK_DIST_PWR_DOWN_EN;
>> +             else
>> +                     reg |= AD9523_CLK_DIST_PWR_DOWN_EN;
>> +             break;
>> +     case IIO_CHAN_INFO_FREQUENCY:
>> +             if (val<= 0) {
>> +                     ret = -EINVAL;
>> +                     goto out;
>> +             }
>> +             ret = ad9523_set_clock_provider(indio_dev, chan->channel, val);
>> +             if (ret<   0)
>> +                     goto out;
>> +             tmp = st->vco_out_freq[st->vco_out_map[chan->channel]] / val;
>> +             tmp = clamp(tmp, 1, 1024);
>> +             reg&= ~(0x3FF<<   8);
>> +             reg |= AD9523_CLK_DIST_DIV(tmp);
>> +             break;
>> +     case IIO_CHAN_INFO_PHASE:
>> +             code = val * 1000000 + val2 % 1000000;
>> +             tmp = (code * AD9523_CLK_DIST_DIV_REV(ret)) / 3141592;
>> +             tmp = clamp(tmp, 0, 63);
>> +             reg&= ~AD9523_CLK_DIST_DIV_PHASE(~0);
>> +             reg |= AD9523_CLK_DIST_DIV_PHASE(tmp);
>> +             break;
>> +     default:
>> +             ret = -EINVAL;
>> +             goto out;
>> +     }
>> +
>> +     ret = ad9523_write(indio_dev, AD9523_CHANNEL_CLOCK_DIST(chan->channel),
>> +                        reg);
>> +     if (ret<   0)
>> +             goto out;
>> +
>> +     ad9523_io_update(indio_dev);
>> +out:
>> +     mutex_unlock(&indio_dev->mlock);
>> +     return ret;
>> +}
>> +
>> +static int ad9523_reg_access(struct iio_dev *indio_dev,
>> +                           unsigned reg, unsigned writeval,
>> +                           unsigned *readval)
>> +{
>> +     int ret;
>> +
>> +     mutex_lock(&indio_dev->mlock);
>> +     if (readval == NULL) {
>> +             ret = ad9523_write(indio_dev, reg | AD9523_R1B, writeval);
>> +             ad9523_io_update(indio_dev);
>> +     } else {
>> +             ret = ad9523_read(indio_dev, reg | AD9523_R1B);
>> +             if (ret<   0)
>> +                     return ret;
>> +             *readval = ret;
>> +             ret = 0;
>> +     }
>> +     mutex_unlock(&indio_dev->mlock);
>> +
>> +     return ret;
>> +}
>> +
>> +static const struct iio_info ad9523_info = {
>> +     .read_raw =&ad9523_read_raw,
>> +     .write_raw =&ad9523_write_raw,
>> +     .debugfs_reg_access =&ad9523_reg_access,
>> +     .attrs =&ad9523_attribute_group,
>> +     .driver_module = THIS_MODULE,
>> +};
>> +
>> +static int ad9523_setup(struct iio_dev *indio_dev)
>> +{
>> +     struct ad9523_state *st = iio_priv(indio_dev);
>> +     struct ad9523_platform_data *pdata = st->pdata;
>> +     struct ad9523_channel_spec *chan;
>> +     unsigned active_mask = 0;
>> +     int ret, i;
>> +
>> +     ret = ad9523_write(indio_dev, AD9523_SERIAL_PORT_CONFIG,
>> +                        AD9523_SER_CONF_SOFT_RESET |
>> +                       (st->spi->mode&   SPI_3WIRE ? 0 :
>> +                       AD9523_SER_CONF_SDO_ACTIVE));
>> +     if (ret<   0)
>> +             return ret;
>> +
>> +     ret = ad9523_write(indio_dev, AD9523_READBACK_CTRL,
>> +                       AD9523_READBACK_CTRL_READ_BUFFERED);
>> +     if (ret<   0)
>> +             return ret;
>> +
>> +     ret = ad9523_io_update(indio_dev);
>> +     if (ret<   0)
>> +             return ret;
>> +
>> +     /*
>> +      * PLL1 Setup
>> +      */
>> +     ret = ad9523_write(indio_dev, AD9523_PLL1_REF_A_DIVIDER,
>> +             pdata->refa_r_div);
>> +     if (ret<   0)
>> +             return ret;
>> +
>> +     ret = ad9523_write(indio_dev, AD9523_PLL1_REF_B_DIVIDER,
>> +             pdata->refb_r_div);
>> +     if (ret<   0)
>> +             return ret;
>> +
>> +     ret = ad9523_write(indio_dev, AD9523_PLL1_FEEDBACK_DIVIDER,
>> +             pdata->pll1_feedback_div);
>> +     if (ret<   0)
>> +             return ret;
>> +
>> +     ret = ad9523_write(indio_dev, AD9523_PLL1_CHARGE_PUMP_CTRL,
>> +             AD9523_PLL1_CHARGE_PUMP_CURRENT_nA(pdata->
>> +                     pll1_charge_pump_current_nA) |
>> +             AD9523_PLL1_CHARGE_PUMP_MODE_NORMAL |
>> +             AD9523_PLL1_BACKLASH_PW_MIN);
>> +     if (ret<   0)
>> +             return ret;
>> +
>> +     ret = ad9523_write(indio_dev, AD9523_PLL1_INPUT_RECEIVERS_CTRL,
>> +             AD_IF(refa_diff_rcv_en, AD9523_PLL1_REFA_RCV_EN) |
>> +             AD_IF(refb_diff_rcv_en, AD9523_PLL1_REFB_RCV_EN) |
>> +             AD_IF(osc_in_diff_en, AD9523_PLL1_OSC_IN_DIFF_EN) |
>> +             AD_IF(osc_in_cmos_neg_inp_en,
>> +                   AD9523_PLL1_OSC_IN_CMOS_NEG_INP_EN) |
>> +             AD_IF(refa_diff_rcv_en, AD9523_PLL1_REFA_DIFF_RCV_EN) |
>> +             AD_IF(refb_diff_rcv_en, AD9523_PLL1_REFB_DIFF_RCV_EN));
>> +     if (ret<   0)
>> +             return ret;
>> +
>> +     ret = ad9523_write(indio_dev, AD9523_PLL1_REF_CTRL,
>> +             AD_IF(zd_in_diff_en, AD9523_PLL1_ZD_IN_DIFF_EN) |
>> +             AD_IF(zd_in_cmos_neg_inp_en,
>> +                   AD9523_PLL1_ZD_IN_CMOS_NEG_INP_EN) |
>> +             AD_IF(zero_delay_mode_internal_en,
>> +                   AD9523_PLL1_ZERO_DELAY_MODE_INT) |
>> +             AD_IF(osc_in_feedback_en, AD9523_PLL1_OSC_IN_PLL_FEEDBACK_EN) |
>> +             AD_IF(refa_cmos_neg_inp_en, AD9523_PLL1_REFA_CMOS_NEG_INP_EN) |
>> +             AD_IF(refb_cmos_neg_inp_en, AD9523_PLL1_REFB_CMOS_NEG_INP_EN));
>> +     if (ret<   0)
>> +             return ret;
>> +
>> +     ret = ad9523_write(indio_dev, AD9523_PLL1_MISC_CTRL,
>> +             AD9523_PLL1_REFB_INDEP_DIV_CTRL_EN |
>> +             AD9523_PLL1_REF_MODE(pdata->ref_mode));
>> +     if (ret<   0)
>> +             return ret;
>> +
>> +     ret = ad9523_write(indio_dev, AD9523_PLL1_LOOP_FILTER_CTRL,
>> +             AD9523_PLL1_LOOP_FILTER_RZERO(pdata->pll1_loop_filter_rzero));
>> +     if (ret<   0)
>> +             return ret;
>> +     /*
>> +      * PLL2 Setup
>> +      */
>> +
>> +     ret = ad9523_write(indio_dev, AD9523_PLL2_CHARGE_PUMP,
>> +             AD9523_PLL2_CHARGE_PUMP_CURRENT_nA(pdata->
>> +                     pll2_charge_pump_current_nA));
>> +     if (ret<   0)
>> +             return ret;
>> +
>> +     ret = ad9523_write(indio_dev, AD9523_PLL2_FEEDBACK_DIVIDER_AB,
>> +             AD9523_PLL2_FB_NDIV_A_CNT(pdata->pll2_ndiv_a_cnt) |
>> +             AD9523_PLL2_FB_NDIV_B_CNT(pdata->pll2_ndiv_b_cnt));
>> +     if (ret<   0)
>> +             return ret;
>> +
>> +     ret = ad9523_write(indio_dev, AD9523_PLL2_CTRL,
>> +             AD9523_PLL2_CHARGE_PUMP_MODE_NORMAL |
>> +             AD9523_PLL2_BACKLASH_CTRL_EN |
>> +             AD_IF(pll2_freq_doubler_en, AD9523_PLL2_FREQ_DOUBLER_EN));
>> +     if (ret<   0)
>> +             return ret;
>> +
>> +     st->vco_freq = (pdata->vcxo_freq * (pdata->pll2_freq_doubler_en ? 2 : 1)
>> +                     / pdata->pll2_r2_div) * AD9523_PLL2_FB_NDIV(pdata->
>> +                     pll2_ndiv_a_cnt, pdata->pll2_ndiv_b_cnt);
>> +
>> +     ret = ad9523_write(indio_dev, AD9523_PLL2_VCO_CTRL,
>> +             AD9523_PLL2_VCO_CALIBRATE);
>> +     if (ret<   0)
>> +             return ret;
>> +
>> +     ret = ad9523_write(indio_dev, AD9523_PLL2_VCO_DIVIDER,
>> +             AD9523_PLL2_VCO_DIV_M1(pdata->pll2_vco_diff_m1) |
>> +             AD9523_PLL2_VCO_DIV_M2(pdata->pll2_vco_diff_m2) |
>> +             AD_IFE(pll2_vco_diff_m1, 0,
>> +                    AD9523_PLL2_VCO_DIV_M1_PWR_DOWN_EN) |
>> +             AD_IFE(pll2_vco_diff_m2, 0,
>> +                    AD9523_PLL2_VCO_DIV_M2_PWR_DOWN_EN));
>> +     if (ret<   0)
>> +             return ret;
>> +
>> +     if (pdata->pll2_vco_diff_m1)
>> +             st->vco_out_freq[AD9523_VCO1] =
>> +                     st->vco_freq / pdata->pll2_vco_diff_m1;
>> +
>> +     if (pdata->pll2_vco_diff_m2)
>> +             st->vco_out_freq[AD9523_VCO2] =
>> +                     st->vco_freq / pdata->pll2_vco_diff_m2;
>> +
>> +     st->vco_out_freq[AD9523_VCXO] = pdata->vcxo_freq;
>> +
>> +     ret = ad9523_write(indio_dev, AD9523_PLL2_R2_DIVIDER,
>> +             AD9523_PLL2_R2_DIVIDER_VAL(pdata->pll2_r2_div));
>> +     if (ret<   0)
>> +             return ret;
>> +
>> +     ret = ad9523_write(indio_dev, AD9523_PLL2_LOOP_FILTER_CTRL,
>> +             AD9523_PLL2_LOOP_FILTER_CPOLE1(pdata->cpole1) |
>> +             AD9523_PLL2_LOOP_FILTER_RZERO(pdata->rzero) |
>> +             AD9523_PLL2_LOOP_FILTER_RPOLE2(pdata->rpole2) |
>> +             AD_IF(rzero_bypass_en,
>> +                   AD9523_PLL2_LOOP_FILTER_RZERO_BYPASS_EN));
>> +     if (ret<   0)
>> +             return ret;
>> +
>> +     for (i = 0; i<   pdata->num_channels; i++) {
>> +             chan =&pdata->channels[i];
>> +             if (chan->channel_num<   AD9523_NUM_CHAN) {
>> +                     active_mask |= (1<<   chan->channel_num);
>> +                     ret = ad9523_write(indio_dev,
>> +                             AD9523_CHANNEL_CLOCK_DIST(chan->channel_num),
>> +                             AD9523_CLK_DIST_DRIVER_MODE(chan->driver_mode) |
>> +                             AD9523_CLK_DIST_DIV(chan->channel_divider) |
>> +                             AD9523_CLK_DIST_DIV_PHASE(chan->divider_phase) |
>> +                             (chan->sync_ignore_en ?
>> +                                     AD9523_CLK_DIST_IGNORE_SYNC_EN : 0) |
>> +                             (chan->divider_output_invert_en ?
>> +                                     AD9523_CLK_DIST_INV_DIV_OUTPUT_EN : 0) |
>> +                             (chan->low_power_mode_en ?
>> +                                     AD9523_CLK_DIST_LOW_PWR_MODE_EN : 0) |
>> +                             (chan->output_dis ?
>> +                                     AD9523_CLK_DIST_PWR_DOWN_EN : 0));
>> +                     if (ret<   0)
>> +                             return ret;
>> +
>> +                     ret = ad9523_vco_out_map(indio_dev, chan->channel_num,
>> +                                        chan->use_alt_clock_src);
>> +                     if (ret<   0)
>> +                             return ret;
>> +
>> +                     st->ad9523_channels[i].type = IIO_ALTVOLTAGE;
>> +                     st->ad9523_channels[i].output = 1;
>> +                     st->ad9523_channels[i].indexed = 1;
>> +                     st->ad9523_channels[i].channel = chan->channel_num;
>> +                     st->ad9523_channels[i].extend_name =
>> +                             chan->extended_name;
>> +                     st->ad9523_channels[i].info_mask =
>> +                             IIO_CHAN_INFO_RAW_SEPARATE_BIT |
>> +                             IIO_CHAN_INFO_PHASE_SEPARATE_BIT |
>> +                             IIO_CHAN_INFO_FREQUENCY_SEPARATE_BIT;
>> +             }
>> +     }
>> +
> looks rather like a for_each_clear_bit.
Yah that sounds like a good match.
I'll then use __set_bit a few lines above as well.

>> +     for (i = 0; i<   AD9523_NUM_CHAN; i++)
>> +             if (!(active_mask&   (1<<   i)))
>> +                     ad9523_write(indio_dev,
>> +                                  AD9523_CHANNEL_CLOCK_DIST(i),
>> +                                  AD9523_CLK_DIST_DRIVER_MODE(TRISTATE) |
>> +                                  AD9523_CLK_DIST_PWR_DOWN_EN);
>> +
>> +     ret = ad9523_write(indio_dev, AD9523_POWER_DOWN_CTRL, 0);
>> +     if (ret<   0)
>> +             return ret;
>> +
>> +     ret = ad9523_write(indio_dev, AD9523_STATUS_SIGNALS,
>> +                        AD9523_STATUS_MONITOR_01_PLL12_LOCKED);
>> +     if (ret<   0)
>> +             return ret;
>> +
>> +     ret = ad9523_io_update(indio_dev);
>> +     if (ret<   0)
>> +             return ret;
>> +
>> +     return 0;
>> +}
>> +
>> +static int __devinit ad9523_probe(struct spi_device *spi)
>> +{
>> +     struct ad9523_platform_data *pdata = spi->dev.platform_data;
>> +     struct iio_dev *indio_dev;
>> +     struct ad9523_state *st;
>> +     int ret;
>> +
>> +     if (!pdata) {
>> +             dev_err(&spi->dev, "no platform data?\n");
>> +             return -EINVAL;
>> +     }
>> +
>> +     indio_dev = iio_device_alloc(sizeof(*st));
>> +     if (indio_dev == NULL)
>> +             return -ENOMEM;
>> +
>> +     st = iio_priv(indio_dev);
>> +
>> +     st->reg = regulator_get(&spi->dev, "vcc");
>> +     if (!IS_ERR(st->reg)) {
>> +             ret = regulator_enable(st->reg);
>> +             if (ret)
>> +                     goto error_put_reg;
>> +     }
>> +
>> +     spi_set_drvdata(spi, indio_dev);
>> +     st->spi = spi;
>> +     st->pdata = pdata;
>> +
>> +     indio_dev->dev.parent =&spi->dev;
>> +     indio_dev->name = (pdata->name[0] != 0) ? pdata->name :
>> +                       spi_get_device_id(spi)->name;
>> +     indio_dev->info =&ad9523_info;
>> +     indio_dev->modes = INDIO_DIRECT_MODE;
>> +     indio_dev->channels = st->ad9523_channels;
>> +     indio_dev->num_channels = pdata->num_channels;
>> +
>> +     ret = ad9523_setup(indio_dev);
>> +     if (ret<   0)
>> +             goto error_disable_reg;
>> +
>> +     ret = iio_device_register(indio_dev);
>> +     if (ret)
>> +             goto error_disable_reg;
>> +
>> +     dev_info(&spi->dev, "probed %s\n", indio_dev->name);
>> +
>> +     return 0;
>> +
>> +error_disable_reg:
>> +     if (!IS_ERR(st->reg))
>> +             regulator_disable(st->reg);
>> +error_put_reg:
>> +     if (!IS_ERR(st->reg))
>> +             regulator_put(st->reg);
>> +
>> +     iio_device_free(indio_dev);
>> +
>> +     return ret;
>> +}
>> +
>> +static int __devexit ad9523_remove(struct spi_device *spi)
>> +{
>> +     struct iio_dev *indio_dev = spi_get_drvdata(spi);
>> +     struct ad9523_state *st = iio_priv(indio_dev);
>> +     struct regulator *reg = st->reg;
>> +
>> +     iio_device_unregister(indio_dev);
>> +
> st is still available so I'd not bother with the temporary for reg.
> Personal preference though.
ok
>> +     if (!IS_ERR(reg)) {
>> +             regulator_disable(reg);
>> +             regulator_put(reg);
>> +     }
>> +
>> +     iio_device_free(indio_dev);
>> +
>> +     return 0;
>> +}
>> +
>> +static const struct spi_device_id ad9523_id[] = {
>> +     {"ad9523", 9523},
>> +     {}
>> +};
>> +MODULE_DEVICE_TABLE(spi, ad9523_id);
>> +
>> +static struct spi_driver ad9523_driver = {
>> +     .driver = {
>> +             .name   = "ad9523",
>> +             .owner  = THIS_MODULE,
>> +     },
>> +     .probe          = ad9523_probe,
>> +     .remove         = __devexit_p(ad9523_remove),
>> +     .id_table       = ad9523_id,
>> +};
>> +module_spi_driver(ad9523_driver);
>> +
>> +MODULE_AUTHOR("Michael Hennerich<hennerich@blackfin.uclinux.org>");
>> +MODULE_DESCRIPTION("Analog Devices AD9523 CLOCKDIST/PLL");
>> +MODULE_LICENSE("GPL v2");
>> diff --git a/include/linux/iio/frequency/ad9523.h b/include/linux/iio/frequency/ad9523.h
>> new file mode 100644
>> index 0000000..e878f09
>> --- /dev/null
>> +++ b/include/linux/iio/frequency/ad9523.h
>> @@ -0,0 +1,195 @@
>> +/*
>> + * AD9523 SPI Low Jitter Clock Generator
>> + *
>> + * Copyright 2012 Analog Devices Inc.
>> + *
>> + * Licensed under the GPL-2.
>> + */
>> +
>> +#ifndef IIO_FREQUENCY_AD9523_H_
>> +#define IIO_FREQUENCY_AD9523_H_
>> +
>> +enum outp_drv_mode {
>> +     TRISTATE,
>> +     LVPECL_8mA,
>> +     LVDS_4mA,
>> +     LVDS_7mA,
>> +     HSTL0_16mA,
>> +     HSTL1_8mA,
>> +     CMOS_CONF1,
>> +     CMOS_CONF2,
>> +     CMOS_CONF3,
>> +     CMOS_CONF4,
>> +     CMOS_CONF5,
>> +     CMOS_CONF6,
>> +     CMOS_CONF7,
>> +     CMOS_CONF8,
>> +     CMOS_CONF9
>> +};
>> +
>> +enum ref_sel_mode {
>> +     NONEREVERTIVE_STAY_ON_REFB,
>> +     REVERT_TO_REFA,
>> +     SELECT_REFA,
>> +     SELECT_REFB,
>> +     EXT_REF_SEL
>> +};
>> +
>> +/**
>> + * struct ad9523_channel_spec - Output channel configuration
>> + *
>> + * @channel_num: Output channel number.
>> + * @divider_output_invert_en: Invert the polarity of the output clock.
>> + * @sync_ignore_en: Ignore chip-level SYNC signal.
>> + * @low_power_mode_en: Reduce power used in the differential output modes.
>> + * @use_alt_clock_src: Channel divider uses alternative clk source.
>> + * @output_dis: Disables, powers down the entire channel.
>> + * @driver_mode: Output driver mode (logic level family).
>> + * @divider_phase: Divider initial phase after a SYNC. Range 0..63
>> +                LSB = 1/2 of a period of the divider input clock.
>> + * @channel_divider: 10-bit channel divider.
>> + * @extended_name: Optional descriptive channel name.
>> + */
>> +
>> +struct ad9523_channel_spec {
>> +     unsigned                channel_num;
>> +     bool                    divider_output_invert_en;
>> +     bool                    sync_ignore_en;
>> +     bool                    low_power_mode_en;
>> +                              /* CH0..CH3 VCXO, CH4..CH9 VCO2 */
>> +     bool                    use_alt_clock_src;
>> +     bool                    output_dis;
>> +     enum outp_drv_mode      driver_mode;
>> +     unsigned char           divider_phase;
>> +     unsigned short          channel_divider;
>> +     char                    extended_name[16];
>> +};
>> +
>> +enum pll1_rzero_resistor {
>> +     RZERO_883_OHM,
>> +     RZERO_677_OHM,
>> +     RZERO_341_OHM,
>> +     RZERO_135_OHM,
>> +     RZERO_10_OHM,
>> +     RZERO_USE_EXT_RES = 8,
>> +};
>> +
>> +enum rpole2_resistor {
>> +     RPOLE2_900_OHM,
>> +     RPOLE2_450_OHM,
>> +     RPOLE2_300_OHM,
>> +     RPOLE2_225_OHM,
>> +};
>> +
>> +enum rzero_resistor {
>> +     RZERO_3250_OHM,
>> +     RZERO_2750_OHM,
>> +     RZERO_2250_OHM,
>> +     RZERO_2100_OHM,
>> +     RZERO_3000_OHM,
>> +     RZERO_2500_OHM,
>> +     RZERO_2000_OHM,
>> +     RZERO_1850_OHM,
>> +};
>> +
>> +enum cpole1_capacitor {
>> +     CPOLE1_0_PF,
>> +     CPOLE1_8_PF,
>> +     CPOLE1_16_PF,
>> +     CPOLE1_24_PF,
> This underscore version would benefit from and explanatory comment!
That's simply a place holder.
I'll add a comment.

Thanks for the review.

-Michael


>> +     _CPOLE1_24_PF,
>> +     CPOLE1_32_PF,
>> +     CPOLE1_40_PF,
>> +     CPOLE1_48_PF,
>> +};
>> +
>> +/**
>> + * struct ad9523_platform_data - platform specific information
>> + *
>> + * @vcxo_freq: External VCXO frequency in Hz
>> + * @refa_diff_rcv_en: REFA differential/single-ended input selection.
>> + * @refb_diff_rcv_en: REFB differential/single-ended input selection.
>> + * @zd_in_diff_en: Zero Delay differential/single-ended input selection.
>> + * @osc_in_diff_en: OSC differential/ single-ended input selection.
>> + * @refa_cmos_neg_inp_en: REFA single-ended neg./pos. input enable.
>> + * @refb_cmos_neg_inp_en: REFB single-ended neg./pos. input enable.
>> + * @zd_in_cmos_neg_inp_en: Zero Delay single-ended neg./pos. input enable.
>> + * @osc_in_cmos_neg_inp_en: OSC single-ended neg./pos. input enable.
>> + * @refa_r_div: PLL1 10-bit REFA R divider.
>> + * @refb_r_div: PLL1 10-bit REFB R divider.
>> + * @pll1_feedback_div: PLL1 10-bit Feedback N divider.
>> + * @pll1_charge_pump_current_nA: Magnitude of PLL1 charge pump current (nA).
>> + * @zero_delay_mode_internal_en: Internal, external Zero Delay mode selection.
>> + * @osc_in_feedback_en: PLL1 feedback path, local feedback from
>> + *                   the OSC_IN receiver or zero delay mode
>> + * @pll1_loop_filter_rzero: PLL1 Loop Filter Zero Resistor selection.
>> + * @ref_mode: Reference selection mode.
>> + * @pll2_charge_pump_current_nA: Magnitude of PLL2 charge pump current (nA).
>> + * @pll2_ndiv_a_cnt: PLL2 Feedback N-divider, A Counter, range 0..4.
>> + * @pll2_ndiv_b_cnt: PLL2 Feedback N-divider, B Counter, range 0..63.
>> + * @pll2_freq_doubler_en: PLL2 frequency doubler enable.
>> + * @pll2_r2_div: PLL2 R2 divider, range 0..31.
>> + * @pll2_vco_diff_m1: VCO1 divider, range 3..5.
>> + * @pll2_vco_diff_m2: VCO2 divider, range 3..5.
>> + * @rpole2: PLL2 loop filter Rpole resistor value.
>> + * @rzero: PLL2 loop filter Rzero resistor value.
>> + * @cpole1: PLL2 loop filter Cpole capacitor value.
>> + * @rzero_bypass_en: PLL2 loop filter Rzero bypass enable.
>> + * @num_channels: Array size of struct ad9523_channel_spec.
>> + * @channels: Pointer to channel array.
>> + * @name: Optional alternative iio device name.
>> + */
>> +
>> +struct ad9523_platform_data {
>> +     unsigned long vcxo_freq;
>> +
>> +     /* Differential/ Single-Ended Input Configuration */
>> +     bool                            refa_diff_rcv_en;
>> +     bool                            refb_diff_rcv_en;
>> +     bool                            zd_in_diff_en;
>> +     bool                            osc_in_diff_en;
>> +
>> +     /*
>> +      * Valid if differential input disabled
>> +      * if false defaults to pos input
>> +      */
>> +     bool                            refa_cmos_neg_inp_en;
>> +     bool                            refb_cmos_neg_inp_en;
>> +     bool                            zd_in_cmos_neg_inp_en;
>> +     bool                            osc_in_cmos_neg_inp_en;
>> +
>> +     /* PLL1 Setting */
>> +     unsigned short                  refa_r_div;
>> +     unsigned short                  refb_r_div;
>> +     unsigned short                  pll1_feedback_div;
>> +     unsigned short                  pll1_charge_pump_current_nA;
>> +     bool                            zero_delay_mode_internal_en;
>> +     bool                            osc_in_feedback_en;
>> +     enum pll1_rzero_resistor        pll1_loop_filter_rzero;
>> +
>> +     /* Reference */
>> +     enum ref_sel_mode               ref_mode;
>> +
>> +     /* PLL2 Setting */
>> +     unsigned int                    pll2_charge_pump_current_nA;
>> +     unsigned char                   pll2_ndiv_a_cnt;
>> +     unsigned char                   pll2_ndiv_b_cnt;
>> +     bool                            pll2_freq_doubler_en;
>> +     unsigned char                   pll2_r2_div;
>> +     unsigned char                   pll2_vco_diff_m1; /* 3..5 */
>> +     unsigned char                   pll2_vco_diff_m2; /* 3..5 */
>> +
>> +     /* Loop Filter PLL2 */
>> +     enum rpole2_resistor            rpole2;
>> +     enum rzero_resistor             rzero;
>> +     enum cpole1_capacitor           cpole1;
>> +     bool                            rzero_bypass_en;
>> +
>> +     /* Output Channel Configuration */
>> +     int                             num_channels;
>> +     struct ad9523_channel_spec      *channels;
>> +
>> +     char                            name[SPI_NAME_SIZE];
>> +};
>> +
>> +#endif /* IIO_FREQUENCY_AD9523_H_ */
>


-- 
Greetings,
Michael

--
Analog Devices GmbH      Wilhelm-Wagenfeld-Str. 6      80807 Muenchen
Sitz der Gesellschaft: Muenchen; Registergericht: Muenchen HRB 40368;
Geschaeftsfuehrer:Dr.Carsten Suckrow, Thomas Wessel, William A. Martin,
Margaret Seif



^ permalink raw reply	[flat|nested] 6+ messages in thread

* [PATCH 1/2] iio: frequency: New driver for AD9523 SPI Low Jitter Clock Generator
@ 2012-05-29 10:41 michael.hennerich
  0 siblings, 0 replies; 6+ messages in thread
From: michael.hennerich @ 2012-05-29 10:41 UTC (permalink / raw)
  To: gregkh; +Cc: jic23, linux-iio, device-drivers-devel, Michael Hennerich

From: Michael Hennerich <michael.hennerich@analog.com>

Changes since V1:

Apply Jonathan's review feedback:
Revise device status attribute names, and split documentation into two sections.
Add additional comments, and fix indention issues.
Remove pointless zero initializations.
Revise return value handling.
Simplify some code sections.
Split store_eeprom and sync handling into separate functions.
Use strtobool where applicable.
Document platform data structures using kernel-doc style.

Use dev_to_iio_dev
write_raw IIO_CHAN_INFO_FREQUENCY: Reject values <= 0
Make patch target drivers/iio

Changes since V2:

Use for_each_clear_bit() and __set_bit() where applicable.
Add descriptive comment.
Avoid temporary for struct regulator.
spi_device_id name use ad9523-1, ad9523 will be added later.

Signed-off-by: Michael Hennerich <michael.hennerich@analog.com>
Acked-by: Jonathan Cameron <jic23@kernel.org>
---
 .../ABI/testing/sysfs-bus-iio-frequency-ad9523     |   37 +
 drivers/iio/Kconfig                                |    1 +
 drivers/iio/Makefile                               |    1 +
 drivers/iio/frequency/Kconfig                      |   23 +
 drivers/iio/frequency/Makefile                     |    5 +
 drivers/iio/frequency/ad9523.c                     | 1057 ++++++++++++++++++++
 include/linux/iio/frequency/ad9523.h               |  195 ++++
 7 files changed, 1319 insertions(+), 0 deletions(-)
 create mode 100644 Documentation/ABI/testing/sysfs-bus-iio-frequency-ad9523
 create mode 100644 drivers/iio/frequency/Kconfig
 create mode 100644 drivers/iio/frequency/Makefile
 create mode 100644 drivers/iio/frequency/ad9523.c
 create mode 100644 include/linux/iio/frequency/ad9523.h

diff --git a/Documentation/ABI/testing/sysfs-bus-iio-frequency-ad9523 b/Documentation/ABI/testing/sysfs-bus-iio-frequency-ad9523
new file mode 100644
index 0000000..2ce9c3f
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-bus-iio-frequency-ad9523
@@ -0,0 +1,37 @@
+What:		/sys/bus/iio/devices/iio:deviceX/pll2_feedback_clk_present
+What:		/sys/bus/iio/devices/iio:deviceX/pll2_reference_clk_present
+What:		/sys/bus/iio/devices/iio:deviceX/pll1_reference_clk_a_present
+What:		/sys/bus/iio/devices/iio:deviceX/pll1_reference_clk_b_present
+What:		/sys/bus/iio/devices/iio:deviceX/pll1_reference_clk_test_present
+What:		/sys/bus/iio/devices/iio:deviceX/vcxo_clk_present
+KernelVersion:	3.4.0
+Contact:	linux-iio@vger.kernel.org
+Description:
+		Reading returns either '1' or '0'.
+		'1' means that the clock in question is present.
+		'0' means that the clock is missing.
+
+What:		/sys/bus/iio/devices/iio:deviceX/pllY_locked
+KernelVersion:	3.4.0
+Contact:	linux-iio@vger.kernel.org
+Description:
+		Reading returns either '1' or '0'. '1' means that the
+		pllY is locked.
+
+What:		/sys/bus/iio/devices/iio:deviceX/store_eeprom
+KernelVersion:	3.4.0
+Contact:	linux-iio@vger.kernel.org
+Description:
+		Writing '1' stores the current device configuration into
+		on-chip EEPROM. After power-up or chip reset the device will
+		automatically load the saved configuration.
+
+What:		/sys/bus/iio/devices/iio:deviceX/sync_dividers
+KernelVersion:	3.4.0
+Contact:	linux-iio@vger.kernel.org
+Description:
+		Writing '1' triggers the clock distribution synchronization
+		functionality. All dividers are reset and the channels start
+		with their predefined phase offsets (out_altvoltageY_phase).
+		Writing this file has the effect as driving the external
+		/SYNC pin low.
diff --git a/drivers/iio/Kconfig b/drivers/iio/Kconfig
index 56eecef..ad09f67 100644
--- a/drivers/iio/Kconfig
+++ b/drivers/iio/Kconfig
@@ -50,5 +50,6 @@ config IIO_CONSUMERS_PER_TRIGGER

 source "drivers/iio/adc/Kconfig"
 source "drivers/iio/amplifiers/Kconfig"
+source "drivers/iio/frequency/Kconfig"

 endif # IIO
diff --git a/drivers/iio/Makefile b/drivers/iio/Makefile
index e425afd..014e0e4 100644
--- a/drivers/iio/Makefile
+++ b/drivers/iio/Makefile
@@ -11,3 +11,4 @@ obj-$(CONFIG_IIO_KFIFO_BUF) += kfifo_buf.o

 obj-y += adc/
 obj-y += amplifiers/
+obj-y += frequency/
diff --git a/drivers/iio/frequency/Kconfig b/drivers/iio/frequency/Kconfig
new file mode 100644
index 0000000..0458c92
--- /dev/null
+++ b/drivers/iio/frequency/Kconfig
@@ -0,0 +1,23 @@
+#
+# Frequency
+#	Direct Digital Synthesis drivers (DDS)
+#	Clock Distribution device drivers
+#	Phase-Locked Loop (PLL) frequency synthesizers
+#
+
+menu "Frequency Synthesizers DDS/PLL"
+
+menu "Clock Generator/Distribution"
+
+config AD9523
+	tristate "Analog Devices AD9523 Low Jitter Clock Generator"
+	depends on SPI
+	help
+	  Say yes here to build support for Analog Devices AD9523 Low Jitter
+	  Clock Generator. The driver provides direct access via sysfs.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called ad9523.
+
+endmenu
+endmenu
diff --git a/drivers/iio/frequency/Makefile b/drivers/iio/frequency/Makefile
new file mode 100644
index 0000000..1b5b224
--- /dev/null
+++ b/drivers/iio/frequency/Makefile
@@ -0,0 +1,5 @@
+#
+# Makefile iio/frequency
+#
+
+obj-$(CONFIG_AD9523) += ad9523.o
diff --git a/drivers/iio/frequency/ad9523.c b/drivers/iio/frequency/ad9523.c
new file mode 100644
index 0000000..7272924
--- /dev/null
+++ b/drivers/iio/frequency/ad9523.c
@@ -0,0 +1,1057 @@
+/*
+ * AD9523 SPI Low Jitter Clock Generator
+ *
+ * Copyright 2012 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+#include <linux/spi/spi.h>
+#include <linux/regulator/consumer.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/frequency/ad9523.h>
+
+#define AD9523_READ	(1 << 15)
+#define AD9523_WRITE	(0 << 15)
+#define AD9523_CNT(x)	(((x) - 1) << 13)
+#define AD9523_ADDR(x)	((x) & 0xFFF)
+
+#define AD9523_R1B	(1 << 16)
+#define AD9523_R2B	(2 << 16)
+#define AD9523_R3B	(3 << 16)
+#define AD9523_TRANSF_LEN(x)			((x) >> 16)
+
+#define AD9523_SERIAL_PORT_CONFIG		(AD9523_R1B | 0x0)
+#define AD9523_VERSION_REGISTER			(AD9523_R1B | 0x2)
+#define AD9523_PART_REGISTER			(AD9523_R1B | 0x3)
+#define AD9523_READBACK_CTRL			(AD9523_R1B | 0x4)
+
+#define AD9523_EEPROM_CUSTOMER_VERSION_ID	(AD9523_R2B | 0x6)
+
+#define AD9523_PLL1_REF_A_DIVIDER		(AD9523_R2B | 0x11)
+#define AD9523_PLL1_REF_B_DIVIDER		(AD9523_R2B | 0x13)
+#define AD9523_PLL1_REF_TEST_DIVIDER		(AD9523_R1B | 0x14)
+#define AD9523_PLL1_FEEDBACK_DIVIDER		(AD9523_R2B | 0x17)
+#define AD9523_PLL1_CHARGE_PUMP_CTRL		(AD9523_R2B | 0x19)
+#define AD9523_PLL1_INPUT_RECEIVERS_CTRL	(AD9523_R1B | 0x1A)
+#define AD9523_PLL1_REF_CTRL			(AD9523_R1B | 0x1B)
+#define AD9523_PLL1_MISC_CTRL			(AD9523_R1B | 0x1C)
+#define AD9523_PLL1_LOOP_FILTER_CTRL		(AD9523_R1B | 0x1D)
+
+#define AD9523_PLL2_CHARGE_PUMP			(AD9523_R1B | 0xF0)
+#define AD9523_PLL2_FEEDBACK_DIVIDER_AB		(AD9523_R1B | 0xF1)
+#define AD9523_PLL2_CTRL			(AD9523_R1B | 0xF2)
+#define AD9523_PLL2_VCO_CTRL			(AD9523_R1B | 0xF3)
+#define AD9523_PLL2_VCO_DIVIDER			(AD9523_R1B | 0xF4)
+#define AD9523_PLL2_LOOP_FILTER_CTRL		(AD9523_R2B | 0xF6)
+#define AD9523_PLL2_R2_DIVIDER			(AD9523_R1B | 0xF7)
+
+#define AD9523_CHANNEL_CLOCK_DIST(ch)		(AD9523_R3B | (0x192 + 3 * ch))
+
+#define AD9523_PLL1_OUTPUT_CTRL			(AD9523_R1B | 0x1BA)
+#define AD9523_PLL1_OUTPUT_CHANNEL_CTRL		(AD9523_R1B | 0x1BB)
+
+#define AD9523_READBACK_0			(AD9523_R1B | 0x22C)
+#define AD9523_READBACK_1			(AD9523_R1B | 0x22D)
+
+#define AD9523_STATUS_SIGNALS			(AD9523_R3B | 0x232)
+#define AD9523_POWER_DOWN_CTRL			(AD9523_R1B | 0x233)
+#define AD9523_IO_UPDATE			(AD9523_R1B | 0x234)
+
+#define AD9523_EEPROM_DATA_XFER_STATUS		(AD9523_R1B | 0xB00)
+#define AD9523_EEPROM_ERROR_READBACK		(AD9523_R1B | 0xB01)
+#define AD9523_EEPROM_CTRL1			(AD9523_R1B | 0xB02)
+#define AD9523_EEPROM_CTRL2			(AD9523_R1B | 0xB03)
+
+/* AD9523_SERIAL_PORT_CONFIG */
+
+#define AD9523_SER_CONF_SDO_ACTIVE		(1 << 7)
+#define AD9523_SER_CONF_SOFT_RESET		(1 << 5)
+
+/* AD9523_READBACK_CTRL */
+#define AD9523_READBACK_CTRL_READ_BUFFERED	(1 << 0)
+
+/* AD9523_PLL1_CHARGE_PUMP_CTRL */
+#define AD9523_PLL1_CHARGE_PUMP_CURRENT_nA(x)	(((x) / 500) & 0x7F)
+#define AD9523_PLL1_CHARGE_PUMP_TRISTATE	(1 << 7)
+#define AD9523_PLL1_CHARGE_PUMP_MODE_NORMAL	(3 << 8)
+#define AD9523_PLL1_CHARGE_PUMP_MODE_PUMP_DOWN	(2 << 8)
+#define AD9523_PLL1_CHARGE_PUMP_MODE_PUMP_UP	(1 << 8)
+#define AD9523_PLL1_CHARGE_PUMP_MODE_TRISTATE	(0 << 8)
+#define AD9523_PLL1_BACKLASH_PW_MIN		(0 << 10)
+#define AD9523_PLL1_BACKLASH_PW_LOW		(1 << 10)
+#define AD9523_PLL1_BACKLASH_PW_HIGH		(2 << 10)
+#define AD9523_PLL1_BACKLASH_PW_MAX		(3 << 10)
+
+/* AD9523_PLL1_INPUT_RECEIVERS_CTRL */
+#define AD9523_PLL1_REF_TEST_RCV_EN		(1 << 7)
+#define AD9523_PLL1_REFB_DIFF_RCV_EN		(1 << 6)
+#define AD9523_PLL1_REFA_DIFF_RCV_EN		(1 << 5)
+#define AD9523_PLL1_REFB_RCV_EN			(1 << 4)
+#define AD9523_PLL1_REFA_RCV_EN			(1 << 3)
+#define AD9523_PLL1_REFA_REFB_PWR_CTRL_EN	(1 << 2)
+#define AD9523_PLL1_OSC_IN_CMOS_NEG_INP_EN	(1 << 1)
+#define AD9523_PLL1_OSC_IN_DIFF_EN		(1 << 0)
+
+/* AD9523_PLL1_REF_CTRL */
+#define AD9523_PLL1_BYPASS_REF_TEST_DIV_EN	(1 << 7)
+#define AD9523_PLL1_BYPASS_FEEDBACK_DIV_EN	(1 << 6)
+#define AD9523_PLL1_ZERO_DELAY_MODE_INT		(1 << 5)
+#define AD9523_PLL1_ZERO_DELAY_MODE_EXT		(0 << 5)
+#define AD9523_PLL1_OSC_IN_PLL_FEEDBACK_EN	(1 << 4)
+#define AD9523_PLL1_ZD_IN_CMOS_NEG_INP_EN	(1 << 3)
+#define AD9523_PLL1_ZD_IN_DIFF_EN		(1 << 2)
+#define AD9523_PLL1_REFB_CMOS_NEG_INP_EN	(1 << 1)
+#define AD9523_PLL1_REFA_CMOS_NEG_INP_EN	(1 << 0)
+
+/* AD9523_PLL1_MISC_CTRL */
+#define AD9523_PLL1_REFB_INDEP_DIV_CTRL_EN	(1 << 7)
+#define AD9523_PLL1_OSC_CTRL_FAIL_VCC_BY2_EN	(1 << 6)
+#define AD9523_PLL1_REF_MODE(x)			((x) << 2)
+#define AD9523_PLL1_BYPASS_REFB_DIV		(1 << 1)
+#define AD9523_PLL1_BYPASS_REFA_DIV		(1 << 0)
+
+/* AD9523_PLL1_LOOP_FILTER_CTRL */
+#define AD9523_PLL1_LOOP_FILTER_RZERO(x)	((x) & 0xF)
+
+/* AD9523_PLL2_CHARGE_PUMP */
+#define AD9523_PLL2_CHARGE_PUMP_CURRENT_nA(x)	((x) / 3500)
+
+/* AD9523_PLL2_FEEDBACK_DIVIDER_AB */
+#define AD9523_PLL2_FB_NDIV_A_CNT(x)		(((x) & 0x3) << 6)
+#define AD9523_PLL2_FB_NDIV_B_CNT(x)		(((x) & 0x3F) << 0)
+#define AD9523_PLL2_FB_NDIV(a, b)		(4 * (b) + (a))
+
+/* AD9523_PLL2_CTRL */
+#define AD9523_PLL2_CHARGE_PUMP_MODE_NORMAL	(3 << 0)
+#define AD9523_PLL2_CHARGE_PUMP_MODE_PUMP_DOWN	(2 << 0)
+#define AD9523_PLL2_CHARGE_PUMP_MODE_PUMP_UP	(1 << 0)
+#define AD9523_PLL2_CHARGE_PUMP_MODE_TRISTATE	(0 << 0)
+#define AD9523_PLL2_BACKLASH_PW_MIN		(0 << 2)
+#define AD9523_PLL2_BACKLASH_PW_LOW		(1 << 2)
+#define AD9523_PLL2_BACKLASH_PW_HIGH		(2 << 2)
+#define AD9523_PLL2_BACKLASH_PW_MAX		(3 << 1)
+#define AD9523_PLL2_BACKLASH_CTRL_EN		(1 << 4)
+#define AD9523_PLL2_FREQ_DOUBLER_EN		(1 << 5)
+#define AD9523_PLL2_LOCK_DETECT_PWR_DOWN_EN	(1 << 7)
+
+/* AD9523_PLL2_VCO_CTRL */
+#define AD9523_PLL2_VCO_CALIBRATE		(1 << 1)
+#define AD9523_PLL2_FORCE_VCO_MIDSCALE		(1 << 2)
+#define AD9523_PLL2_FORCE_REFERENCE_VALID	(1 << 3)
+#define AD9523_PLL2_FORCE_RELEASE_SYNC		(1 << 4)
+
+/* AD9523_PLL2_VCO_DIVIDER */
+#define AD9523_PLL2_VCO_DIV_M1(x)		((((x) - 3) & 0x3) << 0)
+#define AD9523_PLL2_VCO_DIV_M2(x)		((((x) - 3) & 0x3) << 4)
+#define AD9523_PLL2_VCO_DIV_M1_PWR_DOWN_EN	(1 << 2)
+#define AD9523_PLL2_VCO_DIV_M2_PWR_DOWN_EN	(1 << 6)
+
+/* AD9523_PLL2_LOOP_FILTER_CTRL */
+#define AD9523_PLL2_LOOP_FILTER_CPOLE1(x)	(((x) & 0x7) << 0)
+#define AD9523_PLL2_LOOP_FILTER_RZERO(x)	(((x) & 0x7) << 3)
+#define AD9523_PLL2_LOOP_FILTER_RPOLE2(x)	(((x) & 0x7) << 6)
+#define AD9523_PLL2_LOOP_FILTER_RZERO_BYPASS_EN	(1 << 8)
+
+/* AD9523_PLL2_R2_DIVIDER */
+#define AD9523_PLL2_R2_DIVIDER_VAL(x)		(((x) & 0x1F) << 0)
+
+/* AD9523_CHANNEL_CLOCK_DIST */
+#define AD9523_CLK_DIST_DIV_PHASE(x)		(((x) & 0x3F) << 18)
+#define AD9523_CLK_DIST_DIV_PHASE_REV(x)	((ret >> 18) & 0x3F)
+#define AD9523_CLK_DIST_DIV(x)			((((x) - 1) & 0x3FF) << 8)
+#define AD9523_CLK_DIST_DIV_REV(x)		(((ret >> 8) & 0x3FF) + 1)
+#define AD9523_CLK_DIST_INV_DIV_OUTPUT_EN	(1 << 7)
+#define AD9523_CLK_DIST_IGNORE_SYNC_EN		(1 << 6)
+#define AD9523_CLK_DIST_PWR_DOWN_EN		(1 << 5)
+#define AD9523_CLK_DIST_LOW_PWR_MODE_EN		(1 << 4)
+#define AD9523_CLK_DIST_DRIVER_MODE(x)		(((x) & 0xF) << 0)
+
+/* AD9523_PLL1_OUTPUT_CTRL */
+#define AD9523_PLL1_OUTP_CTRL_VCO_DIV_SEL_CH6_M2	(1 << 7)
+#define AD9523_PLL1_OUTP_CTRL_VCO_DIV_SEL_CH5_M2	(1 << 6)
+#define AD9523_PLL1_OUTP_CTRL_VCO_DIV_SEL_CH4_M2	(1 << 5)
+#define AD9523_PLL1_OUTP_CTRL_CMOS_DRV_WEAK		(1 << 4)
+#define AD9523_PLL1_OUTP_CTRL_OUTPUT_DIV_1		(0 << 0)
+#define AD9523_PLL1_OUTP_CTRL_OUTPUT_DIV_2		(1 << 0)
+#define AD9523_PLL1_OUTP_CTRL_OUTPUT_DIV_4		(2 << 0)
+#define AD9523_PLL1_OUTP_CTRL_OUTPUT_DIV_8		(4 << 0)
+#define AD9523_PLL1_OUTP_CTRL_OUTPUT_DIV_16		(8 << 0)
+
+/* AD9523_PLL1_OUTPUT_CHANNEL_CTRL */
+#define AD9523_PLL1_OUTP_CH_CTRL_OUTPUT_PWR_DOWN_EN	(1 << 7)
+#define AD9523_PLL1_OUTP_CH_CTRL_VCO_DIV_SEL_CH9_M2	(1 << 6)
+#define AD9523_PLL1_OUTP_CH_CTRL_VCO_DIV_SEL_CH8_M2	(1 << 5)
+#define AD9523_PLL1_OUTP_CH_CTRL_VCO_DIV_SEL_CH7_M2	(1 << 4)
+#define AD9523_PLL1_OUTP_CH_CTRL_VCXO_SRC_SEL_CH3	(1 << 3)
+#define AD9523_PLL1_OUTP_CH_CTRL_VCXO_SRC_SEL_CH2	(1 << 2)
+#define AD9523_PLL1_OUTP_CH_CTRL_VCXO_SRC_SEL_CH1	(1 << 1)
+#define AD9523_PLL1_OUTP_CH_CTRL_VCXO_SRC_SEL_CH0	(1 << 0)
+
+/* AD9523_READBACK_0 */
+#define AD9523_READBACK_0_STAT_PLL2_REF_CLK		(1 << 7)
+#define AD9523_READBACK_0_STAT_PLL2_FB_CLK		(1 << 6)
+#define AD9523_READBACK_0_STAT_VCXO			(1 << 5)
+#define AD9523_READBACK_0_STAT_REF_TEST			(1 << 4)
+#define AD9523_READBACK_0_STAT_REFB			(1 << 3)
+#define AD9523_READBACK_0_STAT_REFA			(1 << 2)
+#define AD9523_READBACK_0_STAT_PLL2_LD			(1 << 1)
+#define AD9523_READBACK_0_STAT_PLL1_LD			(1 << 0)
+
+/* AD9523_READBACK_1 */
+#define AD9523_READBACK_1_HOLDOVER_ACTIVE		(1 << 3)
+#define AD9523_READBACK_1_AUTOMODE_SEL_REFB		(1 << 2)
+#define AD9523_READBACK_1_VCO_CALIB_IN_PROGRESS		(1 << 0)
+
+/* AD9523_STATUS_SIGNALS */
+#define AD9523_STATUS_SIGNALS_SYNC_MAN_CTRL		(1 << 16)
+#define AD9523_STATUS_MONITOR_01_PLL12_LOCKED		(0x302)
+/* AD9523_POWER_DOWN_CTRL */
+#define AD9523_POWER_DOWN_CTRL_PLL1_PWR_DOWN		(1 << 2)
+#define AD9523_POWER_DOWN_CTRL_PLL2_PWR_DOWN		(1 << 1)
+#define AD9523_POWER_DOWN_CTRL_DIST_PWR_DOWN		(1 << 0)
+
+/* AD9523_IO_UPDATE */
+#define AD9523_IO_UPDATE_EN				(1 << 0)
+
+/* AD9523_EEPROM_DATA_XFER_STATUS */
+#define AD9523_EEPROM_DATA_XFER_IN_PROGRESS		(1 << 0)
+
+/* AD9523_EEPROM_ERROR_READBACK */
+#define AD9523_EEPROM_ERROR_READBACK_FAIL		(1 << 0)
+
+/* AD9523_EEPROM_CTRL1 */
+#define AD9523_EEPROM_CTRL1_SOFT_EEPROM			(1 << 1)
+#define AD9523_EEPROM_CTRL1_EEPROM_WRITE_PROT_DIS	(1 << 0)
+
+/* AD9523_EEPROM_CTRL2 */
+#define AD9523_EEPROM_CTRL2_REG2EEPROM			(1 << 0)
+
+#define AD9523_NUM_CHAN					14
+#define AD9523_NUM_CHAN_ALT_CLK_SRC			10
+
+/* Helpers to avoid excess line breaks */
+#define AD_IFE(_pde, _a, _b) ((pdata->_pde) ? _a : _b)
+#define AD_IF(_pde, _a) AD_IFE(_pde, _a, 0)
+
+enum {
+	AD9523_STAT_PLL1_LD,
+	AD9523_STAT_PLL2_LD,
+	AD9523_STAT_REFA,
+	AD9523_STAT_REFB,
+	AD9523_STAT_REF_TEST,
+	AD9523_STAT_VCXO,
+	AD9523_STAT_PLL2_FB_CLK,
+	AD9523_STAT_PLL2_REF_CLK,
+	AD9523_SYNC,
+	AD9523_EEPROM,
+};
+
+enum {
+	AD9523_VCO1,
+	AD9523_VCO2,
+	AD9523_VCXO,
+	AD9523_NUM_CLK_SRC,
+};
+
+struct ad9523_state {
+	struct spi_device		*spi;
+	struct regulator		*reg;
+	struct ad9523_platform_data	*pdata;
+	struct iio_chan_spec		ad9523_channels[AD9523_NUM_CHAN];
+
+	unsigned long		vcxo_freq;
+	unsigned long		vco_freq;
+	unsigned long		vco_out_freq[AD9523_NUM_CLK_SRC];
+	unsigned char		vco_out_map[AD9523_NUM_CHAN_ALT_CLK_SRC];
+
+	/*
+	 * DMA (thus cache coherency maintenance) requires the
+	 * transfer buffers to live in their own cache lines.
+	 */
+	union {
+		__be32 d32;
+		u8 d8[4];
+	} data[2] ____cacheline_aligned;
+};
+
+static int ad9523_read(struct iio_dev *indio_dev, unsigned addr)
+{
+	struct ad9523_state *st = iio_priv(indio_dev);
+	struct spi_message m;
+	int ret;
+
+	/* We encode the register size 1..3 bytes into the register address.
+	 * On transfer we get the size from the register datum, and make sure
+	 * the result is properly aligned.
+	 */
+
+	struct spi_transfer t[] = {
+		{
+			.tx_buf = &st->data[0].d8[2],
+			.len = 2,
+		}, {
+			.rx_buf = &st->data[1].d8[4 - AD9523_TRANSF_LEN(addr)],
+			.len = AD9523_TRANSF_LEN(addr),
+		},
+	};
+
+	spi_message_init(&m);
+	spi_message_add_tail(&t[0], &m);
+	spi_message_add_tail(&t[1], &m);
+
+	st->data[0].d32 = cpu_to_be32(AD9523_READ |
+				      AD9523_CNT(AD9523_TRANSF_LEN(addr)) |
+				      AD9523_ADDR(addr));
+
+	ret = spi_sync(st->spi, &m);
+	if (ret < 0)
+		dev_err(&indio_dev->dev, "read failed (%d)", ret);
+	else
+		ret = be32_to_cpu(st->data[1].d32) & (0xFFFFFF >>
+				  (8 * (3 - AD9523_TRANSF_LEN(addr))));
+
+	return ret;
+};
+
+static int ad9523_write(struct iio_dev *indio_dev, unsigned addr, unsigned val)
+{
+	struct ad9523_state *st = iio_priv(indio_dev);
+	struct spi_message m;
+	int ret;
+	struct spi_transfer t[] = {
+		{
+			.tx_buf = &st->data[0].d8[2],
+			.len = 2,
+		}, {
+			.tx_buf = &st->data[1].d8[4 - AD9523_TRANSF_LEN(addr)],
+			.len = AD9523_TRANSF_LEN(addr),
+		},
+	};
+
+	spi_message_init(&m);
+	spi_message_add_tail(&t[0], &m);
+	spi_message_add_tail(&t[1], &m);
+
+	st->data[0].d32 = cpu_to_be32(AD9523_WRITE |
+				      AD9523_CNT(AD9523_TRANSF_LEN(addr)) |
+				      AD9523_ADDR(addr));
+	st->data[1].d32 = cpu_to_be32(val);
+
+	ret = spi_sync(st->spi, &m);
+
+	if (ret < 0)
+		dev_err(&indio_dev->dev, "write failed (%d)", ret);
+
+	return ret;
+}
+
+static int ad9523_io_update(struct iio_dev *indio_dev)
+{
+	return ad9523_write(indio_dev, AD9523_IO_UPDATE, AD9523_IO_UPDATE_EN);
+}
+
+static int ad9523_vco_out_map(struct iio_dev *indio_dev,
+			      unsigned ch, bool out)
+{
+	struct ad9523_state *st = iio_priv(indio_dev);
+	int ret;
+	unsigned mask;
+
+	switch (ch) {
+	case 0 ... 3:
+		ret = ad9523_read(indio_dev, AD9523_PLL1_OUTPUT_CHANNEL_CTRL);
+		if (ret < 0)
+			break;
+		mask = AD9523_PLL1_OUTP_CH_CTRL_VCXO_SRC_SEL_CH0 << ch;
+		if (out) {
+			ret |= mask;
+			out = 2;
+		} else {
+			ret &= ~mask;
+		}
+		ret = ad9523_write(indio_dev,
+				   AD9523_PLL1_OUTPUT_CHANNEL_CTRL, ret);
+		break;
+	case 4 ... 6:
+		ret = ad9523_read(indio_dev, AD9523_PLL1_OUTPUT_CTRL);
+		if (ret < 0)
+			break;
+		mask = AD9523_PLL1_OUTP_CTRL_VCO_DIV_SEL_CH4_M2 << (ch - 4);
+		if (out)
+			ret |= mask;
+		else
+			ret &= ~mask;
+		ret = ad9523_write(indio_dev, AD9523_PLL1_OUTPUT_CTRL, ret);
+		break;
+	case 7 ... 9:
+		ret = ad9523_read(indio_dev, AD9523_PLL1_OUTPUT_CHANNEL_CTRL);
+		if (ret < 0)
+			break;
+		mask = AD9523_PLL1_OUTP_CH_CTRL_VCO_DIV_SEL_CH7_M2 << (ch - 7);
+		if (out)
+			ret |= mask;
+		else
+			ret &= ~mask;
+		ret = ad9523_write(indio_dev,
+				   AD9523_PLL1_OUTPUT_CHANNEL_CTRL, ret);
+		break;
+	default:
+		return 0;
+	}
+
+	st->vco_out_map[ch] = out;
+
+	return ret;
+}
+
+static int ad9523_set_clock_provider(struct iio_dev *indio_dev,
+			      unsigned ch, unsigned long freq)
+{
+	struct ad9523_state *st = iio_priv(indio_dev);
+	long tmp1, tmp2;
+	bool use_alt_clk_src;
+
+	switch (ch) {
+	case 0 ... 3:
+		use_alt_clk_src = (freq == st->vco_out_freq[AD9523_VCXO]);
+		break;
+	case 4 ... 9:
+		tmp1 = st->vco_out_freq[AD9523_VCO1] / freq;
+		tmp2 = st->vco_out_freq[AD9523_VCO2] / freq;
+		tmp1 *= freq;
+		tmp2 *= freq;
+		use_alt_clk_src = (abs(tmp1 - freq) > abs(tmp2 - freq));
+		break;
+	default:
+		/* Ch 10..14: No action required, return success */
+		return 0;
+	}
+
+	return ad9523_vco_out_map(indio_dev, ch, use_alt_clk_src);
+}
+
+static int ad9523_store_eeprom(struct iio_dev *indio_dev)
+{
+	int ret, tmp;
+
+	ret = ad9523_write(indio_dev, AD9523_EEPROM_CTRL1,
+			   AD9523_EEPROM_CTRL1_EEPROM_WRITE_PROT_DIS);
+	if (ret < 0)
+		return ret;
+	ret = ad9523_write(indio_dev, AD9523_EEPROM_CTRL2,
+			   AD9523_EEPROM_CTRL2_REG2EEPROM);
+	if (ret < 0)
+		return ret;
+
+	tmp = 4;
+	do {
+		msleep(16);
+		ret = ad9523_read(indio_dev,
+				  AD9523_EEPROM_DATA_XFER_STATUS);
+		if (ret < 0)
+			return ret;
+	} while ((ret & AD9523_EEPROM_DATA_XFER_IN_PROGRESS) && tmp--);
+
+	ret = ad9523_write(indio_dev, AD9523_EEPROM_CTRL1, 0);
+	if (ret < 0)
+		return ret;
+
+	ret = ad9523_read(indio_dev, AD9523_EEPROM_ERROR_READBACK);
+	if (ret < 0)
+		return ret;
+
+	if (ret & AD9523_EEPROM_ERROR_READBACK_FAIL) {
+		dev_err(&indio_dev->dev, "Verify EEPROM failed");
+		ret = -EIO;
+	}
+
+	return ret;
+}
+
+static int ad9523_sync(struct iio_dev *indio_dev)
+{
+	int ret, tmp;
+
+	ret = ad9523_read(indio_dev, AD9523_STATUS_SIGNALS);
+	if (ret < 0)
+		return ret;
+
+	tmp = ret;
+	tmp |= AD9523_STATUS_SIGNALS_SYNC_MAN_CTRL;
+
+	ret = ad9523_write(indio_dev, AD9523_STATUS_SIGNALS, tmp);
+	if (ret < 0)
+		return ret;
+
+	ad9523_io_update(indio_dev);
+	tmp &= ~AD9523_STATUS_SIGNALS_SYNC_MAN_CTRL;
+
+	ret = ad9523_write(indio_dev, AD9523_STATUS_SIGNALS, tmp);
+	if (ret < 0)
+		return ret;
+
+	return ad9523_io_update(indio_dev);
+}
+
+static ssize_t ad9523_store(struct device *dev,
+				struct device_attribute *attr,
+				const char *buf, size_t len)
+{
+	struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+	bool state;
+	int ret;
+
+	ret = strtobool(buf, &state);
+	if (ret < 0)
+		return ret;
+
+	if (!state)
+		return 0;
+
+	mutex_lock(&indio_dev->mlock);
+	switch ((u32)this_attr->address) {
+	case AD9523_SYNC:
+		ret = ad9523_sync(indio_dev);
+		break;
+	case AD9523_EEPROM:
+		ret = ad9523_store_eeprom(indio_dev);
+		break;
+	default:
+		ret = -ENODEV;
+	}
+	mutex_unlock(&indio_dev->mlock);
+
+	return ret ? ret : len;
+}
+
+static ssize_t ad9523_show(struct device *dev,
+			struct device_attribute *attr,
+			char *buf)
+{
+	struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+	int ret;
+
+	mutex_lock(&indio_dev->mlock);
+	ret = ad9523_read(indio_dev, AD9523_READBACK_0);
+	if (ret >= 0) {
+		ret = sprintf(buf, "%d\n", !!(ret & (1 <<
+			(u32)this_attr->address)));
+	}
+	mutex_unlock(&indio_dev->mlock);
+
+	return ret;
+}
+
+static IIO_DEVICE_ATTR(pll1_locked, S_IRUGO,
+			ad9523_show,
+			NULL,
+			AD9523_STAT_PLL1_LD);
+
+static IIO_DEVICE_ATTR(pll2_locked, S_IRUGO,
+			ad9523_show,
+			NULL,
+			AD9523_STAT_PLL2_LD);
+
+static IIO_DEVICE_ATTR(pll1_reference_clk_a_present, S_IRUGO,
+			ad9523_show,
+			NULL,
+			AD9523_STAT_REFA);
+
+static IIO_DEVICE_ATTR(pll1_reference_clk_b_present, S_IRUGO,
+			ad9523_show,
+			NULL,
+			AD9523_STAT_REFB);
+
+static IIO_DEVICE_ATTR(pll1_reference_clk_test_present, S_IRUGO,
+			ad9523_show,
+			NULL,
+			AD9523_STAT_REF_TEST);
+
+static IIO_DEVICE_ATTR(vcxo_clk_present, S_IRUGO,
+			ad9523_show,
+			NULL,
+			AD9523_STAT_VCXO);
+
+static IIO_DEVICE_ATTR(pll2_feedback_clk_present, S_IRUGO,
+			ad9523_show,
+			NULL,
+			AD9523_STAT_PLL2_FB_CLK);
+
+static IIO_DEVICE_ATTR(pll2_reference_clk_present, S_IRUGO,
+			ad9523_show,
+			NULL,
+			AD9523_STAT_PLL2_REF_CLK);
+
+static IIO_DEVICE_ATTR(sync_dividers, S_IWUSR,
+			NULL,
+			ad9523_store,
+			AD9523_SYNC);
+
+static IIO_DEVICE_ATTR(store_eeprom, S_IWUSR,
+			NULL,
+			ad9523_store,
+			AD9523_EEPROM);
+
+static struct attribute *ad9523_attributes[] = {
+	&iio_dev_attr_sync_dividers.dev_attr.attr,
+	&iio_dev_attr_store_eeprom.dev_attr.attr,
+	&iio_dev_attr_pll2_feedback_clk_present.dev_attr.attr,
+	&iio_dev_attr_pll2_reference_clk_present.dev_attr.attr,
+	&iio_dev_attr_pll1_reference_clk_a_present.dev_attr.attr,
+	&iio_dev_attr_pll1_reference_clk_b_present.dev_attr.attr,
+	&iio_dev_attr_pll1_reference_clk_test_present.dev_attr.attr,
+	&iio_dev_attr_vcxo_clk_present.dev_attr.attr,
+	&iio_dev_attr_pll1_locked.dev_attr.attr,
+	&iio_dev_attr_pll2_locked.dev_attr.attr,
+	NULL,
+};
+
+static const struct attribute_group ad9523_attribute_group = {
+	.attrs = ad9523_attributes,
+};
+
+static int ad9523_read_raw(struct iio_dev *indio_dev,
+			   struct iio_chan_spec const *chan,
+			   int *val,
+			   int *val2,
+			   long m)
+{
+	struct ad9523_state *st = iio_priv(indio_dev);
+	unsigned code;
+	int ret;
+
+	mutex_lock(&indio_dev->mlock);
+	ret = ad9523_read(indio_dev, AD9523_CHANNEL_CLOCK_DIST(chan->channel));
+	mutex_unlock(&indio_dev->mlock);
+
+	if (ret < 0)
+		return ret;
+
+	switch (m) {
+	case IIO_CHAN_INFO_RAW:
+		*val = !(ret & AD9523_CLK_DIST_PWR_DOWN_EN);
+		return IIO_VAL_INT;
+	case IIO_CHAN_INFO_FREQUENCY:
+		*val = st->vco_out_freq[st->vco_out_map[chan->channel]] /
+			AD9523_CLK_DIST_DIV_REV(ret);
+		return IIO_VAL_INT;
+	case IIO_CHAN_INFO_PHASE:
+		code = (AD9523_CLK_DIST_DIV_PHASE_REV(ret) * 3141592) /
+			AD9523_CLK_DIST_DIV_REV(ret);
+		*val = code / 1000000;
+		*val2 = (code % 1000000) * 10;
+		return IIO_VAL_INT_PLUS_MICRO;
+	default:
+		return -EINVAL;
+	}
+};
+
+static int ad9523_write_raw(struct iio_dev *indio_dev,
+			    struct iio_chan_spec const *chan,
+			    int val,
+			    int val2,
+			    long mask)
+{
+	struct ad9523_state *st = iio_priv(indio_dev);
+	unsigned reg;
+	int ret, tmp, code;
+
+	mutex_lock(&indio_dev->mlock);
+	ret = ad9523_read(indio_dev, AD9523_CHANNEL_CLOCK_DIST(chan->channel));
+	if (ret < 0)
+		goto out;
+
+	reg = ret;
+
+	switch (mask) {
+	case IIO_CHAN_INFO_RAW:
+		if (val)
+			reg &= ~AD9523_CLK_DIST_PWR_DOWN_EN;
+		else
+			reg |= AD9523_CLK_DIST_PWR_DOWN_EN;
+		break;
+	case IIO_CHAN_INFO_FREQUENCY:
+		if (val <= 0) {
+			ret = -EINVAL;
+			goto out;
+		}
+		ret = ad9523_set_clock_provider(indio_dev, chan->channel, val);
+		if (ret < 0)
+			goto out;
+		tmp = st->vco_out_freq[st->vco_out_map[chan->channel]] / val;
+		tmp = clamp(tmp, 1, 1024);
+		reg &= ~(0x3FF << 8);
+		reg |= AD9523_CLK_DIST_DIV(tmp);
+		break;
+	case IIO_CHAN_INFO_PHASE:
+		code = val * 1000000 + val2 % 1000000;
+		tmp = (code * AD9523_CLK_DIST_DIV_REV(ret)) / 3141592;
+		tmp = clamp(tmp, 0, 63);
+		reg &= ~AD9523_CLK_DIST_DIV_PHASE(~0);
+		reg |= AD9523_CLK_DIST_DIV_PHASE(tmp);
+		break;
+	default:
+		ret = -EINVAL;
+		goto out;
+	}
+
+	ret = ad9523_write(indio_dev, AD9523_CHANNEL_CLOCK_DIST(chan->channel),
+			   reg);
+	if (ret < 0)
+		goto out;
+
+	ad9523_io_update(indio_dev);
+out:
+	mutex_unlock(&indio_dev->mlock);
+	return ret;
+}
+
+static int ad9523_reg_access(struct iio_dev *indio_dev,
+			      unsigned reg, unsigned writeval,
+			      unsigned *readval)
+{
+	int ret;
+
+	mutex_lock(&indio_dev->mlock);
+	if (readval == NULL) {
+		ret = ad9523_write(indio_dev, reg | AD9523_R1B, writeval);
+		ad9523_io_update(indio_dev);
+	} else {
+		ret = ad9523_read(indio_dev, reg | AD9523_R1B);
+		if (ret < 0)
+			return ret;
+		*readval = ret;
+		ret = 0;
+	}
+	mutex_unlock(&indio_dev->mlock);
+
+	return ret;
+}
+
+static const struct iio_info ad9523_info = {
+	.read_raw = &ad9523_read_raw,
+	.write_raw = &ad9523_write_raw,
+	.debugfs_reg_access = &ad9523_reg_access,
+	.attrs = &ad9523_attribute_group,
+	.driver_module = THIS_MODULE,
+};
+
+static int ad9523_setup(struct iio_dev *indio_dev)
+{
+	struct ad9523_state *st = iio_priv(indio_dev);
+	struct ad9523_platform_data *pdata = st->pdata;
+	struct ad9523_channel_spec *chan;
+	unsigned long active_mask = 0;
+	int ret, i;
+
+	ret = ad9523_write(indio_dev, AD9523_SERIAL_PORT_CONFIG,
+			   AD9523_SER_CONF_SOFT_RESET |
+			  (st->spi->mode & SPI_3WIRE ? 0 :
+			  AD9523_SER_CONF_SDO_ACTIVE));
+	if (ret < 0)
+		return ret;
+
+	ret = ad9523_write(indio_dev, AD9523_READBACK_CTRL,
+			  AD9523_READBACK_CTRL_READ_BUFFERED);
+	if (ret < 0)
+		return ret;
+
+	ret = ad9523_io_update(indio_dev);
+	if (ret < 0)
+		return ret;
+
+	/*
+	 * PLL1 Setup
+	 */
+	ret = ad9523_write(indio_dev, AD9523_PLL1_REF_A_DIVIDER,
+		pdata->refa_r_div);
+	if (ret < 0)
+		return ret;
+
+	ret = ad9523_write(indio_dev, AD9523_PLL1_REF_B_DIVIDER,
+		pdata->refb_r_div);
+	if (ret < 0)
+		return ret;
+
+	ret = ad9523_write(indio_dev, AD9523_PLL1_FEEDBACK_DIVIDER,
+		pdata->pll1_feedback_div);
+	if (ret < 0)
+		return ret;
+
+	ret = ad9523_write(indio_dev, AD9523_PLL1_CHARGE_PUMP_CTRL,
+		AD9523_PLL1_CHARGE_PUMP_CURRENT_nA(pdata->
+			pll1_charge_pump_current_nA) |
+		AD9523_PLL1_CHARGE_PUMP_MODE_NORMAL |
+		AD9523_PLL1_BACKLASH_PW_MIN);
+	if (ret < 0)
+		return ret;
+
+	ret = ad9523_write(indio_dev, AD9523_PLL1_INPUT_RECEIVERS_CTRL,
+		AD_IF(refa_diff_rcv_en, AD9523_PLL1_REFA_RCV_EN) |
+		AD_IF(refb_diff_rcv_en, AD9523_PLL1_REFB_RCV_EN) |
+		AD_IF(osc_in_diff_en, AD9523_PLL1_OSC_IN_DIFF_EN) |
+		AD_IF(osc_in_cmos_neg_inp_en,
+		      AD9523_PLL1_OSC_IN_CMOS_NEG_INP_EN) |
+		AD_IF(refa_diff_rcv_en, AD9523_PLL1_REFA_DIFF_RCV_EN) |
+		AD_IF(refb_diff_rcv_en, AD9523_PLL1_REFB_DIFF_RCV_EN));
+	if (ret < 0)
+		return ret;
+
+	ret = ad9523_write(indio_dev, AD9523_PLL1_REF_CTRL,
+		AD_IF(zd_in_diff_en, AD9523_PLL1_ZD_IN_DIFF_EN) |
+		AD_IF(zd_in_cmos_neg_inp_en,
+		      AD9523_PLL1_ZD_IN_CMOS_NEG_INP_EN) |
+		AD_IF(zero_delay_mode_internal_en,
+		      AD9523_PLL1_ZERO_DELAY_MODE_INT) |
+		AD_IF(osc_in_feedback_en, AD9523_PLL1_OSC_IN_PLL_FEEDBACK_EN) |
+		AD_IF(refa_cmos_neg_inp_en, AD9523_PLL1_REFA_CMOS_NEG_INP_EN) |
+		AD_IF(refb_cmos_neg_inp_en, AD9523_PLL1_REFB_CMOS_NEG_INP_EN));
+	if (ret < 0)
+		return ret;
+
+	ret = ad9523_write(indio_dev, AD9523_PLL1_MISC_CTRL,
+		AD9523_PLL1_REFB_INDEP_DIV_CTRL_EN |
+		AD9523_PLL1_REF_MODE(pdata->ref_mode));
+	if (ret < 0)
+		return ret;
+
+	ret = ad9523_write(indio_dev, AD9523_PLL1_LOOP_FILTER_CTRL,
+		AD9523_PLL1_LOOP_FILTER_RZERO(pdata->pll1_loop_filter_rzero));
+	if (ret < 0)
+		return ret;
+	/*
+	 * PLL2 Setup
+	 */
+
+	ret = ad9523_write(indio_dev, AD9523_PLL2_CHARGE_PUMP,
+		AD9523_PLL2_CHARGE_PUMP_CURRENT_nA(pdata->
+			pll2_charge_pump_current_nA));
+	if (ret < 0)
+		return ret;
+
+	ret = ad9523_write(indio_dev, AD9523_PLL2_FEEDBACK_DIVIDER_AB,
+		AD9523_PLL2_FB_NDIV_A_CNT(pdata->pll2_ndiv_a_cnt) |
+		AD9523_PLL2_FB_NDIV_B_CNT(pdata->pll2_ndiv_b_cnt));
+	if (ret < 0)
+		return ret;
+
+	ret = ad9523_write(indio_dev, AD9523_PLL2_CTRL,
+		AD9523_PLL2_CHARGE_PUMP_MODE_NORMAL |
+		AD9523_PLL2_BACKLASH_CTRL_EN |
+		AD_IF(pll2_freq_doubler_en, AD9523_PLL2_FREQ_DOUBLER_EN));
+	if (ret < 0)
+		return ret;
+
+	st->vco_freq = (pdata->vcxo_freq * (pdata->pll2_freq_doubler_en ? 2 : 1)
+			/ pdata->pll2_r2_div) * AD9523_PLL2_FB_NDIV(pdata->
+			pll2_ndiv_a_cnt, pdata->pll2_ndiv_b_cnt);
+
+	ret = ad9523_write(indio_dev, AD9523_PLL2_VCO_CTRL,
+		AD9523_PLL2_VCO_CALIBRATE);
+	if (ret < 0)
+		return ret;
+
+	ret = ad9523_write(indio_dev, AD9523_PLL2_VCO_DIVIDER,
+		AD9523_PLL2_VCO_DIV_M1(pdata->pll2_vco_diff_m1) |
+		AD9523_PLL2_VCO_DIV_M2(pdata->pll2_vco_diff_m2) |
+		AD_IFE(pll2_vco_diff_m1, 0,
+		       AD9523_PLL2_VCO_DIV_M1_PWR_DOWN_EN) |
+		AD_IFE(pll2_vco_diff_m2, 0,
+		       AD9523_PLL2_VCO_DIV_M2_PWR_DOWN_EN));
+	if (ret < 0)
+		return ret;
+
+	if (pdata->pll2_vco_diff_m1)
+		st->vco_out_freq[AD9523_VCO1] =
+			st->vco_freq / pdata->pll2_vco_diff_m1;
+
+	if (pdata->pll2_vco_diff_m2)
+		st->vco_out_freq[AD9523_VCO2] =
+			st->vco_freq / pdata->pll2_vco_diff_m2;
+
+	st->vco_out_freq[AD9523_VCXO] = pdata->vcxo_freq;
+
+	ret = ad9523_write(indio_dev, AD9523_PLL2_R2_DIVIDER,
+		AD9523_PLL2_R2_DIVIDER_VAL(pdata->pll2_r2_div));
+	if (ret < 0)
+		return ret;
+
+	ret = ad9523_write(indio_dev, AD9523_PLL2_LOOP_FILTER_CTRL,
+		AD9523_PLL2_LOOP_FILTER_CPOLE1(pdata->cpole1) |
+		AD9523_PLL2_LOOP_FILTER_RZERO(pdata->rzero) |
+		AD9523_PLL2_LOOP_FILTER_RPOLE2(pdata->rpole2) |
+		AD_IF(rzero_bypass_en,
+		      AD9523_PLL2_LOOP_FILTER_RZERO_BYPASS_EN));
+	if (ret < 0)
+		return ret;
+
+	for (i = 0; i < pdata->num_channels; i++) {
+		chan = &pdata->channels[i];
+		if (chan->channel_num < AD9523_NUM_CHAN) {
+			__set_bit(chan->channel_num, &active_mask);
+			ret = ad9523_write(indio_dev,
+				AD9523_CHANNEL_CLOCK_DIST(chan->channel_num),
+				AD9523_CLK_DIST_DRIVER_MODE(chan->driver_mode) |
+				AD9523_CLK_DIST_DIV(chan->channel_divider) |
+				AD9523_CLK_DIST_DIV_PHASE(chan->divider_phase) |
+				(chan->sync_ignore_en ?
+					AD9523_CLK_DIST_IGNORE_SYNC_EN : 0) |
+				(chan->divider_output_invert_en ?
+					AD9523_CLK_DIST_INV_DIV_OUTPUT_EN : 0) |
+				(chan->low_power_mode_en ?
+					AD9523_CLK_DIST_LOW_PWR_MODE_EN : 0) |
+				(chan->output_dis ?
+					AD9523_CLK_DIST_PWR_DOWN_EN : 0));
+			if (ret < 0)
+				return ret;
+
+			ret = ad9523_vco_out_map(indio_dev, chan->channel_num,
+					   chan->use_alt_clock_src);
+			if (ret < 0)
+				return ret;
+
+			st->ad9523_channels[i].type = IIO_ALTVOLTAGE;
+			st->ad9523_channels[i].output = 1;
+			st->ad9523_channels[i].indexed = 1;
+			st->ad9523_channels[i].channel = chan->channel_num;
+			st->ad9523_channels[i].extend_name =
+				chan->extended_name;
+			st->ad9523_channels[i].info_mask =
+				IIO_CHAN_INFO_RAW_SEPARATE_BIT |
+				IIO_CHAN_INFO_PHASE_SEPARATE_BIT |
+				IIO_CHAN_INFO_FREQUENCY_SEPARATE_BIT;
+		}
+	}
+
+	for_each_clear_bit(i, &active_mask, AD9523_NUM_CHAN)
+		ad9523_write(indio_dev,
+			     AD9523_CHANNEL_CLOCK_DIST(i),
+			     AD9523_CLK_DIST_DRIVER_MODE(TRISTATE) |
+			     AD9523_CLK_DIST_PWR_DOWN_EN);
+
+	ret = ad9523_write(indio_dev, AD9523_POWER_DOWN_CTRL, 0);
+	if (ret < 0)
+		return ret;
+
+	ret = ad9523_write(indio_dev, AD9523_STATUS_SIGNALS,
+			   AD9523_STATUS_MONITOR_01_PLL12_LOCKED);
+	if (ret < 0)
+		return ret;
+
+	ret = ad9523_io_update(indio_dev);
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+
+static int __devinit ad9523_probe(struct spi_device *spi)
+{
+	struct ad9523_platform_data *pdata = spi->dev.platform_data;
+	struct iio_dev *indio_dev;
+	struct ad9523_state *st;
+	int ret;
+
+	if (!pdata) {
+		dev_err(&spi->dev, "no platform data?\n");
+		return -EINVAL;
+	}
+
+	indio_dev = iio_device_alloc(sizeof(*st));
+	if (indio_dev == NULL)
+		return -ENOMEM;
+
+	st = iio_priv(indio_dev);
+
+	st->reg = regulator_get(&spi->dev, "vcc");
+	if (!IS_ERR(st->reg)) {
+		ret = regulator_enable(st->reg);
+		if (ret)
+			goto error_put_reg;
+	}
+
+	spi_set_drvdata(spi, indio_dev);
+	st->spi = spi;
+	st->pdata = pdata;
+
+	indio_dev->dev.parent = &spi->dev;
+	indio_dev->name = (pdata->name[0] != 0) ? pdata->name :
+			  spi_get_device_id(spi)->name;
+	indio_dev->info = &ad9523_info;
+	indio_dev->modes = INDIO_DIRECT_MODE;
+	indio_dev->channels = st->ad9523_channels;
+	indio_dev->num_channels = pdata->num_channels;
+
+	ret = ad9523_setup(indio_dev);
+	if (ret < 0)
+		goto error_disable_reg;
+
+	ret = iio_device_register(indio_dev);
+	if (ret)
+		goto error_disable_reg;
+
+	dev_info(&spi->dev, "probed %s\n", indio_dev->name);
+
+	return 0;
+
+error_disable_reg:
+	if (!IS_ERR(st->reg))
+		regulator_disable(st->reg);
+error_put_reg:
+	if (!IS_ERR(st->reg))
+		regulator_put(st->reg);
+
+	iio_device_free(indio_dev);
+
+	return ret;
+}
+
+static int __devexit ad9523_remove(struct spi_device *spi)
+{
+	struct iio_dev *indio_dev = spi_get_drvdata(spi);
+	struct ad9523_state *st = iio_priv(indio_dev);
+
+	iio_device_unregister(indio_dev);
+
+	if (!IS_ERR(st->reg)) {
+		regulator_disable(st->reg);
+		regulator_put(st->reg);
+	}
+
+	iio_device_free(indio_dev);
+
+	return 0;
+}
+
+static const struct spi_device_id ad9523_id[] = {
+	{"ad9523-1", 9523},
+	{}
+};
+MODULE_DEVICE_TABLE(spi, ad9523_id);
+
+static struct spi_driver ad9523_driver = {
+	.driver = {
+		.name	= "ad9523",
+		.owner	= THIS_MODULE,
+	},
+	.probe		= ad9523_probe,
+	.remove		= __devexit_p(ad9523_remove),
+	.id_table	= ad9523_id,
+};
+module_spi_driver(ad9523_driver);
+
+MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
+MODULE_DESCRIPTION("Analog Devices AD9523 CLOCKDIST/PLL");
+MODULE_LICENSE("GPL v2");
diff --git a/include/linux/iio/frequency/ad9523.h b/include/linux/iio/frequency/ad9523.h
new file mode 100644
index 0000000..12ce3ee
--- /dev/null
+++ b/include/linux/iio/frequency/ad9523.h
@@ -0,0 +1,195 @@
+/*
+ * AD9523 SPI Low Jitter Clock Generator
+ *
+ * Copyright 2012 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2.
+ */
+
+#ifndef IIO_FREQUENCY_AD9523_H_
+#define IIO_FREQUENCY_AD9523_H_
+
+enum outp_drv_mode {
+	TRISTATE,
+	LVPECL_8mA,
+	LVDS_4mA,
+	LVDS_7mA,
+	HSTL0_16mA,
+	HSTL1_8mA,
+	CMOS_CONF1,
+	CMOS_CONF2,
+	CMOS_CONF3,
+	CMOS_CONF4,
+	CMOS_CONF5,
+	CMOS_CONF6,
+	CMOS_CONF7,
+	CMOS_CONF8,
+	CMOS_CONF9
+};
+
+enum ref_sel_mode {
+	NONEREVERTIVE_STAY_ON_REFB,
+	REVERT_TO_REFA,
+	SELECT_REFA,
+	SELECT_REFB,
+	EXT_REF_SEL
+};
+
+/**
+ * struct ad9523_channel_spec - Output channel configuration
+ *
+ * @channel_num: Output channel number.
+ * @divider_output_invert_en: Invert the polarity of the output clock.
+ * @sync_ignore_en: Ignore chip-level SYNC signal.
+ * @low_power_mode_en: Reduce power used in the differential output modes.
+ * @use_alt_clock_src: Channel divider uses alternative clk source.
+ * @output_dis: Disables, powers down the entire channel.
+ * @driver_mode: Output driver mode (logic level family).
+ * @divider_phase: Divider initial phase after a SYNC. Range 0..63
+		   LSB = 1/2 of a period of the divider input clock.
+ * @channel_divider: 10-bit channel divider.
+ * @extended_name: Optional descriptive channel name.
+ */
+
+struct ad9523_channel_spec {
+	unsigned		channel_num;
+	bool			divider_output_invert_en;
+	bool			sync_ignore_en;
+	bool			low_power_mode_en;
+				 /* CH0..CH3 VCXO, CH4..CH9 VCO2 */
+	bool			use_alt_clock_src;
+	bool			output_dis;
+	enum outp_drv_mode	driver_mode;
+	unsigned char		divider_phase;
+	unsigned short		channel_divider;
+	char			extended_name[16];
+};
+
+enum pll1_rzero_resistor {
+	RZERO_883_OHM,
+	RZERO_677_OHM,
+	RZERO_341_OHM,
+	RZERO_135_OHM,
+	RZERO_10_OHM,
+	RZERO_USE_EXT_RES = 8,
+};
+
+enum rpole2_resistor {
+	RPOLE2_900_OHM,
+	RPOLE2_450_OHM,
+	RPOLE2_300_OHM,
+	RPOLE2_225_OHM,
+};
+
+enum rzero_resistor {
+	RZERO_3250_OHM,
+	RZERO_2750_OHM,
+	RZERO_2250_OHM,
+	RZERO_2100_OHM,
+	RZERO_3000_OHM,
+	RZERO_2500_OHM,
+	RZERO_2000_OHM,
+	RZERO_1850_OHM,
+};
+
+enum cpole1_capacitor {
+	CPOLE1_0_PF,
+	CPOLE1_8_PF,
+	CPOLE1_16_PF,
+	CPOLE1_24_PF,
+	_CPOLE1_24_PF, /* place holder */
+	CPOLE1_32_PF,
+	CPOLE1_40_PF,
+	CPOLE1_48_PF,
+};
+
+/**
+ * struct ad9523_platform_data - platform specific information
+ *
+ * @vcxo_freq: External VCXO frequency in Hz
+ * @refa_diff_rcv_en: REFA differential/single-ended input selection.
+ * @refb_diff_rcv_en: REFB differential/single-ended input selection.
+ * @zd_in_diff_en: Zero Delay differential/single-ended input selection.
+ * @osc_in_diff_en: OSC differential/ single-ended input selection.
+ * @refa_cmos_neg_inp_en: REFA single-ended neg./pos. input enable.
+ * @refb_cmos_neg_inp_en: REFB single-ended neg./pos. input enable.
+ * @zd_in_cmos_neg_inp_en: Zero Delay single-ended neg./pos. input enable.
+ * @osc_in_cmos_neg_inp_en: OSC single-ended neg./pos. input enable.
+ * @refa_r_div: PLL1 10-bit REFA R divider.
+ * @refb_r_div: PLL1 10-bit REFB R divider.
+ * @pll1_feedback_div: PLL1 10-bit Feedback N divider.
+ * @pll1_charge_pump_current_nA: Magnitude of PLL1 charge pump current (nA).
+ * @zero_delay_mode_internal_en: Internal, external Zero Delay mode selection.
+ * @osc_in_feedback_en: PLL1 feedback path, local feedback from
+ *			the OSC_IN receiver or zero delay mode
+ * @pll1_loop_filter_rzero: PLL1 Loop Filter Zero Resistor selection.
+ * @ref_mode: Reference selection mode.
+ * @pll2_charge_pump_current_nA: Magnitude of PLL2 charge pump current (nA).
+ * @pll2_ndiv_a_cnt: PLL2 Feedback N-divider, A Counter, range 0..4.
+ * @pll2_ndiv_b_cnt: PLL2 Feedback N-divider, B Counter, range 0..63.
+ * @pll2_freq_doubler_en: PLL2 frequency doubler enable.
+ * @pll2_r2_div: PLL2 R2 divider, range 0..31.
+ * @pll2_vco_diff_m1: VCO1 divider, range 3..5.
+ * @pll2_vco_diff_m2: VCO2 divider, range 3..5.
+ * @rpole2: PLL2 loop filter Rpole resistor value.
+ * @rzero: PLL2 loop filter Rzero resistor value.
+ * @cpole1: PLL2 loop filter Cpole capacitor value.
+ * @rzero_bypass_en: PLL2 loop filter Rzero bypass enable.
+ * @num_channels: Array size of struct ad9523_channel_spec.
+ * @channels: Pointer to channel array.
+ * @name: Optional alternative iio device name.
+ */
+
+struct ad9523_platform_data {
+	unsigned long vcxo_freq;
+
+	/* Differential/ Single-Ended Input Configuration */
+	bool				refa_diff_rcv_en;
+	bool				refb_diff_rcv_en;
+	bool				zd_in_diff_en;
+	bool				osc_in_diff_en;
+
+	/*
+	 * Valid if differential input disabled
+	 * if false defaults to pos input
+	 */
+	bool				refa_cmos_neg_inp_en;
+	bool				refb_cmos_neg_inp_en;
+	bool				zd_in_cmos_neg_inp_en;
+	bool				osc_in_cmos_neg_inp_en;
+
+	/* PLL1 Setting */
+	unsigned short			refa_r_div;
+	unsigned short			refb_r_div;
+	unsigned short			pll1_feedback_div;
+	unsigned short			pll1_charge_pump_current_nA;
+	bool				zero_delay_mode_internal_en;
+	bool				osc_in_feedback_en;
+	enum pll1_rzero_resistor	pll1_loop_filter_rzero;
+
+	/* Reference */
+	enum ref_sel_mode		ref_mode;
+
+	/* PLL2 Setting */
+	unsigned int			pll2_charge_pump_current_nA;
+	unsigned char			pll2_ndiv_a_cnt;
+	unsigned char			pll2_ndiv_b_cnt;
+	bool				pll2_freq_doubler_en;
+	unsigned char			pll2_r2_div;
+	unsigned char			pll2_vco_diff_m1; /* 3..5 */
+	unsigned char			pll2_vco_diff_m2; /* 3..5 */
+
+	/* Loop Filter PLL2 */
+	enum rpole2_resistor		rpole2;
+	enum rzero_resistor		rzero;
+	enum cpole1_capacitor		cpole1;
+	bool				rzero_bypass_en;
+
+	/* Output Channel Configuration */
+	int				num_channels;
+	struct ad9523_channel_spec	*channels;
+
+	char				name[SPI_NAME_SIZE];
+};
+
+#endif /* IIO_FREQUENCY_AD9523_H_ */
--
1.7.0.4



^ permalink raw reply related	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2012-05-29 10:41 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2012-05-21 14:58 [PATCH 1/2] iio: frequency: New driver for AD9523 SPI Low Jitter Clock Generator michael.hennerich
2012-05-21 14:58 ` [PATCH 2/2] iio: frequency: New driver for Analog Devices ADF4350/ADF4351 Wideband Synthesizers michael.hennerich
2012-05-24 13:19   ` Jonathan Cameron
2012-05-24 13:00 ` [PATCH 1/2] iio: frequency: New driver for AD9523 SPI Low Jitter Clock Generator Jonathan Cameron
2012-05-29  9:08   ` Michael Hennerich
2012-05-29 10:41 michael.hennerich

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.