From: Alexandru Tachici <alexandru.tach...@analog.com>

Add support for AD9545 Quad Input, 10-Output, Dual DPLL/IEEE 1588,
1 pps Synchronizer and Jitter Cleaner.

Signed-off-by: Alexandru Tachici <alexandru.tach...@analog.com>
---
 drivers/clk/Kconfig                |    6 +
 drivers/clk/Makefile               |    1 +
 drivers/clk/adi/Kconfig            |   29 +
 drivers/clk/adi/Makefile           |    9 +
 drivers/clk/adi/clk-ad9545-i2c.c   |   61 +
 drivers/clk/adi/clk-ad9545-spi.c   |   75 ++
 drivers/clk/adi/clk-ad9545.c       | 1678 ++++++++++++++++++++++++++++
 drivers/clk/adi/clk-ad9545.h       |   16 +
 include/dt-bindings/clock/ad9545.h |   64 ++
 9 files changed, 1939 insertions(+)
 create mode 100644 drivers/clk/adi/Kconfig
 create mode 100644 drivers/clk/adi/Makefile
 create mode 100644 drivers/clk/adi/clk-ad9545-i2c.c
 create mode 100644 drivers/clk/adi/clk-ad9545-spi.c
 create mode 100644 drivers/clk/adi/clk-ad9545.c
 create mode 100644 drivers/clk/adi/clk-ad9545.h
 create mode 100644 include/dt-bindings/clock/ad9545.h

diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig
index 85856cff506c..80c3faf1f42e 100644
--- a/drivers/clk/Kconfig
+++ b/drivers/clk/Kconfig
@@ -270,6 +270,11 @@ config CLK_LS1028A_PLLDIG
           features of the PLL are currently supported by the driver. By 
default,
           configured bypass mode with this PLL.
 
+config COMMON_CLK_ADI
+       def_bool COMMON_CLK
+       ---help---
+         Support for Analog Devices clock providers.
+
 config COMMON_CLK_XGENE
        bool "Clock driver for APM XGene SoC"
        default ARCH_XGENE
@@ -369,6 +374,7 @@ config COMMON_CLK_FIXED_MMIO
          Support for Memory Mapped IO Fixed clocks
 
 source "drivers/clk/actions/Kconfig"
+source "drivers/clk/adi/Kconfig"
 source "drivers/clk/analogbits/Kconfig"
 source "drivers/clk/baikal-t1/Kconfig"
 source "drivers/clk/bcm/Kconfig"
diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
index dbdc590e7de3..3adc7db7c671 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -73,6 +73,7 @@ obj-$(CONFIG_COMMON_CLK_XGENE)                += clk-xgene.o
 
 # please keep this section sorted lexicographically by directory path name
 obj-y                                  += actions/
+obj-$(CONFIG_COMMON_CLK_ADI)           += adi/
 obj-y                                  += analogbits/
 obj-$(CONFIG_COMMON_CLK_AT91)          += at91/
 obj-$(CONFIG_ARCH_ARTPEC)              += axis/
diff --git a/drivers/clk/adi/Kconfig b/drivers/clk/adi/Kconfig
new file mode 100644
index 000000000000..768b8227f336
--- /dev/null
+++ b/drivers/clk/adi/Kconfig
@@ -0,0 +1,29 @@
+#
+# Analog Devices Clock Drivers
+#
+# When adding new entries keep the list in alphabetical order
+
+menu "Analog Devices Clock Drivers"
+
+config COMMON_CLK_AD9545
+       tristate
+
+config COMMON_CLK_AD9545_I2C
+       tristate "Analog Devices AD9545 via I2C"
+       depends on REGMAP_I2C
+       select COMMON_CLK_AD9545
+       help
+         Say yes here to build support for Analog Devices AD9545
+         Quad Input, 10-Output, Dual DPLL/IEEE 1588,
+         1 pps Synchronizer and Jitter Cleaner via I2C
+
+config COMMON_CLK_AD9545_SPI
+       tristate "Analog Devices AD9545 via SPI"
+       depends on REGMAP_SPI
+       select COMMON_CLK_AD9545
+       help
+         Say yes here to build support for Analog Devices AD9545
+         Quad Input, 10-Output, Dual DPLL/IEEE 1588,
+         1 pps Synchronizer and Jitter Cleaner via SPI
+
+endmenu
diff --git a/drivers/clk/adi/Makefile b/drivers/clk/adi/Makefile
new file mode 100644
index 000000000000..7ba1fded3013
--- /dev/null
+++ b/drivers/clk/adi/Makefile
@@ -0,0 +1,9 @@
+# SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause
+#
+# Makefile for AD9545 Network Clock Generator/Synchronizer
+#
+
+# When adding new entries keep the list in alphabetical order
+obj-$(CONFIG_COMMON_CLK_AD9545) += clk-ad9545.o
+obj-$(CONFIG_COMMON_CLK_AD9545_I2C) += clk-ad9545-i2c.o
+obj-$(CONFIG_COMMON_CLK_AD9545_SPI) += clk-ad9545-spi.o
diff --git a/drivers/clk/adi/clk-ad9545-i2c.c b/drivers/clk/adi/clk-ad9545-i2c.c
new file mode 100644
index 000000000000..923fec28ece2
--- /dev/null
+++ b/drivers/clk/adi/clk-ad9545-i2c.c
@@ -0,0 +1,61 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause
+/*
+ * AD9545 Network Clock Generator/Synchronizer
+ *
+ * Copyright 2020 Analog Devices Inc.
+ */
+
+#include "clk-ad9545.h"
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/iio/iio.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+
+static const struct regmap_config ad9545_regmap_config = {
+       .reg_bits = 16,
+       .val_bits = 8,
+       .max_register = 0x3A3B,
+       .use_single_rw = true,
+};
+
+static int ad9545_i2c_probe(struct i2c_client *client)
+{
+       struct regmap *regmap;
+
+       regmap = devm_regmap_init_i2c(client, &ad9545_regmap_config);
+       if (IS_ERR(regmap)) {
+               dev_err(&client->dev, "devm_regmap_init_i2c failed!\n");
+               return PTR_ERR(regmap);
+       }
+
+       return ad9545_probe(&client->dev, regmap);
+}
+
+static const struct of_device_id ad9545_i2c_of_match[] = {
+       { .compatible = "adi,ad9545" },
+       { }
+};
+MODULE_DEVICE_TABLE(of, ad9545_i2c_of_match);
+
+static const struct i2c_device_id ad9545_i2c_id[] = {
+       {"ad9545"},
+       { }
+};
+MODULE_DEVICE_TABLE(i2c, ad9545_i2c_id);
+
+static struct i2c_driver ad9545_i2c_driver = {
+       .driver = {
+               .name   = "ad9545",
+               .of_match_table = ad9545_i2c_of_match,
+       },
+       .probe_new      = ad9545_i2c_probe,
+       .id_table       = ad9545_i2c_id,
+};
+module_i2c_driver(ad9545_i2c_driver);
+
+MODULE_AUTHOR("Alexandru Tachici <alexandru.tach...@analog.com>");
+MODULE_DESCRIPTION("Analog Devices AD9545 I2C");
+MODULE_LICENSE("Dual BSD/GPL");
diff --git a/drivers/clk/adi/clk-ad9545-spi.c b/drivers/clk/adi/clk-ad9545-spi.c
new file mode 100644
index 000000000000..36ded1026843
--- /dev/null
+++ b/drivers/clk/adi/clk-ad9545-spi.c
@@ -0,0 +1,75 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause
+/*
+ * AD9545 Network Clock Generator/Synchronizer
+ *
+ * Copyright 2020 Analog Devices Inc.
+ */
+
+#include "clk-ad9545.h"
+#include <linux/bitfield.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/iio/iio.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/spi/spi.h>
+
+#define AD9545_CONFIG_0                        0x0000
+
+#define AD9545_4WIRE_SPI               0x3
+#define AD9545_4WIRE_SPI_MSK           GENMASK(4, 3)
+
+static const struct regmap_config ad9545_regmap_config = {
+       .reg_bits = 16,
+       .val_bits = 8,
+       .max_register = 0x3A3B,
+       .use_single_rw = true,
+};
+
+static int ad9545_spi_probe(struct spi_device *spi)
+{
+       struct regmap *regmap;
+       int ret;
+
+       regmap = devm_regmap_init_spi(spi, &ad9545_regmap_config);
+       if (IS_ERR(regmap)) {
+               dev_err(&spi->dev, "devm_regmap_init_spi failed!\n");
+               return PTR_ERR(regmap);
+       }
+
+       if (!(spi->mode & SPI_3WIRE)) {
+               ret = regmap_write(regmap, AD9545_CONFIG_0,
+                                  FIELD_PREP(AD9545_4WIRE_SPI_MSK, 
AD9545_4WIRE_SPI));
+               if (ret < 0)
+                       return ret;
+       }
+
+       return ad9545_probe(&spi->dev, regmap);
+}
+
+static const struct of_device_id ad9545_spi_of_match[] = {
+       { .compatible = "adi,ad9545" },
+       { }
+};
+MODULE_DEVICE_TABLE(of, ad9545_spi_of_match);
+
+static const struct spi_device_id ad9545_spi_id[] = {
+       {"ad9545", 0},
+       { }
+};
+MODULE_DEVICE_TABLE(spi, ad9545_spi_id);
+
+static struct spi_driver ad9545_spi_driver = {
+       .driver = {
+               .name   = "ad9545",
+               .of_match_table = ad9545_spi_of_match,
+       },
+       .probe          = ad9545_spi_probe,
+       .id_table       = ad9545_spi_id,
+};
+module_spi_driver(ad9545_spi_driver);
+
+MODULE_AUTHOR("Alexandru Tachici <alexandru.tach...@analog.com>");
+MODULE_DESCRIPTION("Analog Devices AD9545 SPI");
+MODULE_LICENSE("Dual BSD/GPL");
diff --git a/drivers/clk/adi/clk-ad9545.c b/drivers/clk/adi/clk-ad9545.c
new file mode 100644
index 000000000000..df4008e5a5c7
--- /dev/null
+++ b/drivers/clk/adi/clk-ad9545.c
@@ -0,0 +1,1678 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause
+/*
+ * AD9545 Network Clock Generator/Synchronizer
+ *
+ * Copyright 2020 Analog Devices Inc.
+ */
+
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/property.h>
+#include <linux/rational.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+
+#include <dt-bindings/clock/ad9545.h>
+
+#define AD9545_CONFIG_0                        0x0000
+#define AD9545_PRODUCT_ID_LOW          0x0004
+#define AD9545_PRODUCT_ID_HIGH         0x0005
+#define AD9545_IO_UPDATE               0x000F
+#define AD9545_CHIP_ID                 0x0121
+#define AD9545_SYS_CLK_FB_DIV          0x0200
+#define AD9545_SYS_CLK_INPUT           0x0201
+#define AD9545_SYS_CLK_REF_FREQ                0x0202
+#define AD9545_SYS_STABILITY_T         0x0207
+#define AD9545_REF_A_CTRL              0x0300
+#define AD9545_REF_A_RDIV              0x0400
+#define AD9545_REF_A_PERIOD            0x0404
+#define AD9545_REF_A_OFFSET_LIMIT      0x040C
+#define AD9545_REF_A_MONITOR_HYST      0x040F
+#define AD9545_PHASE_LOCK_THRESH       0x0800
+#define AD9545_FREQ_LOCK_THRESH                0x0805
+#define AD9545_DPLL0_FTW               0x1000
+#define AD9545_DRIVER_0A_CONF          0x10D7
+#define AD9545_SYNC_CTRL0              0x10DB
+#define AD9545_APLL0_M_DIV             0x1081
+#define AD9545_Q0A_DIV                 0x1100
+#define AD9545_Q0A_PHASE               0x1104
+#define AD9545_Q0A_PHASE_CONF          0x1108
+#define AD9545_DPLL0_EN                        0x1200
+#define AD9545_DPLL0_SOURCE            0x1201
+#define AD9545_DPLL0_LOOP_BW           0x1204
+#define AD9545_DPLL0_N_DIV             0x120C
+#define AD9545_DPLL0_FRAC              0x1210
+#define AD9545_DPLL0_MOD               0x1213
+#define AD9545_DRIVER_1A_CONF          0x14D7
+#define AD9545_Q1A_DIV                 0x1500
+#define AD9545_Q1A_PHASE               0x1504
+#define AD9545_Q1A_PHASE_CONF          0x1508
+#define AD9545_CALIB_CLK               0x2000
+#define AD9545_POWER_DOWN_REF          0x2001
+#define AD9545_PWR_CALIB_CH0           0x2100
+#define AD9545_CTRL_CH0                        0x2101
+#define AD9545_DIV_OPS_Q0A             0x2102
+#define AD9545_DPLL0_MODE              0x2105
+#define AD9545_DIV_OPS_Q1A             0x2202
+#define AD9545_NCO0_FREQ               0x2805
+#define AD9545_PLL_STATUS              0x3001
+#define AD9545_PLL0_STATUS             0x3100
+
+#define AD9545_REF_CTRL_DIF_MSK                        GENMASK(3, 2)
+#define AD9545_REF_CTRL_REFA_MSK               GENMASK(5, 4)
+#define AD9545_REF_CTRL_REFAA_MSK              GENMASK(7, 6)
+
+#define AD9545_UPDATE_REGS                     0x1
+#define AD9545_RESET_REGS                      0x81
+
+#define AD9545_SYNC_CTRLX(x)                   (AD9545_SYNC_CTRL0 + ((x) * 
0x400))
+#define AD9545_REF_X_RDIV(x)                   (AD9545_REF_A_RDIV + ((x) * 
0x20))
+#define AD9545_REF_X_PERIOD(x)                 (AD9545_REF_A_PERIOD + ((x) * 
0x20))
+#define AD9545_REF_X_OFFSET_LIMIT(x)           (AD9545_REF_A_OFFSET_LIMIT + 
((x) * 0x20))
+#define AD9545_REF_X_MONITOR_HYST(x)           (AD9545_REF_A_MONITOR_HYST + 
((x) * 0x20))
+
+#define AD9545_SOURCEX_PHASE_THRESH(x)         (AD9545_PHASE_LOCK_THRESH + 
((x) * 0x20))
+#define AD9545_SOURCEX_FREQ_THRESH(x)          (AD9545_FREQ_LOCK_THRESH + ((x) 
* 0x20))
+#define AD9545_NCOX_PHASE_THRESH(x)            
(AD9545_SOURCEX_PHASE_THRESH((x) + 4))
+#define AD9545_NCOX_FREQ_THRESH(x)             (AD9545_SOURCEX_FREQ_THRESH((x) 
+ 4))
+
+#define AD9545_APLLX_M_DIV(x)                  (AD9545_APLL0_M_DIV + ((x) * 
0x400))
+
+#define AD9545_Q0_DIV(x)                       (AD9545_Q0A_DIV + ((x) * 0x9))
+#define AD9545_Q1_DIV(x)                       (AD9545_Q1A_DIV + ((x) * 0x9))
+#define AD9545_QX_DIV(x) ({                                    \
+       typeof(x) x_ = (x);                                     \
+                                                               \
+       (x_ > 5) ? AD9545_Q1_DIV(x_ - 6) : AD9545_Q0_DIV(x_);   \
+})
+
+#define AD9545_Q0_PHASE(x)                     (AD9545_Q0A_PHASE + ((x) * 0x9))
+#define AD9545_Q1_PHASE(x)                     (AD9545_Q1A_PHASE + ((x) * 0x9))
+#define AD9545_QX_PHASE(x) ({                                          \
+       typeof(x) x_ = (x);                                             \
+                                                                       \
+       (x_ > 5) ? AD9545_Q1_PHASE(x_ - 6) : AD9545_Q0_PHASE(x_);       \
+})
+
+#define AD9545_Q0_PHASE_CONF(x)                        (AD9545_Q0A_PHASE_CONF 
+ ((x) * 0x9))
+#define AD9545_Q1_PHASE_CONF(x)                        (AD9545_Q1A_PHASE_CONF 
+ ((x) * 0x9))
+#define AD9545_QX_PHASE_CONF(x) ({                                             
\
+       typeof(x) x_ = (x);                                                     
\
+                                                                               
\
+       (x_ > 5) ? AD9545_Q1_PHASE_CONF(x_ - 6) : AD9545_Q0_PHASE_CONF(x_);     
\
+})
+
+#define AD9545_DPLLX_FTW(x)                    (AD9545_DPLL0_FTW + ((x) * 
0x400))
+#define AD9545_DPLLX_EN(x)                     (AD9545_DPLL0_EN + ((x) * 
0x400))
+#define AD9545_DPLLX_SOURCE(x)                 (AD9545_DPLL0_SOURCE + ((x) * 
0x400))
+#define AD9545_DPLLX_LOOP_BW(x)                        (AD9545_DPLL0_LOOP_BW + 
((x) * 0x400))
+#define AD9545_DPLLX_N_DIV(x)                  (AD9545_DPLL0_N_DIV + ((x) * 
0x400))
+#define AD9545_DPLLX_FRAC_DIV(x)               (AD9545_DPLL0_FRAC + ((x) * 
0x400))
+#define AD9545_DPLLX_MOD_DIV(x)                        (AD9545_DPLL0_MOD + 
((x) * 0x400))
+
+#define AD9545_DIV_OPS_Q0(x)                   (AD9545_DIV_OPS_Q0A + (x))
+#define AD9545_DIV_OPS_Q1(x)                   (AD9545_DIV_OPS_Q1A + (x))
+#define AD9545_DIV_OPS_QX(x) ({                                                
\
+       typeof(x) x_ = (x) / 2;                                         \
+                                                                       \
+       (x_ > 2) ? AD9545_DIV_OPS_Q1(x_ - 3) : AD9545_DIV_OPS_Q0(x_);   \
+})
+
+#define AD9545_PWR_CALIB_CHX(x)                        (AD9545_PWR_CALIB_CH0 + 
((x) * 0x100))
+#define AD9545_PLLX_STATUS(x)                  (AD9545_PLL0_STATUS + ((x) * 
0x100))
+
+#define AD9545_PROFILE_SEL_MODE_MSK            GENMASK(3, 2)
+#define AD9545_PROFILE_SEL_MODE(x)             
FIELD_PREP(AD9545_PROFILE_SEL_MODE_MSK, x)
+
+#define AD9545_NCOX_FREQ(x)                    (AD9545_NCO0_FREQ + ((x) * 
0x40))
+
+/* AD9545_PWR_CALIB_CHX bitfields */
+#define AD9545_PWR_DOWN_CH                     BIT(0)
+#define AD9545_CALIB_APLL                      BIT(1)
+
+/* AD9545_SYNC_CTRLX bitfields */
+#define AD9545_SYNC_CTRL_DPLL_REF_MSK          BIT(2)
+#define AD9545_SYNC_CTRL_MODE_MSK              GENMASK(1, 0)
+
+/* AD9545_QX_PHASE_CONF bitfields */
+#define AD9545_QX_HALF_DIV_MSK                 BIT(5)
+#define AD9545_QX_PHASE_32_MSK                 BIT(6)
+
+/* AD9545_DIV_OPS_QX bitfields */
+#define AD9545_DIV_OPS_MUTE_A_MSK              BIT(2)
+#define AD9545_DIV_OPS_MUTE_AA_MSK             BIT(3)
+
+/* AD9545_PLL_STATUS bitfields */
+#define AD9545_PLLX_LOCK(x, y)                 ((1 << (4 + (x))) & (y))
+
+#define AD9545_SYS_PLL_STABLE_MSK              GENMASK(1, 0)
+#define AD9545_SYS_PLL_STABLE(x)               (((x) & 
AD9545_SYS_PLL_STABLE_MSK) == 0x3)
+
+#define AD9545_APLL_LOCKED(x)                  ((x) & BIT(3))
+
+#define AD9545_SYS_CLK_STABILITY_MS    50
+
+#define AD9545_R_DIV_MAX               0x40000000
+#define AD9545_IN_MAX_TDC_FREQ_HZ      200000
+
+#define AD9545_APLL_M_DIV_MIN          14
+#define AD9545_APLL_M_DIV_MAX          255
+
+#define AD9545_DPLL_MAX_N              1073741823
+#define AD9545_DPLL_MAX_FRAC           116777215
+#define AD9545_DPLL_MAX_MOD            116777215
+
+#define AD9545_NCO_MAX_FREQ            65535
+
+static const unsigned int ad9545_apll_rate_ranges_hz[2][2] = {
+       {2400000000U, 3200000000U}, {3200000000U, 4000000000U}
+};
+
+static const unsigned int ad9545_apll_pfd_rate_ranges_hz[2] = {
+       162000000U, 300000000U
+};
+
+static const unsigned short ad9545_vco_calibration_op[][2] = {
+       {AD9545_CALIB_CLK, 0},
+       {AD9545_IO_UPDATE, AD9545_UPDATE_REGS},
+       {AD9545_CALIB_CLK, BIT(2)},
+       {AD9545_IO_UPDATE, AD9545_UPDATE_REGS},
+};
+
+static const u8 ad9545_tdc_source_mapping[] = {
+       0, 1, 2, 3, 8, 9,
+};
+
+static const u32 ad9545_hyst_scales_bp[] = {
+       0, 3125, 6250, 12500, 25000, 50000, 75000, 87500
+};
+
+static const u32 ad9545_out_source_ua[] = {
+       7500, 12500, 15000
+};
+
+static const char * const ad9545_ref_clk_names[] = {
+       "Ref-A", "Ref-AA", "Ref-B", "Ref-BB",
+};
+
+static const char * const ad9545_in_clk_names[] = {
+       "Ref-A-Div", "Ref-AA-Div", "Ref-B-Div", "Ref-BB-Div",
+};
+
+static const char * const ad9545_out_clk_names[] = {
+       "Q0A-div", "Q0AA-div", "Q0B-div", "Q0BB-div", "Q0C-div", "Q0CC-div", 
"Q1A-div", "Q1AA-div",
+       "Q1B-div", "Q1BB-div",
+};
+
+static const char * const ad9545_pll_clk_names[] = {
+       "PLL0", "PLL1",
+};
+
+static const char * const ad9545_aux_nco_clk_names[] = {
+       "AUX_NCO0", "AUX_NCO1",
+};
+
+enum ad9545_ref_mode {
+       AD9545_SINGLE_ENDED = 0,
+       AD9545_DIFFERENTIAL,
+};
+
+enum ad9545_single_ended_config {
+       AD9545_AC_COUPLED_IF = 0,
+       AD9545_DC_COUPLED_1V2,
+       AD9545_DC_COUPLED_1V8,
+       AD9545_IN_PULL_UP,
+};
+
+enum ad9545_diferential_config {
+       AD9545_AC_COUPLED = 0,
+       AD9545_DC_COUPLED,
+       AD9545_DC_COUPLED_LVDS,
+};
+
+enum ad9545_output_mode {
+       AD9545_SINGLE_DIV_DIF = 0,
+       AD9545_SINGLE_DIV,
+       AD9545_DUAL_DIV,
+};
+
+struct ad9545_out_clk {
+       struct ad9545_state             *st;
+       bool                            output_used;
+       bool                            source_current;
+       enum ad9545_output_mode         output_mode;
+       u32                             source_ua;
+       struct clk_hw                   hw;
+       unsigned int                    address;
+};
+
+struct ad9545_ppl_clk {
+       struct ad9545_state             *st;
+       bool                            pll_used;
+       unsigned int                    address;
+       unsigned int                    loop_bw;
+       struct clk_hw                   hw;
+       u8                              tdc_source;
+};
+
+struct ad9545_ref_in_clk {
+       struct clk_hw                   hw;
+       struct ad9545_state             *st;
+       u32                             r_div_ratio;
+       bool                            ref_used;
+       u32                             d_tol_ppb;
+       u8                              monitor_hyst_scale;
+       u32                             valid_t_ms;
+       struct clk                      *parent_clk;
+       unsigned int                    address;
+       enum ad9545_ref_mode            mode;
+       unsigned int                    freq_thresh_ps;
+       unsigned int                    phase_thresh_ps;
+       union {
+               enum ad9545_single_ended_config         s_conf;
+               enum ad9545_diferential_config          d_conf;
+       };
+};
+
+struct ad9545_aux_nco_clk {
+       struct clk_hw                   hw;
+       bool                            nco_used;
+       struct ad9545_state             *st;
+       unsigned int                    address;
+       unsigned int                    freq_thresh_ps;
+       unsigned int                    phase_thresh_ps;
+};
+
+struct ad9545_sys_clk {
+       bool                            sys_clk_freq_doubler;
+       bool                            sys_clk_crystal;
+       u32                             ref_freq_hz;
+       u32                             sys_freq_hz;
+};
+
+struct ad9545_state {
+       struct device                   *dev;
+       struct regmap                   *regmap;
+       struct ad9545_sys_clk           sys_clk;
+       struct ad9545_ppl_clk           
pll_clks[ARRAY_SIZE(ad9545_pll_clk_names)];
+       struct ad9545_ref_in_clk        
ref_in_clks[ARRAY_SIZE(ad9545_ref_clk_names)];
+       struct ad9545_out_clk           
out_clks[ARRAY_SIZE(ad9545_out_clk_names)];
+       struct ad9545_aux_nco_clk       
aux_nco_clks[ARRAY_SIZE(ad9545_aux_nco_clk_names)];
+       struct clk                      **clks[3];
+};
+
+#define to_ref_in_clk(_hw)     container_of(_hw, struct ad9545_ref_in_clk, hw)
+#define to_pll_clk(_hw)                container_of(_hw, struct 
ad9545_ppl_clk, hw)
+#define to_out_clk(_hw)                container_of(_hw, struct 
ad9545_out_clk, hw)
+#define to_nco_clk(_hw)                container_of(_hw, struct 
ad9545_aux_nco_clk, hw)
+
+static int ad9545_parse_dt_inputs(struct ad9545_state *st)
+{
+       struct fwnode_handle *fwnode;
+       struct fwnode_handle *child;
+       struct clk *clk;
+       bool prop_found;
+       int ref_ind;
+       u32 val;
+       int ret;
+       int i;
+
+       fwnode = dev_fwnode(st->dev);
+
+       prop_found = false;
+       fwnode_for_each_available_child_node(fwnode, child) {
+               if (!fwnode_property_present(child, "adi,r-divider-ratio"))
+                       continue;
+
+               ret = fwnode_property_read_u32(child, "reg", &ref_ind);
+               if (ret < 0) {
+                       dev_err(st->dev, "reg not specified in ref node.");
+                       return ret;
+               }
+
+               if (ref_ind > 3)
+                       return -EINVAL;
+
+               st->ref_in_clks[ref_ind].ref_used = true;
+               st->ref_in_clks[ref_ind].address = ref_ind;
+               st->ref_in_clks[ref_ind].st = st;
+
+               prop_found = fwnode_property_present(child, 
"adi,single-ended-mode");
+               if (prop_found) {
+                       st->ref_in_clks[ref_ind].mode = AD9545_SINGLE_ENDED;
+                       ret = fwnode_property_read_u32(child, 
"adi,single-ended-mode", &val);
+                       if (ret < 0)
+                               return ret;
+
+                       st->ref_in_clks[ref_ind].s_conf = val;
+               } else {
+                       st->ref_in_clks[ref_ind].mode = AD9545_DIFFERENTIAL;
+                       ret = fwnode_property_read_u32(child, 
"adi,differential-mode", &val);
+                       if (ret < 0)
+                               return ret;
+
+                       st->ref_in_clks[ref_ind].d_conf = val;
+               }
+
+               ret = fwnode_property_read_u32(child, "adi,r-divider-ratio", 
&val);
+               if (!ret)
+                       st->ref_in_clks[ref_ind].r_div_ratio = val;
+
+               ret = fwnode_property_read_u32(child, "adi,ref-dtol-pbb", &val);
+               if (ret < 0)
+                       return ret;
+
+               st->ref_in_clks[ref_ind].d_tol_ppb = val;
+
+               ret = fwnode_property_read_u32(child, 
"adi,ref-monitor-hysteresis-pbb", &val);
+               if (ret < 0)
+                       return ret;
+
+               for (i = 0; i < ARRAY_SIZE(ad9545_hyst_scales_bp); i++) {
+                       if (ad9545_hyst_scales_bp[i] == val) {
+                               st->ref_in_clks[ref_ind].monitor_hyst_scale = i;
+                               break;
+                       }
+               }
+
+               if (i == ARRAY_SIZE(ad9545_hyst_scales_bp))
+                       return -EINVAL;
+
+               ret = fwnode_property_read_u32(child, 
"adi,ref-validation-timer-ms", &val);
+               if (ret < 0)
+                       return ret;
+
+               st->ref_in_clks[ref_ind].valid_t_ms = val;
+
+               ret = fwnode_property_read_u32(child, 
"adi,freq-lock-threshold-ps", &val);
+               if (ret < 0)
+                       return ret;
+
+               st->ref_in_clks[ref_ind].freq_thresh_ps = val;
+
+               ret = fwnode_property_read_u32(child, 
"adi,phase-lock-threshold-ps", &val);
+               if (ret < 0)
+                       return ret;
+
+               st->ref_in_clks[ref_ind].phase_thresh_ps = val;
+
+               clk = devm_clk_get(st->dev, ad9545_ref_clk_names[ref_ind]);
+               if (IS_ERR(clk))
+                       return PTR_ERR(clk);
+
+               st->ref_in_clks[ref_ind].parent_clk = clk;
+       }
+
+       return 0;
+}
+
+static int ad9545_parse_dt_plls(struct ad9545_state *st)
+{
+       struct fwnode_handle *fwnode;
+       struct fwnode_handle *child;
+       bool prop_found;
+       u32 val;
+       u32 addr;
+       int ret;
+
+       fwnode = dev_fwnode(st->dev);
+
+       prop_found = false;
+       fwnode_for_each_available_child_node(fwnode, child) {
+               if (!fwnode_property_present(child, "adi,pll-source"))
+                       continue;
+
+               ret = fwnode_property_read_u32(child, "reg", &addr);
+               if (ret < 0)
+                       return ret;
+
+               if (addr > 1)
+                       return -EINVAL;
+
+               st->pll_clks[addr].pll_used = true;
+               st->pll_clks[addr].address = addr;
+
+               ret = fwnode_property_read_u32(child, "adi,pll-source", &val);
+               if (ret < 0)
+                       return ret;
+
+               if (val > 5)
+                       return -EINVAL;
+
+               st->pll_clks[addr].tdc_source = val;
+
+               ret = fwnode_property_read_u32(child, 
"adi,pll-loop-bandwidth-hz", &val);
+               if (ret < 0)
+                       return ret;
+
+               st->pll_clks[addr].loop_bw = val;
+       }
+
+       return 0;
+}
+
+static int ad9545_parse_dt_outputs(struct ad9545_state *st)
+{
+       struct fwnode_handle *fwnode;
+       struct fwnode_handle *child;
+       bool prop_found;
+       int ref_ind;
+       u32 val;
+       int ret;
+
+       fwnode = dev_fwnode(st->dev);
+
+       prop_found = false;
+       fwnode_for_each_available_child_node(fwnode, child) {
+               if (!fwnode_property_present(child, "adi,output-mode"))
+                       continue;
+
+               ret = fwnode_property_read_u32(child, "reg", &ref_ind);
+               if (ret < 0)
+                       return ret;
+
+               if (ref_ind > 9)
+                       return -EINVAL;
+
+               st->out_clks[ref_ind].output_used = true;
+               st->out_clks[ref_ind].address = ref_ind;
+
+               if (fwnode_property_present(child, "adi,current-source"))
+                       st->out_clks[ref_ind].source_current = true;
+
+               ret = fwnode_property_read_u32(child, 
"adi,current-source-microamp", &val);
+               if (ret < 0)
+                       return ret;
+
+               st->out_clks[ref_ind].source_ua = val;
+
+               ret = fwnode_property_read_u32(child, "adi,output-mode", &val);
+               if (ret < 0)
+                       return ret;
+
+               st->out_clks[ref_ind].output_mode = val;
+       }
+
+       return 0;
+}
+
+static int ad9545_parse_dt_ncos(struct ad9545_state *st)
+{
+       struct fwnode_handle *fwnode;
+       struct fwnode_handle *child;
+       bool prop_found;
+       u32 val;
+       u32 addr;
+       int ret;
+
+       fwnode = dev_fwnode(st->dev);
+
+       prop_found = false;
+       fwnode_for_each_available_child_node(fwnode, child) {
+               if (!fwnode_property_present(child, 
"adi,freq-lock-threshold-ps") ||
+                   fwnode_property_present(child, "adi,ref-dtol-pbb"))
+                       continue;
+
+               ret = fwnode_property_read_u32(child, "reg", &addr);
+               if (ret < 0)
+                       return ret;
+
+               if (addr > 1)
+                       return -EINVAL;
+
+               st->aux_nco_clks[addr].nco_used = true;
+               st->aux_nco_clks[addr].address = addr;
+               st->aux_nco_clks[addr].st = st;
+
+               ret = fwnode_property_read_u32(child, 
"adi,freq-lock-threshold-ps", &val);
+               if (ret < 0)
+                       return ret;
+
+               st->aux_nco_clks[addr].freq_thresh_ps = val;
+
+               ret = fwnode_property_read_u32(child, 
"adi,phase-lock-threshold-ps", &val);
+               if (ret < 0)
+                       return ret;
+
+               st->aux_nco_clks[addr].phase_thresh_ps = val;
+       }
+
+       return 0;
+}
+
+static int ad9545_parse_dt(struct ad9545_state *st)
+{
+       struct fwnode_handle *fwnode;
+       int ret;
+
+       fwnode = dev_fwnode(st->dev);
+
+       ret = fwnode_property_read_u32(fwnode, "adi,ref-frequency-hz", 
&st->sys_clk.ref_freq_hz);
+       if (ret < 0)
+               return ret;
+
+       st->sys_clk.sys_clk_crystal = fwnode_property_present(fwnode, 
"adi,ref-crystal");
+       st->sys_clk.sys_clk_freq_doubler = fwnode_property_present(fwnode, 
"adi,freq-doubler");
+
+       ret = ad9545_parse_dt_inputs(st);
+       if (ret < 0)
+               return ret;
+
+       ret = ad9545_parse_dt_plls(st);
+       if (ret < 0)
+               return ret;
+
+       ret = ad9545_parse_dt_outputs(st);
+       if (ret < 0)
+               return ret;
+
+       ret = ad9545_parse_dt_ncos(st);
+       if (ret < 0)
+               return ret;
+
+       return 0;
+}
+
+static int ad9545_check_id(struct ad9545_state *st)
+{
+       u32 chip_id;
+       u32 val;
+       int ret;
+
+       ret = regmap_read(st->regmap, AD9545_PRODUCT_ID_LOW, &val);
+       if (ret < 0)
+               return ret;
+
+       chip_id = val;
+       ret = regmap_read(st->regmap, AD9545_PRODUCT_ID_HIGH, &val);
+       if (ret < 0)
+               return ret;
+
+       chip_id += val << 8;
+       if (chip_id != AD9545_CHIP_ID) {
+               dev_err(st->dev, "Unrecognized CHIP_ID 0x%X\n", chip_id);
+               return -ENODEV;
+       }
+
+       return 0;
+}
+
+static int ad9545_io_update(struct ad9545_state *st)
+{
+       return regmap_write(st->regmap, AD9545_IO_UPDATE, AD9545_UPDATE_REGS);
+}
+
+static int ad9545_sys_clk_setup(struct ad9545_state *st)
+{
+       u64 ref_freq_milihz;
+       __le64 regval64;
+       u8 div_ratio;
+       u32 fosc;
+       int ret;
+       u8 val;
+       u32 fs;
+       int i;
+
+       /*
+        * System frequency must be between 2250 MHz and 2415 MHz.
+        * fs = fosc * K / j
+        * K - feedback divider ratio [4, 255]
+        * j = 1/2 if frequency doubler is enabled
+        */
+       fosc = DIV_ROUND_UP(st->sys_clk.ref_freq_hz, 1000000);
+
+       if (st->sys_clk.sys_clk_freq_doubler)
+               fosc *= 2;
+
+       div_ratio = 0;
+       for (i = 4; i < 256; i++) {
+               fs = i * fosc;
+
+               if (fs > 2250 && fs < 2415) {
+                       div_ratio = i;
+                       break;
+               }
+       }
+
+       if (!div_ratio) {
+               dev_err(st->dev, "No feedback divider ratio for sys clk PLL 
found.\n");
+               return -EINVAL;
+       }
+
+       st->sys_clk.sys_freq_hz = st->sys_clk.ref_freq_hz * div_ratio;
+       if (st->sys_clk.sys_clk_freq_doubler)
+               st->sys_clk.sys_freq_hz *= 2;
+
+       ret = regmap_write(st->regmap, AD9545_SYS_CLK_FB_DIV, div_ratio);
+       if (ret < 0)
+               return ret;
+
+       /* enable crystal maintaining amplifier */
+       val = 0;
+       if (st->sys_clk.sys_clk_crystal)
+               val |= BIT(3);
+
+       if (st->sys_clk.sys_clk_freq_doubler)
+               val |= BIT(0);
+
+       ret = regmap_write(st->regmap, AD9545_SYS_CLK_INPUT, val);
+       if (ret < 0)
+               return ret;
+
+       /* write reference frequency provided at XOA, XOB in milliherz */
+       ref_freq_milihz = mul_u32_u32(st->sys_clk.ref_freq_hz, 1000);
+       regval64 = cpu_to_le64(ref_freq_milihz);
+
+       ret = regmap_bulk_write(st->regmap, AD9545_SYS_CLK_REF_FREQ, &regval64, 
5);
+       if (ret < 0)
+               return ret;
+
+       return regmap_write(st->regmap, AD9545_SYS_STABILITY_T, 
AD9545_SYS_CLK_STABILITY_MS);
+}
+
+static int ad9545_get_q_div(struct ad9545_state *st, int addr, u32 *q_div)
+{
+       __le32 regval;
+       int ret;
+
+       ret = regmap_bulk_read(st->regmap, AD9545_QX_DIV(addr), &regval, 4);
+       if (ret < 0)
+               return ret;
+
+       *q_div = le32_to_cpu(regval);
+
+       return 0;
+}
+
+static int ad9545_set_q_div(struct ad9545_state *st, int addr, u32 q_div)
+{
+       __le32 regval;
+       int ret;
+
+       regval = cpu_to_le32(q_div);
+       ret = regmap_bulk_write(st->regmap, AD9545_QX_DIV(addr), &regval, 4);
+       if (ret < 0)
+               return ret;
+
+       return ad9545_io_update(st);
+}
+
+static unsigned long ad9545_out_clk_recalc_rate(struct clk_hw *hw, unsigned 
long parent_rate)
+{
+       struct ad9545_out_clk *clk = to_out_clk(hw);
+       u32 qdiv;
+       int ret;
+
+       ret = ad9545_get_q_div(clk->st, clk->address, &qdiv);
+       if (ret < 0) {
+               dev_err(clk->st->dev, "Could not read Q div value.");
+               return 0;
+       }
+
+       return div_u64(parent_rate, qdiv);
+}
+
+static long ad9545_out_clk_round_rate(struct clk_hw *hw, unsigned long rate,
+                                     unsigned long *parent_rate)
+{
+       u32 out_rate;
+       u32 qdiv;
+
+       qdiv = div64_u64(*parent_rate, rate);
+       if (!qdiv)
+               out_rate = *parent_rate;
+       else
+               out_rate = div_u64(*parent_rate, qdiv);
+
+       return out_rate;
+}
+
+static int ad9545_out_clk_set_rate(struct clk_hw *hw, unsigned long rate, 
unsigned long parent_rate)
+{
+       struct ad9545_out_clk *clk = to_out_clk(hw);
+       u32 qdiv = div64_u64(parent_rate, rate);
+
+       if (!qdiv)
+               qdiv = 1;
+
+       return ad9545_set_q_div(clk->st, clk->address, qdiv);
+}
+
+static int ad9545_out_clk_set_phase(struct clk_hw *hw, int degrees)
+{
+       struct ad9545_out_clk *clk = to_out_clk(hw);
+       u64 phase_code;
+       u32 phase_conf;
+       __le64 regval;
+       u32 half_div;
+       u32 qdiv;
+       int ret;
+
+       ret = ad9545_get_q_div(clk->st, clk->address, &qdiv);
+       if (ret < 0)
+               return ret;
+
+       ret = regmap_read(clk->st->regmap, AD9545_QX_PHASE_CONF(clk->address), 
&phase_conf);
+       if (ret < 0)
+               return ret;
+
+       half_div = !!(phase_conf & AD9545_QX_HALF_DIV_MSK);
+
+       /* Qxy phase bitfield depends on the current Q div value */
+       phase_code = qdiv;
+       phase_code = div_u64((phase_code * 2 + half_div) * degrees, 360);
+
+       /* Qxy phase bitfield is 33 bits long, with last bit in PHASE_CONF reg 
*/
+       regval = cpu_to_le64(phase_code & 0xFFFFFFFF);
+       ret = regmap_bulk_write(clk->st->regmap, AD9545_QX_PHASE(clk->address), 
&regval, 4);
+       if (ret < 0)
+               return ret;
+
+       if (phase_code > U32_MAX) {
+               ret = regmap_update_bits(clk->st->regmap, 
AD9545_QX_PHASE_CONF(clk->address),
+                                        AD9545_QX_PHASE_32_MSK, 
AD9545_QX_PHASE_32_MSK);
+               if (ret < 0)
+                       return ret;
+       }
+
+       return ad9545_io_update(clk->st);
+}
+
+static int ad9545_out_clk_get_phase(struct clk_hw *hw)
+{
+       struct ad9545_out_clk *clk = to_out_clk(hw);
+       u64 input_edges_nr;
+       u64 phase_code;
+       __le32 regval;
+       u32 phase_conf;
+       u32 qdiv;
+       int ret;
+
+       ret = ad9545_get_q_div(clk->st, clk->address, &qdiv);
+       if (ret < 0)
+               return ret;
+
+       ret = regmap_read(clk->st->regmap, AD9545_QX_PHASE_CONF(clk->address), 
&phase_conf);
+       if (ret < 0)
+               return ret;
+
+       ret = regmap_bulk_read(clk->st->regmap, AD9545_QX_PHASE(clk->address), 
&regval, 4);
+       if (ret < 0)
+               return ret;
+
+       /* Qxy phase bitfield is 33 bits long, with last bit in PHASE_CONF reg 
*/
+       phase_code = !!(phase_conf & AD9545_QX_PHASE_32_MSK);
+       phase_code = (phase_code >> 32) + cpu_to_le32(regval);
+
+       input_edges_nr = 2 * qdiv + !!(phase_conf & AD9545_QX_HALF_DIV_MSK);
+
+       /*
+        * phase = 360 * (Qxy Phase / E) where:
+        * E is the total number of input edges per output period of the 
Q-divider.
+        */
+       return div64_u64(phase_code * 360, input_edges_nr);
+}
+
+static int ad9545_output_muting(struct ad9545_out_clk *clk, bool mute)
+{
+       u8 regval = 0;
+       int ret;
+       u8 mask;
+
+       if (clk->address % 2)
+               mask = AD9545_DIV_OPS_MUTE_AA_MSK;
+       else
+               mask = AD9545_DIV_OPS_MUTE_A_MSK;
+
+       if (mute)
+               regval = mask;
+
+       ret = regmap_update_bits(clk->st->regmap, 
AD9545_DIV_OPS_QX(clk->address), mask, regval);
+       if (ret < 0)
+               return ret;
+
+       return ad9545_io_update(clk->st);
+}
+
+static int ad9545_out_clk_enable(struct clk_hw *hw)
+{
+       struct ad9545_out_clk *clk = to_out_clk(hw);
+
+       return ad9545_output_muting(clk, false);
+}
+
+static void ad9545_out_clk_disable(struct clk_hw *hw)
+{
+       struct ad9545_out_clk *clk = to_out_clk(hw);
+
+       ad9545_output_muting(clk, true);
+}
+
+static int ad9545_out_clk_is_enabled(struct clk_hw *hw)
+{
+       struct ad9545_out_clk *clk = to_out_clk(hw);
+       u32 regval;
+       int ret;
+       u8 mask;
+
+       if (clk->address % 2)
+               mask = AD9545_DIV_OPS_MUTE_AA_MSK;
+       else
+               mask = AD9545_DIV_OPS_MUTE_A_MSK;
+
+       ret = regmap_read(clk->st->regmap, AD9545_DIV_OPS_QX(clk->address), 
&regval);
+       if (ret < 0)
+               return ret;
+
+       return !!(mask & regval);
+}
+
+static const struct clk_ops ad9545_out_clk_ops = {
+       .enable = ad9545_out_clk_enable,
+       .disable = ad9545_out_clk_disable,
+       .is_enabled = ad9545_out_clk_is_enabled,
+       .recalc_rate = ad9545_out_clk_recalc_rate,
+       .round_rate = ad9545_out_clk_round_rate,
+       .set_rate = ad9545_out_clk_set_rate,
+       .set_phase = ad9545_out_clk_set_phase,
+       .get_phase = ad9545_out_clk_get_phase,
+};
+
+static int ad9545_outputs_setup(struct ad9545_state *st)
+{
+       struct clk_init_data init[ARRAY_SIZE(ad9545_out_clk_names)] = {0};
+       int out_i;
+       u16 addr;
+       int ret;
+       u8 reg;
+       int i;
+       int j;
+
+       /* configure current sources */
+       for (i = 0; i < ARRAY_SIZE(ad9545_out_clk_names) / 2; i++) {
+               st->out_clks[i * 2].st = st;
+               st->out_clks[i * 2 + 1].st = st;
+
+               if (st->out_clks[i * 2].output_used)
+                       out_i = i * 2;
+               else if (st->out_clks[i * 2 + 1].output_used)
+                       out_i = i * 2 + 1;
+               else
+                       continue;
+
+               reg = 0;
+               if (st->out_clks[out_i].source_current)
+                       reg = 1;
+
+               for (j = 0; j < ARRAY_SIZE(ad9545_out_source_ua); j++)
+                       if (ad9545_out_source_ua[j] == 
st->out_clks[out_i].source_ua)
+                               reg |= FIELD_PREP(GENMASK(2, 1), i);
+
+               reg |= FIELD_PREP(GENMASK(4, 3), 
st->out_clks[out_i].output_mode);
+
+               if (i < 3)
+                       addr = AD9545_DRIVER_0A_CONF + i;
+               else
+                       addr = AD9545_DRIVER_1A_CONF + (i - 3);
+
+               ret = regmap_write(st->regmap, addr, reg);
+               if (ret < 0)
+                       return ret;
+       }
+
+       st->clks[AD9545_CLK_OUT] = devm_kzalloc(st->dev, 
ARRAY_SIZE(ad9545_out_clk_names) *
+                                               sizeof(struct clk *), 
GFP_KERNEL);
+       if (!st->clks[AD9545_CLK_OUT])
+               return -ENOMEM;
+
+       for (i = 0; i < ARRAY_SIZE(ad9545_out_clk_names); i++) {
+               if (!st->out_clks[i].output_used)
+                       continue;
+
+               init[i].name = ad9545_out_clk_names[i];
+               init[i].ops = &ad9545_out_clk_ops;
+
+               if (i > 5)
+                       init[i].parent_names = &ad9545_pll_clk_names[1];
+               else
+                       init[i].parent_names = &ad9545_pll_clk_names[0];
+
+               init[i].num_parents = 1;
+
+               st->out_clks[i].hw.init = &init[i];
+               ret = devm_clk_hw_register(st->dev, &st->out_clks[i].hw);
+               if (ret < 0)
+                       return ret;
+
+               st->clks[AD9545_CLK_OUT][i] = st->out_clks[i].hw.clk;
+       }
+
+       /* set to autosync to trigger on DPLL freq lock */
+       for (i = 0; i < ARRAY_SIZE(st->pll_clks); i++) {
+               reg = FIELD_PREP(AD9545_SYNC_CTRL_MODE_MSK, 3);
+               ret = regmap_write(st->regmap, AD9545_SYNC_CTRLX(i), reg);
+               if (ret < 0)
+                       return ret;
+       }
+
+       return ad9545_io_update(st);
+}
+
+static int ad9545_set_r_div(struct ad9545_state *st, u32 div, int addr)
+{
+       int ret;
+       u8 reg;
+       int i;
+
+       if (div > AD9545_R_DIV_MAX)
+               return -EINVAL;
+
+       /* r-div ratios are mapped from 0 onward */
+       div -= 1;
+       for (i = 0; i < 4; i++) {
+               reg = (div >> (i * 8)) && 0xFF;
+
+               ret = regmap_write(st->regmap, AD9545_REF_X_RDIV(addr) + i, 
reg);
+               if (ret < 0)
+                       return ret;
+       }
+
+       return ad9545_io_update(st);
+}
+
+static int ad9545_get_r_div(struct ad9545_state *st, int addr, u32 *r_div)
+{
+       int ret;
+       u32 div;
+       u32 reg;
+       int i;
+
+       div = 0;
+       for (i = 0; i < 4; i++) {
+               ret = regmap_read(st->regmap, AD9545_REF_X_RDIV(addr) + i, 
&reg);
+               if (ret < 0)
+                       return ret;
+
+               div += (reg << (i * 8));
+       }
+
+       /* r-div ratios are mapped from 0 onward */
+       *r_div = ++div;
+
+       return 0;
+}
+
+static unsigned long ad9545_in_clk_recalc_rate(struct clk_hw *hw, unsigned 
long parent_rate)
+{
+       struct ad9545_ref_in_clk *clk = to_ref_in_clk(hw);
+       u32 div;
+       int ret;
+
+       ret = ad9545_get_r_div(clk->st, clk->address, &div);
+       if (ret < 0) {
+               dev_err(clk->st->dev, "Could not read r div value.");
+               return 1;
+       }
+
+       return DIV_ROUND_CLOSEST(parent_rate, div);
+}
+
+static const struct clk_ops ad9545_in_clk_ops = {
+       .recalc_rate = ad9545_in_clk_recalc_rate,
+};
+
+static int ad9545_input_refs_setup(struct ad9545_state *st)
+{
+       struct clk_init_data init[4] = {0};
+       __le32 regval;
+       __le64 regval64;
+       u64 period_es;
+       int ret;
+       u32 val;
+       u8 reg;
+       int i;
+
+       /* configure input references */
+       for (i = 0; i < ARRAY_SIZE(st->ref_in_clks); i += 2) {
+               if (st->ref_in_clks[i].mode == AD9545_DIFFERENTIAL) {
+                       reg = BIT(0);
+                       reg |= FIELD_PREP(AD9545_REF_CTRL_DIF_MSK, 
st->ref_in_clks[i].d_conf);
+               } else {
+                       reg = 0;
+                       reg |= FIELD_PREP(AD9545_REF_CTRL_REFA_MSK, 
st->ref_in_clks[i].s_conf);
+                       reg |= FIELD_PREP(AD9545_REF_CTRL_REFAA_MSK, 
st->ref_in_clks[i + 1].s_conf);
+               }
+
+               ret = regmap_write(st->regmap, AD9545_REF_A_CTRL + i * 2, reg);
+               if (ret < 0)
+                       return ret;
+       }
+
+       /* configure refs r dividers */
+       for (i = 0; i < ARRAY_SIZE(st->ref_in_clks); i++) {
+               ret = ad9545_set_r_div(st, st->ref_in_clks[i].r_div_ratio, i);
+               if (ret < 0)
+                       return ret;
+       }
+
+       for (i = 0; i < ARRAY_SIZE(st->ref_in_clks); i++) {
+               if (!st->ref_in_clks[i].ref_used)
+                       continue;
+
+               /* write nominal period in attoseconds */
+               period_es = 1000000000000000000ULL;
+               val = clk_get_rate(st->ref_in_clks[i].parent_clk);
+               if (!val)
+                       return -EINVAL;
+
+               period_es = div_u64(period_es, val);
+
+               regval = cpu_to_le32(st->ref_in_clks[i].d_tol_ppb);
+               ret = regmap_bulk_write(st->regmap, 
AD9545_REF_X_OFFSET_LIMIT(i),
+                                       &regval, 3);
+               if (ret < 0)
+                       return ret;
+
+               regval64 = cpu_to_le64(period_es);
+               ret = regmap_bulk_write(st->regmap, AD9545_REF_X_PERIOD(i), 
&regval64, 8);
+               if (ret < 0)
+                       return ret;
+
+               ret = regmap_write(st->regmap, AD9545_REF_X_MONITOR_HYST(i),
+                                  st->ref_in_clks[i].monitor_hyst_scale);
+               if (ret < 0)
+                       return ret;
+
+               regval = cpu_to_le32(st->ref_in_clks[i].freq_thresh_ps);
+               ret = regmap_bulk_write(st->regmap, 
AD9545_SOURCEX_FREQ_THRESH(i),
+                                       &regval, 3);
+               if (ret < 0)
+                       return ret;
+
+               regval = cpu_to_le32(st->ref_in_clks[i].phase_thresh_ps);
+               ret = regmap_bulk_write(st->regmap, 
AD9545_SOURCEX_PHASE_THRESH(i),
+                                       &regval, 3);
+               if (ret < 0)
+                       return ret;
+
+               init[i].name = ad9545_in_clk_names[i];
+               init[i].ops = &ad9545_in_clk_ops;
+               init[i].parent_names = &ad9545_ref_clk_names[i];
+               init[i].num_parents = 1;
+
+               st->ref_in_clks[i].hw.init = &init[i];
+               ret = devm_clk_hw_register(st->dev, &st->ref_in_clks[i].hw);
+               if (ret < 0)
+                       return ret;
+       }
+
+       /* disable unused references */
+       reg = 0;
+       for (i = 0; i < ARRAY_SIZE(st->ref_in_clks); i++) {
+               if (!st->ref_in_clks[i].ref_used)
+                       reg |= (1 << i);
+       }
+
+       return regmap_write(st->regmap, AD9545_POWER_DOWN_REF, reg);
+}
+
+static int ad9545_set_freerun_freq(struct ad9545_ppl_clk *clk, u32 freq)
+{
+       __le64 regval;
+       u64 ftw = 1;
+       u32 ftw_frac;
+       u32 ftw_int;
+       int ret;
+
+       /*
+        * In case of unlock event the DPLL will go in open-loop mode and output
+        * the freq given by the freerun tuning word.
+        * DPLLx Freerun TW = (2 ^ 48) × (f NCO /f System )
+        */
+       ftw = mul_u64_u32_div(ftw << 48, freq, clk->st->sys_clk.sys_freq_hz);
+
+       /*
+        * Check if FTW is valid:
+        * (2 ^ 48) / FTW = INT.FRAC where:
+        * 7 ≤ INT ≤ 13 and 0.05 ≤ FRAC ≤ 0.95
+        */
+       ftw_int = div64_u64(1ULL << 48, ftw);
+       if (ftw_int < 7 || ftw_int > 13)
+               return -EINVAL;
+
+       div_u64_rem(div64_u64(100 * (1ULL << 48), ftw), 100, &ftw_frac);
+       if (ftw_frac < 5 || ftw_frac > 95)
+               return -EINVAL;
+
+       regval = cpu_to_le64(ftw);
+       ret = regmap_bulk_write(clk->st->regmap, 
AD9545_DPLLX_FTW(clk->address), &regval, 6);
+       if (ret < 0)
+               return ret;
+
+       return ad9545_io_update(clk->st);
+}
+
+static u64 ad9545_calc_pll_params(struct ad9545_ppl_clk *clk, unsigned long 
rate,
+                                 unsigned long parent_rate, u32 *m, u32 *n,
+                                 unsigned long *frac, unsigned long *mod)
+{
+       u32 min_dpll_n_div;
+       u64 output_rate;
+       u32 dpll_n_div;
+       u32 m_div;
+       u64 den;
+       u64 num;
+
+       /* half divider at output requires APLL to generate twice the frequency 
demanded */
+       rate *= 2;
+
+       /*
+        * PFD of APLL has input frequency limits in 162 - 350 Mghz range.
+        * Use APLL to upconvert this freq to Ghz range.
+        */
+       m_div = div_u64(rate, ad9545_apll_pfd_rate_ranges_hz[0] / 2 +
+                       ad9545_apll_pfd_rate_ranges_hz[1] / 2);
+       m_div = clamp_t(u8, m_div, AD9545_APLL_M_DIV_MIN, 
AD9545_APLL_M_DIV_MAX);
+
+       /*
+        * If N + FRAC / MOD = rate / (m_div * parent_rate)
+        * and N = [rate / (m_div * past_rate)]:
+        * We get: FRAC/MOD = (rate / (m_div * parent_rate)) - N
+        */
+       dpll_n_div = div64_u64(rate, parent_rate * m_div);
+
+       /*
+        * APLL has to be able to satisfy output freq bounds
+        * thus output of DPLL has a lower bound
+        */
+       min_dpll_n_div = div_u64(ad9545_apll_rate_ranges_hz[clk->address][0],
+                                AD9545_APLL_M_DIV_MAX * parent_rate);
+       dpll_n_div = clamp_t(u32, dpll_n_div, min_dpll_n_div, 
AD9545_DPLL_MAX_N);
+
+       num = rate - (dpll_n_div * m_div * parent_rate);
+       den = m_div * parent_rate;
+
+       rational_best_approximation(num, den, AD9545_DPLL_MAX_FRAC, 
AD9545_DPLL_MAX_MOD, frac, mod);
+       *m = m_div;
+       *n = dpll_n_div;
+
+       output_rate = mul_u64_u32_div(*frac * parent_rate, m_div, *mod);
+       output_rate += parent_rate * dpll_n_div * m_div;
+
+       return (u32)DIV_ROUND_CLOSEST(output_rate, 2);
+}
+
+static unsigned long ad9545_pll_clk_recalc_rate(struct clk_hw *hw, unsigned 
long parent_rate)
+{
+       struct ad9545_ppl_clk *clk = to_pll_clk(hw);
+       unsigned long output_rate;
+       __le32 regval;
+       u32 frac;
+       u32 mod;
+       int ret;
+       u32 m;
+       u32 n;
+
+       ret = regmap_bulk_read(clk->st->regmap, 
AD9545_DPLLX_N_DIV(clk->address), &regval, 4);
+       if (ret < 0)
+               return ret;
+
+       n = le32_to_cpu(regval) + 1;
+
+       m = 0;
+       ret = regmap_read(clk->st->regmap, AD9545_APLLX_M_DIV(clk->address), 
&m);
+       if (ret < 0)
+               return ret;
+
+       regval = 0;
+       ret = regmap_bulk_read(clk->st->regmap, 
AD9545_DPLLX_FRAC_DIV(clk->address), &regval, 3);
+       if (ret < 0)
+               return ret;
+
+       frac = le32_to_cpu(regval);
+
+       regval = 0;
+       ret = regmap_bulk_read(clk->st->regmap, 
AD9545_DPLLX_MOD_DIV(clk->address), &regval, 3);
+       if (ret < 0)
+               return ret;
+
+       mod = le32_to_cpu(regval);
+
+       /* Output rate of APLL = parent_rate * (N + (Frac / Mod)) * M */
+       output_rate = mul_u64_u32_div(frac * parent_rate, m, mod);
+       output_rate += parent_rate * n * m;
+
+       return output_rate / 2;
+}
+
+static long ad9545_pll_clk_round_rate(struct clk_hw *hw, unsigned long rate,
+                                     unsigned long *parent_rate)
+{
+       struct ad9545_ppl_clk *clk = to_pll_clk(hw);
+       unsigned long frac;
+       unsigned long mod;
+       u32 m;
+       u32 n;
+
+       return ad9545_calc_pll_params(clk, rate, *parent_rate, &m, &n, &frac, 
&mod);
+}
+
+static int ad9545_pll_set_rate(struct clk_hw *hw, unsigned long rate, unsigned 
long parent_rate)
+{
+       struct ad9545_ppl_clk *clk = to_pll_clk(hw);
+       unsigned long out_rate;
+       unsigned long frac;
+       unsigned long mod;
+       __le32 regval;
+       int ret;
+       u32 m;
+       u32 n;
+
+       out_rate = ad9545_calc_pll_params(clk, rate, parent_rate, &m, &n, 
&frac, &mod);
+       if (out_rate != rate)
+               return -EINVAL;
+
+       regval = cpu_to_le32(n - 1);
+       ret = regmap_bulk_write(clk->st->regmap, 
AD9545_DPLLX_N_DIV(clk->address), &regval, 4);
+       if (ret < 0)
+               return ret;
+
+       ret = regmap_write(clk->st->regmap, AD9545_APLLX_M_DIV(clk->address), 
m);
+       if (ret < 0)
+               return ret;
+
+       regval = cpu_to_le32(frac);
+       ret = regmap_bulk_write(clk->st->regmap, 
AD9545_DPLLX_FRAC_DIV(clk->address), &regval, 3);
+       if (ret < 0)
+               return ret;
+
+       regval = cpu_to_le32(mod);
+       ret = regmap_bulk_write(clk->st->regmap, 
AD9545_DPLLX_MOD_DIV(clk->address), &regval, 3);
+       if (ret < 0)
+               return ret;
+
+       return ad9545_set_freerun_freq(clk, div_u64(rate * 2, m));
+}
+
+static const struct clk_ops ad9545_pll_clk_ops = {
+       .recalc_rate = ad9545_pll_clk_recalc_rate,
+       .round_rate = ad9545_pll_clk_round_rate,
+       .set_rate = ad9545_pll_set_rate,
+};
+
+static int ad9545_plls_setup(struct ad9545_state *st)
+{
+       struct clk_init_data init[2] = {0};
+       struct ad9545_ppl_clk *pll;
+       __le32 regval;
+       int ret;
+       u8 reg;
+       int i;
+
+       st->clks[AD9545_CLK_PLL] = devm_kzalloc(st->dev, 
ARRAY_SIZE(ad9545_pll_clk_names) *
+                                               sizeof(struct clk *), 
GFP_KERNEL);
+       if (!st->clks[AD9545_CLK_PLL])
+               return -ENOMEM;
+
+       for (i = 0; i < 2; i++) {
+               pll = &st->pll_clks[i];
+               if (!pll->pll_used)
+                       continue;
+
+               /* enable pll profile */
+               ret = regmap_write(st->regmap, AD9545_DPLLX_EN(i), 1);
+               if (ret < 0)
+                       return ret;
+
+               /* set TDC source */
+               reg = ad9545_tdc_source_mapping[pll->tdc_source];
+               ret = regmap_write(st->regmap, AD9545_DPLLX_SOURCE(i), reg);
+               if (ret < 0)
+                       return ret;
+
+               /* write loop bandwidth in microhertz */
+               regval = cpu_to_le32(pll->loop_bw * 1000000);
+               ret = regmap_bulk_write(st->regmap, AD9545_DPLLX_LOOP_BW(i), 
&regval, 4);
+               if (ret < 0)
+                       return ret;
+
+               pll->st = st;
+               pll->address = i;
+
+               init[i].name = ad9545_pll_clk_names[i];
+               init[i].ops = &ad9545_pll_clk_ops;
+               if (pll->tdc_source > 3)
+                       init[i].parent_names = 
&ad9545_aux_nco_clk_names[pll->tdc_source - 4];
+               else
+                       init[i].parent_names = 
&ad9545_in_clk_names[pll->tdc_source];
+
+               init[i].num_parents = 1;
+
+               pll->hw.init = &init[i];
+               ret = devm_clk_hw_register(st->dev, &pll->hw);
+               if (ret < 0)
+                       return ret;
+
+               st->clks[AD9545_CLK_PLL][i] = pll->hw.clk;
+       }
+
+       return 0;
+}
+
+static int ad9545_get_nco_freq(struct ad9545_state *st, int addr, u32 *freq)
+{
+       __le16 regval;
+       int ret;
+
+       ret = regmap_bulk_read(st->regmap, AD9545_NCOX_FREQ(addr), &regval, 2);
+       if (ret < 0)
+               return ret;
+
+       *freq = le16_to_cpu(regval);
+       return 0;
+}
+
+static int ad9545_set_nco_freq(struct ad9545_state *st, int addr, u32 freq)
+{
+       __le32 regval;
+       int ret;
+
+       regval = cpu_to_le32(freq);
+       ret = regmap_bulk_write(st->regmap, AD9545_NCOX_FREQ(addr), &regval, 2);
+       if (ret < 0)
+               return ret;
+
+       return ad9545_io_update(st);
+}
+
+static unsigned long ad9545_nco_clk_recalc_rate(struct clk_hw *hw, unsigned 
long parent_rate)
+{
+       struct ad9545_aux_nco_clk *clk = to_nco_clk(hw);
+       u32 rate;
+       int ret;
+
+       ret = ad9545_get_nco_freq(clk->st, clk->address, &rate);
+       if (ret < 0) {
+               dev_err(clk->st->dev, "Could not read NCO freq.");
+               return 0;
+       }
+
+       return rate;
+}
+
+static long ad9545_nco_clk_round_rate(struct clk_hw *hw, unsigned long rate,
+                                     unsigned long *parent_rate)
+{
+       return clamp_t(u16, rate, 1, AD9545_NCO_MAX_FREQ);
+}
+
+static int ad9545_nco_clk_set_rate(struct clk_hw *hw, unsigned long rate, 
unsigned long parent_rate)
+{
+       struct ad9545_aux_nco_clk *clk = to_nco_clk(hw);
+
+       return ad9545_set_nco_freq(clk->st, clk->address, rate);
+}
+
+static const struct clk_ops ad9545_nco_clk_ops = {
+       .recalc_rate = ad9545_nco_clk_recalc_rate,
+       .round_rate = ad9545_nco_clk_round_rate,
+       .set_rate = ad9545_nco_clk_set_rate,
+};
+
+static int ad9545_aux_ncos_setup(struct ad9545_state *st)
+{
+       struct clk_init_data init[2] = {0};
+       struct ad9545_aux_nco_clk *nco;
+       __le32 regval;
+       int ret;
+       int i;
+
+       st->clks[AD9545_CLK_NCO] = devm_kzalloc(st->dev, 
ARRAY_SIZE(ad9545_aux_nco_clk_names) *
+                                               sizeof(struct clk *), 
GFP_KERNEL);
+       if (!st->clks[AD9545_CLK_NCO])
+               return -ENOMEM;
+
+       for (i = 0; i < ARRAY_SIZE(st->aux_nco_clks); i++) {
+               nco = &st->aux_nco_clks[i];
+               if (!nco->nco_used)
+                       continue;
+
+               regval = cpu_to_le32(nco->freq_thresh_ps);
+               ret = regmap_bulk_write(st->regmap, AD9545_NCOX_FREQ_THRESH(i), 
&regval, 3);
+               if (ret < 0)
+                       return ret;
+
+               regval = cpu_to_le32(nco->phase_thresh_ps);
+               ret = regmap_bulk_write(st->regmap, 
AD9545_NCOX_PHASE_THRESH(i), &regval, 3);
+               if (ret < 0)
+                       return ret;
+
+               init[i].name = ad9545_aux_nco_clk_names[i];
+               init[i].ops = &ad9545_nco_clk_ops;
+
+               nco->hw.init = &init[i];
+               ret = devm_clk_hw_register(st->dev, &nco->hw);
+               if (ret < 0)
+                       return ret;
+
+               st->clks[AD9545_CLK_NCO][i] = nco->hw.clk;
+       }
+
+       return 0;
+}
+
+static int ad9545_calib_system_clock(struct ad9545_state *st)
+{
+       int ret;
+       u32 reg;
+       int i;
+       int j;
+
+       for (i = 0; i < 2; i++) {
+               for (j = 0; j < ARRAY_SIZE(ad9545_vco_calibration_op); j++) {
+                       ret = regmap_write(st->regmap, 
ad9545_vco_calibration_op[j][0],
+                                          ad9545_vco_calibration_op[j][1]);
+                       if (ret < 0)
+                               return ret;
+               }
+
+               /* wait for sys pll to lock and become stable */
+               msleep(50 + AD9545_SYS_CLK_STABILITY_MS);
+
+               ret = regmap_read(st->regmap, AD9545_PLL_STATUS, &reg);
+               if (ret < 0)
+                       return ret;
+
+               if (AD9545_SYS_PLL_STABLE(reg)) {
+                       ret = regmap_write(st->regmap, AD9545_CALIB_CLK, 0);
+                       if (ret < 0)
+                               return ret;
+
+                       return ad9545_io_update(st);
+               }
+       }
+
+       dev_err(st->dev, "System PLL unlocked.\n");
+       return -EIO;
+}
+
+static int ad9545_calib_apll(struct ad9545_state *st, int i)
+{
+       int cal_count;
+       u32 reg;
+       int ret;
+
+       /* APLL VCO calibration operation */
+       cal_count = 0;
+       while (cal_count < 2) {
+               ret = regmap_write(st->regmap, AD9545_PWR_CALIB_CHX(i), 0);
+               if (ret < 0)
+                       return ret;
+
+               ret = ad9545_io_update(st);
+               if (ret < 0)
+                       return ret;
+
+               ret = regmap_write(st->regmap, AD9545_PWR_CALIB_CHX(i),
+                                  AD9545_CALIB_APLL);
+               if (ret < 0)
+                       return ret;
+
+               ret = ad9545_io_update(st);
+               if (ret < 0)
+                       return ret;
+
+               cal_count += 1;
+               msleep(100);
+
+               ret = regmap_read(st->regmap, AD9545_PLLX_STATUS(i), &reg);
+               if (ret < 0)
+                       return ret;
+
+               if (AD9545_APLL_LOCKED(reg)) {
+                       ret = regmap_write(st->regmap, AD9545_PWR_CALIB_CHX(i), 
0);
+                       if (ret < 0)
+                               return ret;
+
+                       ret = ad9545_io_update(st);
+                       if (ret < 0)
+                               return ret;
+
+                       cal_count = 2;
+                       break;
+               }
+       }
+
+       return ret;
+}
+
+static int ad9545_calib_aplls(struct ad9545_state *st)
+{
+       int ret;
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(ad9545_pll_clk_names); i++) {
+               if (!st->pll_clks[i].pll_used)
+                       continue;
+
+               ret = ad9545_calib_apll(st, i);
+               if (ret < 0)
+                       return ret;
+       }
+
+       return 0;
+}
+
+static struct clk *ad9545_clk_src_twocell_get(struct of_phandle_args *clkspec, 
void *data)
+{
+       unsigned int clk_address = clkspec->args[1];
+       unsigned int clk_type = clkspec->args[0];
+       struct clk ***clks = data;
+
+       if (clk_type > AD9545_CLK_NCO) {
+               pr_err("%s: invalid clock type %u\n", __func__, clk_type);
+               return ERR_PTR(-EINVAL);
+       }
+       if ((clk_type == AD9545_CLK_PLL && clk_address > AD9545_PLL1) ||
+           (clk_type == AD9545_CLK_OUT && clk_address > AD9545_Q1BB) ||
+           (clk_type == AD9545_CLK_NCO && clk_address > AD9545_NCO1)) {
+               pr_err("%s: invalid clock address %u\n", __func__, clk_address);
+               return ERR_PTR(-EINVAL);
+       }
+
+       return clks[clk_type][clk_address];
+}
+
+static int ad9545_setup(struct ad9545_state *st)
+{
+       int ret;
+       u32 val;
+       int i;
+
+       ret = regmap_update_bits(st->regmap, AD9545_CONFIG_0, 
AD9545_RESET_REGS, AD9545_RESET_REGS);
+       if (ret < 0)
+               return ret;
+
+       ret = ad9545_sys_clk_setup(st);
+       if (ret < 0)
+               return ret;
+
+       ret = ad9545_input_refs_setup(st);
+       if (ret < 0)
+               return ret;
+
+       ret = ad9545_aux_ncos_setup(st);
+       if (ret < 0)
+               return ret;
+
+       ret = ad9545_calib_system_clock(st);
+       if (ret < 0)
+               return ret;
+
+       ret = ad9545_calib_aplls(st);
+       if (ret < 0)
+               return ret;
+
+       ret = ad9545_io_update(st);
+       if (ret < 0)
+               return ret;
+
+       ret = ad9545_plls_setup(st);
+       if (ret < 0)
+               return ret;
+
+       ret = ad9545_outputs_setup(st);
+       if (ret < 0)
+               return ret;
+
+       ret = of_clk_add_provider(st->dev->of_node, ad9545_clk_src_twocell_get,
+                                 &st->clks[AD9545_CLK_OUT]);
+       if (ret < 0)
+               return ret;
+
+       ret = ad9545_calib_aplls(st);
+       if (ret < 0)
+               return ret;
+
+       /* check locks */
+       ret = regmap_read(st->regmap, AD9545_PLL_STATUS, &val);
+       for (i = 0; i < ARRAY_SIZE(st->pll_clks); i++)
+               if (st->pll_clks[i].pll_used && !AD9545_PLLX_LOCK(i, val))
+                       dev_warn(st->dev, "PLL%d unlocked.\n", i);
+
+       return 0;
+}
+
+int ad9545_probe(struct device *dev, struct regmap *regmap)
+{
+       struct ad9545_state *st;
+       int ret;
+
+       st = devm_kzalloc(dev, sizeof(struct ad9545_state), GFP_KERNEL);
+       if (!st)
+               return -ENOMEM;
+
+       st->dev = dev;
+       st->regmap = regmap;
+
+       ret = ad9545_check_id(st);
+       if (ret < 0)
+               return ret;
+
+       ret = ad9545_parse_dt(st);
+       if (ret < 0)
+               return ret;
+
+       return ad9545_setup(st);
+}
+EXPORT_SYMBOL_GPL(ad9545_probe);
+
+MODULE_AUTHOR("Alexandru Tachici <alexandru.tach...@analog.com>");
+MODULE_DESCRIPTION("Analog Devices AD9545");
+MODULE_LICENSE("Dual BSD/GPL");
diff --git a/drivers/clk/adi/clk-ad9545.h b/drivers/clk/adi/clk-ad9545.h
new file mode 100644
index 000000000000..641acabe9c8e
--- /dev/null
+++ b/drivers/clk/adi/clk-ad9545.h
@@ -0,0 +1,16 @@
+/* SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause */
+/*
+ * AD9545 Network Clock Generator/Synchronizer
+ *
+ * Copyright 2020 Analog Devices Inc.
+ */
+
+#ifndef _CLK_AD9545_H_
+#define _CLK_AD9545_H_
+
+struct device;
+struct regmap;
+
+int ad9545_probe(struct device *dev, struct regmap *regmap);
+
+#endif /* _CLK_AD9545_H_ */
diff --git a/include/dt-bindings/clock/ad9545.h 
b/include/dt-bindings/clock/ad9545.h
new file mode 100644
index 000000000000..8b45e1b45249
--- /dev/null
+++ b/include/dt-bindings/clock/ad9545.h
@@ -0,0 +1,64 @@
+/* SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause */
+/*
+ * AD9545 Network Clock Generator/Synchronizer
+ *
+ * Copyright 2020 Analog Devices Inc.
+ */
+
+#ifndef _DT_BINDINGS_CLOCK_AD9545_H_
+#define _DT_BINDINGS_CLOCK_AD9545_H_
+
+/* Input Driver Mode
+ * Use for adi,single-ended-mode:
+ */
+#define DRIVER_MODE_AC_COUPLED_IF      0
+#define DRIVER_MODE_DC_COUPLED_1V2     1
+#define DRIVER_MODE_DC_COUPLED_1V8     2
+#define DRIVER_MODE_IN_PULL_UP         3
+
+/* Input Driver Mode
+ * Use for adi,differential-mode:
+ */
+#define DRIVER_MODE_AC_COUPLED         0
+#define DRIVER_MODE_DC_COUPLED         1
+#define DRIVER_MODE_DC_COUPLED_LVDS    2
+
+/* Output Driver Mode
+ * Use for adi,output-mode:
+ */
+#define DRIVER_MODE_SINGLE_DIV_DIF     0
+#define DRIVER_MODE_SINGLE_DIV         1
+#define DRIVER_MODE_DUAL_DIV           2
+
+/* Clock types */
+#define AD9545_CLK_OUT                 0
+#define AD9545_CLK_PLL                 1
+#define AD9545_CLK_NCO                 2
+
+/* PLL addresses */
+#define AD9545_PLL0                    0
+#define AD9545_PLL1                    1
+
+/* Outputs addresses */
+#define AD9545_Q0A                     0
+#define AD9545_Q0AA                    1
+#define AD9545_Q0B                     2
+#define AD9545_Q0BB                    3
+#define AD9545_Q0C                     4
+#define AD9545_Q0CC                    5
+#define AD9545_Q1A                     6
+#define AD9545_Q1AA                    7
+#define AD9545_Q1B                     8
+#define AD9545_Q1BB                    9
+
+/* NCO addresses */
+#define AD9545_NCO0                    0
+#define AD9545_NCO1                    1
+
+/* Ex:
+ * Output Q0C clock: <&ad9545_clock AD9545_CLK_OUT AD9545_Q0C>;
+ * PLL0 clock: <&ad9545_clock AD9545_CLK_PLL AD9545_PLL0>;
+ * NCO1 clock: <&ad9545_clock AD9545_CLK_NCO AD9545_NCO1>;
+ */
+
+#endif /* _DT_BINDINGS_CLOCK_AD9545_H_ */
-- 
2.20.1

Reply via email to