devicetree.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 0/3] ADF41513/ADF41510 PLL frequency synthesizers
@ 2025-11-10 15:44 Rodrigo Alencar via B4 Relay
  2025-11-10 15:44 ` [PATCH 1/3] iio: frequency: adf41513: driver implementation Rodrigo Alencar via B4 Relay
                   ` (4 more replies)
  0 siblings, 5 replies; 15+ messages in thread
From: Rodrigo Alencar via B4 Relay @ 2025-11-10 15:44 UTC (permalink / raw)
  To: linux-kernel, linux-iio, devicetree, linux-doc
  Cc: Jonathan Cameron, David Lechner, Andy Shevchenko,
	Lars-Peter Clausen, Michael Hennerich, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Jonathan Corbet,
	Rodrigo Alencar

This patch series adds support for the Analog Devices ADF41513 and ADF41510
ultralow noise PLL frequency synthesizers. These devices are designed for
implementing local oscillators (LOs) in high-frequency applications.

The ADF41513 covers frequencies from 1 GHz to 26.5 GHz, while the ADF41510
operates from 1 GHz to 10 GHz. Both devices feature exceptional phase noise
performance and flexible frequency synthesis capabilities.

Key features supported by this driver:
- Integer-N and fractional-N operation modes
- Ultra-low phase noise (-235 dBc/Hz integer-N, -231 dBc/Hz fractional-N)
- High maximum PFD frequency (250 MHz integer-N, 125 MHz fractional-N)
- 25-bit fixed modulus or 49-bit variable modulus fractional modes
- Programmable charge pump currents with 16x range
- Digital lock detect functionality
- Phase resync capability for consistent output phase
- Clock framework integration for system clock generation

The series includes:
1. Core driver implementation with full register programming support
2. Device tree bindings documentation
3. IIO subsystem documentation with usage examples

The driver integrates with both the IIO subsystem (for direct hardware control)
and the Linux clock framework (for use as a system clock source), providing
flexibility for different use cases.

Signed-off-by: Rodrigo Alencar <rodrigo.alencar@analog.com>
---
Rodrigo Alencar (3):
      iio: frequency: adf41513: driver implementation
      dt-bindings: iio: frequency: add adf41513
      docs: iio: add documentation for adf41513 driver

 .../bindings/iio/frequency/adi,adf41513.yaml       |  268 ++++
 Documentation/iio/adf41513.rst                     |  377 +++++
 Documentation/iio/index.rst                        |    1 +
 MAINTAINERS                                        |    9 +
 drivers/iio/frequency/Kconfig                      |   11 +
 drivers/iio/frequency/Makefile                     |    1 +
 drivers/iio/frequency/adf41513.c                   | 1435 ++++++++++++++++++++
 7 files changed, 2102 insertions(+)
---
base-commit: d16d1c2553248f9b859b86c94344d8b81f0297cd
change-id: 20251110-adf41513-iio-driver-aaca8a7f808e

Best regards,
-- 
Rodrigo Alencar <rodrigo.alencar@analog.com>



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

* [PATCH 1/3] iio: frequency: adf41513: driver implementation
  2025-11-10 15:44 [PATCH 0/3] ADF41513/ADF41510 PLL frequency synthesizers Rodrigo Alencar via B4 Relay
@ 2025-11-10 15:44 ` Rodrigo Alencar via B4 Relay
  2025-11-10 16:30   ` Andy Shevchenko
  2025-11-13 13:13   ` Marcelo Schmitt
  2025-11-10 15:44 ` [PATCH 2/3] dt-bindings: iio: frequency: add adf41513 Rodrigo Alencar via B4 Relay
                   ` (3 subsequent siblings)
  4 siblings, 2 replies; 15+ messages in thread
From: Rodrigo Alencar via B4 Relay @ 2025-11-10 15:44 UTC (permalink / raw)
  To: linux-kernel, linux-iio, devicetree, linux-doc
  Cc: Jonathan Cameron, David Lechner, Andy Shevchenko,
	Lars-Peter Clausen, Michael Hennerich, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Jonathan Corbet,
	Rodrigo Alencar

From: Rodrigo Alencar <rodrigo.alencar@analog.com>

- ADF41513: 1 GHz to 26.5 GHz frequency range
- ADF41510: 1 GHz to 10 GHz frequency range
- Integer-N and fractional-N operation modes
- Ultra-low phase noise (-235 dBc/Hz integer-N, -231 dBc/Hz fractional-N)
- High maximum PFD frequency (250 MHz integer-N, 125 MHz fractional-N)
- 25-bit fixed modulus or 49-bit variable modulus fractional modes
- Programmable charge pump currents with 16x range
- Digital lock detect functionality
- Phase resync capability for consistent output phase
- Clock framework integration for system clock generation

Signed-off-by: Rodrigo Alencar <rodrigo.alencar@analog.com>
---
 MAINTAINERS                      |    7 +
 drivers/iio/frequency/Kconfig    |   11 +
 drivers/iio/frequency/Makefile   |    1 +
 drivers/iio/frequency/adf41513.c | 1435 ++++++++++++++++++++++++++++++++++++++
 4 files changed, 1454 insertions(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index 31d98efb1ad1..8df4a0d216c8 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1601,6 +1601,13 @@ W:	https://ez.analog.com/linux-software-drivers
 F:	Documentation/devicetree/bindings/iio/amplifiers/adi,ada4250.yaml
 F:	drivers/iio/amplifiers/ada4250.c
 
+ANALOG DEVICES INC ADF41513 DRIVER
+M:	Rodrigo Alencar <rodrigo.alencar@analog.com>
+L:	linux-iio@vger.kernel.org
+S:	Supported
+W:	https://ez.analog.com/linux-software-drivers
+F:	drivers/iio/frequency/adf41513.c
+
 ANALOG DEVICES INC ADF4377 DRIVER
 M:	Antoniu Miclaus <antoniu.miclaus@analog.com>
 L:	linux-iio@vger.kernel.org
diff --git a/drivers/iio/frequency/Kconfig b/drivers/iio/frequency/Kconfig
index 583cbdf4e8cd..0e2522c6d4aa 100644
--- a/drivers/iio/frequency/Kconfig
+++ b/drivers/iio/frequency/Kconfig
@@ -29,6 +29,17 @@ endmenu
 
 menu "Phase-Locked Loop (PLL) frequency synthesizers"
 
+config ADF41513
+	tristate "Analog Devices ADF41513 PLL Frequency Synthesizer"
+	depends on SPI
+	depends on COMMON_CLK
+	help
+	  Say yes here to build support for Analog Devices ADF41513
+	  26.5 GHz Integer-N/Fractional-N PLL Frequency Synthesizer.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called adf41513.
+
 config ADF4350
 	tristate "Analog Devices ADF4350/ADF4351 Wideband Synthesizers"
 	depends on SPI
diff --git a/drivers/iio/frequency/Makefile b/drivers/iio/frequency/Makefile
index 70d0e0b70e80..53b4d01414d8 100644
--- a/drivers/iio/frequency/Makefile
+++ b/drivers/iio/frequency/Makefile
@@ -5,6 +5,7 @@
 
 # When adding new entries keep the list in alphabetical order
 obj-$(CONFIG_AD9523) += ad9523.o
+obj-$(CONFIG_ADF41513) += adf41513.o
 obj-$(CONFIG_ADF4350) += adf4350.o
 obj-$(CONFIG_ADF4371) += adf4371.o
 obj-$(CONFIG_ADF4377) += adf4377.o
diff --git a/drivers/iio/frequency/adf41513.c b/drivers/iio/frequency/adf41513.c
new file mode 100644
index 000000000000..6a4f2dc42e6c
--- /dev/null
+++ b/drivers/iio/frequency/adf41513.c
@@ -0,0 +1,1435 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * ADF41513 SPI PLL Frequency Synthesizer driver
+ *
+ * Copyright 2025 Analog Devices Inc.
+ */
+
+#include <linux/bitfield.h>
+#include <linux/bits.h>
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/gpio/consumer.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/math64.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/property.h>
+#include <linux/regulator/consumer.h>
+#include <linux/spi/spi.h>
+
+/* Registers */
+#define ADF41513_REG0		0
+#define ADF41513_REG1		1
+#define ADF41513_REG2		2
+#define ADF41513_REG3		3
+#define ADF41513_REG4		4
+#define ADF41513_REG5		5
+#define ADF41513_REG6		6
+#define ADF41513_REG7		7
+#define ADF41513_REG8		8
+#define ADF41513_REG9		9
+#define ADF41513_REG10		10
+#define ADF41513_REG11		11
+#define ADF41513_REG12		12
+#define ADF41513_REG13		13
+#define ADF41513_REG_NUM	14
+
+#define ADF41513_SYNC_REG0	BIT_U16(ADF41513_REG0)
+#define ADF41513_SYNC_REG1	BIT_U16(ADF41513_REG1)
+#define ADF41513_SYNC_REG2	BIT_U16(ADF41513_REG2)
+#define ADF41513_SYNC_REG3	BIT_U16(ADF41513_REG3)
+#define ADF41513_SYNC_REG4	BIT_U16(ADF41513_REG4)
+#define ADF41513_SYNC_REG5	BIT_U16(ADF41513_REG5)
+#define ADF41513_SYNC_REG6	BIT_U16(ADF41513_REG6)
+#define ADF41513_SYNC_REG7	BIT_U16(ADF41513_REG7)
+#define ADF41513_SYNC_REG9	BIT_U16(ADF41513_REG9)
+#define ADF41513_SYNC_REG11	BIT_U16(ADF41513_REG11)
+#define ADF41513_SYNC_REG12	BIT_U16(ADF41513_REG12)
+#define ADF41513_SYNC_REG13	BIT_U16(ADF41513_REG13)
+#define ADF41513_SYNC_DIFF	0
+#define ADF41513_SYNC_ALL	GENMASK_U16(ADF41513_REG13, ADF41513_REG0)
+
+/* REG0 Bit Definitions */
+#define ADF41513_REG0_CTRL_BITS_MSK		GENMASK_U32(3, 0)
+#define ADF41513_REG0_INT_MSK			GENMASK_U32(19, 4)
+#define ADF41513_REG0_INT(x)			FIELD_PREP(ADF41513_REG0_INT_MSK, x)
+#define ADF41513_REG0_VAR_MOD_MSK		BIT_U32(28)
+#define ADF41513_REG0_VAR_MOD(x)		FIELD_PREP(ADF41513_REG0_VAR_MOD_MSK, x)
+
+/* REG1 Bit Definitions */
+#define ADF41513_REG1_FRAC1_MSK			GENMASK_U32(28, 4)
+#define ADF41513_REG1_FRAC1(x)			FIELD_PREP(ADF41513_REG1_FRAC1_MSK, x)
+#define ADF41513_REG1_DITHER2_MSK		BIT_U32(31)
+#define ADF41513_REG1_DITHER2(x)		FIELD_PREP(ADF41513_REG1_DITHER2_MSK, x)
+
+/* REG2 Bit Definitions */
+#define ADF41513_REG2_PHASE_VAL_MSK		GENMASK_U32(15, 4)
+#define ADF41513_REG2_PHASE_VAL(x)		FIELD_PREP(ADF41513_REG2_PHASE_VAL_MSK, x)
+#define ADF41513_REG2_PHASE_ADJ_MSK		BIT_U32(31)
+#define ADF41513_REG2_PHASE_ADJ(x)		FIELD_PREP(ADF41513_REG2_PHASE_ADJ_MSK, x)
+
+/* REG3 Bit Definitions */
+#define ADF41513_REG3_FRAC2_MSK			GENMASK_U32(27, 4)
+#define ADF41513_REG3_FRAC2(x)			FIELD_PREP(ADF41513_REG3_FRAC2_MSK, x)
+
+/* REG4 Bit Definitions */
+#define ADF41513_REG4_MOD2_MSK			GENMASK_U32(27, 4)
+#define ADF41513_REG4_MOD2(x)			FIELD_PREP(ADF41513_REG4_MOD2_MSK, x)
+
+/* REG5 Bit Definitions */
+#define ADF41513_REG5_CLK1_DIV_MSK		GENMASK_U32(15, 4)
+#define ADF41513_REG5_CLK1_DIV(x)		FIELD_PREP(ADF41513_REG5_CLK1_DIV_MSK, x)
+#define ADF41513_REG5_R_CNT_MSK			GENMASK_U32(20, 16)
+#define ADF41513_REG5_R_CNT(x)			FIELD_PREP(ADF41513_REG5_R_CNT_MSK, x)
+#define ADF41513_REG5_REF_DOUBLER_MSK		BIT_U32(21)
+#define ADF41513_REG5_REF_DOUBLER(x)		FIELD_PREP(ADF41513_REG5_REF_DOUBLER_MSK, x)
+#define ADF41513_REG5_RDIV2_MSK			BIT_U32(22)
+#define ADF41513_REG5_RDIV2(x)			FIELD_PREP(ADF41513_REG5_RDIV2_MSK, x)
+#define ADF41513_REG5_PRESCALER_MSK		BIT_U32(23)
+#define ADF41513_REG5_PRESCALER(x)		FIELD_PREP(ADF41513_REG5_PRESCALER_MSK, x)
+#define ADF41513_REG5_LSB_P1_MSK		BIT_U32(24)
+#define ADF41513_REG5_LSB_P1(x)			FIELD_PREP(ADF41513_REG5_LSB_P1_MSK, x)
+#define ADF41513_REG5_CP_CURRENT_MSK		GENMASK_U32(28, 25)
+#define ADF41513_REG5_CP_CURRENT(x)		FIELD_PREP(ADF41513_REG5_CP_CURRENT_MSK, x)
+#define ADF41513_REG5_DLD_MODES_MSK		GENMASK_U32(31, 30)
+#define ADF41513_REG5_DLD_MODES(x)		FIELD_PREP(ADF41513_REG5_DLD_MODES_MSK, x)
+
+/* REG6 Bit Definitions */
+#define ADF41513_REG6_COUNTER_RESET_MSK		BIT_U32(4)
+#define ADF41513_REG6_COUNTER_RESET(x)		FIELD_PREP(ADF41513_REG6_COUNTER_RESET_MSK, x)
+#define ADF41513_REG6_CP_TRISTATE_MSK		BIT_U32(5)
+#define ADF41513_REG6_CP_TRISTATE(x)		FIELD_PREP(ADF41513_REG6_CP_TRISTATE_MSK, x)
+#define ADF41513_REG6_POWER_DOWN_MSK		BIT_U32(6)
+#define ADF41513_REG6_POWER_DOWN(x)		FIELD_PREP(ADF41513_REG6_POWER_DOWN_MSK, x)
+#define ADF41513_REG6_PD_POLARITY_MSK		BIT_U32(7)
+#define ADF41513_REG6_PD_POLARITY(x)		FIELD_PREP(ADF41513_REG6_PD_POLARITY_MSK, x)
+#define ADF41513_REG6_LDP_MSK			GENMASK_U32(9, 8)
+#define ADF41513_REG6_LDP(x)			FIELD_PREP(ADF41513_REG6_LDP_MSK, x)
+#define ADF41513_REG6_CP_TRISTATE_PD_ON_MSK	BIT_U32(16)
+#define ADF41513_REG6_CP_TRISTATE_PD_ON(x)	FIELD_PREP(ADF41513_REG6_CP_TRISTATE_PD_ON_MSK, x)
+#define ADF41513_REG6_SD_RESET_MSK		BIT_U32(17)
+#define ADF41513_REG6_SD_RESET(x)		FIELD_PREP(ADF41513_REG6_SD_RESET_MSK, x)
+#define ADF41513_REG6_LOL_ENABLE_MSK		BIT_U32(18)
+#define ADF41513_REG6_LOL_ENABLE(x)		FIELD_PREP(ADF41513_REG6_LOL_ENABLE_MSK, x)
+#define ADF41513_REG6_ABP_MSK			BIT_U32(19)
+#define ADF41513_REG6_ABP(x)			FIELD_PREP(ADF41513_REG6_ABP_MSK, x)
+#define ADF41513_REG6_INT_MODE_MSK		BIT_U32(20)
+#define ADF41513_REG6_INT_MODE(x)		FIELD_PREP(ADF41513_REG6_INT_MODE_MSK, x)
+#define ADF41513_REG6_BLEED_ENABLE_MSK		BIT_U32(22)
+#define ADF41513_REG6_BLEED_ENABLE(x)		FIELD_PREP(ADF41513_REG6_BLEED_ENABLE_MSK, x)
+#define ADF41513_REG6_BLEED_POLARITY_MSK	BIT_U32(23)
+#define ADF41513_REG6_BLEED_POLARITY(x)		FIELD_PREP(ADF41513_REG6_BLEED_POLARITY_MSK, x)
+#define ADF41513_REG6_BLEED_CURRENT_MSK		GENMASK_U32(31, 24)
+#define ADF41513_REG6_BLEED_CURRENT(x)		FIELD_PREP(ADF41513_REG6_BLEED_CURRENT_MSK, x)
+
+/* REG7 Bit Definitions */
+#define ADF41513_REG7_CLK2_DIV_MSK		GENMASK_U32(17, 6)
+#define ADF41513_REG7_CLK2_DIV(x)		FIELD_PREP(ADF41513_REG7_CLK2_DIV_MSK, x)
+#define ADF41513_REG7_CLK_DIV_MODE_MSK		GENMASK_U32(19, 18)
+#define ADF41513_REG7_CLK_DIV_MODE(x)		FIELD_PREP(ADF41513_REG7_CLK_DIV_MODE_MSK, x)
+#define ADF41513_REG7_PS_BIAS_MSK		GENMASK_U32(21, 20)
+#define ADF41513_REG7_PS_BIAS(x)		FIELD_PREP(ADF41513_REG7_PS_BIAS_MSK, x)
+#define ADF41513_REG7_N_DELAY_MSK		GENMASK_U32(23, 22)
+#define ADF41513_REG7_N_DELAY(x)		FIELD_PREP(ADF41513_REG7_N_DELAY_MSK, x)
+#define ADF41513_REG7_LD_CLK_SEL_MSK		BIT_U32(26)
+#define ADF41513_REG7_LD_CLK_SEL(x)		FIELD_PREP(ADF41513_REG7_LD_CLK_SEL_MSK, x)
+#define ADF41513_REG7_LD_COUNT_MSK		GENMASK_U32(29, 27)
+#define ADF41513_REG7_LD_COUNT(x)		FIELD_PREP(ADF41513_REG7_LD_COUNT_MSK, x)
+
+/* REG9 Bit Definitions */
+#define ADF41513_REG9_LD_BIAS_MSK		GENMASK_U32(31, 30)
+#define ADF41513_REG9_LD_BIAS(x)		FIELD_PREP(ADF41513_REG9_LD_BIAS_MSK, x)
+
+/* REG11 Bit Definitions */
+#define ADF41513_REG11_POWER_DOWN_SEL_MSK	BIT_U32(31)
+#define ADF41513_REG11_POWER_DOWN_SEL(x)	FIELD_PREP(ADF41513_REG11_POWER_DOWN_SEL_MSK, x)
+
+/* REG12 Bit Definitions */
+#define ADF41513_REG12_READBACK_SEL_MSK		GENMASK_U32(19, 14)
+#define ADF41513_REG12_READBACK_SEL(x)		FIELD_PREP(ADF41513_REG12_READBACK_SEL_MSK, x)
+#define ADF41513_REG12_LE_SELECT_MSK		BIT_U32(20)
+#define ADF41513_REG12_LE_SELECT(x)		FIELD_PREP(ADF41513_REG12_LE_SELECT_MSK, x)
+#define ADF41513_REG12_MASTER_RESET_MSK		BIT_U32(22)
+#define ADF41513_REG12_MASTER_RESET(x)		FIELD_PREP(ADF41513_REG12_MASTER_RESET_MSK, x)
+#define ADF41513_REG12_LOGIC_LEVEL_MSK		BIT_U32(27)
+#define ADF41513_REG12_LOGIC_LEVEL(x)		FIELD_PREP(ADF41513_REG12_LOGIC_LEVEL_MSK, x)
+#define ADF41513_REG12_MUXOUT_MSK		GENMASK_U32(31, 28)
+#define ADF41513_REG12_MUXOUT(x)		FIELD_PREP(ADF41513_REG12_MUXOUT_MSK, x)
+
+/* MUXOUT Selection */
+#define ADF41513_MUXOUT_TRISTATE		0x0
+#define ADF41513_MUXOUT_DVDD			0x1
+#define ADF41513_MUXOUT_DGND			0x2
+#define ADF41513_MUXOUT_R_DIV			0x3
+#define ADF41513_MUXOUT_N_DIV			0x4
+#define ADF41513_MUXOUT_DIG_LD			0x6
+#define ADF41513_MUXOUT_SDO			0x7
+#define ADF41513_MUXOUT_READBACK		0x8
+#define ADF41513_MUXOUT_CLK1_DIV		0xA
+#define ADF41513_MUXOUT_R_DIV2			0xD
+#define ADF41513_MUXOUT_N_DIV2			0xE
+
+/* DLD Mode Selection */
+#define ADF41513_DLD_TRISTATE			0x0
+#define ADF41513_DLD_DIG_LD			0x1
+#define ADF41513_DLD_LOW			0x2
+#define ADF41513_DLD_HIGH			0x3
+
+/* Prescaler Selection */
+#define ADF41513_PRESCALER_4_5			0
+#define ADF41513_PRESCALER_8_9			1
+#define ADF41513_PRESCALER_AUTO			2
+
+/* Specifications */
+#define ADF41513_MIN_RF_FREQ			1000000000ULL	/* 1 GHz */
+#define ADF41510_MAX_RF_FREQ			10000000000ULL	/* 10 GHz */
+#define ADF41513_MAX_RF_FREQ			26500000000ULL	/* 26.5 GHz */
+
+#define ADF41513_MIN_REF_FREQ			10000000U	/* 10 MHz */
+#define ADF41513_MAX_REF_FREQ			800000000U	/* 800 MHz */
+#define ADF41513_MAX_REF_FREQ_DOUBLER		225000000U	/* 225 MHz */
+
+#define ADF41513_MAX_PFD_FREQ_INT_N_HZ		250000000U		/* 250 MHz */
+#define ADF41513_MAX_PFD_FREQ_FRAC_N_HZ		125000000U		/* 125 MHz */
+#define ADF41513_MAX_PFD_FREQ_INT_N_UHZ		250000000000000ULL	/* 250 MHz */
+#define ADF41513_MAX_PFD_FREQ_FRAC_N_UHZ	125000000000000ULL	/* 125 MHz */
+
+#define ADF41513_MIN_INT_4_5			20
+#define ADF41513_MAX_INT_4_5			511
+#define ADF41513_MIN_INT_8_9			64
+#define ADF41513_MAX_INT_8_9			1023
+
+#define ADF41513_MIN_INT_FRAC_4_5		23
+#define ADF41513_MIN_INT_FRAC_8_9		75
+
+#define ADF41513_MIN_R_CNT			1
+#define ADF41513_MAX_R_CNT			32
+
+#define ADF41513_MIN_CP_VOLTAGE_MV		810
+#define ADF41513_MAX_CP_VOLTAGE_MV		12960
+
+#define ADF41513_MAX_CLK_DIVIDER		4095
+#define ADF41513_MAX_LD_BIAS_UA			40
+#define ADF41513_LD_BIAS_STEP_UA		10
+
+/* Fixed Modulus */
+#define ADF41513_FIXED_MODULUS			BIT_U64(25)	/* 2^25 */
+
+/* Variable Modulus */
+#define ADF41513_MAX_MOD2			((1 << 24) - 1)	/* 2^24 - 1 */
+
+/* Frequency conversion constants */
+#define ADF41513_HZ_TO_UHZ			1000000ULL	/* Convert Hz to uHz */
+#define ADF41513_HZ_DECIMAL_PRECISION		6
+
+enum {
+	ADF41513_FREQ,
+	ADF41513_POWER_DOWN,
+	ADF41513_FREQ_RESOLUTION,
+	ADF41513_FREQ_REFIN
+};
+
+enum adf41513_pll_mode {
+	ADF41513_MODE_INTEGER_N,
+	ADF41513_MODE_FIXED_MODULUS,
+	ADF41513_MODE_VARIABLE_MODULUS,
+	ADF41513_MODE_INVALID
+};
+
+struct adf41513_chip_info {
+	const char *name;
+	bool has_prescaler_8_9;
+	u64 max_rf_freq_hz;
+};
+
+struct adf41513_data {
+	u64 power_up_frequency;
+
+	u8 ref_div_factor;
+	bool ref_doubler_en;
+	bool ref_div2_en;
+
+	u32 charge_pump_voltage_mv;
+	bool phase_detector_polarity;
+
+	u8 muxout_select;
+	bool muxout_1v8_en;
+
+	u8 lock_detect_precision;
+	u8 lock_detect_count;
+	u8 lock_detect_bias;
+	bool fast_lock_en;
+
+	u16 phase_resync_clk_div[2];
+	bool phase_resync_en;
+	bool load_enable_sync;
+
+	u64 freq_resolution_uhz;
+};
+
+struct adf41513_pll_settings {
+	enum adf41513_pll_mode mode;
+
+	u64 target_frequency_uhz;
+	u64 actual_frequency_uhz;
+	u64 pfd_frequency_uhz;
+
+	/* pll parameters */
+	u16 int_value;
+	u32 frac1;
+	u32 frac2;
+	u32 mod2;
+
+	/* reference path parameters */
+	u8 r_counter;
+	u8 ref_doubler;
+	u8 ref_div2;
+	u8 prescaler;
+};
+
+struct adf41513_state {
+	const struct adf41513_chip_info *chip_info;
+	struct spi_device *spi;
+	struct gpio_desc *lock_detect;
+	struct gpio_desc *chip_enable;
+	struct clk *ref_clk;
+	struct clk *clk_out;
+	struct clk_hw clk_hw;
+
+	u64 ref_freq_hz;
+
+	/*
+	 * Lock for accessing device registers. Some operations require
+	 * multiple consecutive R/W operations, during which the device
+	 * shouldn't be interrupted. The buffers are also shared across
+	 * all operations so need to be protected on stand alone reads and
+	 * writes.
+	 */
+	struct mutex lock;
+
+	/* Cached register values */
+	u32 regs[ADF41513_REG_NUM];
+	u32 regs_hw[ADF41513_REG_NUM];
+
+	/* PLL dt data */
+	struct adf41513_data data;
+
+	/* PLL configuration */
+	struct adf41513_pll_settings settings;
+
+	/*
+	 * DMA (thus cache coherency maintenance) may require that
+	 * transfer buffers live in their own cache lines.
+	 */
+	__be32 buf __aligned(IIO_DMA_MINALIGN);
+};
+
+#define to_adf41513_state(_hw) container_of(_hw, struct adf41513_state, clk_hw)
+
+static const u32 adf41513_cp_voltage_mv[] = {
+	810, 1620, 2430, 3240, 4050, 4860, 5670, 6480, 7290, 8100,
+	8910, 9720, 10530, 11340, 12150, 12960
+};
+
+static const char * const adf41513_muxout_modes[] = {
+	[ADF41513_MUXOUT_TRISTATE] = "high_z",
+	[ADF41513_MUXOUT_DVDD] = "muxout_high",
+	[ADF41513_MUXOUT_DGND] = "muxout_low",
+	[ADF41513_MUXOUT_R_DIV] = "f_div_rclk",
+	[ADF41513_MUXOUT_N_DIV] = "f_div_nclk",
+	[ADF41513_MUXOUT_DIG_LD] = "lock_detect",
+	[ADF41513_MUXOUT_SDO] = "serial_data",
+	[ADF41513_MUXOUT_READBACK] = "readback",
+	[ADF41513_MUXOUT_CLK1_DIV] = "f_div_clk1",
+	[ADF41513_MUXOUT_R_DIV2] = "f_div_rclk_2",
+	[ADF41513_MUXOUT_N_DIV2] = "f_div_nclk_2",
+};
+
+static int adf41513_parse_uhz(const char *str, u64 *freq_uhz)
+{
+	u64 uhz = 0;
+	int f_count = ADF41513_HZ_DECIMAL_PRECISION;
+	bool frac_part = false;
+
+	if (str[0] == '+')
+		str++;
+
+	while (*str && f_count > 0) {
+		if ('0' <= *str && *str <= '9') {
+			uhz = uhz * 10 + *str - '0';
+			if (frac_part)
+				f_count--;
+		} else if (*str == '\n') {
+			if (*(str + 1) == '\0')
+				break;
+			return -EINVAL;
+		} else if (*str == '.' && !frac_part) {
+			frac_part = true;
+		} else {
+			return -EINVAL;
+		}
+		str++;
+	}
+
+	for (; f_count > 0; f_count--)
+		uhz *= 10;
+
+	*freq_uhz = uhz;
+
+	return 0;
+}
+
+static int adf41513_uhz_to_str(u64 freq_uhz, char *buf)
+{
+	u64 frac_part;
+	u64 int_part = div64_u64_rem(freq_uhz, ADF41513_HZ_TO_UHZ, &frac_part);
+
+	return sysfs_emit(buf, "%llu.%06llu\n", int_part, frac_part);
+}
+
+static int adf41513_sync_config(struct adf41513_state *st, u16 sync_mask)
+{
+	int ret;
+	int i;
+
+	/* write registers in reverse order (R13 to R0)*/
+	for (i = ADF41513_REG13; i >= ADF41513_REG0; i--) {
+		if (st->regs_hw[i] != st->regs[i] || sync_mask & BIT_U16(i)) {
+			st->buf = cpu_to_be32(st->regs[i] | i);
+			ret = spi_write(st->spi, &st->buf, sizeof(st->buf));
+			if (ret < 0)
+				return ret;
+			st->regs_hw[i] = st->regs[i];
+			dev_dbg(&st->spi->dev, "REG%d <= 0x%08X\n", i, st->regs[i] | i);
+		}
+	}
+
+	return 0;
+}
+
+static u64 adf41513_pll_get_rate(struct adf41513_state *st)
+{
+	if (st->settings.mode != ADF41513_MODE_INVALID)
+		return st->settings.actual_frequency_uhz;
+
+	/* get pll settings from regs_hw */
+	st->settings.int_value =
+		FIELD_GET(ADF41513_REG0_INT_MSK, st->regs_hw[ADF41513_REG0]);
+	st->settings.frac1 =
+		FIELD_GET(ADF41513_REG1_FRAC1_MSK, st->regs_hw[ADF41513_REG1]);
+	st->settings.frac2 =
+		FIELD_GET(ADF41513_REG3_FRAC2_MSK, st->regs_hw[ADF41513_REG3]);
+	st->settings.mod2 =
+		FIELD_GET(ADF41513_REG4_MOD2_MSK, st->regs_hw[ADF41513_REG4]);
+	st->settings.r_counter =
+		FIELD_GET(ADF41513_REG5_R_CNT_MSK, st->regs_hw[ADF41513_REG5]);
+	st->settings.ref_doubler =
+		FIELD_GET(ADF41513_REG5_REF_DOUBLER_MSK, st->regs_hw[ADF41513_REG5]);
+	st->settings.ref_div2 =
+		FIELD_GET(ADF41513_REG5_RDIV2_MSK, st->regs_hw[ADF41513_REG5]);
+	st->settings.prescaler =
+		FIELD_GET(ADF41513_REG5_PRESCALER_MSK, st->regs_hw[ADF41513_REG5]);
+
+	/* calculate pfd frequency */
+	st->settings.pfd_frequency_uhz = st->ref_freq_hz * ADF41513_HZ_TO_UHZ;
+	if (st->settings.ref_doubler)
+		st->settings.pfd_frequency_uhz <<= 1;
+	if (st->settings.ref_div2)
+		st->settings.pfd_frequency_uhz >>= 1;
+	st->settings.pfd_frequency_uhz = div_u64(st->settings.pfd_frequency_uhz,
+						 st->settings.r_counter);
+	st->settings.actual_frequency_uhz =
+		(u64)st->settings.int_value * st->settings.pfd_frequency_uhz;
+
+	/* check if int mode is selected */
+	if (FIELD_GET(ADF41513_REG6_INT_MODE_MSK, st->regs_hw[ADF41513_REG6])) {
+		st->settings.mode = ADF41513_MODE_INTEGER_N;
+	} else {
+		st->settings.actual_frequency_uhz +=
+			mul_u64_u64_div_u64(st->settings.frac1,
+					    st->settings.pfd_frequency_uhz,
+					    ADF41513_FIXED_MODULUS);
+
+		/* check if variable modulus is selected */
+		if (FIELD_GET(ADF41513_REG0_VAR_MOD_MSK, st->regs_hw[ADF41513_REG0])) {
+			st->settings.actual_frequency_uhz +=
+				mul_u64_u64_div_u64(st->settings.frac2,
+						    st->settings.pfd_frequency_uhz,
+						    ADF41513_FIXED_MODULUS * st->settings.mod2);
+
+			st->settings.mode = ADF41513_MODE_VARIABLE_MODULUS;
+		} else {
+			st->settings.mode = ADF41513_MODE_FIXED_MODULUS;
+		}
+	}
+
+	st->settings.target_frequency_uhz = st->settings.actual_frequency_uhz;
+
+	return st->settings.actual_frequency_uhz;
+}
+
+static int adf41513_calc_pfd_frequency(struct adf41513_state *st,
+				       struct adf41513_pll_settings *result,
+				       u64 fpfd_limit_uhz)
+{
+	/* set initial values from platform data */
+	result->ref_div2 = st->data.ref_div2_en ? 1 : 0;
+	result->ref_doubler = st->data.ref_doubler_en ? 1 : 0;
+
+	if (st->data.ref_doubler_en &&
+	    st->ref_freq_hz > ADF41513_MAX_REF_FREQ_DOUBLER) {
+		result->ref_doubler = 0;
+		dev_warn(&st->spi->dev, "Disabling ref doubler due to high reference frequency\n");
+	}
+
+	/* set R counter starting with the div factor from platform data */
+	result->r_counter = st->data.ref_div_factor - 1;
+
+	do {
+		result->r_counter++;
+		result->pfd_frequency_uhz = div64_u64(st->ref_freq_hz * ADF41513_HZ_TO_UHZ *
+					    (result->ref_doubler ? 2 : 1), ((result->r_counter) *
+					    (result->ref_div2 ? 2 : 1)));
+	} while (result->pfd_frequency_uhz > fpfd_limit_uhz);
+
+	if (result->r_counter > ADF41513_MAX_R_CNT) {
+		dev_err(&st->spi->dev, "Cannot optimize PFD frequency\n");
+		return -ERANGE;
+	}
+
+	return 0;
+}
+
+static int adf41513_calc_integer_n(struct adf41513_state *st,
+				   struct adf41513_pll_settings *result)
+{
+	u16 max_int = (st->chip_info->has_prescaler_8_9) ?
+		      ADF41513_MAX_INT_8_9 : ADF41513_MAX_INT_4_5;
+
+	u64 freq_error_uhz;
+	u16 int_value = (u16)div64_u64_rem(result->target_frequency_uhz,
+		result->pfd_frequency_uhz, &freq_error_uhz);
+
+	/* check if N is close to an integer (within tolerance) */
+	if (freq_error_uhz > (result->pfd_frequency_uhz >> 1) && int_value < max_int) {
+		int_value++;
+		freq_error_uhz = result->pfd_frequency_uhz - freq_error_uhz;
+	}
+
+	if (freq_error_uhz > st->data.freq_resolution_uhz)
+		return -ERANGE;
+
+	/* set prescaler */
+	if (st->chip_info->has_prescaler_8_9 && int_value >= ADF41513_MIN_INT_8_9 &&
+	    int_value <= ADF41513_MAX_INT_8_9)
+		result->prescaler = 1;
+	else if (int_value >= ADF41513_MIN_INT_4_5 && int_value <= ADF41513_MAX_INT_4_5)
+		result->prescaler = 0;
+	else
+		return -ERANGE;
+
+	result->actual_frequency_uhz = (u64)int_value * result->pfd_frequency_uhz;
+
+	result->mode = ADF41513_MODE_INTEGER_N;
+	result->int_value = int_value;
+	result->frac1 = 0;
+	result->frac2 = 0;
+	result->mod2 = 0;
+
+	return 0;
+}
+
+static int adf41513_calc_fixed_mod(struct adf41513_state *st,
+				   struct adf41513_pll_settings *result)
+{
+	const u64 resolution_uhz = div64_u64(result->pfd_frequency_uhz, ADF41513_FIXED_MODULUS);
+
+	u64 fractional_part_uhz, freq_error_uhz;
+	u32 frac1;
+
+	/* in fixed modulus LSB_P1 adds a frequency offset of f_pfd/2^26 */
+	u64 target_frequency_uhz = result->target_frequency_uhz - (resolution_uhz >> 1);
+	u16 int_value = (u16)div64_u64_rem(target_frequency_uhz, result->pfd_frequency_uhz,
+					   &fractional_part_uhz);
+
+	/* set prescaler */
+	if (st->chip_info->has_prescaler_8_9 && int_value >= ADF41513_MIN_INT_FRAC_8_9 &&
+	    int_value <= ADF41513_MAX_INT_8_9)
+		result->prescaler = 1;
+	else if (int_value >= ADF41513_MIN_INT_FRAC_4_5 && int_value <= ADF41513_MAX_INT_4_5)
+		result->prescaler = 0;
+	else
+		return -ERANGE;
+
+	/* calculate frac1 */
+	frac1 = mul_u64_u64_div_u64(fractional_part_uhz, ADF41513_FIXED_MODULUS,
+				    result->pfd_frequency_uhz);
+
+	freq_error_uhz = fractional_part_uhz -
+		mul_u64_u64_div_u64((u64)frac1, result->pfd_frequency_uhz, ADF41513_FIXED_MODULUS);
+
+	if (freq_error_uhz > (resolution_uhz >> 1) && frac1 < (ADF41513_FIXED_MODULUS - 1)) {
+		frac1++;
+		freq_error_uhz = resolution_uhz - freq_error_uhz;
+	}
+
+	if (freq_error_uhz > st->data.freq_resolution_uhz)
+		return -ERANGE;
+
+	/* calculate actual frequency in uHz */
+	result->actual_frequency_uhz = (u64)int_value * result->pfd_frequency_uhz +
+		(resolution_uhz >> 1) +
+		mul_u64_u64_div_u64((u64)frac1, result->pfd_frequency_uhz, ADF41513_FIXED_MODULUS);
+
+	result->mode = ADF41513_MODE_FIXED_MODULUS;
+	result->int_value = int_value;
+	result->frac1 = frac1;
+	result->frac2 = 0;
+	result->mod2 = 0;
+
+	return 0;
+}
+
+static int adf41513_calc_variable_mod(struct adf41513_state *st,
+				      struct adf41513_pll_settings *result)
+{
+	u64 fractional_part_uhz, tmp, remaining_frac;
+	u32 frac1, frac2, mod2;
+	u16 int_value = (u16)div64_u64_rem(result->target_frequency_uhz,
+		result->pfd_frequency_uhz, &fractional_part_uhz);
+
+	/* set prescaler */
+	if (st->chip_info->has_prescaler_8_9 && int_value >= ADF41513_MIN_INT_FRAC_8_9 &&
+	    int_value <= ADF41513_MAX_INT_8_9)
+		result->prescaler = 1;
+	else if (int_value >= ADF41513_MIN_INT_FRAC_4_5 && int_value <= ADF41513_MAX_INT_4_5)
+		result->prescaler = 0;
+	else
+		return -ERANGE;
+
+	/* calculate required MOD2 based on target resolution / 2 (enhanced for uHz) */
+	mod2 = min_t(u64,
+		     DIV64_U64_ROUND_CLOSEST(result->pfd_frequency_uhz * 2,
+					     st->data.freq_resolution_uhz * ADF41513_FIXED_MODULUS),
+		     ADF41513_MAX_MOD2);
+
+	/* ensure MOD2 is at least 2 */
+	if (mod2 < 2)
+		mod2 = 2;
+
+	/* calculate frac1 and frac2 */
+	frac1 = mul_u64_u64_div_u64(fractional_part_uhz, ADF41513_FIXED_MODULUS,
+				    result->pfd_frequency_uhz);
+	remaining_frac = fractional_part_uhz -
+		mul_u64_u64_div_u64((u64)frac1, result->pfd_frequency_uhz, ADF41513_FIXED_MODULUS);
+	frac2 = mul_u64_u64_div_u64(remaining_frac, ADF41513_FIXED_MODULUS * mod2,
+				    result->pfd_frequency_uhz);
+
+	/* calculate actual frequency in uHz */
+	tmp = (u64)frac1 * mod2 + frac2;
+	result->actual_frequency_uhz = (u64)int_value * result->pfd_frequency_uhz +
+		mul_u64_u64_div_u64(tmp, result->pfd_frequency_uhz, ADF41513_FIXED_MODULUS * mod2);
+
+	result->mode = ADF41513_MODE_VARIABLE_MODULUS;
+	result->int_value = int_value;
+	result->frac1 = frac1;
+	result->frac2 = frac2;
+	result->mod2 = mod2;
+
+	return 0;
+}
+
+static int adf41513_calc_pll_settings(struct adf41513_state *st,
+				      struct adf41513_pll_settings *result,
+				      u64 rf_out_uhz)
+{
+	const u64 max_rf_freq_uhz = st->chip_info->max_rf_freq_hz * ADF41513_HZ_TO_UHZ;
+	const u64 min_rf_freq_uhz = ADF41513_MIN_RF_FREQ * ADF41513_HZ_TO_UHZ;
+
+	u64 pfd_freq_limit_uhz;
+	int ret;
+
+	/* input validation */
+	if (rf_out_uhz < min_rf_freq_uhz || rf_out_uhz > max_rf_freq_uhz) {
+		dev_err(&st->spi->dev, "RF frequency %llu uHz out of range [%llu, %llu] uHz\n",
+			rf_out_uhz, min_rf_freq_uhz, max_rf_freq_uhz);
+		return -EINVAL;
+	}
+	result->target_frequency_uhz = rf_out_uhz;
+
+	/* try integer-N first (best phase noise performance) */
+	pfd_freq_limit_uhz = min_t(u64, div_u64(rf_out_uhz, ADF41513_MIN_INT_4_5),
+				   ADF41513_MAX_PFD_FREQ_INT_N_UHZ);
+	ret = adf41513_calc_pfd_frequency(st, result, pfd_freq_limit_uhz);
+	if (ret < 0)
+		return ret;
+
+	ret = adf41513_calc_integer_n(st, result);
+	if (ret < 0) {
+		/* try fractional-N: recompute pfd frequency if necessary */
+		pfd_freq_limit_uhz = min_t(u64, div_u64(rf_out_uhz, ADF41513_MIN_INT_FRAC_4_5),
+					   ADF41513_MAX_PFD_FREQ_FRAC_N_UHZ);
+		if (pfd_freq_limit_uhz < result->pfd_frequency_uhz) {
+			ret = adf41513_calc_pfd_frequency(st, result, pfd_freq_limit_uhz);
+			if (ret < 0)
+				return ret;
+		}
+
+		/* fixed-modulus attempt */
+		ret = adf41513_calc_fixed_mod(st, result);
+		if (ret < 0) {
+			/* variable-modulus attempt */
+			ret = adf41513_calc_variable_mod(st, result);
+			if (ret < 0) {
+				dev_err(&st->spi->dev,
+					"no valid PLL configuration found for %llu uHz\n",
+					rf_out_uhz);
+				return -EINVAL;
+			}
+		}
+	}
+
+	return 0;
+}
+
+static int adf41513_set_frequency(struct adf41513_state *st, u64 freq_uhz, u16 sync_mask)
+{
+	struct adf41513_pll_settings result;
+	u32 bleed_value = 0;
+	int ret;
+
+	/* calculate pll settings candidate */
+	ret = adf41513_calc_pll_settings(st, &result, freq_uhz);
+	if (ret < 0)
+		return ret;
+
+	/* apply computed results to state pll settings */
+	memcpy(&st->settings, &result, sizeof(struct adf41513_pll_settings));
+
+	/* log calculation result */
+	dev_dbg(&st->spi->dev, "%s mode: int=%u, frac1=%u, frac2=%u, mod2=%u, fpdf=%llu Hz, prescaler=%s\n",
+		(result.mode == ADF41513_MODE_INTEGER_N) ? "integer-n" :
+		(result.mode == ADF41513_MODE_FIXED_MODULUS) ? "fixed-modulus" : "variable-modulus",
+		result.int_value, result.frac1, result.frac2, result.mod2,
+		div64_u64(result.pfd_frequency_uhz, ADF41513_HZ_TO_UHZ),
+		result.prescaler ? "8/9" : "4/5");
+
+	/* int */
+	st->regs[ADF41513_REG0] = ADF41513_REG0_INT(st->settings.int_value);
+	if (st->settings.mode == ADF41513_MODE_VARIABLE_MODULUS)
+		st->regs[ADF41513_REG0] |= ADF41513_REG0_VAR_MOD_MSK;
+	/* frac1 */
+	st->regs[ADF41513_REG1] = ADF41513_REG1_FRAC1(st->settings.frac1);
+	if (st->settings.mode != ADF41513_MODE_INTEGER_N)
+		st->regs[ADF41513_REG1] |= ADF41513_REG1_DITHER2_MSK;
+
+	/* frac2 */
+	st->regs[ADF41513_REG3] = ADF41513_REG3_FRAC2(st->settings.frac2);
+	/* mod2 */
+	st->regs[ADF41513_REG4] &= ADF41513_REG4_MOD2_MSK;
+	st->regs[ADF41513_REG4] |= ADF41513_REG4_MOD2(st->settings.mod2);
+
+	/* r-cnt | doubler | rdiv2 | prescaler */
+	st->regs[ADF41513_REG5] &= ~(ADF41513_REG5_R_CNT_MSK |
+				     ADF41513_REG5_REF_DOUBLER_MSK |
+				     ADF41513_REG5_RDIV2_MSK |
+				     ADF41513_REG5_PRESCALER_MSK);
+	st->regs[ADF41513_REG5] |= ADF41513_REG5_R_CNT(st->settings.r_counter % 32) |
+				   ADF41513_REG5_REF_DOUBLER(st->settings.ref_doubler) |
+				   ADF41513_REG5_RDIV2(st->settings.ref_div2) |
+				   ADF41513_REG5_PRESCALER(st->settings.prescaler);
+
+	/* Enable integer mode if no fractional part */
+	if (st->settings.mode == ADF41513_MODE_INTEGER_N) {
+		/* integer mode */
+		st->regs[ADF41513_REG6] |= ADF41513_REG6_INT_MODE_MSK;
+		st->regs[ADF41513_REG6] &= ~ADF41513_REG6_BLEED_ENABLE_MSK;
+	} else {
+		/* fractional mode */
+		st->regs[ADF41513_REG6] &= ~ADF41513_REG6_INT_MODE_MSK;
+		st->regs[ADF41513_REG6] |= ADF41513_REG6_BLEED_ENABLE_MSK;
+	}
+
+	/* set bleed current value */
+	if (st->data.phase_detector_polarity)
+		bleed_value = 90;
+	else
+		bleed_value = 144;
+
+	bleed_value *= FIELD_GET(ADF41513_REG5_CP_CURRENT_MSK, st->regs[ADF41513_REG5]) + 1;
+	bleed_value = mul_u64_u64_div_u64(st->settings.pfd_frequency_uhz,
+					  (u64)bleed_value,
+					  16 * 100000000000000ULL /* 100 MHz in uHz */);
+
+	st->regs[ADF41513_REG6] &= ~ADF41513_REG6_BLEED_CURRENT_MSK;
+	st->regs[ADF41513_REG6] |= ADF41513_REG6_BLEED_CURRENT(bleed_value);
+
+	return adf41513_sync_config(st, sync_mask | ADF41513_SYNC_REG0);
+}
+
+static int adf41513_suspend(struct adf41513_state *st)
+{
+	st->regs[ADF41513_REG6] |= ADF41513_REG6_POWER_DOWN(1);
+	if (st->data.load_enable_sync)
+		st->regs[ADF41513_REG12] &= ~ADF41513_REG12_LE_SELECT_MSK;
+
+	return adf41513_sync_config(st, ADF41513_SYNC_DIFF);
+}
+
+static int adf41513_resume(struct adf41513_state *st)
+{
+	int ret;
+
+	st->regs[ADF41513_REG6] &= ~ADF41513_REG6_POWER_DOWN_MSK;
+	ret = adf41513_sync_config(st, ADF41513_SYNC_DIFF);
+	if (ret < 0)
+		return ret;
+
+	/* check if load enable sync needs to be set */
+	if (st->data.load_enable_sync) {
+		st->regs[ADF41513_REG12] |= ADF41513_REG12_LE_SELECT_MSK;
+		ret = adf41513_sync_config(st, ADF41513_SYNC_DIFF);
+	}
+
+	return ret;
+}
+
+static ssize_t adf41513_read(struct iio_dev *indio_dev,
+			     uintptr_t private,
+			     const struct iio_chan_spec *chan,
+			     char *buf)
+{
+	struct adf41513_state *st = iio_priv(indio_dev);
+	u64 val = 0;
+	int ret = 0;
+
+	guard(mutex)(&st->lock);
+
+	switch ((u32)private) {
+	case ADF41513_FREQ:
+		ret = adf41513_uhz_to_str(adf41513_pll_get_rate(st), buf);
+		if (st->lock_detect)
+			if (!gpiod_get_value_cansleep(st->lock_detect)) {
+				dev_dbg(&st->spi->dev, "PLL un-locked\n");
+				ret = -EBUSY;
+			}
+		break;
+	case ADF41513_FREQ_REFIN:
+		st->ref_freq_hz = clk_get_rate(st->ref_clk);
+		val = st->ref_freq_hz;
+		ret = sysfs_emit(buf, "%llu\n", val);
+		break;
+	case ADF41513_FREQ_RESOLUTION:
+		ret = adf41513_uhz_to_str(st->data.freq_resolution_uhz, buf);
+		break;
+	case ADF41513_POWER_DOWN:
+		val = (u64)FIELD_GET(ADF41513_REG6_POWER_DOWN_MSK, st->regs_hw[ADF41513_REG6]);
+		ret = sysfs_emit(buf, "%llu\n", val);
+		break;
+	default:
+		ret = -EINVAL;
+		break;
+	}
+
+	return ret;
+}
+
+static ssize_t adf41513_write(struct iio_dev *indio_dev,
+			      uintptr_t private,
+			      const struct iio_chan_spec *chan,
+			      const char *buf, size_t len)
+{
+	struct adf41513_state *st = iio_priv(indio_dev);
+	unsigned long long readin;
+	unsigned long tmp;
+	u64 freq_uhz;
+	int ret;
+
+	guard(mutex)(&st->lock);
+
+	switch ((u32)private) {
+	case ADF41513_FREQ:
+		ret = adf41513_parse_uhz(buf, &freq_uhz);
+		if (ret)
+			return ret;
+		ret = adf41513_set_frequency(st, freq_uhz, ADF41513_SYNC_DIFF);
+		break;
+	case ADF41513_FREQ_REFIN:
+		ret = kstrtoull(buf, 10, &readin);
+		if (ret)
+			return ret;
+
+		if (readin < ADF41513_MIN_REF_FREQ || readin > ADF41513_MAX_REF_FREQ) {
+			ret = -EINVAL;
+			break;
+		}
+
+		tmp = clk_round_rate(st->ref_clk, readin);
+		if (tmp != readin) {
+			ret = -EINVAL;
+			break;
+		}
+		ret = clk_set_rate(st->ref_clk, tmp);
+		if (ret < 0)
+			break;
+
+		st->ref_freq_hz = readin;
+		/* update RF out */
+		ret = adf41513_set_frequency(st,
+					     st->settings.target_frequency_uhz,
+					     ADF41513_SYNC_DIFF);
+		break;
+	case ADF41513_FREQ_RESOLUTION:
+		ret = adf41513_parse_uhz(buf, &freq_uhz);
+		if (ret)
+			return ret;
+		if (freq_uhz == 0)
+			ret = -EINVAL;
+		else
+			st->data.freq_resolution_uhz = freq_uhz;
+		break;
+	case ADF41513_POWER_DOWN:
+		ret = kstrtoull(buf, 10, &readin);
+		if (ret)
+			return ret;
+
+		if (readin)
+			ret = adf41513_suspend(st);
+		else
+			ret = adf41513_resume(st);
+		break;
+	default:
+		ret = -EINVAL;
+		break;
+	}
+
+	return ret ? ret : len;
+}
+
+#define _ADF41513_EXT_INFO(_name, _ident) { \
+	.name = _name, \
+	.read = adf41513_read, \
+	.write = adf41513_write, \
+	.private = _ident, \
+	.shared = IIO_SEPARATE, \
+}
+
+static const struct iio_chan_spec_ext_info adf41513_ext_info[] = {
+	/*
+	 * Ideally we use IIO_CHAN_INFO_FREQUENCY, but there are
+	 * values > 2^32 with sub-Hz resolution
+	 */
+	_ADF41513_EXT_INFO("frequency", ADF41513_FREQ),
+	_ADF41513_EXT_INFO("frequency_resolution", ADF41513_FREQ_RESOLUTION),
+	_ADF41513_EXT_INFO("refin_frequency", ADF41513_FREQ_REFIN),
+	_ADF41513_EXT_INFO("powerdown", ADF41513_POWER_DOWN),
+	{ },
+};
+
+static const struct iio_chan_spec adf41513_chan = {
+	.type = IIO_ALTVOLTAGE,
+	.indexed = 1,
+	.output = 1,
+	.channel = 0,
+	.ext_info = adf41513_ext_info,
+};
+
+static int adf41513_reg_access(struct iio_dev *indio_dev,
+			       unsigned int reg,
+			       unsigned int writeval,
+			       unsigned int *readval)
+{
+	struct adf41513_state *st = iio_priv(indio_dev);
+	int ret;
+
+	if (reg > ADF41513_REG13)
+		return -EINVAL;
+
+	guard(mutex)(&st->lock);
+
+	if (!readval) {
+		/* direct register access invalidates cached pll settings */
+		st->settings.mode = ADF41513_MODE_INVALID;
+
+		st->regs[reg] = writeval & ~0xF; /* Clear control bits */
+		ret = adf41513_sync_config(st, 1 << reg);
+	} else {
+		*readval = st->regs_hw[reg];
+		ret = 0;
+	}
+
+	return ret;
+}
+
+static const struct iio_info adf41513_info = {
+	.debugfs_reg_access = &adf41513_reg_access,
+};
+
+static void adf41513_clk_del_provider(void *data)
+{
+	struct adf41513_state *st = data;
+
+	of_clk_del_provider(st->spi->dev.of_node);
+}
+
+static unsigned long adf41513_clk_recalc_rate(struct clk_hw *hw,
+					      unsigned long parent_rate)
+{
+	struct adf41513_state *st = to_adf41513_state(hw);
+
+	guard(mutex)(&st->lock);
+	return (unsigned long)div_u64(adf41513_pll_get_rate(st), ADF41513_HZ_TO_UHZ);
+}
+
+static long adf41513_clk_round_rate(struct clk_hw *hw,
+				    unsigned long rate,
+				    unsigned long *parent_rate)
+{
+	return rate;
+}
+
+static int adf41513_clk_set_rate(struct clk_hw *hw,
+				 unsigned long rate,
+				 unsigned long parent_rate)
+{
+	struct adf41513_state *st = to_adf41513_state(hw);
+	u64 freq_uhz = (u64)rate * ADF41513_HZ_TO_UHZ;
+
+	if (parent_rate < ADF41513_MIN_REF_FREQ ||
+	    parent_rate > ADF41513_MAX_REF_FREQ)
+		return -EINVAL;
+
+	guard(mutex)(&st->lock);
+	st->ref_freq_hz = parent_rate;
+	return adf41513_set_frequency(st, freq_uhz, ADF41513_SYNC_DIFF);
+}
+
+static int adf41513_clk_prepare(struct clk_hw *hw)
+{
+	struct adf41513_state *st = to_adf41513_state(hw);
+
+	guard(mutex)(&st->lock);
+	return adf41513_resume(st);
+}
+
+static void adf41513_clk_unprepare(struct clk_hw *hw)
+{
+	struct adf41513_state *st = to_adf41513_state(hw);
+
+	guard(mutex)(&st->lock);
+	adf41513_suspend(st);
+}
+
+static int adf41513_clk_is_enabled(struct clk_hw *hw)
+{
+	struct adf41513_state *st = to_adf41513_state(hw);
+
+	guard(mutex)(&st->lock);
+	return (st->regs_hw[ADF41513_REG6] & ADF41513_REG6_POWER_DOWN_MSK) == 0;
+}
+
+static const struct clk_ops adf41513_clk_ops = {
+	.recalc_rate = adf41513_clk_recalc_rate,
+	.round_rate = adf41513_clk_round_rate,
+	.set_rate = adf41513_clk_set_rate,
+	.prepare = adf41513_clk_prepare,
+	.unprepare = adf41513_clk_unprepare,
+	.is_enabled = adf41513_clk_is_enabled,
+};
+
+static int adf41513_clk_register(struct adf41513_state *st)
+{
+	struct spi_device *spi = st->spi;
+	struct clk_init_data init;
+	struct clk *clk = NULL;
+	const char *parent_name;
+	int ret;
+
+	if (!device_property_present(&spi->dev, "#clock-cells"))
+		return 0;
+
+	if (device_property_read_string(&spi->dev, "clock-output-names", &init.name)) {
+		init.name = devm_kasprintf(&spi->dev, GFP_KERNEL, "%s-clk",
+					   fwnode_get_name(dev_fwnode(&spi->dev)));
+		if (!init.name)
+			return -ENOMEM;
+	}
+
+	parent_name = of_clk_get_parent_name(spi->dev.of_node, 0);
+	if (!parent_name)
+		return -EINVAL;
+
+	init.ops = &adf41513_clk_ops;
+	init.parent_names = &parent_name;
+	init.num_parents = 1;
+	init.flags = CLK_SET_RATE_PARENT;
+
+	st->clk_hw.init = &init;
+	clk = devm_clk_register(&spi->dev, &st->clk_hw);
+	if (IS_ERR(clk))
+		return PTR_ERR(clk);
+
+	ret = of_clk_add_provider(spi->dev.of_node, of_clk_src_simple_get, clk);
+	if (ret)
+		return ret;
+
+	st->clk_out = clk;
+
+	return devm_add_action_or_reset(&spi->dev, adf41513_clk_del_provider, st);
+}
+
+static int adf41513_parse_fw(struct adf41513_state *st)
+{
+	struct device *dev = &st->spi->dev;
+
+	int ret;
+	u32 tmp;
+	u32 cp_resistance;
+	u32 cp_current;
+
+	/* power-up frequency */
+	st->data.power_up_frequency = ADF41510_MAX_RF_FREQ; /* Default 10 GHz */
+	if (!device_property_read_u64(dev, "adi,power-up-frequency",
+				      &st->data.power_up_frequency)) {
+		if (st->data.power_up_frequency < ADF41513_MIN_RF_FREQ ||
+		    st->data.power_up_frequency > ADF41513_MAX_RF_FREQ)
+			return dev_err_probe(dev, -ERANGE,
+					     "power-up frequency %llu Hz out of range\n",
+					     st->data.power_up_frequency);
+	}
+
+	/* reference divider factor - optional minimal value */
+	st->data.ref_div_factor = ADF41513_MIN_R_CNT; /* Default R = 1 */
+	if (!device_property_read_u32(dev, "adi,reference-div-factor", &tmp)) {
+		if (tmp >= ADF41513_MIN_R_CNT && tmp <= ADF41513_MAX_R_CNT)
+			st->data.ref_div_factor = tmp;
+		else
+			return dev_err_probe(dev, -ERANGE,
+					     "invalid reference div factor %u\n", tmp);
+	}
+
+	/* reference controls */
+	st->data.ref_doubler_en = device_property_read_bool(dev, "adi,reference-doubler-enable");
+	st->data.ref_div2_en = device_property_read_bool(dev, "adi,reference-div2-enable");
+
+	/* charge pump resistor */
+	cp_resistance = 2700; /* Default 2.7 kOhms */
+	if (!device_property_read_u32(dev, "adi,charge-pump-resistor-ohms", &tmp)) {
+		if (tmp >= 1800 && tmp <= 10000)
+			cp_resistance = tmp;
+		else
+			return dev_err_probe(dev, -ERANGE, "RSET %u Ohms out of range\n", tmp);
+	}
+
+	/* charge pump current */
+	st->data.charge_pump_voltage_mv = 6480; /* Default 2.7 kOhm * 2.4 mA = 6.48 V */
+	if (!device_property_read_u32(dev, "adi,charge-pump-current-microamp", &cp_current)) {
+		tmp = (cp_current * cp_resistance) / 1000; /* Convert to mV */
+		 /* Validate charge pump voltage */
+		if (tmp >= ADF41513_MIN_CP_VOLTAGE_MV && tmp <= ADF41513_MAX_CP_VOLTAGE_MV)
+			st->data.charge_pump_voltage_mv = tmp;
+		else
+			return dev_err_probe(dev, -ERANGE,
+				"charge pump current %u uA (R_set = %u Ohms) out of range\n",
+				cp_current, cp_resistance);
+	}
+
+	/* phase detector polarity */
+	st->data.phase_detector_polarity =
+		device_property_read_bool(dev, "adi,phase-detector-polarity-positive-enable");
+
+	/* muxout selection */
+	ret = device_property_match_property_string(dev, "adi,muxout-select",
+						    adf41513_muxout_modes,
+						    ARRAY_SIZE(adf41513_muxout_modes));
+	if (ret >= 0)
+		st->data.muxout_select = ret;
+	else
+		st->data.muxout_select = ADF41513_MUXOUT_TRISTATE;
+
+	/* muxout logic level: default 3v3 */
+	st->data.muxout_1v8_en = device_property_read_bool(dev, "adi,muxout-level-1v8-enable");
+
+	/* lock detector settings */
+	st->data.lock_detect_bias = 0; /* default 40 uA */
+	if (!device_property_read_u32(dev, "lock-detect-bias-microamp", &tmp)) {
+		tmp = (ADF41513_MAX_LD_BIAS_UA - tmp) / ADF41513_LD_BIAS_STEP_UA;
+		if (tmp > 3)
+			return dev_err_probe(dev, -ERANGE,
+					     "invalid lock detect bias setting: %u\n", tmp);
+		else
+			st->data.lock_detect_bias = tmp;
+	}
+
+	st->data.lock_detect_precision = 0; /* default 0 */
+	if (!device_property_read_u32(dev, "adi,lock-detect-precision", &tmp)) {
+		if (tmp > 3)
+			return dev_err_probe(dev, -ERANGE,
+					     "invalid lock detect precision: %u\n", tmp);
+		else
+			st->data.lock_detect_precision = tmp;
+	}
+
+	st->data.lock_detect_count = 0; /* default 0 */
+	if (!device_property_read_u32(dev, "adi,lock-detect-count", &tmp)) {
+		if (tmp > 7)
+			return dev_err_probe(dev, -ERANGE,
+					     "lock detect count setting too high %u\n", tmp);
+		else
+			st->data.lock_detect_count = tmp;
+	}
+
+	/* lock detect clk select */
+	st->data.fast_lock_en = device_property_read_bool(dev, "adi,fast-lock-enable");
+
+	/* phase resync configuration */
+	st->data.phase_resync_en = device_property_read_bool(dev, "adi,phase-resync-enable");
+
+	st->data.phase_resync_clk_div[0] = 1;
+	if (!device_property_read_u32(dev, "adi,12bit-clk-divider", &tmp)) {
+		if (tmp > ADF41513_MAX_CLK_DIVIDER)
+			return dev_err_probe(dev, -ERANGE, "invalid clk1 divider: %u\n", tmp);
+		st->data.phase_resync_clk_div[0] = tmp;
+	}
+
+	st->data.phase_resync_clk_div[1] = 1;
+	if (!device_property_read_u32(dev, "adi,12bit-clk2-divider", &tmp)) {
+		if (tmp > ADF41513_MAX_CLK_DIVIDER)
+			return dev_err_probe(dev, -ERANGE, "invalid clk2 divider: %u\n", tmp);
+		st->data.phase_resync_clk_div[1] = tmp;
+	}
+
+	/* load enable sync with ref input */
+	st->data.load_enable_sync = device_property_read_bool(dev, "adi,le-sync-enable");
+
+	/* initial frequency resolution: Default to 1 Hz */
+	st->data.freq_resolution_uhz = ADF41513_HZ_TO_UHZ;
+	if (!device_property_read_u64(dev, "adi,freq-resolution",
+				      &st->data.freq_resolution_uhz)) {
+		if (!st->data.freq_resolution_uhz)
+			return dev_err_probe(dev, -ERANGE, "frequency resolution cannot be zero\n");
+	}
+
+	return 0;
+}
+
+static int adf41513_setup(struct adf41513_state *st)
+{
+	int ret;
+	u32 cp_index;
+
+	memset(st->regs_hw, 0xFF, sizeof(st->regs_hw));
+
+	/* assume DLD pin is used for digital lock detect */
+	st->regs[ADF41513_REG5] |= ADF41513_REG5_DLD_MODES(ADF41513_DLD_DIG_LD);
+
+	/* configure charge pump current settings */
+	cp_index = find_closest(st->data.charge_pump_voltage_mv,
+				adf41513_cp_voltage_mv,
+				ARRAY_SIZE(adf41513_cp_voltage_mv));
+	st->regs[ADF41513_REG5] |= ADF41513_REG5_CP_CURRENT(cp_index);
+
+	/* Configure phase detector polarity */
+	if (st->data.phase_detector_polarity)
+		st->regs[ADF41513_REG6] |= ADF41513_REG6_PD_POLARITY_MSK;
+
+	/* narrow ABP | loss of lock detect enable | SD reset | LDP from data */
+	st->regs[ADF41513_REG6] |= ADF41513_REG6_ABP_MSK |
+				   ADF41513_REG6_LOL_ENABLE_MSK |
+				   ADF41513_REG6_SD_RESET_MSK |
+				   ADF41513_REG6_LDP(st->data.lock_detect_precision);
+
+	/* LD count from data | PS bias */
+	st->regs[ADF41513_REG7] |= ADF41513_REG7_LD_COUNT(st->data.lock_detect_count) |
+				   ADF41513_REG7_LD_CLK_SEL(st->data.fast_lock_en) |
+				   ADF41513_REG7_PS_BIAS(2);
+
+	/* enable phase resync and configure the clk divs */
+	st->regs[ADF41513_REG5] |= ADF41513_REG5_CLK1_DIV(st->data.phase_resync_clk_div[0]);
+	st->regs[ADF41513_REG7] |= ADF41513_REG7_CLK_DIV_MODE(st->data.phase_resync_en ? 2 : 0) |
+				   ADF41513_REG7_CLK2_DIV(st->data.phase_resync_clk_div[1]);
+
+	/* lock detect bias */
+	st->regs[ADF41513_REG9] |= ADF41513_REG9_LD_BIAS(st->data.lock_detect_bias);
+
+	/* power down select */
+	st->regs[ADF41513_REG11] |= ADF41513_REG11_POWER_DOWN_SEL_MSK;
+
+	/* muxout */
+	st->regs[ADF41513_REG12] |= ADF41513_REG12_MUXOUT(st->data.muxout_select) |
+				    ADF41513_REG12_LOGIC_LEVEL(st->data.muxout_1v8_en ? 0 : 1) |
+				    ADF41513_REG12_LE_SELECT(st->data.phase_resync_en);
+
+	/* set power-up frequency */
+	ret = adf41513_set_frequency(st, (u64)st->data.power_up_frequency * ADF41513_HZ_TO_UHZ,
+				     ADF41513_SYNC_ALL);
+	if (ret < 0) {
+		return dev_err_probe(&st->spi->dev, ret,
+				     "Failed to set power-up frequency: %d\n", ret);
+	}
+
+	return 0;
+}
+
+static void adf41513_power_down(void *data)
+{
+	struct adf41513_state *st = data;
+
+	guard(mutex)(&st->lock);
+	adf41513_suspend(st);
+
+	if (st->chip_enable)
+		gpiod_set_value_cansleep(st->chip_enable, 0);
+}
+
+static int adf41513_pm_suspend(struct device *dev)
+{
+	struct adf41513_state *st = dev_get_drvdata(dev);
+
+	return adf41513_suspend(st);
+}
+
+static int adf41513_pm_resume(struct device *dev)
+{
+	struct adf41513_state *st = dev_get_drvdata(dev);
+
+	return adf41513_resume(st);
+}
+
+static const struct adf41513_chip_info adf41513_chip_info = {
+	.name = "adf41513",
+	.has_prescaler_8_9 = true,
+	.max_rf_freq_hz = ADF41513_MAX_RF_FREQ,
+};
+
+static const struct adf41513_chip_info adf41510_chip_info = {
+	.name = "adf41510",
+	.has_prescaler_8_9 = false,
+	.max_rf_freq_hz = ADF41510_MAX_RF_FREQ,
+};
+
+static int adf41513_probe(struct spi_device *spi)
+{
+	struct iio_dev *indio_dev;
+	struct adf41513_state *st;
+
+	int ret;
+
+	indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
+	if (!indio_dev)
+		return -ENOMEM;
+
+	st = iio_priv(indio_dev);
+	st->spi = spi;
+	st->chip_info = spi_get_device_match_data(spi);
+
+	/* get reference clock */
+	st->ref_clk = devm_clk_get_enabled(&spi->dev, NULL);
+	if (IS_ERR(st->ref_clk))
+		return -EPROBE_DEFER;
+
+	/* parse device properties */
+	ret = adf41513_parse_fw(st);
+	if (ret)
+		return ret;
+
+	spi_set_drvdata(spi, st);
+
+	/* vcc regulator */
+	ret = devm_regulator_get_enable(&spi->dev, "vcc");
+	if (ret)
+		return dev_err_probe(&spi->dev, ret,
+				     "failed to get and enable vcc regulator\n");
+
+	/* get chip enable gpio */
+	st->chip_enable = devm_gpiod_get_optional(&spi->dev, "chip-enable", GPIOD_OUT_HIGH);
+	if (IS_ERR(st->chip_enable))
+		return dev_err_probe(&spi->dev, PTR_ERR(st->chip_enable),
+				     "fail to request chip enable GPIO\n");
+
+	/* get lock detect gpio */
+	st->lock_detect = devm_gpiod_get_optional(&spi->dev, "lock-detect", GPIOD_IN);
+	if (IS_ERR(st->lock_detect))
+		return dev_err_probe(&spi->dev, PTR_ERR(st->lock_detect),
+				     "fail to request lock detect GPIO\n");
+
+	/* get reference frequency */
+	st->ref_freq_hz = clk_get_rate(st->ref_clk);
+
+	/* validate reference frequency */
+	if (st->ref_freq_hz < ADF41513_MIN_REF_FREQ || st->ref_freq_hz > ADF41513_MAX_REF_FREQ)
+		return dev_err_probe(&spi->dev, -ERANGE,
+				     "reference frequency %llu Hz out of range\n",
+				     st->ref_freq_hz);
+
+	ret = devm_mutex_init(&spi->dev, &st->lock);
+	if (ret)
+		return dev_err_probe(&spi->dev, ret, "failed to initialize mutex: %d\n", ret);
+
+	ret = adf41513_clk_register(st);
+	if (ret < 0)
+		return dev_err_probe(&spi->dev, ret, "failed to register clock: %d\n", ret);
+
+	/* configure IIO device */
+	indio_dev->name = st->chip_info->name;
+	indio_dev->info = &adf41513_info;
+	indio_dev->modes = INDIO_DIRECT_MODE;
+	indio_dev->channels = &adf41513_chan;
+	indio_dev->num_channels = 1;
+
+	/* initialize device */
+	ret = adf41513_setup(st);
+	if (ret < 0)
+		return ret;
+
+	/* add power down action */
+	ret = devm_add_action_or_reset(&spi->dev, adf41513_power_down, st);
+	if (ret)
+		return dev_err_probe(&spi->dev, ret, "Failed to add power down action: %d\n", ret);
+
+	/* register IIO device */
+	ret = devm_iio_device_register(&spi->dev, indio_dev);
+	if (ret)
+		return dev_err_probe(&spi->dev, ret, "Failed to register IIO device: %d\n", ret);
+
+	dev_info(&spi->dev, "ADF41513 PLL synthesizer registered\n");
+
+	return 0;
+}
+
+static const struct spi_device_id adf41513_id[] = {
+	{"adf41510", (kernel_ulong_t)&adf41510_chip_info},
+	{"adf41513", (kernel_ulong_t)&adf41513_chip_info},
+	{}
+};
+MODULE_DEVICE_TABLE(spi, adf41513_id);
+
+static const struct of_device_id adf41513_of_match[] = {
+	{ .compatible = "adi,adf41510", .data = &adf41510_chip_info },
+	{ .compatible = "adi,adf41513", .data = &adf41513_chip_info },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, adf41513_of_match);
+
+DEFINE_SIMPLE_DEV_PM_OPS(adf41513_pm_ops,
+			 adf41513_pm_suspend,
+			 adf41513_pm_resume);
+
+static struct spi_driver adf41513_driver = {
+	.driver = {
+		.name = "adf41513",
+		.pm = pm_ptr(&adf41513_pm_ops),
+		.of_match_table = adf41513_of_match,
+	},
+	.probe = adf41513_probe,
+	.id_table = adf41513_id,
+};
+module_spi_driver(adf41513_driver);
+
+MODULE_AUTHOR("Rodrigo Alencar <rodrigo.alencar@analog.com>");
+MODULE_DESCRIPTION("Analog Devices ADF41513 PLL Frequency Synthesizer");
+MODULE_LICENSE("GPL");

-- 
2.43.0



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

* [PATCH 2/3] dt-bindings: iio: frequency: add adf41513
  2025-11-10 15:44 [PATCH 0/3] ADF41513/ADF41510 PLL frequency synthesizers Rodrigo Alencar via B4 Relay
  2025-11-10 15:44 ` [PATCH 1/3] iio: frequency: adf41513: driver implementation Rodrigo Alencar via B4 Relay
@ 2025-11-10 15:44 ` Rodrigo Alencar via B4 Relay
  2025-11-11  8:05   ` Krzysztof Kozlowski
  2025-11-13 13:21   ` Marcelo Schmitt
  2025-11-10 15:44 ` [PATCH 3/3] docs: iio: add documentation for adf41513 driver Rodrigo Alencar via B4 Relay
                   ` (2 subsequent siblings)
  4 siblings, 2 replies; 15+ messages in thread
From: Rodrigo Alencar via B4 Relay @ 2025-11-10 15:44 UTC (permalink / raw)
  To: linux-kernel, linux-iio, devicetree, linux-doc
  Cc: Jonathan Cameron, David Lechner, Andy Shevchenko,
	Lars-Peter Clausen, Michael Hennerich, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Jonathan Corbet,
	Rodrigo Alencar

From: Rodrigo Alencar <rodrigo.alencar@analog.com>

ultralow noise PLL frequency synthesizer that can be used to
implement local oscillators (LOs) as high as 26.5 GHz

Signed-off-by: Rodrigo Alencar <rodrigo.alencar@analog.com>
---
 .../bindings/iio/frequency/adi,adf41513.yaml       | 268 +++++++++++++++++++++
 MAINTAINERS                                        |   1 +
 2 files changed, 269 insertions(+)

diff --git a/Documentation/devicetree/bindings/iio/frequency/adi,adf41513.yaml b/Documentation/devicetree/bindings/iio/frequency/adi,adf41513.yaml
new file mode 100644
index 000000000000..7e1ad80d68af
--- /dev/null
+++ b/Documentation/devicetree/bindings/iio/frequency/adi,adf41513.yaml
@@ -0,0 +1,268 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/iio/frequency/adi,adf41513.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Analog Devices ADF41513 PLL Frequency Synthesizer
+
+maintainers:
+  - Rodrigo Alencar <rodrigo.alencar@analog.com>
+
+description:
+  The ADF41513 is an ultralow noise frequency synthesizer that can be used to
+  implement local oscillators (LOs) as high as 26.5 GHz in the upconversion and
+  downconversion sections of wireless receivers and transmitters. The ADF41510
+  supports frequencies up to 10 GHz.
+
+  https://www.analog.com/en/products/adf41513.html
+  https://www.analog.com/en/products/adf41510.html
+
+properties:
+  compatible:
+    enum:
+      - adi,adf41510
+      - adi,adf41513
+
+  reg:
+    maxItems: 1
+
+  spi-max-frequency:
+    maximum: 25000000
+
+  clocks:
+    maxItems: 1
+    description: Clock that provides the reference input frequency.
+
+  '#clock-cells':
+    const: 0
+
+  clock-output-names:
+    maxItems: 1
+
+  vcc-supply:
+    description: Power supply for the device (3.3V)
+
+  chip-enable-gpios:
+    description:
+      GPIO that controls the chip enable pin. A logic low on this pin
+      powers down the device and puts the charge pump output into
+      three-state mode.
+    maxItems: 1
+
+  lock-detect-gpios:
+    description:
+      GPIO for lock detect functionality. When configured for digital lock
+      detect, this pin will output a logic high when the PLL is locked.
+    maxItems: 1
+
+  adi,power-up-frequency:
+    $ref: /schemas/types.yaml#/definitions/uint64
+    minimum: 1000000000
+    maximum: 26500000000
+    default: 10000000000
+    description:
+      The PLL tunes to this frequency (in Hz) on driver probe.
+      Range is 1 GHz to 26.5 GHz for ADF41513, and 1 GHz to 10 GHz for ADF41510.
+
+  adi,reference-div-factor:
+    $ref: /schemas/types.yaml#/definitions/uint32
+    minimum: 1
+    maximum: 32
+    description:
+      Reference division factor (R Counter). If not specified, the driver
+      will calculate the optimal value automatically.
+
+  adi,reference-doubler-enable:
+    description:
+      Enables the reference doubler. The maximum reference frequency when
+      the doubler is enabled is 225 MHz.
+    type: boolean
+
+  adi,reference-div2-enable:
+    description:
+      Enables the reference divide-by-2 function. This provides a 50%
+      duty cycle signal to the PFD.
+    type: boolean
+
+  adi,charge-pump-current-microamp:
+    minimum: 450
+    maximum: 7200
+    default: 2400
+    description:
+      Charge pump current in microamps. The value will be rounded to the
+      nearest supported value.
+
+  adi,charge-pump-resistor-ohms:
+    minimum: 1800
+    maximum: 10000
+    default: 2700
+    description:
+      External charge pump resistor value in ohms. This sets the maximum
+      charge pump current along with the charge pump current setting.
+
+  adi,muxout-select:
+    description:
+      On chip multiplexer output selection.
+      high_z - MUXOUT Pin set to high-Z. (default)
+      muxout_high - MUXOUT Pin set to high.
+      muxout_low - MUXOUT Pin set to low.
+      f_div_rclk - MUXOUT Pin set to R divider output
+      f_div_nclk - MUXOUT Pin set to N divider output
+      lock_detect - MUXOUT Pin set to Digital lock detect
+      serial_data - MUXOUT Pin set to Serial data output
+      readback - MUXOUT Pin set to Readback mode
+      f_div_clk1 - MUXOUT Pin set to CLK1 divider output
+      f_div_rclk_2 - MUXOUT Pin set to R divider/2 output
+      f_div_nclk_2 - MUXOUT Pin set to N divider/2 output
+    enum: [high_z, muxout_high, muxout_low, f_div_rclk, f_div_nclk, lock_detect,
+           serial_data, readback, f_div_clk1, f_div_rclk_2, f_div_nclk_2]
+
+  adi,muxout-level-1v8-enable:
+    description:
+      Set MUXOUT and DLD logic levels to 1.8V. Default is 3.3V.
+    type: boolean
+
+  adi,phase-detector-polarity-positive-enable:
+    description:
+      Set phase detector polarity to positive. Default is negative.
+      Use positive polarity with non-inverting loop filter and VCO with
+      positive tuning slope, or with inverting loop filter and VCO with
+      negative tuning slope.
+    type: boolean
+
+  adi,lock-detect-precision:
+    $ref: /schemas/types.yaml#/definitions/uint32
+    minimum: 0
+    maximum: 3
+    description:
+      Lock detector precision setting. Controls the sensitivity of the
+      lock detector. Lower values of precision increases the lock detector
+      window size.
+
+  adi,lock-detect-count:
+    $ref: /schemas/types.yaml#/definitions/uint32
+    minimum: 0
+    maximum: 7
+    description: |
+      Lock detector count setting (3-bit value). Determines the number of
+      consecutive phase detector cycles that must be within the lock detector
+      window before lock is declared. The count grows in powers of two of the
+      programmed value:
+      - if adi,fast-lock-enable is set count = 2 * 2^value
+      - if adi,fast-lock-enable is not set count = 64 * 2^value
+
+  adi,lock-detect-bias-microamp:
+    description:
+      Lock detector bias current. Controls the lock detector window size
+      along with the lock detector precision setting. Lower bias current
+      increases the window size.
+    enum: [10, 20, 30, 40]
+
+  adi,fast-lock-enable:
+    description:
+      Enable fast lock mode. This changes the lock detector clock selection
+      for faster lock indication.
+    type: boolean
+
+  adi,phase-resync-enable:
+    description:
+      Enable phase resync functionality. This produces a consistent output
+      phase offset with respect to the input reference.
+    type: boolean
+
+  adi,12bit-clk-divider:
+    $ref: /schemas/types.yaml#/definitions/uint32
+    minimum: 0
+    maximum: 4095
+    description:
+      CLK1 divider value used when adi,phase-resync-enable is set
+
+  adi,12bit-clk2-divider:
+    $ref: /schemas/types.yaml#/definitions/uint32
+    minimum: 0
+    maximum: 4095
+    description:
+      CLK2 divider value used when adi,phase-resync-enable is set
+
+  adi,le-sync-enable:
+    description:
+      Synchronize the rising edge of LE on an SPI write with the falling
+      edge of the reference signal to prevent glitches.
+    type: boolean
+
+  adi,freq-resolution:
+    $ref: /schemas/types.yaml#/definitions/uint64
+    minimum: 1
+    default: 1000000
+    description:
+      Initial frequency resolution in micro-Hz (µHz) for the algorithm to achieve.
+      This influences the choice between fixed and variable modulus modes.
+      Default is 1000000 µHz (1 Hz).
+
+required:
+  - compatible
+  - reg
+  - clocks
+  - vcc-supply
+
+allOf:
+  - $ref: /schemas/spi/spi-peripheral-props.yaml#
+
+unevaluatedProperties: false
+
+examples:
+  - |
+    spi {
+        #address-cells = <1>;
+        #size-cells = <0>;
+
+        pll@0 {
+            compatible = "adi,adf41513";
+            reg = <0>;
+            spi-max-frequency = <10000000>;
+            clocks = <&ref_clk>;
+            vcc-supply = <&vcc_3v3>;
+            #clock-cells = <0>;
+
+            adi,power-up-frequency = /bits/ 64 <12000000000>;
+            adi,charge-pump-current-microamp = <2400>;
+            adi,phase-detector-polarity-positive-enable;
+        };
+    };
+  - |
+    #include <dt-bindings/gpio/gpio.h>
+    spi {
+        #address-cells = <1>;
+        #size-cells = <0>;
+
+        /* Example with advanced features enabled */
+        pll_advanced@0 {
+            compatible = "adi,adf41513";
+            reg = <0>;
+            spi-max-frequency = <25000000>;
+            clocks = <&ref_clk>;
+            vcc-supply = <&vcc_3v3>;
+            chip-enable-gpios = <&gpio0 10 GPIO_ACTIVE_HIGH>;
+            lock-detect-gpios = <&gpio0 11 GPIO_ACTIVE_HIGH>;
+            #clock-cells = <0>;
+            clock-output-names = "adf41513_clk";
+
+            adi,power-up-frequency = /bits/ 64 <15500000000>;
+            adi,charge-pump-current-microamp = <3600>;
+            adi,charge-pump-resistor-ohms = <2700>;
+            adi,reference-doubler-enable;
+            adi,muxout-select = "lock_detect";
+            adi,lock-detect-precision = <1>;
+            adi,lock-detect-count = <4>;
+            adi,lock-detect-bias-microamp = <40>;
+            adi,fast-lock-enable;
+            adi,phase-resync-enable;
+            adi,12bit-clk-divider = <1>;
+            adi,12bit-clk2-divider = <200>;
+            adi,le-sync-enable;
+            adi,freq-resolution = /bits/ 64 <1000000>;
+            adi,phase-detector-polarity-positive-enable;
+        };
+    };
+...
diff --git a/MAINTAINERS b/MAINTAINERS
index 8df4a0d216c8..1bbcff826238 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1606,6 +1606,7 @@ M:	Rodrigo Alencar <rodrigo.alencar@analog.com>
 L:	linux-iio@vger.kernel.org
 S:	Supported
 W:	https://ez.analog.com/linux-software-drivers
+F:	Documentation/devicetree/bindings/iio/frequency/adi,adf41513.yaml
 F:	drivers/iio/frequency/adf41513.c
 
 ANALOG DEVICES INC ADF4377 DRIVER

-- 
2.43.0



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

* [PATCH 3/3] docs: iio: add documentation for adf41513 driver
  2025-11-10 15:44 [PATCH 0/3] ADF41513/ADF41510 PLL frequency synthesizers Rodrigo Alencar via B4 Relay
  2025-11-10 15:44 ` [PATCH 1/3] iio: frequency: adf41513: driver implementation Rodrigo Alencar via B4 Relay
  2025-11-10 15:44 ` [PATCH 2/3] dt-bindings: iio: frequency: add adf41513 Rodrigo Alencar via B4 Relay
@ 2025-11-10 15:44 ` Rodrigo Alencar via B4 Relay
  2025-11-10 16:38 ` [PATCH 0/3] ADF41513/ADF41510 PLL frequency synthesizers Andy Shevchenko
  2025-11-16 16:10 ` Jonathan Cameron
  4 siblings, 0 replies; 15+ messages in thread
From: Rodrigo Alencar via B4 Relay @ 2025-11-10 15:44 UTC (permalink / raw)
  To: linux-kernel, linux-iio, devicetree, linux-doc
  Cc: Jonathan Cameron, David Lechner, Andy Shevchenko,
	Lars-Peter Clausen, Michael Hennerich, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Jonathan Corbet,
	Rodrigo Alencar

From: Rodrigo Alencar <rodrigo.alencar@analog.com>

add documentation for adf41513 driver which describes the device
driver files and shows how userspace may consume the ABI for various
tasks

Signed-off-by: Rodrigo Alencar <rodrigo.alencar@analog.com>
---
 Documentation/iio/adf41513.rst | 377 +++++++++++++++++++++++++++++++++++++++++
 Documentation/iio/index.rst    |   1 +
 MAINTAINERS                    |   1 +
 3 files changed, 379 insertions(+)

diff --git a/Documentation/iio/adf41513.rst b/Documentation/iio/adf41513.rst
new file mode 100644
index 000000000000..a2d5425d49c9
--- /dev/null
+++ b/Documentation/iio/adf41513.rst
@@ -0,0 +1,377 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+===============
+ADF41513 driver
+===============
+
+This driver supports Analog Devices' ADF41513 and ADF41510 PLL frequency
+synthesizers on SPI bus.
+
+1. Supported devices
+====================
+
+* `ADF41510 <https://www.analog.com/ADF41510>`_
+* `ADF41513 <https://www.analog.com/ADF41513>`_
+
+The ADF41513 is an ultralow noise frequency synthesizer that can be used to
+implement local oscillators (LOs) as high as 26.5 GHz in the upconversion and
+downconversion sections of wireless receivers and transmitters. The ADF41510
+is a similar device that supports frequencies up to 10 GHz.
+
+Both devices support integer-N and fractional-N operation modes, providing
+excellent phase noise performance and flexible frequency generation
+capabilities.
+
+Key Features:
+
+- **ADF41513**: 1 GHz to 26.5 GHz frequency range
+- **ADF41510**: 1 GHz to 10 GHz frequency range
+- Integer-N and fractional-N operation modes
+- Ultra-low phase noise (-235 dBc/Hz integer-N, -231 dBc/Hz fractional-N)
+- High maximum PFD frequency (250 MHz integer-N, 125 MHz fractional-N)
+- 25-bit fixed modulus or 49-bit variable modulus fractional modes
+- Programmable charge pump currents with 16x range
+- Digital lock detect functionality
+- Phase resync capability for consistent output phase
+- Clock framework integration for system clock generation
+
+2. Device attributes
+====================
+
+The ADF41513 driver provides the following IIO extended attributes for
+frequency control and monitoring:
+
+Each IIO device has a device folder under ``/sys/bus/iio/devices/iio:deviceX``,
+where X is the IIO index of the device. Under these folders reside a set of
+device files that provide access to the synthesizer's functionality.
+
+The following table shows the ADF41513 related device files:
+
++----------------------+-------------------------------------------------------+
+| Device file          | Description                                           |
++======================+=======================================================+
+| frequency            | RF output frequency control and readback (Hz)         |
++----------------------+-------------------------------------------------------+
+| frequency_resolution | Target frequency resolution control (Hz)              |
++----------------------+-------------------------------------------------------+
+| refin_frequency      | Reference input frequency control and readback (Hz)   |
++----------------------+-------------------------------------------------------+
+| powerdown            | Power management control (0=active, 1=power down)     |
++----------------------+-------------------------------------------------------+
+
+2.1 Frequency Control
+----------------------
+
+The ``frequency`` attribute controls the RF output frequency with sub-Hz precision.
+The driver automatically selects between integer-N and fractional-N modes to
+achieve the requested frequency with the best possible phase noise performance.
+
+**Supported ranges:**
+
+- **ADF41513**: 1,000,000,000 Hz to 26,500,000,000 Hz (1 GHz to 26.5 GHz)
+- **ADF41510**: 1,000,000,000 Hz to 10,000,000,000 Hz (1 GHz to 10 GHz)
+
+The frequency is specified in Hz, for sub-Hz precision use decimal notation.
+For example, 12.102000000 GHz would be written as "12102000000.000000".
+
+2.2 Frequency Resolution Control
+--------------------------------
+
+The ``frequency_resolution`` attribute controls the target frequency resolution
+that the driver attempts to achieve. This affects the choice between fixed
+modulus (25-bit) and variable modulus (49-bit) fractional-N modes:
+
+- **Fixed modulus**: Resolution = f_PFD / 2^25 (typically ~3 Hz with 100 MHz PFD)
+- **Variable modulus**: Resolution = f_PFD / 2^49 (µHz resolution possible)
+
+Default resolution is 1 Hz (1,000,000 µHz).
+
+2.3 Reference Input Control
+---------------------------
+
+The ``refin_frequency`` attribute allows control of the reference input frequency
+when using a programmable reference clock. The supported range is 10 MHz to
+800 MHz.
+
+2.4 Power Management
+--------------------
+
+The ``powerdown`` attribute provides software power control:
+
+- **0**: Device active and operational
+- **1**: Device in power-down mode (low power consumption)
+
+3. Operating modes
+==================
+
+3.1 Integer-N Mode
+------------------
+
+When the requested frequency can be achieved as an integer multiple of the PFD
+frequency (within the specified resolution tolerance), the driver automatically
+selects integer-N mode for optimal phase noise performance.
+
+In integer-N mode:
+
+- Phase noise: -235 dBc/Hz normalized floor
+- Frequency resolution: f_PFD (same as PFD frequency)
+- Maximum PFD frequency: 250 MHz
+- Bleed current: Disabled for best performance
+
+3.2 Fractional-N Mode
+---------------------
+
+When sub-integer frequency steps are required, the driver automatically selects
+fractional-N mode using either fixed or variable modulus.
+
+**Fixed Modulus (25-bit)**:
+
+- Used when variable modulus is not required
+- Resolution: f_PFD / 2^25
+- Simpler implementation, faster settling
+
+**Variable Modulus (49-bit)**:
+
+- Used for maximum resolution requirements
+- Resolution: f_PFD / 2^49 (theoretical)
+- Exact frequency synthesis capability
+
+In fractional-N mode:
+
+- Phase noise: -231 dBc/Hz normalized floor
+- Maximum PFD frequency: 125 MHz
+- Bleed current: Automatically enabled and optimized
+- Dithering: Enabled to reduce fractional spurs
+
+3.3 Automatic Mode Selection
+----------------------------
+
+The driver automatically selects the optimal operating mode based on:
+
+1. **Frequency accuracy requirements**: Determined by frequency_resolution setting
+2. **Phase noise optimization**: Integer-N preferred when possible
+3. **PFD frequency constraints**: Different limits for integer vs fractional modes
+4. **Prescaler selection**: Automatic 4/5 vs 8/9 prescaler selection based on frequency
+
+4. Clock framework integration
+==============================
+
+The ADF41513 driver integrates with the Linux Common Clock Framework (CCF) when
+the device tree includes clock provider properties (``#clock-cells = <0>``).
+
+When enabled, the driver registers as a clock provider, allowing other kernel
+drivers to use the synthesizer output as a system clock source.
+
+**Device tree configuration for clock provider**:
+
+.. code-block:: dts
+
+    adf41513@0 {
+        compatible = "adi,adf41513";
+        reg = <0>;
+        /* ... other properties ... */
+        #clock-cells = <0>;
+        clock-output-names = "adf41513_clk";
+    };
+
+**Clock consumer example**:
+
+.. code-block:: dts
+
+    some_device {
+        clocks = <&adf41513>;
+        clock-names = "ref_clk";
+    };
+
+5. Device tree configuration
+============================
+
+See ``Documentation/devicetree/bindings/iio/frequency/adi,adf41513.yaml`` for
+complete device tree binding documentation.
+
+**Basic configuration example**:
+
+.. code-block:: dts
+
+    adf41513@0 {
+        compatible = "adi,adf41513";
+        reg = <0>;
+        spi-max-frequency = <10000000>;
+        clocks = <&ref_clk>;
+        clock-names = "clkin";
+        vcc-supply = <&vcc_3v3>;
+
+        adi,power-up-frequency = /bits/ 64 <12000000000>;
+    };
+
+6. Performance characteristics
+==============================
+
+6.1 Phase Noise Performance
+----------------------------
+
+The ADF41513 provides excellent phase noise performance:
+
+**Integer-N mode**:
+
+- Normalized phase noise floor: -235 dBc/Hz
+- 1/f noise corner: Typically around 10 kHz offset
+- Best performance mode for integer frequency multiples
+
+**Fractional-N mode**:
+
+- Normalized phase noise floor: -231 dBc/Hz
+- Additional fractional spurs may be present
+- Optimized with automatic bleed current and dithering
+
+6.2 Frequency Ranges and Prescalers
+------------------------------------
+
+The driver automatically selects the appropriate prescaler based on frequency:
+
+**4/5 Prescaler**:
+
+- RF frequency range: 1 GHz to 16 GHz
+- Integer-N: N = 20 to 511
+- Fractional-N: N = 23 to 511
+
+**8/9 Prescaler** (ADF41513 only):
+
+- RF frequency range: 1 GHz to 26.5 GHz
+- Integer-N: N = 64 to 1023
+- Fractional-N: N = 75 to 1023
+
+6.3 Reference Input Requirements
+---------------------------------
+
+- Frequency range: 10 MHz to 800 MHz
+- Input sensitivity: -10 dBm to +8 dBm
+- Slew rate requirement: >320 V/µs (>500 V/µs recommended)
+- With doubler enabled: Maximum 225 MHz reference frequency
+
+7. Usage examples
+=================
+
+7.1 Basic Frequency Setting
+----------------------------
+
+Set output frequency to 12.102 GHz:
+
+.. code-block:: bash
+
+    root:/sys/bus/iio/devices/iio:device0> echo 12102000000 > out_altvoltage0_frequency
+
+Read current frequency:
+
+.. code-block:: bash
+
+    root:/sys/bus/iio/devices/iio:device0> cat out_altvoltage0_frequency
+    12101999999.582767
+
+7.2 High Resolution Frequency Control
+-------------------------------------
+
+Configure for sub-Hz resolution and set a precise frequency:
+
+.. code-block:: bash
+
+    # Set resolution to 0.1 Hz (100,000 µHz)
+    root:/sys/bus/iio/devices/iio:device0> echo 0.1 > out_altvoltage0_frequency_resolution
+
+    # Set frequency to 12.102 GHz (1 µHz precision)
+    root:/sys/bus/iio/devices/iio:device0> echo 12102000000 > out_altvoltage0_frequency
+    root:/sys/bus/iio/devices/iio:device0> cat out_altvoltage0_frequency
+    12101999999.980131
+
+7.3 Reference Frequency Control
+-------------------------------
+
+Change reference input frequency (if using programmable reference):
+
+.. code-block:: bash
+
+    # Set reference to 122.88 MHz
+    root:/sys/bus/iio/devices/iio:device0> echo 122880000 > out_altvoltage0_refin_frequency
+
+    # Verify the change
+    root:/sys/bus/iio/devices/iio:device0> cat out_altvoltage0_refin_frequency
+    122880000
+
+7.4 Power Management
+--------------------
+
+Power down the device:
+
+.. code-block:: bash
+
+    root:/sys/bus/iio/devices/iio:device0> echo 1 > out_altvoltage0_powerdown
+
+    # Power back up
+    root:/sys/bus/iio/devices/iio:device0> echo 0 > out_altvoltage0_powerdown
+
+7.5 Monitor Lock Status
+-----------------------
+
+When lock detect GPIO is configured, check if PLL is locked:
+
+.. code-block:: bash
+
+    # Read frequency - will return error if not locked
+    root:/sys/bus/iio/devices/iio:device0> cat out_altvoltage0_frequency
+
+If the PLL is not locked, the frequency read will return ``-EBUSY`` (Device or resource busy).
+
+8. Advanced features
+====================
+
+8.1 Phase Resync
+-----------------
+
+When enabled in device tree (``adi,phase-resync-enable``), the phase resync
+feature provides a consistent output phase relationship to the reference input.
+This is particularly useful for beamforming applications where phase coherence
+across multiple synthesizers is required.
+
+8.2 Charge Pump Optimization
+-----------------------------
+
+The driver automatically optimizes charge pump settings:
+
+- **Current**: Programmable from 0.45 mA to 7.2 mA
+- **Bleed current**: Automatically calculated and enabled in fractional-N mode
+- **Loop filter integration**: Works with external passive loop filters
+
+8.3 Spurious Performance
+------------------------
+
+The driver includes automatic spur reduction techniques:
+
+- **Integer boundary spur avoidance**: Automatic PFD frequency optimization
+- **Fractional spur reduction**: Automatic dithering and bleed current
+- **Reference spur suppression**: High PFD frequency operation when possible
+
+9. Troubleshooting
+==================
+
+9.1 Common Issues
+-----------------
+
+**"PLL un-locked" errors**:
+
+- Check reference input signal quality and level
+- Verify loop filter design matches charge pump current
+- Ensure VCO tuning range covers target frequency
+- Check power supply voltage and stability
+
+**Frequency accuracy issues**:
+
+- Verify reference frequency accuracy
+- Check frequency_resolution setting
+- Ensure target frequency is within device range
+
+**High spurious content**:
+
+- Verify PCB layout follows ADI recommendations
+- Check loop filter component values and quality
+- Ensure adequate power supply decoupling
+- Verify reference input signal purity
diff --git a/Documentation/iio/index.rst b/Documentation/iio/index.rst
index 315ae37d6fd4..420669af60db 100644
--- a/Documentation/iio/index.rst
+++ b/Documentation/iio/index.rst
@@ -29,6 +29,7 @@ Industrial I/O Kernel Drivers
    ad7625
    ad7944
    ade9000
+   adf41513
    adis16475
    adis16480
    adis16550
diff --git a/MAINTAINERS b/MAINTAINERS
index 1bbcff826238..833020f01c3c 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1607,6 +1607,7 @@ L:	linux-iio@vger.kernel.org
 S:	Supported
 W:	https://ez.analog.com/linux-software-drivers
 F:	Documentation/devicetree/bindings/iio/frequency/adi,adf41513.yaml
+F:	Documentation/iio/adf41513.rst
 F:	drivers/iio/frequency/adf41513.c
 
 ANALOG DEVICES INC ADF4377 DRIVER

-- 
2.43.0



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

* Re: [PATCH 1/3] iio: frequency: adf41513: driver implementation
  2025-11-10 15:44 ` [PATCH 1/3] iio: frequency: adf41513: driver implementation Rodrigo Alencar via B4 Relay
@ 2025-11-10 16:30   ` Andy Shevchenko
  2025-11-10 16:41     ` Andy Shevchenko
  2025-11-11  8:43     ` Nuno Sá
  2025-11-13 13:13   ` Marcelo Schmitt
  1 sibling, 2 replies; 15+ messages in thread
From: Andy Shevchenko @ 2025-11-10 16:30 UTC (permalink / raw)
  To: rodrigo.alencar
  Cc: linux-kernel, linux-iio, devicetree, linux-doc, Jonathan Cameron,
	David Lechner, Andy Shevchenko, Lars-Peter Clausen,
	Michael Hennerich, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Jonathan Corbet

On Mon, Nov 10, 2025 at 03:44:44PM +0000, Rodrigo Alencar via B4 Relay wrote:
> 
> - ADF41513: 1 GHz to 26.5 GHz frequency range
> - ADF41510: 1 GHz to 10 GHz frequency range
> - Integer-N and fractional-N operation modes
> - Ultra-low phase noise (-235 dBc/Hz integer-N, -231 dBc/Hz fractional-N)
> - High maximum PFD frequency (250 MHz integer-N, 125 MHz fractional-N)
> - 25-bit fixed modulus or 49-bit variable modulus fractional modes
> - Programmable charge pump currents with 16x range
> - Digital lock detect functionality
> - Phase resync capability for consistent output phase
> - Clock framework integration for system clock generation

It is like a list from the marketing material. Please
1) make sure you are writing the commit message;
2) implement minimum basic functionality and split features to the next
patches, 1.5kLoCs is hard to review.

...

> +#include <linux/bitfield.h>
> +#include <linux/bits.h>
> +#include <linux/clk.h>
> +#include <linux/clk-provider.h>
> +#include <linux/device.h>
> +#include <linux/err.h>
> +#include <linux/gpio/consumer.h>
> +#include <linux/iio/iio.h>
> +#include <linux/iio/sysfs.h>
> +#include <linux/math64.h>
> +#include <linux/module.h>
> +#include <linux/mod_devicetable.h>
> +#include <linux/property.h>
> +#include <linux/regulator/consumer.h>
> +#include <linux/spi/spi.h>

At least types.h is missing. Follow IWYU. Have you passed internal review? I
believe we need to start asking Analog Devices to provide a Rb tag of known
developers on the submitted code to make sure it was passed the internal
review.

...

> +/* Specifications */
> +#define ADF41513_MIN_RF_FREQ			1000000000ULL	/* 1 GHz */
> +#define ADF41510_MAX_RF_FREQ			10000000000ULL	/* 10 GHz */
> +#define ADF41513_MAX_RF_FREQ			26500000000ULL	/* 26.5 GHz */

We have HZ_PER_MHZ, also you can move HZ_PER_GHZ to the units.h and use it here.

> +
> +#define ADF41513_MIN_REF_FREQ			10000000U	/* 10 MHz */
> +#define ADF41513_MAX_REF_FREQ			800000000U	/* 800 MHz */
> +#define ADF41513_MAX_REF_FREQ_DOUBLER		225000000U	/* 225 MHz */
> +
> +#define ADF41513_MAX_PFD_FREQ_INT_N_HZ		250000000U		/* 250 MHz */
> +#define ADF41513_MAX_PFD_FREQ_FRAC_N_HZ		125000000U		/* 125 MHz */
> +#define ADF41513_MAX_PFD_FREQ_INT_N_UHZ		250000000000000ULL	/* 250 MHz */
> +#define ADF41513_MAX_PFD_FREQ_FRAC_N_UHZ	125000000000000ULL	/* 125 MHz */

Ditto.

...

> +#define ADF41513_MIN_CP_VOLTAGE_MV		810
> +#define ADF41513_MAX_CP_VOLTAGE_MV		12960

_mV

...

> +#define ADF41513_MAX_LD_BIAS_UA			40
> +#define ADF41513_LD_BIAS_STEP_UA		10

_uA


...

> +#define ADF41513_MAX_MOD2			((1 << 24) - 1)	/* 2^24 - 1 */

Why not BIT()?

...

> +/* Frequency conversion constants */
> +#define ADF41513_HZ_TO_UHZ			1000000ULL	/* Convert Hz to uHz */

Put it to units.h.

...

> +enum {
> +	ADF41513_FREQ,
> +	ADF41513_POWER_DOWN,
> +	ADF41513_FREQ_RESOLUTION,
> +	ADF41513_FREQ_REFIN

Doesn't sound like a terminator to me, add a comma.

> +};
> +
> +enum adf41513_pll_mode {
> +	ADF41513_MODE_INTEGER_N,
> +	ADF41513_MODE_FIXED_MODULUS,
> +	ADF41513_MODE_VARIABLE_MODULUS,
> +	ADF41513_MODE_INVALID

Ditto.

> +};

...

> +struct adf41513_data {

Run `pahole` and act accordingly.

> +	u64 power_up_frequency;
> +
> +	u8 ref_div_factor;
> +	bool ref_doubler_en;
> +	bool ref_div2_en;
> +
> +	u32 charge_pump_voltage_mv;
> +	bool phase_detector_polarity;
> +
> +	u8 muxout_select;
> +	bool muxout_1v8_en;
> +
> +	u8 lock_detect_precision;
> +	u8 lock_detect_count;
> +	u8 lock_detect_bias;
> +	bool fast_lock_en;
> +
> +	u16 phase_resync_clk_div[2];
> +	bool phase_resync_en;
> +	bool load_enable_sync;
> +
> +	u64 freq_resolution_uhz;
> +};
> +
> +struct adf41513_pll_settings {
> +	enum adf41513_pll_mode mode;
> +
> +	u64 target_frequency_uhz;
> +	u64 actual_frequency_uhz;
> +	u64 pfd_frequency_uhz;
> +
> +	/* pll parameters */
> +	u16 int_value;
> +	u32 frac1;
> +	u32 frac2;
> +	u32 mod2;
> +
> +	/* reference path parameters */
> +	u8 r_counter;
> +	u8 ref_doubler;
> +	u8 ref_div2;
> +	u8 prescaler;
> +};

...

> +static const u32 adf41513_cp_voltage_mv[] = {
> +	810, 1620, 2430, 3240, 4050, 4860, 5670, 6480, 7290, 8100,
> +	8910, 9720, 10530, 11340, 12150, 12960

Make it power-of-two items per line, even with the comments to show
the indexing, like

	810, 1620, 2430, 3240, 4050, 4860, 5670, 6480,	/* 0 - 7 */

> +};

...

> +static int adf41513_parse_uhz(const char *str, u64 *freq_uhz)

My gosh, please, try to check what kernel already has. We try hard to avoid Yet
Another Best Parser in the World to happen, really.

...

In any case, I stopped my review here, you have more than enough to fix.
Please, come next time with a tag from one whose name is in the MAINTAINERS.
From now on it will be my requirement as a reviewer of IIO subsystem.

-- 
With Best Regards,
Andy Shevchenko



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

* Re: [PATCH 0/3] ADF41513/ADF41510 PLL frequency synthesizers
  2025-11-10 15:44 [PATCH 0/3] ADF41513/ADF41510 PLL frequency synthesizers Rodrigo Alencar via B4 Relay
                   ` (2 preceding siblings ...)
  2025-11-10 15:44 ` [PATCH 3/3] docs: iio: add documentation for adf41513 driver Rodrigo Alencar via B4 Relay
@ 2025-11-10 16:38 ` Andy Shevchenko
  2025-11-16 16:10 ` Jonathan Cameron
  4 siblings, 0 replies; 15+ messages in thread
From: Andy Shevchenko @ 2025-11-10 16:38 UTC (permalink / raw)
  To: rodrigo.alencar
  Cc: linux-kernel, linux-iio, devicetree, linux-doc, Jonathan Cameron,
	David Lechner, Andy Shevchenko, Lars-Peter Clausen,
	Michael Hennerich, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Jonathan Corbet

On Mon, Nov 10, 2025 at 03:44:43PM +0000, Rodrigo Alencar via B4 Relay wrote:
> This patch series adds support for the Analog Devices ADF41513 and ADF41510
> ultralow noise PLL frequency synthesizers. These devices are designed for
> implementing local oscillators (LOs) in high-frequency applications.
> 
> The ADF41513 covers frequencies from 1 GHz to 26.5 GHz, while the ADF41510
> operates from 1 GHz to 10 GHz. Both devices feature exceptional phase noise
> performance and flexible frequency synthesis capabilities.
> 
> Key features supported by this driver:
> - Integer-N and fractional-N operation modes
> - Ultra-low phase noise (-235 dBc/Hz integer-N, -231 dBc/Hz fractional-N)
> - High maximum PFD frequency (250 MHz integer-N, 125 MHz fractional-N)
> - 25-bit fixed modulus or 49-bit variable modulus fractional modes
> - Programmable charge pump currents with 16x range
> - Digital lock detect functionality
> - Phase resync capability for consistent output phase
> - Clock framework integration for system clock generation
> 
> The series includes:
> 1. Core driver implementation with full register programming support
> 2. Device tree bindings documentation
> 3. IIO subsystem documentation with usage examples
> 
> The driver integrates with both the IIO subsystem (for direct hardware control)
> and the Linux clock framework (for use as a system clock source), providing
> flexibility for different use cases.

When cover letter is better than the commit message :-)

-- 
With Best Regards,
Andy Shevchenko



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

* Re: [PATCH 1/3] iio: frequency: adf41513: driver implementation
  2025-11-10 16:30   ` Andy Shevchenko
@ 2025-11-10 16:41     ` Andy Shevchenko
  2025-11-16 16:07       ` Jonathan Cameron
  2025-11-11  8:43     ` Nuno Sá
  1 sibling, 1 reply; 15+ messages in thread
From: Andy Shevchenko @ 2025-11-10 16:41 UTC (permalink / raw)
  To: rodrigo.alencar
  Cc: linux-kernel, linux-iio, devicetree, linux-doc, Jonathan Cameron,
	David Lechner, Andy Shevchenko, Lars-Peter Clausen,
	Michael Hennerich, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Jonathan Corbet

On Mon, Nov 10, 2025 at 06:30:38PM +0200, Andy Shevchenko wrote:
> On Mon, Nov 10, 2025 at 03:44:44PM +0000, Rodrigo Alencar via B4 Relay wrote:

...

> In any case, I stopped my review here, you have more than enough to fix.
> Please, come next time with a tag from one whose name is in the MAINTAINERS.
> From now on it will be my requirement as a reviewer of IIO subsystem.

And to be more clear on this requirement (for all submissions from @analog.com
and related): A name from approximately below list I want to see as Rb.

	Antoniu Miclaus <antoniu.miclaus@analog.com>
	Marcelo Schmitt <marcelo.schmitt@analog.com>
	Michael Hennerich <michael.hennerich@analog.com>
        Nuno Sá <nuno.sa@analog.com>

(those people seems experienced and I have heard of / from).

-- 
With Best Regards,
Andy Shevchenko



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

* Re: [PATCH 2/3] dt-bindings: iio: frequency: add adf41513
  2025-11-10 15:44 ` [PATCH 2/3] dt-bindings: iio: frequency: add adf41513 Rodrigo Alencar via B4 Relay
@ 2025-11-11  8:05   ` Krzysztof Kozlowski
  2025-11-13 13:21   ` Marcelo Schmitt
  1 sibling, 0 replies; 15+ messages in thread
From: Krzysztof Kozlowski @ 2025-11-11  8:05 UTC (permalink / raw)
  To: Rodrigo Alencar
  Cc: linux-kernel, linux-iio, devicetree, linux-doc, Jonathan Cameron,
	David Lechner, Andy Shevchenko, Lars-Peter Clausen,
	Michael Hennerich, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Jonathan Corbet

On Mon, Nov 10, 2025 at 03:44:45PM +0000, Rodrigo Alencar wrote:
> ultralow noise PLL frequency synthesizer that can be used to
> implement local oscillators (LOs) as high as 26.5 GHz
> 
> Signed-off-by: Rodrigo Alencar <rodrigo.alencar@analog.com>
> ---

Please organize the patch documenting compatible (DT bindings) before their user.
See also: https://elixir.bootlin.com/linux/v6.14-rc6/source/Documentation/devicetree/bindings/submitting-patches.rst#L46

...

> +  clocks:
> +    maxItems: 1
> +    description: Clock that provides the reference input frequency.
> +
> +  '#clock-cells':
> +    const: 0
> +
> +  clock-output-names:
> +    maxItems: 1
> +
> +  vcc-supply:
> +    description: Power supply for the device (3.3V)
> +
> +  chip-enable-gpios:

enable-gpios

> +    description:
> +      GPIO that controls the chip enable pin. A logic low on this pin
> +      powers down the device and puts the charge pump output into
> +      three-state mode.
> +    maxItems: 1
> +
> +  lock-detect-gpios:
> +    description:
> +      GPIO for lock detect functionality. When configured for digital lock
> +      detect, this pin will output a logic high when the PLL is locked.
> +    maxItems: 1
> +
> +  adi,power-up-frequency:
> +    $ref: /schemas/types.yaml#/definitions/uint64

Use standard unit suffixes. Frequency is in Hz for example.

> +    minimum: 1000000000
> +    maximum: 26500000000
> +    default: 10000000000
> +    description:
> +      The PLL tunes to this frequency (in Hz) on driver probe.
> +      Range is 1 GHz to 26.5 GHz for ADF41513, and 1 GHz to 10 GHz for ADF41510.
> +
> +  adi,reference-div-factor:
> +    $ref: /schemas/types.yaml#/definitions/uint32
> +    minimum: 1
> +    maximum: 32
> +    description:
> +      Reference division factor (R Counter). If not specified, the driver
> +      will calculate the optimal value automatically.

Then why do you need this property? If driver calculates the optimal,
why anyone would put wrong or sub-optimal value to DT?

Drop.

> +
> +  adi,reference-doubler-enable:
> +    description:
> +      Enables the reference doubler. The maximum reference frequency when
> +      the doubler is enabled is 225 MHz.
> +    type: boolean
> +
> +  adi,reference-div2-enable:
> +    description:
> +      Enables the reference divide-by-2 function. This provides a 50%
> +      duty cycle signal to the PFD.
> +    type: boolean
> +
> +  adi,charge-pump-current-microamp:
> +    minimum: 450
> +    maximum: 7200
> +    default: 2400
> +    description:
> +      Charge pump current in microamps. The value will be rounded to the
> +      nearest supported value.
> +
> +  adi,charge-pump-resistor-ohms:
> +    minimum: 1800
> +    maximum: 10000
> +    default: 2700
> +    description:
> +      External charge pump resistor value in ohms. This sets the maximum
> +      charge pump current along with the charge pump current setting.
> +
> +  adi,muxout-select:
> +    description:
> +      On chip multiplexer output selection.
> +      high_z - MUXOUT Pin set to high-Z. (default)
> +      muxout_high - MUXOUT Pin set to high.
> +      muxout_low - MUXOUT Pin set to low.
> +      f_div_rclk - MUXOUT Pin set to R divider output
> +      f_div_nclk - MUXOUT Pin set to N divider output
> +      lock_detect - MUXOUT Pin set to Digital lock detect
> +      serial_data - MUXOUT Pin set to Serial data output
> +      readback - MUXOUT Pin set to Readback mode
> +      f_div_clk1 - MUXOUT Pin set to CLK1 divider output
> +      f_div_rclk_2 - MUXOUT Pin set to R divider/2 output
> +      f_div_nclk_2 - MUXOUT Pin set to N divider/2 output
> +    enum: [high_z, muxout_high, muxout_low, f_div_rclk, f_div_nclk, lock_detect,
> +           serial_data, readback, f_div_clk1, f_div_rclk_2, f_div_nclk_2]
> +
> +  adi,muxout-level-1v8-enable:
> +    description:
> +      Set MUXOUT and DLD logic levels to 1.8V. Default is 3.3V.
> +    type: boolean
> +
> +  adi,phase-detector-polarity-positive-enable:
> +    description:
> +      Set phase detector polarity to positive. Default is negative.
> +      Use positive polarity with non-inverting loop filter and VCO with
> +      positive tuning slope, or with inverting loop filter and VCO with
> +      negative tuning slope.
> +    type: boolean
> +
> +  adi,lock-detect-precision:
> +    $ref: /schemas/types.yaml#/definitions/uint32
> +    minimum: 0
> +    maximum: 3
> +    description:
> +      Lock detector precision setting. Controls the sensitivity of the
> +      lock detector. Lower values of precision increases the lock detector
> +      window size.
> +
> +  adi,lock-detect-count:
> +    $ref: /schemas/types.yaml#/definitions/uint32
> +    minimum: 0
> +    maximum: 7
> +    description: |
> +      Lock detector count setting (3-bit value). Determines the number of
> +      consecutive phase detector cycles that must be within the lock detector
> +      window before lock is declared. The count grows in powers of two of the
> +      programmed value:
> +      - if adi,fast-lock-enable is set count = 2 * 2^value
> +      - if adi,fast-lock-enable is not set count = 64 * 2^value
> +
> +  adi,lock-detect-bias-microamp:
> +    description:
> +      Lock detector bias current. Controls the lock detector window size
> +      along with the lock detector precision setting. Lower bias current
> +      increases the window size.
> +    enum: [10, 20, 30, 40]
> +
> +  adi,fast-lock-enable:
> +    description:
> +      Enable fast lock mode. This changes the lock detector clock selection
> +      for faster lock indication.
> +    type: boolean
> +
> +  adi,phase-resync-enable:
> +    description:
> +      Enable phase resync functionality. This produces a consistent output
> +      phase offset with respect to the input reference.
> +    type: boolean
> +
> +  adi,12bit-clk-divider:
> +    $ref: /schemas/types.yaml#/definitions/uint32
> +    minimum: 0
> +    maximum: 4095
> +    description:
> +      CLK1 divider value used when adi,phase-resync-enable is set
> +
> +  adi,12bit-clk2-divider:
> +    $ref: /schemas/types.yaml#/definitions/uint32
> +    minimum: 0
> +    maximum: 4095
> +    description:
> +      CLK2 divider value used when adi,phase-resync-enable is set
> +
> +  adi,le-sync-enable:
> +    description:
> +      Synchronize the rising edge of LE on an SPI write with the falling
> +      edge of the reference signal to prevent glitches.
> +    type: boolean
> +
> +  adi,freq-resolution:
> +    $ref: /schemas/types.yaml#/definitions/uint64
> +    minimum: 1
> +    default: 1000000
> +    description:
> +      Initial frequency resolution in micro-Hz (µHz) for the algorithm to achieve.
> +      This influences the choice between fixed and variable modulus modes.
> +      Default is 1000000 µHz (1 Hz).
> +
> +required:
> +  - compatible
> +  - reg
> +  - clocks
> +  - vcc-supply
> +
> +allOf:
> +  - $ref: /schemas/spi/spi-peripheral-props.yaml#
> +
> +unevaluatedProperties: false
> +
> +examples:
> +  - |
> +    spi {
> +        #address-cells = <1>;
> +        #size-cells = <0>;
> +
> +        pll@0 {
> +            compatible = "adi,adf41513";
> +            reg = <0>;
> +            spi-max-frequency = <10000000>;
> +            clocks = <&ref_clk>;
> +            vcc-supply = <&vcc_3v3>;
> +            #clock-cells = <0>;
> +
> +            adi,power-up-frequency = /bits/ 64 <12000000000>;
> +            adi,charge-pump-current-microamp = <2400>;
> +            adi,phase-detector-polarity-positive-enable;
> +        };
> +    };
> +  - |
> +    #include <dt-bindings/gpio/gpio.h>
> +    spi {
> +        #address-cells = <1>;
> +        #size-cells = <0>;
> +
> +        /* Example with advanced features enabled */
> +        pll_advanced@0 {

pll@

> +            compatible = "adi,adf41513";
> +            reg = <0>;
> +            spi-max-frequency = <25000000>;
> +            clocks = <&ref_clk>;
> +            vcc-supply = <&vcc_3v3>;
> +            chip-enable-gpios = <&gpio0 10 GPIO_ACTIVE_HIGH>;
> +            lock-detect-gpios = <&gpio0 11 GPIO_ACTIVE_HIGH>;
> +            #clock-cells = <0>;
> +            clock-output-names = "adf41513_clk";
> +
> +            adi,power-up-frequency = /bits/ 64 <15500000000>;
> +            adi,charge-pump-current-microamp = <3600>;
> +            adi,charge-pump-resistor-ohms = <2700>;
> +            adi,reference-doubler-enable;
> +            adi,muxout-select = "lock_detect";
> +            adi,lock-detect-precision = <1>;
> +            adi,lock-detect-count = <4>;
> +            adi,lock-detect-bias-microamp = <40>;
> +            adi,fast-lock-enable;
> +            adi,phase-resync-enable;
> +            adi,12bit-clk-divider = <1>;
> +            adi,12bit-clk2-divider = <200>;
> +            adi,le-sync-enable;
> +            adi,freq-resolution = /bits/ 64 <1000000>;
> +            adi,phase-detector-polarity-positive-enable;
> +        };
> +    };
> +...
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 8df4a0d216c8..1bbcff826238 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -1606,6 +1606,7 @@ M:	Rodrigo Alencar <rodrigo.alencar@analog.com>
>  L:	linux-iio@vger.kernel.org
>  S:	Supported
>  W:	https://ez.analog.com/linux-software-drivers
> +F:	Documentation/devicetree/bindings/iio/frequency/adi,adf41513.yaml
>  F:	drivers/iio/frequency/adf41513.c
>  
>  ANALOG DEVICES INC ADF4377 DRIVER
> 
> -- 
> 2.43.0
> 

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

* Re: [PATCH 1/3] iio: frequency: adf41513: driver implementation
  2025-11-10 16:30   ` Andy Shevchenko
  2025-11-10 16:41     ` Andy Shevchenko
@ 2025-11-11  8:43     ` Nuno Sá
  1 sibling, 0 replies; 15+ messages in thread
From: Nuno Sá @ 2025-11-11  8:43 UTC (permalink / raw)
  To: Andy Shevchenko, rodrigo.alencar
  Cc: linux-kernel, linux-iio, devicetree, linux-doc, Jonathan Cameron,
	David Lechner, Andy Shevchenko, Lars-Peter Clausen,
	Michael Hennerich, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Jonathan Corbet

Hi Andy,

On Mon, 2025-11-10 at 18:30 +0200, Andy Shevchenko wrote:
> On Mon, Nov 10, 2025 at 03:44:44PM +0000, Rodrigo Alencar via B4 Relay wrote:
> > 
> > - ADF41513: 1 GHz to 26.5 GHz frequency range
> > - ADF41510: 1 GHz to 10 GHz frequency range
> > - Integer-N and fractional-N operation modes
> > - Ultra-low phase noise (-235 dBc/Hz integer-N, -231 dBc/Hz fractional-N)
> > - High maximum PFD frequency (250 MHz integer-N, 125 MHz fractional-N)
> > - 25-bit fixed modulus or 49-bit variable modulus fractional modes
> > - Programmable charge pump currents with 16x range
> > - Digital lock detect functionality
> > - Phase resync capability for consistent output phase
> > - Clock framework integration for system clock generation
> 
> It is like a list from the marketing material. Please
> 1) make sure you are writing the commit message;
> 2) implement minimum basic functionality and split features to the next
> patches, 1.5kLoCs is hard to review.
> 
> ...
> 
> > +#include <linux/bitfield.h>
> > +#include <linux/bits.h>
> > +#include <linux/clk.h>
> > +#include <linux/clk-provider.h>
> > +#include <linux/device.h>
> > +#include <linux/err.h>
> > +#include <linux/gpio/consumer.h>
> > +#include <linux/iio/iio.h>
> > +#include <linux/iio/sysfs.h>
> > +#include <linux/math64.h>
> > +#include <linux/module.h>
> > +#include <linux/mod_devicetable.h>
> > +#include <linux/property.h>
> > +#include <linux/regulator/consumer.h>
> > +#include <linux/spi/spi.h>
> 
> At least types.h is missing. Follow IWYU. Have you passed internal review? I
> believe we need to start asking Analog Devices to provide a Rb tag of known
> developers on the submitted code to make sure it was passed the internal
> review.
> 
> ...
> 
> > +/* Specifications */
> > +#define ADF41513_MIN_RF_FREQ			1000000000ULL	/* 1 GHz */
> > +#define ADF41510_MAX_RF_FREQ			10000000000ULL	/* 10 GHz */
> > +#define ADF41513_MAX_RF_FREQ			26500000000ULL	/* 26.5 GHz */
> 
> We have HZ_PER_MHZ, also you can move HZ_PER_GHZ to the units.h and use it here.
> 
> > +
> > +#define ADF41513_MIN_REF_FREQ			10000000U	/* 10 MHz */
> > +#define ADF41513_MAX_REF_FREQ			800000000U	/* 800 MHz */
> > +#define ADF41513_MAX_REF_FREQ_DOUBLER		225000000U	/* 225 MHz */
> > +
> > +#define ADF41513_MAX_PFD_FREQ_INT_N_HZ		250000000U		/* 250 MHz */
> > +#define ADF41513_MAX_PFD_FREQ_FRAC_N_HZ		125000000U		/* 125 MHz */
> > +#define ADF41513_MAX_PFD_FREQ_INT_N_UHZ		250000000000000ULL	/* 250 MHz */
> > +#define ADF41513_MAX_PFD_FREQ_FRAC_N_UHZ	125000000000000ULL	/* 125 MHz */
> 
> Ditto.
> 
> ...
> 
> > +#define ADF41513_MIN_CP_VOLTAGE_MV		810
> > +#define ADF41513_MAX_CP_VOLTAGE_MV		12960
> 
> _mV
> 
> ...
> 
> > +#define ADF41513_MAX_LD_BIAS_UA			40
> > +#define ADF41513_LD_BIAS_STEP_UA		10
> 
> _uA
> 
> 
> ...
> 
> > +#define ADF41513_MAX_MOD2			((1 << 24) - 1)	/* 2^24 - 1 */
> 
> Why not BIT()?
> 
> ...
> 
> > +/* Frequency conversion constants */
> > +#define ADF41513_HZ_TO_UHZ			1000000ULL	/* Convert Hz to uHz */
> 
> Put it to units.h.
> 
> ...
> 
> > +enum {
> > +	ADF41513_FREQ,
> > +	ADF41513_POWER_DOWN,
> > +	ADF41513_FREQ_RESOLUTION,
> > +	ADF41513_FREQ_REFIN
> 
> Doesn't sound like a terminator to me, add a comma.
> 
> > +};
> > +
> > +enum adf41513_pll_mode {
> > +	ADF41513_MODE_INTEGER_N,
> > +	ADF41513_MODE_FIXED_MODULUS,
> > +	ADF41513_MODE_VARIABLE_MODULUS,
> > +	ADF41513_MODE_INVALID
> 
> Ditto.
> 
> > +};
> 
> ...
> 
> > +struct adf41513_data {
> 
> Run `pahole` and act accordingly.
> 
> > +	u64 power_up_frequency;
> > +
> > +	u8 ref_div_factor;
> > +	bool ref_doubler_en;
> > +	bool ref_div2_en;
> > +
> > +	u32 charge_pump_voltage_mv;
> > +	bool phase_detector_polarity;
> > +
> > +	u8 muxout_select;
> > +	bool muxout_1v8_en;
> > +
> > +	u8 lock_detect_precision;
> > +	u8 lock_detect_count;
> > +	u8 lock_detect_bias;
> > +	bool fast_lock_en;
> > +
> > +	u16 phase_resync_clk_div[2];
> > +	bool phase_resync_en;
> > +	bool load_enable_sync;
> > +
> > +	u64 freq_resolution_uhz;
> > +};
> > +
> > +struct adf41513_pll_settings {
> > +	enum adf41513_pll_mode mode;
> > +
> > +	u64 target_frequency_uhz;
> > +	u64 actual_frequency_uhz;
> > +	u64 pfd_frequency_uhz;
> > +
> > +	/* pll parameters */
> > +	u16 int_value;
> > +	u32 frac1;
> > +	u32 frac2;
> > +	u32 mod2;
> > +
> > +	/* reference path parameters */
> > +	u8 r_counter;
> > +	u8 ref_doubler;
> > +	u8 ref_div2;
> > +	u8 prescaler;
> > +};
> 
> ...
> 
> > +static const u32 adf41513_cp_voltage_mv[] = {
> > +	810, 1620, 2430, 3240, 4050, 4860, 5670, 6480, 7290, 8100,
> > +	8910, 9720, 10530, 11340, 12150, 12960
> 
> Make it power-of-two items per line, even with the comments to show
> the indexing, like
> 
> 	810, 1620, 2430, 3240, 4050, 4860, 5670, 6480,	/* 0 - 7 */
> 
> > +};
> 
> ...
> 
> > +static int adf41513_parse_uhz(const char *str, u64 *freq_uhz)
> 
> My gosh, please, try to check what kernel already has. We try hard to avoid Yet
> Another Best Parser in the World to happen, really.
> 

I think there's more into this. I did not went into much detail but IIUC it's
doing the same as (to some degree) as __iio_str_to_fixpoint() but the part needs
u64 for the integer part. Being this one case not sure if we should rush into doing
something in IIO core. Or the whole thing might end up being done completely
different.

Having said the above We should have a proper comment justifying the function.

> ...
> 
> In any case, I stopped my review here, you have more than enough to fix.
> Please, come next time with a tag from one whose name is in the MAINTAINERS.
> From now on it will be my requirement as a reviewer of IIO subsystem.


Not really sure what happened here. We are trying to make things better (as more people
in ADI from different teams start contributing) and have a process where internal reviews
do happen (mostly even public in github) and once approved it can go upstream. We also have
a fairly good CI in place to catch things like bindings, checkpatch, smatch, cocci and compiler
analyzers.

For this particular part we did had at least one round of reviewing where I actually stated

"...We'll need some iterations on this one and skipped more questionable/complicated things for now.
..."

I guess Rodrigo was eager to send his first patch series but hopefully, lessons learned.

- Nuno Sá

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

* Re: [PATCH 1/3] iio: frequency: adf41513: driver implementation
  2025-11-10 15:44 ` [PATCH 1/3] iio: frequency: adf41513: driver implementation Rodrigo Alencar via B4 Relay
  2025-11-10 16:30   ` Andy Shevchenko
@ 2025-11-13 13:13   ` Marcelo Schmitt
  2025-11-13 14:39     ` Nuno Sá
  1 sibling, 1 reply; 15+ messages in thread
From: Marcelo Schmitt @ 2025-11-13 13:13 UTC (permalink / raw)
  To: rodrigo.alencar
  Cc: linux-kernel, linux-iio, devicetree, linux-doc, Jonathan Cameron,
	David Lechner, Andy Shevchenko, Lars-Peter Clausen,
	Michael Hennerich, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Jonathan Corbet

Hi Rodrigo,

A couple of comments inline since this is on the mailing list.
As mentioned in the other thread, we ought to continue the review of this internally.

On 11/10, Rodrigo Alencar via B4 Relay wrote:
> From: Rodrigo Alencar <rodrigo.alencar@analog.com>
> 
> - ADF41513: 1 GHz to 26.5 GHz frequency range
> - ADF41510: 1 GHz to 10 GHz frequency range
> - Integer-N and fractional-N operation modes
> - Ultra-low phase noise (-235 dBc/Hz integer-N, -231 dBc/Hz fractional-N)
> - High maximum PFD frequency (250 MHz integer-N, 125 MHz fractional-N)
> - 25-bit fixed modulus or 49-bit variable modulus fractional modes
> - Programmable charge pump currents with 16x range
> - Digital lock detect functionality
> - Phase resync capability for consistent output phase
> - Clock framework integration for system clock generation
> 
> Signed-off-by: Rodrigo Alencar <rodrigo.alencar@analog.com>
> ---
...
> +
> +static int adf41513_parse_uhz(const char *str, u64 *freq_uhz)
> +{
> +	u64 uhz = 0;
> +	int f_count = ADF41513_HZ_DECIMAL_PRECISION;
> +	bool frac_part = false;
> +
> +	if (str[0] == '+')
> +		str++;
> +
> +	while (*str && f_count > 0) {
> +		if ('0' <= *str && *str <= '9') {
> +			uhz = uhz * 10 + *str - '0';
> +			if (frac_part)
> +				f_count--;
> +		} else if (*str == '\n') {
> +			if (*(str + 1) == '\0')
> +				break;
> +			return -EINVAL;
> +		} else if (*str == '.' && !frac_part) {
> +			frac_part = true;
> +		} else {
> +			return -EINVAL;
> +		}
> +		str++;
> +	}
> +
> +	for (; f_count > 0; f_count--)
> +		uhz *= 10;
> +
> +	*freq_uhz = uhz;
> +
> +	return 0;
> +}
didn't check the details, but can't the sub-Hz resolution be supported with
.write_raw_get_fmt()?
e.g.

static int adf41513_write_raw_get_fmt(struct iio_dev *indio_dev,
				    struct iio_chan_spec const *chan, long mask)
{
	switch (mask) {
	case IIO_CHAN_INFO_FREQUENCY:
		return IIO_VAL_INT_64;
	default:
		return IIO_VAL_INT_PLUS_MICRO;
	}
}

static const struct iio_info adf41513_info = {
...
	.write_raw_get_fmt = adf41513_write_raw_get_fmt(),
};

...
> +
> +static ssize_t adf41513_write(struct iio_dev *indio_dev,
> +			      uintptr_t private,
> +			      const struct iio_chan_spec *chan,
> +			      const char *buf, size_t len)
> +{
> +	struct adf41513_state *st = iio_priv(indio_dev);
> +	unsigned long long readin;
> +	unsigned long tmp;
> +	u64 freq_uhz;
> +	int ret;
> +
> +	guard(mutex)(&st->lock);
> +
> +	switch ((u32)private) {
> +	case ADF41513_FREQ:
> +		ret = adf41513_parse_uhz(buf, &freq_uhz);
> +		if (ret)
> +			return ret;
> +		ret = adf41513_set_frequency(st, freq_uhz, ADF41513_SYNC_DIFF);
> +		break;
> +	case ADF41513_FREQ_REFIN:
> +		ret = kstrtoull(buf, 10, &readin);
> +		if (ret)
> +			return ret;
> +
> +		if (readin < ADF41513_MIN_REF_FREQ || readin > ADF41513_MAX_REF_FREQ) {
Can, alternatively, this check be made with in_range() macro?
If so, then
#include <linux/minmax.h>

Same question/suggestion to other similar value bounds checks throughout the driver.

> +			ret = -EINVAL;
> +			break;
> +		}
> +

With best regards,
Marcelo

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

* Re: [PATCH 2/3] dt-bindings: iio: frequency: add adf41513
  2025-11-10 15:44 ` [PATCH 2/3] dt-bindings: iio: frequency: add adf41513 Rodrigo Alencar via B4 Relay
  2025-11-11  8:05   ` Krzysztof Kozlowski
@ 2025-11-13 13:21   ` Marcelo Schmitt
  1 sibling, 0 replies; 15+ messages in thread
From: Marcelo Schmitt @ 2025-11-13 13:21 UTC (permalink / raw)
  To: rodrigo.alencar
  Cc: linux-kernel, linux-iio, devicetree, linux-doc, Jonathan Cameron,
	David Lechner, Andy Shevchenko, Lars-Peter Clausen,
	Michael Hennerich, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Jonathan Corbet

Hi Rodrigo,

Many comments inline in addition to what Krzysztof has already mentioned.

On 11/10, Rodrigo Alencar via B4 Relay wrote:
> From: Rodrigo Alencar <rodrigo.alencar@analog.com>
> 
> ultralow noise PLL frequency synthesizer that can be used to
> implement local oscillators (LOs) as high as 26.5 GHz
> 
> Signed-off-by: Rodrigo Alencar <rodrigo.alencar@analog.com>
> ---
>  .../bindings/iio/frequency/adi,adf41513.yaml       | 268 +++++++++++++++++++++
>  MAINTAINERS                                        |   1 +
>  2 files changed, 269 insertions(+)
> 
> diff --git a/Documentation/devicetree/bindings/iio/frequency/adi,adf41513.yaml b/Documentation/devicetree/bindings/iio/frequency/adi,adf41513.yaml
> new file mode 100644
> index 000000000000..7e1ad80d68af
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/iio/frequency/adi,adf41513.yaml
> @@ -0,0 +1,268 @@
> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/iio/frequency/adi,adf41513.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: Analog Devices ADF41513 PLL Frequency Synthesizer
> +
> +maintainers:
> +  - Rodrigo Alencar <rodrigo.alencar@analog.com>
> +
> +description:
> +  The ADF41513 is an ultralow noise frequency synthesizer that can be used to
> +  implement local oscillators (LOs) as high as 26.5 GHz in the upconversion and
> +  downconversion sections of wireless receivers and transmitters. The ADF41510
> +  supports frequencies up to 10 GHz.
> +
> +  https://www.analog.com/en/products/adf41513.html
> +  https://www.analog.com/en/products/adf41510.html
> +
> +properties:
> +  compatible:
> +    enum:
> +      - adi,adf41510
> +      - adi,adf41513
> +
> +  reg:
> +    maxItems: 1
> +
> +  spi-max-frequency:
> +    maximum: 25000000
> +
> +  clocks:
> +    maxItems: 1
> +    description: Clock that provides the reference input frequency.
> +
> +  '#clock-cells':
> +    const: 0
> +
> +  clock-output-names:
> +    maxItems: 1
> +
> +  vcc-supply:
> +    description: Power supply for the device (3.3V)
I see we can have AVDD1 == ... == AVDD5 == VP == 3.3V.
But we should document them all here in case somebody wants to use a separate
supply for any of those.

> +
> +  chip-enable-gpios:
> +    description:
> +      GPIO that controls the chip enable pin. A logic low on this pin
> +      powers down the device and puts the charge pump output into
> +      three-state mode.
> +    maxItems: 1
> +
> +  lock-detect-gpios:
> +    description:
> +      GPIO for lock detect functionality. When configured for digital lock
> +      detect, this pin will output a logic high when the PLL is locked.
> +    maxItems: 1
> +
> +  adi,power-up-frequency:
> +    $ref: /schemas/types.yaml#/definitions/uint64
> +    minimum: 1000000000
> +    maximum: 26500000000
> +    default: 10000000000
> +    description:
> +      The PLL tunes to this frequency (in Hz) on driver probe.
> +      Range is 1 GHz to 26.5 GHz for ADF41513, and 1 GHz to 10 GHz for ADF41510.
The PLL settings are also controllable at runtime and independent of hw
connections, right? Can't this be just a sw/driver default instead of a dt
property?

> +
> +  adi,reference-div-factor:
> +    $ref: /schemas/types.yaml#/definitions/uint32
> +    minimum: 1
> +    maximum: 32
> +    description:
> +      Reference division factor (R Counter). If not specified, the driver
> +      will calculate the optimal value automatically.
> +
> +  adi,reference-doubler-enable:
> +    description:
> +      Enables the reference doubler. The maximum reference frequency when
> +      the doubler is enabled is 225 MHz.
> +    type: boolean
This also seems like runtime configuration. At first glance, my feeling is that
it impacts the output frequency and thus should probably be somehow handled
in adf41513_write() IIO_CHAN_INFO_FREQUENCY case. By the way, if IIO_VAL_INT_64
format allows setting the frequency resolution in sub-Hz (see comment in driver file),
then I also suggest to use usual IIO _raw interfaces, e.g.

static const struct iio_info adf41513_info = {
  .write_raw = adf41513_write(),
	.write_raw_get_fmt = adf41513_write_raw_get_fmt(),
	.debugfs_reg_access = &adf41513_reg_access,
};

> +
> +  adi,reference-div2-enable:
> +    description:
> +      Enables the reference divide-by-2 function. This provides a 50%
> +      duty cycle signal to the PFD.
> +    type: boolean
This one also seems to be associated with output frequency and runtime
configurable, no?

> +
> +  adi,charge-pump-current-microamp:
> +    minimum: 450
> +    maximum: 7200
> +    default: 2400
> +    description:
> +      Charge pump current in microamps. The value will be rounded to the
> +      nearest supported value.
> +
> +  adi,charge-pump-resistor-ohms:
> +    minimum: 1800
> +    maximum: 10000
> +    default: 2700
> +    description:
> +      External charge pump resistor value in ohms. This sets the maximum
> +      charge pump current along with the charge pump current setting.

hmm these charge-pump props seem a bit tricky. IIUC, the achievable charge pump
output currents depend on the charge pump resistor value and the cp output
current would be something that a user would latter want to fine tune at
runtime. I'd keep adi,charge-pump-resistor-ohms only, pick a default for the
pump current, then provide an IIO attribute to allow tweaking the charge pump
current for fine tuning the loop filter frequency response. This might need
a new IIO ABI.

> +
> +  adi,muxout-select:
> +    description:
> +      On chip multiplexer output selection.
> +      high_z - MUXOUT Pin set to high-Z. (default)
> +      muxout_high - MUXOUT Pin set to high.
> +      muxout_low - MUXOUT Pin set to low.
> +      f_div_rclk - MUXOUT Pin set to R divider output
> +      f_div_nclk - MUXOUT Pin set to N divider output
> +      lock_detect - MUXOUT Pin set to Digital lock detect
> +      serial_data - MUXOUT Pin set to Serial data output
> +      readback - MUXOUT Pin set to Readback mode
> +      f_div_clk1 - MUXOUT Pin set to CLK1 divider output
> +      f_div_rclk_2 - MUXOUT Pin set to R divider/2 output
> +      f_div_nclk_2 - MUXOUT Pin set to N divider/2 output
> +    enum: [high_z, muxout_high, muxout_low, f_div_rclk, f_div_nclk, lock_detect,
> +           serial_data, readback, f_div_clk1, f_div_rclk_2, f_div_nclk_2]

I don't think this should be a dt property. The mux output can be controlled at
runtime by updating register 12. Also, if somebody sets, for example,
'muxout_high' here, then the user would not be able to set the mux output to
something else latter? Would not be able to output the configured RF output
frequency for the example?

> +
> +  adi,muxout-level-1v8-enable:
> +    description:
> +      Set MUXOUT and DLD logic levels to 1.8V. Default is 3.3V.
> +    type: boolean
> +
> +  adi,phase-detector-polarity-positive-enable:
> +    description:
> +      Set phase detector polarity to positive. Default is negative.
> +      Use positive polarity with non-inverting loop filter and VCO with
> +      positive tuning slope, or with inverting loop filter and VCO with
> +      negative tuning slope.
> +    type: boolean
> +
> +  adi,lock-detect-precision:
> +    $ref: /schemas/types.yaml#/definitions/uint32
> +    minimum: 0
> +    maximum: 3
> +    description:
> +      Lock detector precision setting. Controls the sensitivity of the
> +      lock detector. Lower values of precision increases the lock detector
> +      window size.
This sounds more like something that could be supported through an IIO device
attribute.

> +
> +  adi,lock-detect-count:
> +    $ref: /schemas/types.yaml#/definitions/uint32
> +    minimum: 0
> +    maximum: 7
> +    description: |
> +      Lock detector count setting (3-bit value). Determines the number of
> +      consecutive phase detector cycles that must be within the lock detector
> +      window before lock is declared. The count grows in powers of two of the
> +      programmed value:
> +      - if adi,fast-lock-enable is set count = 2 * 2^value
> +      - if adi,fast-lock-enable is not set count = 64 * 2^value
> +
> +  adi,lock-detect-bias-microamp:
> +    description:
> +      Lock detector bias current. Controls the lock detector window size
> +      along with the lock detector precision setting. Lower bias current
> +      increases the window size.
> +    enum: [10, 20, 30, 40]
> +
> +  adi,fast-lock-enable:
> +    description:
> +      Enable fast lock mode. This changes the lock detector clock selection
> +      for faster lock indication.
> +    type: boolean
adi,lock-detect-count, adi,lock-detect-bias-microamp, and adi,fast-lock-enable
also sound like they could be IIO device properties.

> +
> +  adi,phase-resync-enable:
> +    description:
> +      Enable phase resync functionality. This produces a consistent output
> +      phase offset with respect to the input reference.
> +    type: boolean
IIUC, this would work similarly to the description of out_altvoltageY_phase ABI,
except the phase would be relative to the reference clock instead of a second
output channel. New IIO ABI? Or, maybe, provide out_altvoltage0_frequency and
out_altvoltage1_frequency? One of the altvoltageY would be the frequency of
muxout while the other would be the frequency of the reference clock. Then phase
resync would be supported through out_altvoltageY_phase. Not sure if having
a "virtual" output channel would be misleading, though. Extending the
out_altvoltageY_phase ABI might be an alternative.

> +
> +  adi,12bit-clk-divider:
> +    $ref: /schemas/types.yaml#/definitions/uint32
> +    minimum: 0
> +    maximum: 4095
> +    description:
> +      CLK1 divider value used when adi,phase-resync-enable is set
> +
> +  adi,12bit-clk2-divider:
> +    $ref: /schemas/types.yaml#/definitions/uint32
> +    minimum: 0
> +    maximum: 4095
> +    description:
> +      CLK2 divider value used when adi,phase-resync-enable is set
CLK1 and CLK2 would be calculated by the driver according to the value set
through out_altvoltageY_phase (if the idea above makes sense). Then
adi,12bit-clk-divider and adi,12bit-clk2-divider would also not be needed.

> +
> +  adi,le-sync-enable:
> +    description:
> +      Synchronize the rising edge of LE on an SPI write with the falling
> +      edge of the reference signal to prevent glitches.
> +    type: boolean
> +
> +  adi,freq-resolution:
> +    $ref: /schemas/types.yaml#/definitions/uint64
> +    minimum: 1
> +    default: 1000000
> +    description:
> +      Initial frequency resolution in micro-Hz (µHz) for the algorithm to achieve.
> +      This influences the choice between fixed and variable modulus modes.
> +      Default is 1000000 µHz (1 Hz).
This one also looks like it could be a driver default instead of a dt prop.


Best regards,
Marcelo

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

* Re: [PATCH 1/3] iio: frequency: adf41513: driver implementation
  2025-11-13 13:13   ` Marcelo Schmitt
@ 2025-11-13 14:39     ` Nuno Sá
  0 siblings, 0 replies; 15+ messages in thread
From: Nuno Sá @ 2025-11-13 14:39 UTC (permalink / raw)
  To: Marcelo Schmitt, rodrigo.alencar
  Cc: linux-kernel, linux-iio, devicetree, linux-doc, Jonathan Cameron,
	David Lechner, Andy Shevchenko, Lars-Peter Clausen,
	Michael Hennerich, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Jonathan Corbet

On Thu, 2025-11-13 at 10:13 -0300, Marcelo Schmitt wrote:
> Hi Rodrigo,
> 
> A couple of comments inline since this is on the mailing list.
> As mentioned in the other thread, we ought to continue the review of this internally.
> 
> On 11/10, Rodrigo Alencar via B4 Relay wrote:
> > From: Rodrigo Alencar <rodrigo.alencar@analog.com>
> > 
> > - ADF41513: 1 GHz to 26.5 GHz frequency range
> > - ADF41510: 1 GHz to 10 GHz frequency range
> > - Integer-N and fractional-N operation modes
> > - Ultra-low phase noise (-235 dBc/Hz integer-N, -231 dBc/Hz fractional-N)
> > - High maximum PFD frequency (250 MHz integer-N, 125 MHz fractional-N)
> > - 25-bit fixed modulus or 49-bit variable modulus fractional modes
> > - Programmable charge pump currents with 16x range
> > - Digital lock detect functionality
> > - Phase resync capability for consistent output phase
> > - Clock framework integration for system clock generation
> > 
> > Signed-off-by: Rodrigo Alencar <rodrigo.alencar@analog.com>
> > ---
> ...
> > +
> > +static int adf41513_parse_uhz(const char *str, u64 *freq_uhz)
> > +{
> > +	u64 uhz = 0;
> > +	int f_count = ADF41513_HZ_DECIMAL_PRECISION;
> > +	bool frac_part = false;
> > +
> > +	if (str[0] == '+')
> > +		str++;
> > +
> > +	while (*str && f_count > 0) {
> > +		if ('0' <= *str && *str <= '9') {
> > +			uhz = uhz * 10 + *str - '0';
> > +			if (frac_part)
> > +				f_count--;
> > +		} else if (*str == '\n') {
> > +			if (*(str + 1) == '\0')
> > +				break;
> > +			return -EINVAL;
> > +		} else if (*str == '.' && !frac_part) {
> > +			frac_part = true;
> > +		} else {
> > +			return -EINVAL;
> > +		}
> > +		str++;
> > +	}
> > +
> > +	for (; f_count > 0; f_count--)
> > +		uhz *= 10;
> > +
> > +	*freq_uhz = uhz;
> > +
> > +	return 0;
> > +}
> didn't check the details, but can't the sub-Hz resolution be supported with
> .write_raw_get_fmt()?
> e.g.
> 
> static int adf41513_write_raw_get_fmt(struct iio_dev *indio_dev,
> 				    struct iio_chan_spec const *chan, long mask)
> {
> 	switch (mask) {
> 	case IIO_CHAN_INFO_FREQUENCY:
> 		return IIO_VAL_INT_64;
> 	default:
> 		return IIO_VAL_INT_PLUS_MICRO;

I think the above is already the default anyways... But the key here is that (I think) the goal
is to be able to do things like:

echo integer.fractional > out_altvoltage0_frequency

where integer can be u64. If I'm not missing anything, we cannot do that through the standard
interfaces.

- Nuno Sá

> 	}
> }
> 
> static const struct iio_info adf41513_info = {
> ...
> 	.write_raw_get_fmt = adf41513_write_raw_get_fmt(),
> };
> 
> ...
> > +
> > +static ssize_t adf41513_write(struct iio_dev *indio_dev,
> > +			      uintptr_t private,
> > +			      const struct iio_chan_spec *chan,
> > +			      const char *buf, size_t len)
> > +{
> > +	struct adf41513_state *st = iio_priv(indio_dev);
> > +	unsigned long long readin;
> > +	unsigned long tmp;
> > +	u64 freq_uhz;
> > +	int ret;
> > +
> > +	guard(mutex)(&st->lock);
> > +
> > +	switch ((u32)private) {
> > +	case ADF41513_FREQ:
> > +		ret = adf41513_parse_uhz(buf, &freq_uhz);
> > +		if (ret)
> > +			return ret;
> > +		ret = adf41513_set_frequency(st, freq_uhz, ADF41513_SYNC_DIFF);
> > +		break;
> > +	case ADF41513_FREQ_REFIN:
> > +		ret = kstrtoull(buf, 10, &readin);
> > +		if (ret)
> > +			return ret;
> > +
> > +		if (readin < ADF41513_MIN_REF_FREQ || readin > ADF41513_MAX_REF_FREQ) {
> Can, alternatively, this check be made with in_range() macro?
> If so, then
> #include <linux/minmax.h>
> 
> Same question/suggestion to other similar value bounds checks throughout the driver.
> 
> > +			ret = -EINVAL;
> > +			break;
> > +		}
> > +
> 
> With best regards,
> Marcelo

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

* Re: [PATCH 1/3] iio: frequency: adf41513: driver implementation
  2025-11-10 16:41     ` Andy Shevchenko
@ 2025-11-16 16:07       ` Jonathan Cameron
  0 siblings, 0 replies; 15+ messages in thread
From: Jonathan Cameron @ 2025-11-16 16:07 UTC (permalink / raw)
  To: Andy Shevchenko
  Cc: rodrigo.alencar, linux-kernel, linux-iio, devicetree, linux-doc,
	David Lechner, Andy Shevchenko, Lars-Peter Clausen,
	Michael Hennerich, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Jonathan Corbet

On Mon, 10 Nov 2025 18:41:27 +0200
Andy Shevchenko <andriy.shevchenko@intel.com> wrote:

> On Mon, Nov 10, 2025 at 06:30:38PM +0200, Andy Shevchenko wrote:
> > On Mon, Nov 10, 2025 at 03:44:44PM +0000, Rodrigo Alencar via B4 Relay wrote:  
> 
> ...
> 
> > In any case, I stopped my review here, you have more than enough to fix.
> > Please, come next time with a tag from one whose name is in the MAINTAINERS.
> > From now on it will be my requirement as a reviewer of IIO subsystem.  
> 
> And to be more clear on this requirement (for all submissions from @analog.com
> and related): A name from approximately below list I want to see as Rb.
> 
> 	Antoniu Miclaus <antoniu.miclaus@analog.com>
> 	Marcelo Schmitt <marcelo.schmitt@analog.com>
> 	Michael Hennerich <michael.hennerich@analog.com>
>         Nuno Sá <nuno.sa@analog.com>
> 
> (those people seems experienced and I have heard of / from).
> 

From Nuno's mail seems that in general Analog actually have a better
process for this than many (which is excellent to hear btw!)
RB tags that come before being posted to the list only really have
meaning if we know and have built up trust with the reviewers.
This is different to ones on the list where we can see what the
feedback was that has been acted on.

I'll note that ADI sometimes works with external companies, so
there are various other people who would work as well. Given
that may change over time I'll not call them out explicitly but
you can probably figure out who I mean.  I have no idea if they
also help with pre submission reviews though.

Note that Andy's request here is specific to Analog given they
have the folk to do it.  I don't want this sort of requirement
to apply to folk working for other companies who have a much
smaller presence in IIO!  (just for the record, as Andy was very
clear this was a request specific to Analog).

Jonathan

p.s. Bad luck Rodrigo for being the submitter who triggered this!
Looking forward to v2 - I'll probably not add more noise by
reviewing this version.


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

* Re: [PATCH 0/3] ADF41513/ADF41510 PLL frequency synthesizers
  2025-11-10 15:44 [PATCH 0/3] ADF41513/ADF41510 PLL frequency synthesizers Rodrigo Alencar via B4 Relay
                   ` (3 preceding siblings ...)
  2025-11-10 16:38 ` [PATCH 0/3] ADF41513/ADF41510 PLL frequency synthesizers Andy Shevchenko
@ 2025-11-16 16:10 ` Jonathan Cameron
  2025-11-17  8:37   ` Nuno Sá
  4 siblings, 1 reply; 15+ messages in thread
From: Jonathan Cameron @ 2025-11-16 16:10 UTC (permalink / raw)
  To: Rodrigo Alencar via B4 Relay
  Cc: rodrigo.alencar, linux-kernel, linux-iio, devicetree, linux-doc,
	David Lechner, Andy Shevchenko, Lars-Peter Clausen,
	Michael Hennerich, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Jonathan Corbet

On Mon, 10 Nov 2025 15:44:43 +0000
Rodrigo Alencar via B4 Relay <devnull+rodrigo.alencar.analog.com@kernel.org> wrote:

> This patch series adds support for the Analog Devices ADF41513 and ADF41510
> ultralow noise PLL frequency synthesizers. These devices are designed for
> implementing local oscillators (LOs) in high-frequency applications.
> 
> The ADF41513 covers frequencies from 1 GHz to 26.5 GHz, while the ADF41510
> operates from 1 GHz to 10 GHz. Both devices feature exceptional phase noise
> performance and flexible frequency synthesis capabilities.
> 
> Key features supported by this driver:
> - Integer-N and fractional-N operation modes
> - Ultra-low phase noise (-235 dBc/Hz integer-N, -231 dBc/Hz fractional-N)
> - High maximum PFD frequency (250 MHz integer-N, 125 MHz fractional-N)
> - 25-bit fixed modulus or 49-bit variable modulus fractional modes
> - Programmable charge pump currents with 16x range
> - Digital lock detect functionality
> - Phase resync capability for consistent output phase
> - Clock framework integration for system clock generation
> 
> The series includes:
> 1. Core driver implementation with full register programming support
> 2. Device tree bindings documentation
> 3. IIO subsystem documentation with usage examples
> 
> The driver integrates with both the IIO subsystem (for direct hardware control)
> and the Linux clock framework (for use as a system clock source), providing
> flexibility for different use cases.

For v2, provide a little more info on why we need both interface types
specifically what you can do with IIO that you can't do with a clock
driver.  Also +CC the clk driver folk and list from MAINTAINERS.

We have evolved to this dual interface state drivers, but I'm not sure
we aren't in a case 'If we were doing this again we'd never start from
here.'

Jonathan


> 
> Signed-off-by: Rodrigo Alencar <rodrigo.alencar@analog.com>
> ---
> Rodrigo Alencar (3):
>       iio: frequency: adf41513: driver implementation
>       dt-bindings: iio: frequency: add adf41513
>       docs: iio: add documentation for adf41513 driver
> 
>  .../bindings/iio/frequency/adi,adf41513.yaml       |  268 ++++
>  Documentation/iio/adf41513.rst                     |  377 +++++
>  Documentation/iio/index.rst                        |    1 +
>  MAINTAINERS                                        |    9 +
>  drivers/iio/frequency/Kconfig                      |   11 +
>  drivers/iio/frequency/Makefile                     |    1 +
>  drivers/iio/frequency/adf41513.c                   | 1435 ++++++++++++++++++++
>  7 files changed, 2102 insertions(+)
> ---
> base-commit: d16d1c2553248f9b859b86c94344d8b81f0297cd
> change-id: 20251110-adf41513-iio-driver-aaca8a7f808e
> 
> Best regards,


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

* Re: [PATCH 0/3] ADF41513/ADF41510 PLL frequency synthesizers
  2025-11-16 16:10 ` Jonathan Cameron
@ 2025-11-17  8:37   ` Nuno Sá
  0 siblings, 0 replies; 15+ messages in thread
From: Nuno Sá @ 2025-11-17  8:37 UTC (permalink / raw)
  To: Jonathan Cameron, Rodrigo Alencar via B4 Relay
  Cc: rodrigo.alencar, linux-kernel, linux-iio, devicetree, linux-doc,
	David Lechner, Andy Shevchenko, Lars-Peter Clausen,
	Michael Hennerich, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Jonathan Corbet

On Sun, 2025-11-16 at 16:10 +0000, Jonathan Cameron wrote:
> On Mon, 10 Nov 2025 15:44:43 +0000
> Rodrigo Alencar via B4 Relay <devnull+rodrigo.alencar.analog.com@kernel.org> wrote:
> 
> > This patch series adds support for the Analog Devices ADF41513 and ADF41510
> > ultralow noise PLL frequency synthesizers. These devices are designed for
> > implementing local oscillators (LOs) in high-frequency applications.
> > 
> > The ADF41513 covers frequencies from 1 GHz to 26.5 GHz, while the ADF41510
> > operates from 1 GHz to 10 GHz. Both devices feature exceptional phase noise
> > performance and flexible frequency synthesis capabilities.
> > 
> > Key features supported by this driver:
> > - Integer-N and fractional-N operation modes
> > - Ultra-low phase noise (-235 dBc/Hz integer-N, -231 dBc/Hz fractional-N)
> > - High maximum PFD frequency (250 MHz integer-N, 125 MHz fractional-N)
> > - 25-bit fixed modulus or 49-bit variable modulus fractional modes
> > - Programmable charge pump currents with 16x range
> > - Digital lock detect functionality
> > - Phase resync capability for consistent output phase
> > - Clock framework integration for system clock generation
> > 
> > The series includes:
> > 1. Core driver implementation with full register programming support
> > 2. Device tree bindings documentation
> > 3. IIO subsystem documentation with usage examples
> > 
> > The driver integrates with both the IIO subsystem (for direct hardware control)
> > and the Linux clock framework (for use as a system clock source), providing
> > flexibility for different use cases.
> 
> For v2, provide a little more info on why we need both interface types
> specifically what you can do with IIO that you can't do with a clock
> driver.  Also +CC the clk driver folk and list from MAINTAINERS.
> 
> We have evolved to this dual interface state drivers, but I'm not sure
> we aren't in a case 'If we were doing this again we'd never start from
> here.'

Yeah, in some devices I do think that we should likely only have the clock driver
(for devices where we only control the channel frequency). For others, we do have
offer more control through IIO which adds some value.

- Nuno Sá

> 
> Jonathan
> 
> 
> > 
> > Signed-off-by: Rodrigo Alencar <rodrigo.alencar@analog.com>
> > ---
> > Rodrigo Alencar (3):
> >       iio: frequency: adf41513: driver implementation
> >       dt-bindings: iio: frequency: add adf41513
> >       docs: iio: add documentation for adf41513 driver
> > 
> >  .../bindings/iio/frequency/adi,adf41513.yaml       |  268 ++++
> >  Documentation/iio/adf41513.rst                     |  377 +++++
> >  Documentation/iio/index.rst                        |    1 +
> >  MAINTAINERS                                        |    9 +
> >  drivers/iio/frequency/Kconfig                      |   11 +
> >  drivers/iio/frequency/Makefile                     |    1 +
> >  drivers/iio/frequency/adf41513.c                   | 1435 ++++++++++++++++++++
> >  7 files changed, 2102 insertions(+)
> > ---
> > base-commit: d16d1c2553248f9b859b86c94344d8b81f0297cd
> > change-id: 20251110-adf41513-iio-driver-aaca8a7f808e
> > 
> > Best regards,

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

end of thread, other threads:[~2025-11-17  8:36 UTC | newest]

Thread overview: 15+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-11-10 15:44 [PATCH 0/3] ADF41513/ADF41510 PLL frequency synthesizers Rodrigo Alencar via B4 Relay
2025-11-10 15:44 ` [PATCH 1/3] iio: frequency: adf41513: driver implementation Rodrigo Alencar via B4 Relay
2025-11-10 16:30   ` Andy Shevchenko
2025-11-10 16:41     ` Andy Shevchenko
2025-11-16 16:07       ` Jonathan Cameron
2025-11-11  8:43     ` Nuno Sá
2025-11-13 13:13   ` Marcelo Schmitt
2025-11-13 14:39     ` Nuno Sá
2025-11-10 15:44 ` [PATCH 2/3] dt-bindings: iio: frequency: add adf41513 Rodrigo Alencar via B4 Relay
2025-11-11  8:05   ` Krzysztof Kozlowski
2025-11-13 13:21   ` Marcelo Schmitt
2025-11-10 15:44 ` [PATCH 3/3] docs: iio: add documentation for adf41513 driver Rodrigo Alencar via B4 Relay
2025-11-10 16:38 ` [PATCH 0/3] ADF41513/ADF41510 PLL frequency synthesizers Andy Shevchenko
2025-11-16 16:10 ` Jonathan Cameron
2025-11-17  8:37   ` Nuno Sá

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).