diff mbox

[v8] clk: add si5351 i2c common clock driver

Message ID 1365709350-8344-1-git-send-email-sebastian.hesselbarth@gmail.com (mailing list archive)
State New, archived
Headers show

Commit Message

Sebastian Hesselbarth April 11, 2013, 7:42 p.m. UTC
This patch adds a common clock driver for Silicon Labs Si5351a/b/c
i2c programmable clock generators. Currently, the driver does not
support VXCO feature of si5351b. Passing platform_data or DT bindings
selectively allows to overwrite stored Si5351 configuration which is
very helpful for clock generators with empty eeprom configuration.
Corresponding device tree binding documentation is also added.

Signed-off-by: Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
Tested-by: Daniel Mack <zonque@gmail.com>
Acked-by: Guenter Roeck <linux@roeck-us.net>
---
Changes from v7:
- readd clk powerup on clkout set_rate (Suggested by Michal Bachraty)

Changes from v6:
- make invalid DT data parsing fatal (Suggested by Guenter Roeck)
- add more variant checks and return errors
- remove inline from _si5351_* functions
- remove pll reset/clkout powerdown for gapless tuning
  (Provided by Michal Backraty)

Changes from v5:
- removed __clk_set_flags
- remove CONFIG_OF dependency
- introduce si5351_platform_data (non-DT untested)
- parse DT into si5351_platform_data (tested)
- minor cleanups

Changes from v4:
- move from clk-private.h to clk-provider.h (Reported by Mike Turquette)
- use __clk_set_flags() helper

Changes from v3:
- add silabs prefix to custom DT properties (Reported by Lars-Peter Clausen)
- use sizeof(*foo) instead of sizeof(struct bar) (Reported by Lars-Peter Clausen)
- check return value of of_clk_add_provider (Reported by Lars-Peter Clausen)
- clean up i2c client init (Reported by Lars-Peter Clausen)
- silence successful probe (Suggested by Lars-Peter Clausen)
- make CONFIG_CLK_SI5351 depend on CONFIG_OF

Changes from v2:
- add curly brackets to if-else-statements (Reported by Daniel Mack)
- fix div-by-zero for clk6/clk7 (Reported by Daniel Mack)
- fix parameter address calculation for clk6/clk7

Changes from v1:
- remove .is_enabled functions as they read from i2c
  (Reported by Daniel Mack)
- add CLK_SET_RATE_PARENT on clkout reparent if clkout uses
  its own multisync

Cc: Grant Likely <grant.likely@secretlab.ca>
Cc: Rob Herring <rob.herring@calxeda.com>
Cc: Rob Landley <rob@landley.net>
Cc: Mike Turquette <mturquette@linaro.org>
Cc: Stephen Warren <swarren@nvidia.com>
Cc: Thierry Reding <thierry.reding@avionic-design.de>
Cc: Dom Cobley <popcornmix@gmail.com>
Cc: Linus Walleij <linus.walleij@linaro.org>
Cc: Arnd Bergmann <arnd@arndb.de>
Cc: Andrew Morton <akpm@linux-foundation.org>
Cc: Pawel Moll <pawel.moll@arm.com>
Cc: Mark Brown <broonie@opensource.wolfsonmicro.com>
Cc: Russell King - ARM Linux <linux@arm.linux.org.uk>
Cc: Rabeeh Khoury <rabeeh@solid-run.com>
Cc: Daniel Mack <zonque@gmail.com>
Cc: Jean-Francois Moine <moinejf@free.fr>
Cc: Lars-Peter Clausen <lars@metafoo.de>
Cc: Guenter Roeck <linux@roeck-us.net>
Cc: Michal Bachraty <michal.bachraty@streamunlimited.com>
Cc: devicetree-discuss@lists.ozlabs.org
Cc: linux-doc@vger.kernel.org
Cc: linux-kernel@vger.kernel.org
Cc: linux-arm-kernel@lists.infradead.org
---
 .../devicetree/bindings/clock/silabs,si5351.txt    |  114 ++
 .../devicetree/bindings/vendor-prefixes.txt        |    1 +
 drivers/clk/Kconfig                                |    9 +
 drivers/clk/Makefile                               |    1 +
 drivers/clk/clk-si5351.c                           | 1510 ++++++++++++++++++++
 drivers/clk/clk-si5351.h                           |  155 ++
 include/linux/platform_data/si5351.h               |  114 ++
 7 files changed, 1904 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/clock/silabs,si5351.txt
 create mode 100644 drivers/clk/clk-si5351.c
 create mode 100644 drivers/clk/clk-si5351.h
 create mode 100644 include/linux/platform_data/si5351.h

Comments

Michal Bachraty April 12, 2013, 11:43 a.m. UTC | #1
On Thursday, April 11, 2013 21:42:29 Sebastian Hesselbarth wrote:
> This patch adds a common clock driver for Silicon Labs Si5351a/b/c
> i2c programmable clock generators. Currently, the driver does not
> support VXCO feature of si5351b. Passing platform_data or DT bindings
> selectively allows to overwrite stored Si5351 configuration which is
> very helpful for clock generators with empty eeprom configuration.
> Corresponding device tree binding documentation is also added.

Tested-by: Michal Bachraty <michal.bachraty@streamunlimited.com>

Many thanks,

Michal

> Signed-off-by: Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
> Tested-by: Daniel Mack <zonque@gmail.com>
> Acked-by: Guenter Roeck <linux@roeck-us.net>
> ---
> Changes from v7:
> - readd clk powerup on clkout set_rate (Suggested by Michal Bachraty)
> 
> Changes from v6:
> - make invalid DT data parsing fatal (Suggested by Guenter Roeck)
> - add more variant checks and return errors
> - remove inline from _si5351_* functions
> - remove pll reset/clkout powerdown for gapless tuning
>   (Provided by Michal Backraty)
> 
> Changes from v5:
> - removed __clk_set_flags
> - remove CONFIG_OF dependency
> - introduce si5351_platform_data (non-DT untested)
> - parse DT into si5351_platform_data (tested)
> - minor cleanups
> 
> Changes from v4:
> - move from clk-private.h to clk-provider.h (Reported by Mike Turquette)
> - use __clk_set_flags() helper
> 
> Changes from v3:
> - add silabs prefix to custom DT properties (Reported by Lars-Peter Clausen)
> - use sizeof(*foo) instead of sizeof(struct bar) (Reported by Lars-Peter
> Clausen) - check return value of of_clk_add_provider (Reported by
> Lars-Peter Clausen) - clean up i2c client init (Reported by Lars-Peter
> Clausen)
> - silence successful probe (Suggested by Lars-Peter Clausen)
> - make CONFIG_CLK_SI5351 depend on CONFIG_OF
> 
> Changes from v2:
> - add curly brackets to if-else-statements (Reported by Daniel Mack)
> - fix div-by-zero for clk6/clk7 (Reported by Daniel Mack)
> - fix parameter address calculation for clk6/clk7
> 
> Changes from v1:
> - remove .is_enabled functions as they read from i2c
>   (Reported by Daniel Mack)
> - add CLK_SET_RATE_PARENT on clkout reparent if clkout uses
>   its own multisync
> 
> Cc: Grant Likely <grant.likely@secretlab.ca>
> Cc: Rob Herring <rob.herring@calxeda.com>
> Cc: Rob Landley <rob@landley.net>
> Cc: Mike Turquette <mturquette@linaro.org>
> Cc: Stephen Warren <swarren@nvidia.com>
> Cc: Thierry Reding <thierry.reding@avionic-design.de>
> Cc: Dom Cobley <popcornmix@gmail.com>
> Cc: Linus Walleij <linus.walleij@linaro.org>
> Cc: Arnd Bergmann <arnd@arndb.de>
> Cc: Andrew Morton <akpm@linux-foundation.org>
> Cc: Pawel Moll <pawel.moll@arm.com>
> Cc: Mark Brown <broonie@opensource.wolfsonmicro.com>
> Cc: Russell King - ARM Linux <linux@arm.linux.org.uk>
> Cc: Rabeeh Khoury <rabeeh@solid-run.com>
> Cc: Daniel Mack <zonque@gmail.com>
> Cc: Jean-Francois Moine <moinejf@free.fr>
> Cc: Lars-Peter Clausen <lars@metafoo.de>
> Cc: Guenter Roeck <linux@roeck-us.net>
> Cc: Michal Bachraty <michal.bachraty@streamunlimited.com>
> Cc: devicetree-discuss@lists.ozlabs.org
> Cc: linux-doc@vger.kernel.org
> Cc: linux-kernel@vger.kernel.org
> Cc: linux-arm-kernel@lists.infradead.org
> ---
>  .../devicetree/bindings/clock/silabs,si5351.txt    |  114 ++
>  .../devicetree/bindings/vendor-prefixes.txt        |    1 +
>  drivers/clk/Kconfig                                |    9 +
>  drivers/clk/Makefile                               |    1 +
>  drivers/clk/clk-si5351.c                           | 1510
> ++++++++++++++++++++ drivers/clk/clk-si5351.h                           | 
> 155 ++
>  include/linux/platform_data/si5351.h               |  114 ++
>  7 files changed, 1904 insertions(+)
>  create mode 100644
> Documentation/devicetree/bindings/clock/silabs,si5351.txt create mode
> 100644 drivers/clk/clk-si5351.c
>  create mode 100644 drivers/clk/clk-si5351.h
>  create mode 100644 include/linux/platform_data/si5351.h
> 
> diff --git a/Documentation/devicetree/bindings/clock/silabs,si5351.txt
> b/Documentation/devicetree/bindings/clock/silabs,si5351.txt new file mode
> 100644
> index 0000000..cc37465
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/clock/silabs,si5351.txt
> @@ -0,0 +1,114 @@
> +Binding for Silicon Labs Si5351a/b/c programmable i2c clock generator.
> +
> +Reference
> +[1] Si5351A/B/C Data Sheet
> +    http://www.silabs.com/Support%20Documents/TechnicalDocs/Si5351.pdf
> +
> +The Si5351a/b/c are programmable i2c clock generators with upto 8 output
> +clocks. Si5351a also has a reduced pin-count package (MSOP10) where only
> +3 output clocks are accessible. The internal structure of the clock
> +generators can be found in [1].
> +
> +==I2C device node==
> +
> +Required properties:
> +- compatible: shall be one of "silabs,si5351{a,a-msop,b,c}".
> +- reg: i2c device address, shall be 0x60 or 0x61.
> +- #clock-cells: from common clock binding; shall be set to 1.
> +- clocks: from common clock binding; list of parent clock
> +  handles, shall be xtal reference clock or xtal and clkin for
> +  si5351c only.
> +- #address-cells: shall be set to 1.
> +- #size-cells: shall be set to 0.
> +
> +Optional properties:
> +- silabs,pll-source: pair of (number, source) for each pll. Allows
> +  to overwrite clock source of pll A (number=0) or B (number=1).
> +
> +==Child nodes==
> +
> +Each of the clock outputs can be overwritten individually by
> +using a child node to the I2C device node. If a child node for a clock
> +output is not set, the eeprom configuration is not overwritten.
> +
> +Required child node properties:
> +- reg: number of clock output.
> +
> +Optional child node properties:
> +- silabs,clock-source: source clock of the output divider stage N, shall be
> +  0 = multisynth N
> +  1 = multisynth 0 for output clocks 0-3, else multisynth4
> +  2 = xtal
> +  3 = clkin (si5351c only)
> +- silabs,drive-strength: output drive strength in mA, shall be one of
> {2,4,6,8}. +- silabs,multisynth-source: source pll A(0) or B(1) of
> corresponding multisynth +  divider.
> +- silabs,pll-master: boolean, multisynth can change pll frequency.
> +
> +==Example==
> +
> +/* 25MHz reference crystal */
> +ref25: ref25M {
> +	compatible = "fixed-clock";
> +	#clock-cells = <0>;
> +	clock-frequency = <25000000>;
> +};
> +
> +i2c-master-node {
> +
> +	/* Si5351a msop10 i2c clock generator */
> +	si5351a: clock-generator@60 {
> +		compatible = "silabs,si5351a-msop";
> +		reg = <0x60>;
> +		#address-cells = <1>;
> +		#size-cells = <0>;
> +		#clock-cells = <1>;
> +
> +		/* connect xtal input to 25MHz reference */
> +		clocks = <&ref25>;
> +
> +		/* connect xtal input as source of pll0 and pll1 */
> +		silabs,pll-source = <0 0>, <1 0>;
> +
> +		/*
> +		 * overwrite clkout0 configuration with:
> +		 * - 8mA output drive strength
> +		 * - pll0 as clock source of multisynth0
> +		 * - multisynth0 as clock source of output divider
> +		 * - multisynth0 can change pll0
> +		 * - set initial clock frequency of 74.25MHz
> +		 */
> +		clkout0 {
> +			reg = <0>;
> +			silabs,drive-strength = <8>;
> +			silabs,multisynth-source = <0>;
> +			silabs,clock-source = <0>;
> +			silabs,pll-master;
> +			clock-frequency = <74250000>;
> +		};
> +
> +		/*
> +		 * overwrite clkout1 configuration with:
> +		 * - 4mA output drive strength
> +		 * - pll1 as clock source of multisynth1
> +		 * - multisynth1 as clock source of output divider
> +		 * - multisynth1 can change pll1
> +		 */
> +		clkout1 {
> +			reg = <1>;
> +			silabs,drive-strength = <4>;
> +			silabs,multisynth-source = <1>;
> +			silabs,clock-source = <0>;
> +			pll-master;
> +		};
> +
> +		/*
> +		 * overwrite clkout2 configuration with:
> +		 * - xtal as clock source of output divider
> +		 */
> +		clkout2 {
> +			reg = <2>;
> +			silabs,clock-source = <2>;
> +		};
> +	};
> +};
> diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt
> b/Documentation/devicetree/bindings/vendor-prefixes.txt index
> 19e1ef7..ca60849 100644
> --- a/Documentation/devicetree/bindings/vendor-prefixes.txt
> +++ b/Documentation/devicetree/bindings/vendor-prefixes.txt
> @@ -48,6 +48,7 @@ samsung	Samsung Semiconductor
>  sbs	Smart Battery System
>  schindler	Schindler
>  sil	Silicon Image
> +silabs	Silicon Laboratories
>  simtek
>  sirf	SiRF Technology, Inc.
>  snps 	Synopsys, Inc.
> diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig
> index a47e6ee..5039e41 100644
> --- a/drivers/clk/Kconfig
> +++ b/drivers/clk/Kconfig
> @@ -55,6 +55,15 @@ config COMMON_CLK_MAX77686
>  	---help---
>  	  This driver supports Maxim 77686 crystal oscillator clock.
> 
> +config COMMON_CLK_SI5351
> +	tristate "Clock driver for SiLabs 5351A/B/C"
> +	depends on I2C
> +	select REGMAP_I2C
> +	select RATIONAL
> +	---help---
> +	  This driver supports Silicon Labs 5351A/B/C programmable clock
> +	  generators.
> +
>  config CLK_TWL6040
>  	tristate "External McPDM functional clock from twl6040"
>  	depends on TWL6040_CORE
> diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
> index 300d477..92ca698 100644
> --- a/drivers/clk/Makefile
> +++ b/drivers/clk/Makefile
> @@ -33,4 +33,5 @@ obj-$(CONFIG_X86)		+= x86/
>  # Chip specific
>  obj-$(CONFIG_COMMON_CLK_WM831X) += clk-wm831x.o
>  obj-$(CONFIG_COMMON_CLK_MAX77686) += clk-max77686.o
> +obj-$(CONFIG_COMMON_CLK_SI5351) += clk-si5351.o
>  obj-$(CONFIG_CLK_TWL6040)	+= clk-twl6040.o
> diff --git a/drivers/clk/clk-si5351.c b/drivers/clk/clk-si5351.c
> new file mode 100644
> index 0000000..8927284
> --- /dev/null
> +++ b/drivers/clk/clk-si5351.c
> @@ -0,0 +1,1510 @@
> +/*
> + * clk-si5351.c: Silicon Laboratories Si5351A/B/C I2C Clock Generator
> + *
> + * Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
> + * Rabeeh Khoury <rabeeh@solid-run.com>
> + *
> + * References:
> + * [1] "Si5351A/B/C Data Sheet"
> + *     http://www.silabs.com/Support%20Documents/TechnicalDocs/Si5351.pdf
> + * [2] "Manually Generating an Si5351 Register Map"
> + *     http://www.silabs.com/Support%20Documents/TechnicalDocs/AN619.pdf
> + *
> + * This program is free software; you can redistribute  it and/or modify it
> + * under  the terms of  the GNU General  Public License as published by
> the + * Free Software Foundation;  either version 2 of the  License, or (at
> your + * option) any later version.
> + */
> +
> +#include <linux/module.h>
> +#include <linux/kernel.h>
> +#include <linux/clkdev.h>
> +#include <linux/clk-provider.h>
> +#include <linux/delay.h>
> +#include <linux/err.h>
> +#include <linux/errno.h>
> +#include <linux/rational.h>
> +#include <linux/i2c.h>
> +#include <linux/of_platform.h>
> +#include <linux/platform_data/si5351.h>
> +#include <linux/regmap.h>
> +#include <linux/slab.h>
> +#include <linux/string.h>
> +#include <asm/div64.h>
> +
> +#include "clk-si5351.h"
> +
> +struct si5351_driver_data;
> +
> +struct si5351_parameters {
> +	unsigned long	p1;
> +	unsigned long	p2;
> +	unsigned long	p3;
> +	int		valid;
> +};
> +
> +struct si5351_hw_data {
> +	struct clk_hw			hw;
> +	struct si5351_driver_data	*drvdata;
> +	struct si5351_parameters	params;
> +	unsigned char			num;
> +};
> +
> +struct si5351_driver_data {
> +	enum si5351_variant	variant;
> +	struct i2c_client	*client;
> +	struct regmap		*regmap;
> +	struct clk_onecell_data onecell;
> +
> +	struct clk		*pxtal;
> +	const char		*pxtal_name;
> +	struct clk_hw		xtal;
> +	struct clk		*pclkin;
> +	const char		*pclkin_name;
> +	struct clk_hw		clkin;
> +
> +	struct si5351_hw_data	pll[2];
> +	struct si5351_hw_data	*msynth;
> +	struct si5351_hw_data	*clkout;
> +};
> +
> +static const char const *si5351_input_names[] = {
> +	"xtal", "clkin"
> +};
> +static const char const *si5351_pll_names[] = {
> +	"plla", "pllb", "vxco"
> +};
> +static const char const *si5351_msynth_names[] = {
> +	"ms0", "ms1", "ms2", "ms3", "ms4", "ms5", "ms6", "ms7"
> +};
> +static const char const *si5351_clkout_names[] = {
> +	"clk0", "clk1", "clk2", "clk3", "clk4", "clk5", "clk6", "clk7"
> +};
> +
> +/*
> + * Si5351 i2c regmap
> + */
> +static inline u8 si5351_reg_read(struct si5351_driver_data *drvdata, u8
> reg) +{
> +	u32 val;
> +	int ret;
> +
> +	ret = regmap_read(drvdata->regmap, reg, &val);
> +	if (ret) {
> +		dev_err(&drvdata->client->dev,
> +			"unable to read from reg%02x\n", reg);
> +		return 0;
> +	}
> +
> +	return (u8)val;
> +}
> +
> +static inline int si5351_bulk_read(struct si5351_driver_data *drvdata,
> +				   u8 reg, u8 count, u8 *buf)
> +{
> +	return regmap_bulk_read(drvdata->regmap, reg, buf, count);
> +}
> +
> +static inline int si5351_reg_write(struct si5351_driver_data *drvdata,
> +				   u8 reg, u8 val)
> +{
> +	return regmap_write(drvdata->regmap, reg, val);
> +}
> +
> +static inline int si5351_bulk_write(struct si5351_driver_data *drvdata,
> +				    u8 reg, u8 count, const u8 *buf)
> +{
> +	return regmap_raw_write(drvdata->regmap, reg, buf, count);
> +}
> +
> +static inline int si5351_set_bits(struct si5351_driver_data *drvdata,
> +				  u8 reg, u8 mask, u8 val)
> +{
> +	return regmap_update_bits(drvdata->regmap, reg, mask, val);
> +}
> +
> +static inline u8 si5351_msynth_params_address(int num)
> +{
> +	if (num > 5)
> +		return SI5351_CLK6_PARAMETERS + (num - 6);
> +	return SI5351_CLK0_PARAMETERS + (SI5351_PARAMETERS_LENGTH * num);
> +}
> +
> +static void si5351_read_parameters(struct si5351_driver_data *drvdata,
> +				   u8 reg, struct si5351_parameters *params)
> +{
> +	u8 buf[SI5351_PARAMETERS_LENGTH];
> +
> +	switch (reg) {
> +	case SI5351_CLK6_PARAMETERS:
> +	case SI5351_CLK7_PARAMETERS:
> +		buf[0] = si5351_reg_read(drvdata, reg);
> +		params->p1 = buf[0];
> +		params->p2 = 0;
> +		params->p3 = 1;
> +		break;
> +	default:
> +		si5351_bulk_read(drvdata, reg, SI5351_PARAMETERS_LENGTH, buf);
> +		params->p1 = ((buf[2] & 0x03) << 16) | (buf[3] << 8) | buf[4];
> +		params->p2 = ((buf[5] & 0x0f) << 16) | (buf[6] << 8) | buf[7];
> +		params->p3 = ((buf[5] & 0xf0) << 12) | (buf[0] << 8) | buf[1];
> +	}
> +	params->valid = 1;
> +}
> +
> +static void si5351_write_parameters(struct si5351_driver_data *drvdata,
> +				    u8 reg, struct si5351_parameters *params)
> +{
> +	u8 buf[SI5351_PARAMETERS_LENGTH];
> +
> +	switch (reg) {
> +	case SI5351_CLK6_PARAMETERS:
> +	case SI5351_CLK7_PARAMETERS:
> +		buf[0] = params->p1 & 0xff;
> +		si5351_reg_write(drvdata, reg, buf[0]);
> +		break;
> +	default:
> +		buf[0] = ((params->p3 & 0x0ff00) >> 8) & 0xff;
> +		buf[1] = params->p3 & 0xff;
> +		/* save rdiv and divby4 */
> +		buf[2] = si5351_reg_read(drvdata, reg + 2) & ~0x03;
> +		buf[2] |= ((params->p1 & 0x30000) >> 16) & 0x03;
> +		buf[3] = ((params->p1 & 0x0ff00) >> 8) & 0xff;
> +		buf[4] = params->p1 & 0xff;
> +		buf[5] = ((params->p3 & 0xf0000) >> 12) |
> +			((params->p2 & 0xf0000) >> 16);
> +		buf[6] = ((params->p2 & 0x0ff00) >> 8) & 0xff;
> +		buf[7] = params->p2 & 0xff;
> +		si5351_bulk_write(drvdata, reg, SI5351_PARAMETERS_LENGTH, buf);
> +	}
> +}
> +
> +static bool si5351_regmap_is_volatile(struct device *dev, unsigned int reg)
> +{
> +	switch (reg) {
> +	case SI5351_DEVICE_STATUS:
> +	case SI5351_INTERRUPT_STATUS:
> +	case SI5351_PLL_RESET:
> +		return true;
> +	}
> +	return false;
> +}
> +
> +static bool si5351_regmap_is_writeable(struct device *dev, unsigned int
> reg) +{
> +	/* reserved registers */
> +	if (reg >= 4 && reg <= 8)
> +		return false;
> +	if (reg >= 10 && reg <= 14)
> +		return false;
> +	if (reg >= 173 && reg <= 176)
> +		return false;
> +	if (reg >= 178 && reg <= 182)
> +		return false;
> +	/* read-only */
> +	if (reg == SI5351_DEVICE_STATUS)
> +		return false;
> +	return true;
> +}
> +
> +static struct regmap_config si5351_regmap_config = {
> +	.reg_bits = 8,
> +	.val_bits = 8,
> +	.cache_type = REGCACHE_RBTREE,
> +	.max_register = 187,
> +	.writeable_reg = si5351_regmap_is_writeable,
> +	.volatile_reg = si5351_regmap_is_volatile,
> +};
> +
> +/*
> + * Si5351 xtal clock input
> + */
> +static int si5351_xtal_prepare(struct clk_hw *hw)
> +{
> +	struct si5351_driver_data *drvdata =
> +		container_of(hw, struct si5351_driver_data, xtal);
> +	si5351_set_bits(drvdata, SI5351_FANOUT_ENABLE,
> +			SI5351_XTAL_ENABLE, SI5351_XTAL_ENABLE);
> +	return 0;
> +}
> +
> +static void si5351_xtal_unprepare(struct clk_hw *hw)
> +{
> +	struct si5351_driver_data *drvdata =
> +		container_of(hw, struct si5351_driver_data, xtal);
> +	si5351_set_bits(drvdata, SI5351_FANOUT_ENABLE,
> +			SI5351_XTAL_ENABLE, 0);
> +}
> +
> +static const struct clk_ops si5351_xtal_ops = {
> +	.prepare = si5351_xtal_prepare,
> +	.unprepare = si5351_xtal_unprepare,
> +};
> +
> +/*
> + * Si5351 clkin clock input (Si5351C only)
> + */
> +static int si5351_clkin_prepare(struct clk_hw *hw)
> +{
> +	struct si5351_driver_data *drvdata =
> +		container_of(hw, struct si5351_driver_data, clkin);
> +	si5351_set_bits(drvdata, SI5351_FANOUT_ENABLE,
> +			SI5351_CLKIN_ENABLE, SI5351_CLKIN_ENABLE);
> +	return 0;
> +}
> +
> +static void si5351_clkin_unprepare(struct clk_hw *hw)
> +{
> +	struct si5351_driver_data *drvdata =
> +		container_of(hw, struct si5351_driver_data, clkin);
> +	si5351_set_bits(drvdata, SI5351_FANOUT_ENABLE,
> +			SI5351_CLKIN_ENABLE, 0);
> +}
> +
> +/*
> + * CMOS clock source constraints:
> + * The input frequency range of the PLL is 10Mhz to 40MHz.
> + * If CLKIN is >40MHz, the input divider must be used.
> + */
> +static unsigned long si5351_clkin_recalc_rate(struct clk_hw *hw,
> +					      unsigned long parent_rate)
> +{
> +	struct si5351_driver_data *drvdata =
> +		container_of(hw, struct si5351_driver_data, clkin);
> +	unsigned long rate;
> +	unsigned char idiv;
> +
> +	rate = parent_rate;
> +	if (parent_rate > 160000000) {
> +		idiv = SI5351_CLKIN_DIV_8;
> +		rate /= 8;
> +	} else if (parent_rate > 80000000) {
> +		idiv = SI5351_CLKIN_DIV_4;
> +		rate /= 4;
> +	} else if (parent_rate > 40000000) {
> +		idiv = SI5351_CLKIN_DIV_2;
> +		rate /= 2;
> +	} else {
> +		idiv = SI5351_CLKIN_DIV_1;
> +	}
> +
> +	si5351_set_bits(drvdata, SI5351_PLL_INPUT_SOURCE,
> +			SI5351_CLKIN_DIV_MASK, idiv);
> +
> +	dev_dbg(&drvdata->client->dev, "%s - clkin div = %d, rate = %lu\n",
> +		__func__, (1 << (idiv >> 6)), rate);
> +
> +	return rate;
> +}
> +
> +static const struct clk_ops si5351_clkin_ops = {
> +	.prepare = si5351_clkin_prepare,
> +	.unprepare = si5351_clkin_unprepare,
> +	.recalc_rate = si5351_clkin_recalc_rate,
> +};
> +
> +/*
> + * Si5351 vxco clock input (Si5351B only)
> + */
> +
> +static int si5351_vxco_prepare(struct clk_hw *hw)
> +{
> +	struct si5351_hw_data *hwdata =
> +		container_of(hw, struct si5351_hw_data, hw);
> +
> +	dev_warn(&hwdata->drvdata->client->dev, "VXCO currently unsupported\n");
> +
> +	return 0;
> +}
> +
> +static void si5351_vxco_unprepare(struct clk_hw *hw)
> +{
> +}
> +
> +static unsigned long si5351_vxco_recalc_rate(struct clk_hw *hw,
> +					     unsigned long parent_rate)
> +{
> +	return 0;
> +}
> +
> +static int si5351_vxco_set_rate(struct clk_hw *hw, unsigned long rate,
> +				unsigned long parent)
> +{
> +	return 0;
> +}
> +
> +static const struct clk_ops si5351_vxco_ops = {
> +	.prepare = si5351_vxco_prepare,
> +	.unprepare = si5351_vxco_unprepare,
> +	.recalc_rate = si5351_vxco_recalc_rate,
> +	.set_rate = si5351_vxco_set_rate,
> +};
> +
> +/*
> + * Si5351 pll a/b
> + *
> + * Feedback Multisynth Divider Equations [2]
> + *
> + * fVCO = fIN * (a + b/c)
> + *
> + * with 15 + 0/1048575 <= (a + b/c) <= 90 + 0/1048575 and
> + * fIN = fXTAL or fIN = fCLKIN/CLKIN_DIV
> + *
> + * Feedback Multisynth Register Equations
> + *
> + * (1) MSNx_P1[17:0] = 128 * a + floor(128 * b/c) - 512
> + * (2) MSNx_P2[19:0] = 128 * b - c * floor(128 * b/c) = (128*b) mod c
> + * (3) MSNx_P3[19:0] = c
> + *
> + * Transposing (2) yields: (4) floor(128 * b/c) = (128 * b / MSNx_P2)/c
> + *
> + * Using (4) on (1) yields:
> + * MSNx_P1 = 128 * a + (128 * b/MSNx_P2)/c - 512
> + * MSNx_P1 + 512 + MSNx_P2/c = 128 * a + 128 * b/c
> + *
> + * a + b/c = (MSNx_P1 + MSNx_P2/MSNx_P3 + 512)/128
> + *         = (MSNx_P1*MSNx_P3 + MSNx_P2 + 512*MSNx_P3)/(128*MSNx_P3)
> + *
> + */
> +static int _si5351_pll_reparent(struct si5351_driver_data *drvdata,
> +				int num, enum si5351_pll_src parent)
> +{
> +	u8 mask = (num == 0) ? SI5351_PLLA_SOURCE : SI5351_PLLB_SOURCE;
> +
> +	if (parent == SI5351_PLL_SRC_DEFAULT)
> +		return 0;
> +
> +	if (num > 2)
> +		return -EINVAL;
> +
> +	if (drvdata->variant != SI5351_VARIANT_C &&
> +	    parent != SI5351_PLL_SRC_XTAL)
> +		return -EINVAL;
> +
> +	si5351_set_bits(drvdata, SI5351_PLL_INPUT_SOURCE, mask,
> +			(parent == SI5351_PLL_SRC_XTAL) ? 0 : mask);
> +	return 0;
> +}
> +
> +static unsigned char si5351_pll_get_parent(struct clk_hw *hw)
> +{
> +	struct si5351_hw_data *hwdata =
> +		container_of(hw, struct si5351_hw_data, hw);
> +	u8 mask = (hwdata->num == 0) ? SI5351_PLLA_SOURCE : SI5351_PLLB_SOURCE;
> +	u8 val;
> +
> +	val = si5351_reg_read(hwdata->drvdata, SI5351_PLL_INPUT_SOURCE);
> +
> +	return (val & mask) ? 1 : 0;
> +}
> +
> +static int si5351_pll_set_parent(struct clk_hw *hw, u8 index)
> +{
> +	struct si5351_hw_data *hwdata =
> +		container_of(hw, struct si5351_hw_data, hw);
> +
> +	if (hwdata->drvdata->variant != SI5351_VARIANT_C &&
> +	    index > 0)
> +		return -EPERM;
> +
> +	if (index > 1)
> +		return -EINVAL;
> +
> +	return _si5351_pll_reparent(hwdata->drvdata, hwdata->num,
> +			     (index == 0) ? SI5351_PLL_SRC_XTAL :
> +			     SI5351_PLL_SRC_CLKIN);
> +}
> +
> +static unsigned long si5351_pll_recalc_rate(struct clk_hw *hw,
> +					    unsigned long parent_rate)
> +{
> +	struct si5351_hw_data *hwdata =
> +		container_of(hw, struct si5351_hw_data, hw);
> +	u8 reg = (hwdata->num == 0) ? SI5351_PLLA_PARAMETERS :
> +		SI5351_PLLB_PARAMETERS;
> +	unsigned long long rate;
> +
> +	if (!hwdata->params.valid)
> +		si5351_read_parameters(hwdata->drvdata, reg, &hwdata->params);
> +
> +	if (hwdata->params.p3 == 0)
> +		return parent_rate;
> +
> +	/* fVCO = fIN * (P1*P3 + 512*P3 + P2)/(128*P3) */
> +	rate  = hwdata->params.p1 * hwdata->params.p3;
> +	rate += 512 * hwdata->params.p3;
> +	rate += hwdata->params.p2;
> +	rate *= parent_rate;
> +	do_div(rate, 128 * hwdata->params.p3);
> +
> +	dev_dbg(&hwdata->drvdata->client->dev,
> +		"%s - %s: p1 = %lu, p2 = %lu, p3 = %lu, parent_rate = %lu, rate = 
%lu\n",
> +		__func__, __clk_get_name(hwdata->hw.clk),
> +		hwdata->params.p1, hwdata->params.p2, hwdata->params.p3,
> +		parent_rate, (unsigned long)rate);
> +
> +	return (unsigned long)rate;
> +}
> +
> +static long si5351_pll_round_rate(struct clk_hw *hw, unsigned long rate,
> +				  unsigned long *parent_rate)
> +{
> +	struct si5351_hw_data *hwdata =
> +		container_of(hw, struct si5351_hw_data, hw);
> +	unsigned long rfrac, denom, a, b, c;
> +	unsigned long long lltmp;
> +
> +	if (rate < SI5351_PLL_VCO_MIN)
> +		rate = SI5351_PLL_VCO_MIN;
> +	if (rate > SI5351_PLL_VCO_MAX)
> +		rate = SI5351_PLL_VCO_MAX;
> +
> +	/* determine integer part of feedback equation */
> +	a = rate / *parent_rate;
> +
> +	if (a < SI5351_PLL_A_MIN)
> +		rate = *parent_rate * SI5351_PLL_A_MIN;
> +	if (a > SI5351_PLL_A_MAX)
> +		rate = *parent_rate * SI5351_PLL_A_MAX;
> +
> +	/* find best approximation for b/c = fVCO mod fIN */
> +	denom = 1000 * 1000;
> +	lltmp = rate % (*parent_rate);
> +	lltmp *= denom;
> +	do_div(lltmp, *parent_rate);
> +	rfrac = (unsigned long)lltmp;
> +
> +	b = 0;
> +	c = 1;
> +	if (rfrac)
> +		rational_best_approximation(rfrac, denom,
> +				    SI5351_PLL_B_MAX, SI5351_PLL_C_MAX, &b, &c);
> +
> +	/* calculate parameters */
> +	hwdata->params.p3  = c;
> +	hwdata->params.p2  = (128 * b) % c;
> +	hwdata->params.p1  = 128 * a;
> +	hwdata->params.p1 += (128 * b / c);
> +	hwdata->params.p1 -= 512;
> +
> +	/* recalculate rate by fIN * (a + b/c) */
> +	lltmp  = *parent_rate;
> +	lltmp *= b;
> +	do_div(lltmp, c);
> +
> +	rate  = (unsigned long)lltmp;
> +	rate += *parent_rate * a;
> +
> +	dev_dbg(&hwdata->drvdata->client->dev,
> +		"%s - %s: a = %lu, b = %lu, c = %lu, parent_rate = %lu, rate = %lu\n",
> +		__func__, __clk_get_name(hwdata->hw.clk), a, b, c,
> +		*parent_rate, rate);
> +
> +	return rate;
> +}
> +
> +static int si5351_pll_set_rate(struct clk_hw *hw, unsigned long rate,
> +			       unsigned long parent_rate)
> +{
> +	struct si5351_hw_data *hwdata =
> +		container_of(hw, struct si5351_hw_data, hw);
> +	u8 reg = (hwdata->num == 0) ? SI5351_PLLA_PARAMETERS :
> +		SI5351_PLLB_PARAMETERS;
> +
> +	/* write multisynth parameters */
> +	si5351_write_parameters(hwdata->drvdata, reg, &hwdata->params);
> +
> +	/* plla/pllb ctrl is in clk6/clk7 ctrl registers */
> +	si5351_set_bits(hwdata->drvdata, SI5351_CLK6_CTRL + hwdata->num,
> +		SI5351_CLK_INTEGER_MODE,
> +		(hwdata->params.p2 == 0) ? SI5351_CLK_INTEGER_MODE : 0);
> +
> +	dev_dbg(&hwdata->drvdata->client->dev,
> +		"%s - %s: p1 = %lu, p2 = %lu, p3 = %lu, parent_rate = %lu, rate = 
%lu\n",
> +		__func__, __clk_get_name(hwdata->hw.clk),
> +		hwdata->params.p1, hwdata->params.p2, hwdata->params.p3,
> +		parent_rate, rate);
> +
> +	return 0;
> +}
> +
> +static const struct clk_ops si5351_pll_ops = {
> +	.set_parent = si5351_pll_set_parent,
> +	.get_parent = si5351_pll_get_parent,
> +	.recalc_rate = si5351_pll_recalc_rate,
> +	.round_rate = si5351_pll_round_rate,
> +	.set_rate = si5351_pll_set_rate,
> +};
> +
> +/*
> + * Si5351 multisync divider
> + *
> + * for fOUT <= 150 MHz:
> + *
> + * fOUT = (fIN * (a + b/c)) / CLKOUTDIV
> + *
> + * with 6 + 0/1048575 <= (a + b/c) <= 1800 + 0/1048575 and
> + * fIN = fVCO0, fVCO1
> + *
> + * Output Clock Multisynth Register Equations
> + *
> + * MSx_P1[17:0] = 128 * a + floor(128 * b/c) - 512
> + * MSx_P2[19:0] = 128 * b - c * floor(128 * b/c) = (128*b) mod c
> + * MSx_P3[19:0] = c
> + *
> + * MS[6,7] are integer (P1) divide only, P2 = 0, P3 = 0
> + *
> + * for 150MHz < fOUT <= 160MHz:
> + *
> + * MSx_P1 = 0, MSx_P2 = 0, MSx_P3 = 1, MSx_INT = 1, MSx_DIVBY4 = 11b
> + */
> +static int _si5351_msynth_reparent(struct si5351_driver_data *drvdata,
> +				   int num, enum si5351_multisynth_src parent)
> +{
> +	if (parent == SI5351_MULTISYNTH_SRC_DEFAULT)
> +		return 0;
> +
> +	if (num > 8)
> +		return -EINVAL;
> +
> +	si5351_set_bits(drvdata, SI5351_CLK0_CTRL + num, SI5351_CLK_PLL_SELECT,
> +			(parent == SI5351_MULTISYNTH_SRC_VCO0) ? 0 :
> +			SI5351_CLK_PLL_SELECT);
> +	return 0;
> +}
> +
> +static unsigned char si5351_msynth_get_parent(struct clk_hw *hw)
> +{
> +	struct si5351_hw_data *hwdata =
> +		container_of(hw, struct si5351_hw_data, hw);
> +	u8 val;
> +
> +	val = si5351_reg_read(hwdata->drvdata, SI5351_CLK0_CTRL + hwdata->num);
> +
> +	return (val & SI5351_CLK_PLL_SELECT) ? 1 : 0;
> +}
> +
> +static int si5351_msynth_set_parent(struct clk_hw *hw, u8 index)
> +{
> +	struct si5351_hw_data *hwdata =
> +		container_of(hw, struct si5351_hw_data, hw);
> +
> +	return _si5351_msynth_reparent(hwdata->drvdata, hwdata->num,
> +			       (index == 0) ? SI5351_MULTISYNTH_SRC_VCO0 :
> +			       SI5351_MULTISYNTH_SRC_VCO1);
> +}
> +
> +static unsigned long si5351_msynth_recalc_rate(struct clk_hw *hw,
> +					       unsigned long parent_rate)
> +{
> +	struct si5351_hw_data *hwdata =
> +		container_of(hw, struct si5351_hw_data, hw);
> +	u8 reg = si5351_msynth_params_address(hwdata->num);
> +	unsigned long long rate;
> +	unsigned long m;
> +
> +	if (!hwdata->params.valid)
> +		si5351_read_parameters(hwdata->drvdata, reg, &hwdata->params);
> +
> +	if (hwdata->params.p3 == 0)
> +		return parent_rate;
> +
> +	/*
> +	 * multisync0-5: fOUT = (128 * P3 * fIN) / (P1*P3 + P2 + 512*P3)
> +	 * multisync6-7: fOUT = fIN / P1
> +	 */
> +	rate = parent_rate;
> +	if (hwdata->num > 5) {
> +		m = hwdata->params.p1;
> +	} else if ((si5351_reg_read(hwdata->drvdata, reg + 2) &
> +		    SI5351_OUTPUT_CLK_DIVBY4) == SI5351_OUTPUT_CLK_DIVBY4) {
> +		m = 4;
> +	} else {
> +		rate *= 128 * hwdata->params.p3;
> +		m = hwdata->params.p1 * hwdata->params.p3;
> +		m += hwdata->params.p2;
> +		m += 512 * hwdata->params.p3;
> +	}
> +
> +	if (m == 0)
> +		return 0;
> +	do_div(rate, m);
> +
> +	dev_dbg(&hwdata->drvdata->client->dev,
> +		"%s - %s: p1 = %lu, p2 = %lu, p3 = %lu, m = %lu, parent_rate = %lu, 
rate
> = %lu\n", +		__func__, __clk_get_name(hwdata->hw.clk),
> +		hwdata->params.p1, hwdata->params.p2, hwdata->params.p3,
> +		m, parent_rate, (unsigned long)rate);
> +
> +	return (unsigned long)rate;
> +}
> +
> +static long si5351_msynth_round_rate(struct clk_hw *hw, unsigned long rate,
> +				     unsigned long *parent_rate)
> +{
> +	struct si5351_hw_data *hwdata =
> +		container_of(hw, struct si5351_hw_data, hw);
> +	unsigned long long lltmp;
> +	unsigned long a, b, c;
> +	int divby4;
> +
> +	/* multisync6-7 can only handle freqencies < 150MHz */
> +	if (hwdata->num >= 6 && rate > SI5351_MULTISYNTH67_MAX_FREQ)
> +		rate = SI5351_MULTISYNTH67_MAX_FREQ;
> +
> +	/* multisync frequency is 1MHz .. 160MHz */
> +	if (rate > SI5351_MULTISYNTH_MAX_FREQ)
> +		rate = SI5351_MULTISYNTH_MAX_FREQ;
> +	if (rate < SI5351_MULTISYNTH_MIN_FREQ)
> +		rate = SI5351_MULTISYNTH_MIN_FREQ;
> +
> +	divby4 = 0;
> +	if (rate > SI5351_MULTISYNTH_DIVBY4_FREQ)
> +		divby4 = 1;
> +
> +	/* multisync can set pll */
> +	if (__clk_get_flags(hwdata->hw.clk) & CLK_SET_RATE_PARENT) {
> +		/*
> +		 * find largest integer divider for max
> +		 * vco frequency and given target rate
> +		 */
> +		if (divby4 == 0) {
> +			lltmp = SI5351_PLL_VCO_MAX;
> +			do_div(lltmp, rate);
> +			a = (unsigned long)lltmp;
> +		} else
> +			a = 4;
> +
> +		b = 0;
> +		c = 1;
> +
> +		*parent_rate = a * rate;
> +	} else {
> +		unsigned long rfrac, denom;
> +
> +		/* disable divby4 */
> +		if (divby4) {
> +			rate = SI5351_MULTISYNTH_DIVBY4_FREQ;
> +			divby4 = 0;
> +		}
> +
> +		/* determine integer part of divider equation */
> +		a = *parent_rate / rate;
> +		if (a < SI5351_MULTISYNTH_A_MIN)
> +			a = SI5351_MULTISYNTH_A_MIN;
> +		if (hwdata->num >= 6 && a > SI5351_MULTISYNTH67_A_MAX)
> +			a = SI5351_MULTISYNTH67_A_MAX;
> +		else if (a > SI5351_MULTISYNTH_A_MAX)
> +			a = SI5351_MULTISYNTH_A_MAX;
> +
> +		/* find best approximation for b/c = fVCO mod fOUT */
> +		denom = 1000 * 1000;
> +		lltmp = (*parent_rate) % rate;
> +		lltmp *= denom;
> +		do_div(lltmp, rate);
> +		rfrac = (unsigned long)lltmp;
> +
> +		b = 0;
> +		c = 1;
> +		if (rfrac)
> +			rational_best_approximation(rfrac, denom,
> +			    SI5351_MULTISYNTH_B_MAX, SI5351_MULTISYNTH_C_MAX,
> +			    &b, &c);
> +	}
> +
> +	/* recalculate rate by fOUT = fIN / (a + b/c) */
> +	lltmp  = *parent_rate;
> +	lltmp *= c;
> +	do_div(lltmp, a * c + b);
> +	rate  = (unsigned long)lltmp;
> +
> +	/* calculate parameters */
> +	if (divby4) {
> +		hwdata->params.p3 = 1;
> +		hwdata->params.p2 = 0;
> +		hwdata->params.p1 = 0;
> +	} else {
> +		hwdata->params.p3  = c;
> +		hwdata->params.p2  = (128 * b) % c;
> +		hwdata->params.p1  = 128 * a;
> +		hwdata->params.p1 += (128 * b / c);
> +		hwdata->params.p1 -= 512;
> +	}
> +
> +	dev_dbg(&hwdata->drvdata->client->dev,
> +		"%s - %s: a = %lu, b = %lu, c = %lu, divby4 = %d, parent_rate = %lu, 
rate
> = %lu\n", +		__func__, __clk_get_name(hwdata->hw.clk), a, b, c, divby4,
> +		*parent_rate, rate);
> +
> +	return rate;
> +}
> +
> +static int si5351_msynth_set_rate(struct clk_hw *hw, unsigned long rate,
> +				  unsigned long parent_rate)
> +{
> +	struct si5351_hw_data *hwdata =
> +		container_of(hw, struct si5351_hw_data, hw);
> +	u8 reg = si5351_msynth_params_address(hwdata->num);
> +	int divby4 = 0;
> +
> +	/* write multisynth parameters */
> +	si5351_write_parameters(hwdata->drvdata, reg, &hwdata->params);
> +
> +	if (rate > SI5351_MULTISYNTH_DIVBY4_FREQ)
> +		divby4 = 1;
> +
> +	/* enable/disable integer mode and divby4 on multisynth0-5 */
> +	if (hwdata->num < 6) {
> +		si5351_set_bits(hwdata->drvdata, reg + 2,
> +				SI5351_OUTPUT_CLK_DIVBY4,
> +				(divby4) ? SI5351_OUTPUT_CLK_DIVBY4 : 0);
> +		si5351_set_bits(hwdata->drvdata, SI5351_CLK0_CTRL + hwdata->num,
> +			SI5351_CLK_INTEGER_MODE,
> +			(hwdata->params.p2 == 0) ? SI5351_CLK_INTEGER_MODE : 0);
> +	}
> +
> +	dev_dbg(&hwdata->drvdata->client->dev,
> +		"%s - %s: p1 = %lu, p2 = %lu, p3 = %lu, divby4 = %d, parent_rate = %lu,
> rate = %lu\n", +		__func__, __clk_get_name(hwdata->hw.clk),
> +		hwdata->params.p1, hwdata->params.p2, hwdata->params.p3,
> +		divby4, parent_rate, rate);
> +
> +	return 0;
> +}
> +
> +static const struct clk_ops si5351_msynth_ops = {
> +	.set_parent = si5351_msynth_set_parent,
> +	.get_parent = si5351_msynth_get_parent,
> +	.recalc_rate = si5351_msynth_recalc_rate,
> +	.round_rate = si5351_msynth_round_rate,
> +	.set_rate = si5351_msynth_set_rate,
> +};
> +
> +/*
> + * Si5351 clkout divider
> + */
> +static int _si5351_clkout_reparent(struct si5351_driver_data *drvdata,
> +				   int num, enum si5351_clkout_src parent)
> +{
> +	u8 val;
> +
> +	if (num > 8)
> +		return -EINVAL;
> +
> +	switch (parent) {
> +	case SI5351_CLKOUT_SRC_MSYNTH_N:
> +		val = SI5351_CLK_INPUT_MULTISYNTH_N;
> +		break;
> +	case SI5351_CLKOUT_SRC_MSYNTH_0_4:
> +		/* clk0/clk4 can only connect to its own multisync */
> +		if (num == 0 || num == 4)
> +			val = SI5351_CLK_INPUT_MULTISYNTH_N;
> +		else
> +			val = SI5351_CLK_INPUT_MULTISYNTH_0_4;
> +		break;
> +	case SI5351_CLKOUT_SRC_XTAL:
> +		val = SI5351_CLK_INPUT_XTAL;
> +		break;
> +	case SI5351_CLKOUT_SRC_CLKIN:
> +		if (drvdata->variant != SI5351_VARIANT_C)
> +			return -EINVAL;
> +
> +		val = SI5351_CLK_INPUT_CLKIN;
> +		break;
> +	default:
> +		return 0;
> +	}
> +
> +	si5351_set_bits(drvdata, SI5351_CLK0_CTRL + num,
> +			SI5351_CLK_INPUT_MASK, val);
> +	return 0;
> +}
> +
> +static int _si5351_clkout_set_drive_strength(
> +	struct si5351_driver_data *drvdata, int num,
> +	enum si5351_drive_strength drive)
> +{
> +	u8 mask;
> +
> +	if (num > 8)
> +		return -EINVAL;
> +
> +	switch (drive) {
> +	case SI5351_DRIVE_2MA:
> +		mask = SI5351_CLK_DRIVE_STRENGTH_2MA;
> +		break;
> +	case SI5351_DRIVE_4MA:
> +		mask = SI5351_CLK_DRIVE_STRENGTH_4MA;
> +		break;
> +	case SI5351_DRIVE_6MA:
> +		mask = SI5351_CLK_DRIVE_STRENGTH_6MA;
> +		break;
> +	case SI5351_DRIVE_8MA:
> +		mask = SI5351_CLK_DRIVE_STRENGTH_8MA;
> +		break;
> +	default:
> +		return 0;
> +	}
> +
> +	si5351_set_bits(drvdata, SI5351_CLK0_CTRL + num,
> +			SI5351_CLK_DRIVE_STRENGTH_MASK, mask);
> +	return 0;
> +}
> +
> +static int si5351_clkout_prepare(struct clk_hw *hw)
> +{
> +	struct si5351_hw_data *hwdata =
> +		container_of(hw, struct si5351_hw_data, hw);
> +
> +	si5351_set_bits(hwdata->drvdata, SI5351_CLK0_CTRL + hwdata->num,
> +			SI5351_CLK_POWERDOWN, 0);
> +	si5351_set_bits(hwdata->drvdata, SI5351_OUTPUT_ENABLE_CTRL,
> +			(1 << hwdata->num), 0);
> +	return 0;
> +}
> +
> +static void si5351_clkout_unprepare(struct clk_hw *hw)
> +{
> +	struct si5351_hw_data *hwdata =
> +		container_of(hw, struct si5351_hw_data, hw);
> +
> +	si5351_set_bits(hwdata->drvdata, SI5351_CLK0_CTRL + hwdata->num,
> +			SI5351_CLK_POWERDOWN, SI5351_CLK_POWERDOWN);
> +	si5351_set_bits(hwdata->drvdata, SI5351_OUTPUT_ENABLE_CTRL,
> +			(1 << hwdata->num), (1 << hwdata->num));
> +}
> +
> +static u8 si5351_clkout_get_parent(struct clk_hw *hw)
> +{
> +	struct si5351_hw_data *hwdata =
> +		container_of(hw, struct si5351_hw_data, hw);
> +	int index = 0;
> +	unsigned char val;
> +
> +	val = si5351_reg_read(hwdata->drvdata, SI5351_CLK0_CTRL + hwdata->num);
> +	switch (val & SI5351_CLK_INPUT_MASK) {
> +	case SI5351_CLK_INPUT_MULTISYNTH_N:
> +		index = 0;
> +		break;
> +	case SI5351_CLK_INPUT_MULTISYNTH_0_4:
> +		index = 1;
> +		break;
> +	case SI5351_CLK_INPUT_XTAL:
> +		index = 2;
> +		break;
> +	case SI5351_CLK_INPUT_CLKIN:
> +		index = 3;
> +		break;
> +	}
> +
> +	return index;
> +}
> +
> +static int si5351_clkout_set_parent(struct clk_hw *hw, u8 index)
> +{
> +	struct si5351_hw_data *hwdata =
> +		container_of(hw, struct si5351_hw_data, hw);
> +	enum si5351_clkout_src parent = SI5351_CLKOUT_SRC_DEFAULT;
> +
> +	switch (index) {
> +	case 0:
> +		parent = SI5351_CLKOUT_SRC_MSYNTH_N;
> +		break;
> +	case 1:
> +		parent = SI5351_CLKOUT_SRC_MSYNTH_0_4;
> +		break;
> +	case 2:
> +		parent = SI5351_CLKOUT_SRC_XTAL;
> +		break;
> +	case 3:
> +		parent = SI5351_CLKOUT_SRC_CLKIN;
> +		break;
> +	}
> +
> +	return _si5351_clkout_reparent(hwdata->drvdata, hwdata->num, parent);
> +}
> +
> +static unsigned long si5351_clkout_recalc_rate(struct clk_hw *hw,
> +					       unsigned long parent_rate)
> +{
> +	struct si5351_hw_data *hwdata =
> +		container_of(hw, struct si5351_hw_data, hw);
> +	unsigned char reg;
> +	unsigned char rdiv;
> +
> +	if (hwdata->num > 5)
> +		reg = si5351_msynth_params_address(hwdata->num) + 2;
> +	else
> +		reg = SI5351_CLK6_7_OUTPUT_DIVIDER;
> +
> +	rdiv = si5351_reg_read(hwdata->drvdata, reg);
> +	if (hwdata->num == 6) {
> +		rdiv &= SI5351_OUTPUT_CLK6_DIV_MASK;
> +	} else {
> +		rdiv &= SI5351_OUTPUT_CLK_DIV_MASK;
> +		rdiv >>= SI5351_OUTPUT_CLK_DIV_SHIFT;
> +	}
> +
> +	return parent_rate >> rdiv;
> +}
> +
> +static long si5351_clkout_round_rate(struct clk_hw *hw, unsigned long rate,
> +				     unsigned long *parent_rate)
> +{
> +	struct si5351_hw_data *hwdata =
> +		container_of(hw, struct si5351_hw_data, hw);
> +	unsigned char rdiv;
> +
> +	/* clkout6/7 can only handle output freqencies < 150MHz */
> +	if (hwdata->num >= 6 && rate > SI5351_CLKOUT67_MAX_FREQ)
> +		rate = SI5351_CLKOUT67_MAX_FREQ;
> +
> +	/* clkout freqency is 8kHz - 160MHz */
> +	if (rate > SI5351_CLKOUT_MAX_FREQ)
> +		rate = SI5351_CLKOUT_MAX_FREQ;
> +	if (rate < SI5351_CLKOUT_MIN_FREQ)
> +		rate = SI5351_CLKOUT_MIN_FREQ;
> +
> +	/* request frequency if multisync master */
> +	if (__clk_get_flags(hwdata->hw.clk) & CLK_SET_RATE_PARENT) {
> +		/* use r divider for frequencies below 1MHz */
> +		rdiv = SI5351_OUTPUT_CLK_DIV_1;
> +		while (rate < SI5351_MULTISYNTH_MIN_FREQ &&
> +		       rdiv < SI5351_OUTPUT_CLK_DIV_128) {
> +			rdiv += 1;
> +			rate *= 2;
> +		}
> +		*parent_rate = rate;
> +	} else {
> +		unsigned long new_rate, new_err, err;
> +
> +		/* round to closed rdiv */
> +		rdiv = SI5351_OUTPUT_CLK_DIV_1;
> +		new_rate = *parent_rate;
> +		err = abs(new_rate - rate);
> +		do {
> +			new_rate >>= 1;
> +			new_err = abs(new_rate - rate);
> +			if (new_err > err || rdiv == SI5351_OUTPUT_CLK_DIV_128)
> +				break;
> +			rdiv++;
> +			err = new_err;
> +		} while (1);
> +	}
> +	rate = *parent_rate >> rdiv;
> +
> +	dev_dbg(&hwdata->drvdata->client->dev,
> +		"%s - %s: rdiv = %u, parent_rate = %lu, rate = %lu\n",
> +		__func__, __clk_get_name(hwdata->hw.clk), (1 << rdiv),
> +		*parent_rate, rate);
> +
> +	return rate;
> +}
> +
> +static int si5351_clkout_set_rate(struct clk_hw *hw, unsigned long rate,
> +				  unsigned long parent_rate)
> +{
> +	struct si5351_hw_data *hwdata =
> +		container_of(hw, struct si5351_hw_data, hw);
> +	unsigned long new_rate, new_err, err;
> +	unsigned char rdiv;
> +
> +	/* round to closed rdiv */
> +	rdiv = SI5351_OUTPUT_CLK_DIV_1;
> +	new_rate = parent_rate;
> +	err = abs(new_rate - rate);
> +	do {
> +		new_rate >>= 1;
> +		new_err = abs(new_rate - rate);
> +		if (new_err > err || rdiv == SI5351_OUTPUT_CLK_DIV_128)
> +			break;
> +		rdiv++;
> +		err = new_err;
> +	} while (1);
> +
> +	/* write output divider */
> +	switch (hwdata->num) {
> +	case 6:
> +		si5351_set_bits(hwdata->drvdata, SI5351_CLK6_7_OUTPUT_DIVIDER,
> +				SI5351_OUTPUT_CLK6_DIV_MASK, rdiv);
> +		break;
> +	case 7:
> +		si5351_set_bits(hwdata->drvdata, SI5351_CLK6_7_OUTPUT_DIVIDER,
> +				SI5351_OUTPUT_CLK_DIV_MASK,
> +				rdiv << SI5351_OUTPUT_CLK_DIV_SHIFT);
> +		break;
> +	default:
> +		si5351_set_bits(hwdata->drvdata,
> +				si5351_msynth_params_address(hwdata->num) + 2,
> +				SI5351_OUTPUT_CLK_DIV_MASK,
> +				rdiv << SI5351_OUTPUT_CLK_DIV_SHIFT);
> +	}
> +
> +	/* powerup clkout */
> +	si5351_set_bits(hwdata->drvdata, SI5351_CLK0_CTRL + hwdata->num,
> +			SI5351_CLK_POWERDOWN, 0);
> +
> +	dev_dbg(&hwdata->drvdata->client->dev,
> +		"%s - %s: rdiv = %u, parent_rate = %lu, rate = %lu\n",
> +		__func__, __clk_get_name(hwdata->hw.clk), (1 << rdiv),
> +		parent_rate, rate);
> +
> +	return 0;
> +}
> +
> +static const struct clk_ops si5351_clkout_ops = {
> +	.prepare = si5351_clkout_prepare,
> +	.unprepare = si5351_clkout_unprepare,
> +	.set_parent = si5351_clkout_set_parent,
> +	.get_parent = si5351_clkout_get_parent,
> +	.recalc_rate = si5351_clkout_recalc_rate,
> +	.round_rate = si5351_clkout_round_rate,
> +	.set_rate = si5351_clkout_set_rate,
> +};
> +
> +/*
> + * Si5351 i2c probe and DT
> + */
> +#ifdef CONFIG_OF
> +static const struct of_device_id si5351_dt_ids[] = {
> +	{ .compatible = "silabs,si5351a", .data = (void *)SI5351_VARIANT_A, },
> +	{ .compatible = "silabs,si5351a-msop",
> +					 .data = (void *)SI5351_VARIANT_A3, },
> +	{ .compatible = "silabs,si5351b", .data = (void *)SI5351_VARIANT_B, },
> +	{ .compatible = "silabs,si5351c", .data = (void *)SI5351_VARIANT_C, },
> +	{ }
> +};
> +MODULE_DEVICE_TABLE(of, si5351_dt_ids);
> +
> +static int si5351_dt_parse(struct i2c_client *client)
> +{
> +	struct device_node *child, *np = client->dev.of_node;
> +	struct si5351_platform_data *pdata;
> +	const struct of_device_id *match;
> +	struct property *prop;
> +	const __be32 *p;
> +	int num = 0;
> +	u32 val;
> +
> +	if (np == NULL)
> +		return 0;
> +
> +	match = of_match_node(si5351_dt_ids, np);
> +	if (match == NULL)
> +		return -EINVAL;
> +
> +	pdata = devm_kzalloc(&client->dev, sizeof(*pdata), GFP_KERNEL);
> +	if (!pdata)
> +		return -ENOMEM;
> +
> +	pdata->variant = (enum si5351_variant)match->data;
> +	pdata->clk_xtal = of_clk_get(np, 0);
> +	if (!IS_ERR(pdata->clk_xtal))
> +		clk_put(pdata->clk_xtal);
> +	pdata->clk_clkin = of_clk_get(np, 1);
> +	if (!IS_ERR(pdata->clk_clkin))
> +		clk_put(pdata->clk_clkin);
> +
> +	/*
> +	 * property silabs,pll-source : <num src>, [<..>]
> +	 * allow to selectively set pll source
> +	 */
> +	of_property_for_each_u32(np, "silabs,pll-source", prop, p, num) {
> +		if (num >= 2) {
> +			dev_err(&client->dev,
> +				"invalid pll %d on pll-source prop\n", num);
> +			return -EINVAL;
> +		}
> +
> +		p = of_prop_next_u32(prop, p, &val);
> +		if (!p) {
> +			dev_err(&client->dev,
> +				"missing pll-source for pll %d\n", num);
> +			return -EINVAL;
> +		}
> +
> +		switch (val) {
> +		case 0:
> +			pdata->pll_src[num] = SI5351_PLL_SRC_XTAL;
> +			break;
> +		case 1:
> +			if (pdata->variant != SI5351_VARIANT_C) {
> +				dev_err(&client->dev,
> +					"invalid parent %d for pll %d\n",
> +					val, num);
> +				return -EINVAL;
> +			}
> +			pdata->pll_src[num] = SI5351_PLL_SRC_CLKIN;
> +			break;
> +		default:
> +			dev_err(&client->dev,
> +				 "invalid parent %d for pll %d\n", val, num);
> +			return -EINVAL;
> +		}
> +	}
> +
> +	/* per clkout properties */
> +	for_each_child_of_node(np, child) {
> +		if (of_property_read_u32(child, "reg", &num)) {
> +			dev_err(&client->dev, "missing reg property of %s\n",
> +				child->name);
> +			return -EINVAL;
> +		}
> +
> +		if (num >= 8 ||
> +		    (pdata->variant == SI5351_VARIANT_A3 && num >= 3)) {
> +			dev_err(&client->dev, "invalid clkout %d\n", num);
> +			return -EINVAL;
> +		}
> +
> +		if (!of_property_read_u32(child, "silabs,multisynth-source",
> +					  &val)) {
> +			switch (val) {
> +			case 0:
> +				pdata->clkout[num].multisynth_src =
> +					SI5351_MULTISYNTH_SRC_VCO0;
> +				break;
> +			case 1:
> +				pdata->clkout[num].multisynth_src =
> +					SI5351_MULTISYNTH_SRC_VCO1;
> +				break;
> +			default:
> +				dev_err(&client->dev,
> +					"invalid parent %d for multisynth %d\n",
> +					val, num);
> +				return -EINVAL;
> +			}
> +		}
> +
> +		if (!of_property_read_u32(child, "silabs,clock-source", &val)) {
> +			switch (val) {
> +			case 0:
> +				pdata->clkout[num].clkout_src =
> +					SI5351_CLKOUT_SRC_MSYNTH_N;
> +				break;
> +			case 1:
> +				pdata->clkout[num].clkout_src =
> +					SI5351_CLKOUT_SRC_MSYNTH_0_4;
> +				break;
> +			case 2:
> +				pdata->clkout[num].clkout_src =
> +					SI5351_CLKOUT_SRC_XTAL;
> +				break;
> +			case 3:
> +				if (pdata->variant != SI5351_VARIANT_C) {
> +					dev_err(&client->dev,
> +						"invalid parent %d for clkout %d\n",
> +						val, num);
> +					return -EINVAL;
> +				}
> +				pdata->clkout[num].clkout_src =
> +					SI5351_CLKOUT_SRC_CLKIN;
> +				break;
> +			default:
> +				dev_err(&client->dev,
> +					"invalid parent %d for clkout %d\n",
> +					val, num);
> +				return -EINVAL;
> +			}
> +		}
> +
> +		if (!of_property_read_u32(child, "silabs,drive-strength",
> +					  &val)) {
> +			switch (val) {
> +			case SI5351_DRIVE_2MA:
> +			case SI5351_DRIVE_4MA:
> +			case SI5351_DRIVE_6MA:
> +			case SI5351_DRIVE_8MA:
> +				pdata->clkout[num].drive = val;
> +				break;
> +			default:
> +				dev_err(&client->dev,
> +					"invalid drive strength %d for clkout %d\n",
> +					val, num);
> +				return -EINVAL;
> +			}
> +		}
> +
> +		if (!of_property_read_u32(child, "clock-frequency", &val))
> +			pdata->clkout[num].rate = val;
> +
> +		pdata->clkout[num].pll_master =
> +			of_property_read_bool(child, "silabs,pll-master");
> +	}
> +	client->dev.platform_data = pdata;
> +
> +	return 0;
> +}
> +#else
> +static int si5351_dt_parse(struct i2c_client *client)
> +{
> +	return 0;
> +}
> +#endif /* CONFIG_OF */
> +
> +static int si5351_i2c_probe(struct i2c_client *client,
> +			    const struct i2c_device_id *id)
> +{
> +	struct si5351_platform_data *pdata;
> +	struct si5351_driver_data *drvdata;
> +	struct clk_init_data init;
> +	struct clk *clk;
> +	const char *parent_names[4];
> +	u8 num_parents, num_clocks;
> +	int ret, n;
> +
> +	ret = si5351_dt_parse(client);
> +	if (ret)
> +		return ret;
> +
> +	pdata = client->dev.platform_data;
> +	if (!pdata)
> +		return -EINVAL;
> +
> +	drvdata = devm_kzalloc(&client->dev, sizeof(*drvdata), GFP_KERNEL);
> +	if (drvdata == NULL) {
> +		dev_err(&client->dev, "unable to allocate driver data\n");
> +		return -ENOMEM;
> +	}
> +
> +	i2c_set_clientdata(client, drvdata);
> +	drvdata->client = client;
> +	drvdata->variant = pdata->variant;
> +	drvdata->pxtal = pdata->clk_xtal;
> +	drvdata->pclkin = pdata->clk_clkin;
> +
> +	drvdata->regmap = devm_regmap_init_i2c(client, &si5351_regmap_config);
> +	if (IS_ERR(drvdata->regmap)) {
> +		dev_err(&client->dev, "failed to allocate register map\n");
> +		return PTR_ERR(drvdata->regmap);
> +	}
> +
> +	/* Disable interrupts */
> +	si5351_reg_write(drvdata, SI5351_INTERRUPT_MASK, 0xf0);
> +	/* Set disabled output drivers to drive low */
> +	si5351_reg_write(drvdata, SI5351_CLK3_0_DISABLE_STATE, 0x00);
> +	si5351_reg_write(drvdata, SI5351_CLK7_4_DISABLE_STATE, 0x00);
> +	/* Ensure pll select is on XTAL for Si5351A/B */
> +	if (drvdata->variant != SI5351_VARIANT_C)
> +		si5351_set_bits(drvdata, SI5351_PLL_INPUT_SOURCE,
> +				SI5351_PLLA_SOURCE | SI5351_PLLB_SOURCE, 0);
> +
> +	/* setup clock configuration */
> +	for (n = 0; n < 2; n++) {
> +		ret = _si5351_pll_reparent(drvdata, n, pdata->pll_src[n]);
> +		if (ret) {
> +			dev_err(&client->dev,
> +				"failed to reparent pll %d to %d\n",
> +				n, pdata->pll_src[n]);
> +			return ret;
> +		}
> +	}
> +
> +	for (n = 0; n < 8; n++) {
> +		ret = _si5351_msynth_reparent(drvdata, n,
> +					      pdata->clkout[n].multisynth_src);
> +		if (ret) {
> +			dev_err(&client->dev,
> +				"failed to reparent multisynth %d to %d\n",
> +				n, pdata->clkout[n].multisynth_src);
> +			return ret;
> +		}
> +
> +		ret = _si5351_clkout_reparent(drvdata, n,
> +					      pdata->clkout[n].clkout_src);
> +		if (ret) {
> +			dev_err(&client->dev,
> +				"failed to reparent clkout %d to %d\n",
> +				n, pdata->clkout[n].clkout_src);
> +			return ret;
> +		}
> +
> +		ret = _si5351_clkout_set_drive_strength(drvdata, n,
> +							pdata->clkout[n].drive);
> +		if (ret) {
> +			dev_err(&client->dev,
> +				"failed set drive strength of clkout%d to %d\n",
> +				n, pdata->clkout[n].drive);
> +			return ret;
> +		}
> +	}
> +
> +	/* register xtal input clock gate */
> +	memset(&init, 0, sizeof(init));
> +	init.name = si5351_input_names[0];
> +	init.ops = &si5351_xtal_ops;
> +	init.flags = 0;
> +	if (!IS_ERR(drvdata->pxtal)) {
> +		drvdata->pxtal_name = __clk_get_name(drvdata->pxtal);
> +		init.parent_names = &drvdata->pxtal_name;
> +		init.num_parents = 1;
> +	}
> +	drvdata->xtal.init = &init;
> +	clk = devm_clk_register(&client->dev, &drvdata->xtal);
> +	if (IS_ERR(clk)) {
> +		dev_err(&client->dev, "unable to register %s\n", init.name);
> +		return PTR_ERR(clk);
> +	}
> +
> +	/* register clkin input clock gate */
> +	if (drvdata->variant == SI5351_VARIANT_C) {
> +		memset(&init, 0, sizeof(init));
> +		init.name = si5351_input_names[1];
> +		init.ops = &si5351_clkin_ops;
> +		if (!IS_ERR(drvdata->pclkin)) {
> +			drvdata->pclkin_name = __clk_get_name(drvdata->pclkin);
> +			init.parent_names = &drvdata->pclkin_name;
> +			init.num_parents = 1;
> +		}
> +		drvdata->clkin.init = &init;
> +		clk = devm_clk_register(&client->dev, &drvdata->clkin);
> +		if (IS_ERR(clk)) {
> +			dev_err(&client->dev, "unable to register %s\n",
> +				init.name);
> +			return PTR_ERR(clk);
> +		}
> +	}
> +
> +	/* Si5351C allows to mux either xtal or clkin to PLL input */
> +	num_parents = (drvdata->variant == SI5351_VARIANT_C) ? 2 : 1;
> +	parent_names[0] = si5351_input_names[0];
> +	parent_names[1] = si5351_input_names[1];
> +
> +	/* register PLLA */
> +	drvdata->pll[0].num = 0;
> +	drvdata->pll[0].drvdata = drvdata;
> +	drvdata->pll[0].hw.init = &init;
> +	memset(&init, 0, sizeof(init));
> +	init.name = si5351_pll_names[0];
> +	init.ops = &si5351_pll_ops;
> +	init.flags = 0;
> +	init.parent_names = parent_names;
> +	init.num_parents = num_parents;
> +	clk = devm_clk_register(&client->dev, &drvdata->pll[0].hw);
> +	if (IS_ERR(clk)) {
> +		dev_err(&client->dev, "unable to register %s\n", init.name);
> +		return -EINVAL;
> +	}
> +
> +	/* register PLLB or VXCO (Si5351B) */
> +	drvdata->pll[1].num = 1;
> +	drvdata->pll[1].drvdata = drvdata;
> +	drvdata->pll[1].hw.init = &init;
> +	memset(&init, 0, sizeof(init));
> +	if (drvdata->variant == SI5351_VARIANT_B) {
> +		init.name = si5351_pll_names[2];
> +		init.ops = &si5351_vxco_ops;
> +		init.flags = CLK_IS_ROOT;
> +		init.parent_names = NULL;
> +		init.num_parents = 0;
> +	} else {
> +		init.name = si5351_pll_names[1];
> +		init.ops = &si5351_pll_ops;
> +		init.flags = 0;
> +		init.parent_names = parent_names;
> +		init.num_parents = num_parents;
> +	}
> +	clk = devm_clk_register(&client->dev, &drvdata->pll[1].hw);
> +	if (IS_ERR(clk)) {
> +		dev_err(&client->dev, "unable to register %s\n", init.name);
> +		return -EINVAL;
> +	}
> +
> +	/* register clk multisync and clk out divider */
> +	num_clocks = (drvdata->variant == SI5351_VARIANT_A3) ? 3 : 8;
> +	parent_names[0] = si5351_pll_names[0];
> +	if (drvdata->variant == SI5351_VARIANT_B)
> +		parent_names[1] = si5351_pll_names[2];
> +	else
> +		parent_names[1] = si5351_pll_names[1];
> +
> +	drvdata->msynth = devm_kzalloc(&client->dev, num_clocks *
> +				       sizeof(*drvdata->msynth), GFP_KERNEL);
> +	drvdata->clkout = devm_kzalloc(&client->dev, num_clocks *
> +				       sizeof(*drvdata->clkout), GFP_KERNEL);
> +
> +	drvdata->onecell.clk_num = num_clocks;
> +	drvdata->onecell.clks = devm_kzalloc(&client->dev,
> +		num_clocks * sizeof(*drvdata->onecell.clks), GFP_KERNEL);
> +
> +	if (WARN_ON(!drvdata->msynth || !drvdata->clkout ||
> +		    !drvdata->onecell.clks))
> +		return -ENOMEM;
> +
> +	for (n = 0; n < num_clocks; n++) {
> +		drvdata->msynth[n].num = n;
> +		drvdata->msynth[n].drvdata = drvdata;
> +		drvdata->msynth[n].hw.init = &init;
> +		memset(&init, 0, sizeof(init));
> +		init.name = si5351_msynth_names[n];
> +		init.ops = &si5351_msynth_ops;
> +		init.flags = 0;
> +		if (pdata->clkout[n].pll_master)
> +			init.flags |= CLK_SET_RATE_PARENT;
> +		init.parent_names = parent_names;
> +		init.num_parents = 2;
> +		clk = devm_clk_register(&client->dev, &drvdata->msynth[n].hw);
> +		if (IS_ERR(clk)) {
> +			dev_err(&client->dev, "unable to register %s\n",
> +				init.name);
> +			return -EINVAL;
> +		}
> +	}
> +
> +	num_parents = (drvdata->variant == SI5351_VARIANT_C) ? 4 : 3;
> +	parent_names[2] = si5351_input_names[0];
> +	parent_names[3] = si5351_input_names[1];
> +	for (n = 0; n < num_clocks; n++) {
> +		parent_names[0] = si5351_msynth_names[n];
> +		parent_names[1] = (n < 4) ? si5351_msynth_names[0] :
> +			si5351_msynth_names[4];
> +
> +		drvdata->clkout[n].num = n;
> +		drvdata->clkout[n].drvdata = drvdata;
> +		drvdata->clkout[n].hw.init = &init;
> +		memset(&init, 0, sizeof(init));
> +		init.name = si5351_clkout_names[n];
> +		init.ops = &si5351_clkout_ops;
> +		init.flags = 0;
> +		if (pdata->clkout[n].clkout_src == SI5351_CLKOUT_SRC_MSYNTH_N)
> +			init.flags |= CLK_SET_RATE_PARENT;
> +		init.parent_names = parent_names;
> +		init.num_parents = num_parents;
> +		clk = devm_clk_register(&client->dev, &drvdata->clkout[n].hw);
> +		if (IS_ERR(clk)) {
> +			dev_err(&client->dev, "unable to register %s\n",
> +				init.name);
> +			return -EINVAL;
> +		}
> +		drvdata->onecell.clks[n] = clk;
> +	}
> +
> +	ret = of_clk_add_provider(client->dev.of_node, of_clk_src_onecell_get,
> +				  &drvdata->onecell);
> +	if (ret) {
> +		dev_err(&client->dev, "unable to add clk provider\n");
> +		return ret;
> +	}
> +
> +	return 0;
> +}
> +
> +static const struct i2c_device_id si5351_i2c_ids[] = {
> +	{ "silabs,si5351", 0 },
> +	{ }
> +};
> +MODULE_DEVICE_TABLE(i2c, si5351_i2c_ids);
> +
> +static struct i2c_driver si5351_driver = {
> +	.driver = {
> +		.name = "si5351",
> +		.of_match_table = of_match_ptr(si5351_dt_ids),
> +	},
> +	.probe = si5351_i2c_probe,
> +	.id_table = si5351_i2c_ids,
> +};
> +module_i2c_driver(si5351_driver);
> +
> +MODULE_AUTHOR("Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com");
> +MODULE_DESCRIPTION("Silicon Labs Si5351A/B/C clock generator driver");
> +MODULE_LICENSE("GPL");
> diff --git a/drivers/clk/clk-si5351.h b/drivers/clk/clk-si5351.h
> new file mode 100644
> index 0000000..af41b50
> --- /dev/null
> +++ b/drivers/clk/clk-si5351.h
> @@ -0,0 +1,155 @@
> +/*
> + * clk-si5351.h: Silicon Laboratories Si5351A/B/C I2C Clock Generator
> + *
> + * Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
> + * Rabeeh Khoury <rabeeh@solid-run.com>
> + *
> + * This program is free software; you can redistribute  it and/or modify it
> + * under  the terms of  the GNU General  Public License as published by
> the + * Free Software Foundation;  either version 2 of the  License, or (at
> your + * option) any later version.
> + */
> +
> +#ifndef _CLK_SI5351_H_
> +#define _CLK_SI5351_H_
> +
> +#define SI5351_BUS_BASE_ADDR			0x60
> +
> +#define SI5351_PLL_VCO_MIN			600000000
> +#define SI5351_PLL_VCO_MAX			900000000
> +#define SI5351_MULTISYNTH_MIN_FREQ		1000000
> +#define SI5351_MULTISYNTH_DIVBY4_FREQ		150000000
> +#define SI5351_MULTISYNTH_MAX_FREQ		160000000
> +#define SI5351_MULTISYNTH67_MAX_FREQ		SI5351_MULTISYNTH_DIVBY4_FREQ
> +#define SI5351_CLKOUT_MIN_FREQ			8000
> +#define SI5351_CLKOUT_MAX_FREQ			SI5351_MULTISYNTH_MAX_FREQ
> +#define SI5351_CLKOUT67_MAX_FREQ		SI5351_MULTISYNTH67_MAX_FREQ
> +
> +#define SI5351_PLL_A_MIN			15
> +#define SI5351_PLL_A_MAX			90
> +#define SI5351_PLL_B_MAX			(SI5351_PLL_C_MAX-1)
> +#define SI5351_PLL_C_MAX			1048575
> +#define SI5351_MULTISYNTH_A_MIN			6
> +#define SI5351_MULTISYNTH_A_MAX			1800
> +#define SI5351_MULTISYNTH67_A_MAX		254
> +#define SI5351_MULTISYNTH_B_MAX			(SI5351_MULTISYNTH_C_MAX-1)
> +#define SI5351_MULTISYNTH_C_MAX			1048575
> +#define SI5351_MULTISYNTH_P1_MAX		((1<<18)-1)
> +#define SI5351_MULTISYNTH_P2_MAX		((1<<20)-1)
> +#define SI5351_MULTISYNTH_P3_MAX		((1<<20)-1)
> +
> +#define SI5351_DEVICE_STATUS			0
> +#define SI5351_INTERRUPT_STATUS			1
> +#define SI5351_INTERRUPT_MASK			2
> +#define  SI5351_STATUS_SYS_INIT			(1<<7)
> +#define  SI5351_STATUS_LOL_B			(1<<6)
> +#define  SI5351_STATUS_LOL_A			(1<<5)
> +#define  SI5351_STATUS_LOS			(1<<4)
> +#define SI5351_OUTPUT_ENABLE_CTRL		3
> +#define SI5351_OEB_PIN_ENABLE_CTRL		9
> +#define SI5351_PLL_INPUT_SOURCE			15
> +#define  SI5351_CLKIN_DIV_MASK			(3<<6)
> +#define  SI5351_CLKIN_DIV_1			(0<<6)
> +#define  SI5351_CLKIN_DIV_2			(1<<6)
> +#define  SI5351_CLKIN_DIV_4			(2<<6)
> +#define  SI5351_CLKIN_DIV_8			(3<<6)
> +#define  SI5351_PLLB_SOURCE			(1<<3)
> +#define  SI5351_PLLA_SOURCE			(1<<2)
> +
> +#define SI5351_CLK0_CTRL			16
> +#define SI5351_CLK1_CTRL			17
> +#define SI5351_CLK2_CTRL			18
> +#define SI5351_CLK3_CTRL			19
> +#define SI5351_CLK4_CTRL			20
> +#define SI5351_CLK5_CTRL			21
> +#define SI5351_CLK6_CTRL			22
> +#define SI5351_CLK7_CTRL			23
> +#define  SI5351_CLK_POWERDOWN			(1<<7)
> +#define  SI5351_CLK_INTEGER_MODE		(1<<6)
> +#define  SI5351_CLK_PLL_SELECT			(1<<5)
> +#define  SI5351_CLK_INVERT			(1<<4)
> +#define  SI5351_CLK_INPUT_MASK			(3<<2)
> +#define  SI5351_CLK_INPUT_XTAL			(0<<2)
> +#define  SI5351_CLK_INPUT_CLKIN			(1<<2)
> +#define  SI5351_CLK_INPUT_MULTISYNTH_0_4	(2<<2)
> +#define  SI5351_CLK_INPUT_MULTISYNTH_N		(3<<2)
> +#define  SI5351_CLK_DRIVE_STRENGTH_MASK		(3<<0)
> +#define  SI5351_CLK_DRIVE_STRENGTH_2MA		(0<<0)
> +#define  SI5351_CLK_DRIVE_STRENGTH_4MA		(1<<0)
> +#define  SI5351_CLK_DRIVE_STRENGTH_6MA		(2<<0)
> +#define  SI5351_CLK_DRIVE_STRENGTH_8MA		(3<<0)
> +
> +#define SI5351_CLK3_0_DISABLE_STATE		24
> +#define SI5351_CLK7_4_DISABLE_STATE		25
> +#define  SI5351_CLK_DISABLE_STATE_LOW		0
> +#define  SI5351_CLK_DISABLE_STATE_HIGH		1
> +#define  SI5351_CLK_DISABLE_STATE_FLOAT		2
> +#define  SI5351_CLK_DISABLE_STATE_NEVER		3
> +
> +#define SI5351_PARAMETERS_LENGTH		8
> +#define SI5351_PLLA_PARAMETERS			26
> +#define SI5351_PLLB_PARAMETERS			34
> +#define SI5351_CLK0_PARAMETERS			42
> +#define SI5351_CLK1_PARAMETERS			50
> +#define SI5351_CLK2_PARAMETERS			58
> +#define SI5351_CLK3_PARAMETERS			66
> +#define SI5351_CLK4_PARAMETERS			74
> +#define SI5351_CLK5_PARAMETERS			82
> +#define SI5351_CLK6_PARAMETERS			90
> +#define SI5351_CLK7_PARAMETERS			91
> +#define SI5351_CLK6_7_OUTPUT_DIVIDER		92
> +#define  SI5351_OUTPUT_CLK_DIV_MASK		(7 << 4)
> +#define  SI5351_OUTPUT_CLK6_DIV_MASK		(7 << 0)
> +#define  SI5351_OUTPUT_CLK_DIV_SHIFT		4
> +#define  SI5351_OUTPUT_CLK_DIV6_SHIFT		0
> +#define  SI5351_OUTPUT_CLK_DIV_1		0
> +#define  SI5351_OUTPUT_CLK_DIV_2		1
> +#define  SI5351_OUTPUT_CLK_DIV_4		2
> +#define  SI5351_OUTPUT_CLK_DIV_8		3
> +#define  SI5351_OUTPUT_CLK_DIV_16		4
> +#define  SI5351_OUTPUT_CLK_DIV_32		5
> +#define  SI5351_OUTPUT_CLK_DIV_64		6
> +#define  SI5351_OUTPUT_CLK_DIV_128		7
> +#define  SI5351_OUTPUT_CLK_DIVBY4		(3<<2)
> +
> +#define SI5351_SSC_PARAM0			149
> +#define SI5351_SSC_PARAM1			150
> +#define SI5351_SSC_PARAM2			151
> +#define SI5351_SSC_PARAM3			152
> +#define SI5351_SSC_PARAM4			153
> +#define SI5351_SSC_PARAM5			154
> +#define SI5351_SSC_PARAM6			155
> +#define SI5351_SSC_PARAM7			156
> +#define SI5351_SSC_PARAM8			157
> +#define SI5351_SSC_PARAM9			158
> +#define SI5351_SSC_PARAM10			159
> +#define SI5351_SSC_PARAM11			160
> +#define SI5351_SSC_PARAM12			161
> +
> +#define SI5351_VXCO_PARAMETERS_LOW		162
> +#define SI5351_VXCO_PARAMETERS_MID		163
> +#define SI5351_VXCO_PARAMETERS_HIGH		164
> +
> +#define SI5351_CLK0_PHASE_OFFSET		165
> +#define SI5351_CLK1_PHASE_OFFSET		166
> +#define SI5351_CLK2_PHASE_OFFSET		167
> +#define SI5351_CLK3_PHASE_OFFSET		168
> +#define SI5351_CLK4_PHASE_OFFSET		169
> +#define SI5351_CLK5_PHASE_OFFSET		170
> +
> +#define SI5351_PLL_RESET			177
> +#define  SI5351_PLL_RESET_B			(1<<7)
> +#define  SI5351_PLL_RESET_A			(1<<5)
> +
> +#define SI5351_CRYSTAL_LOAD			183
> +#define  SI5351_CRYSTAL_LOAD_MASK		(3<<6)
> +#define  SI5351_CRYSTAL_LOAD_6PF		(1<<6)
> +#define  SI5351_CRYSTAL_LOAD_8PF		(2<<6)
> +#define  SI5351_CRYSTAL_LOAD_10PF		(3<<6)
> +
> +#define SI5351_FANOUT_ENABLE			187
> +#define  SI5351_CLKIN_ENABLE			(1<<7)
> +#define  SI5351_XTAL_ENABLE			(1<<6)
> +#define  SI5351_MULTISYNTH_ENABLE		(1<<4)
> +
> +#endif
> diff --git a/include/linux/platform_data/si5351.h
> b/include/linux/platform_data/si5351.h new file mode 100644
> index 0000000..92dabca
> --- /dev/null
> +++ b/include/linux/platform_data/si5351.h
> @@ -0,0 +1,114 @@
> +/*
> + * Si5351A/B/C programmable clock generator platform_data.
> + */
> +
> +#ifndef __LINUX_PLATFORM_DATA_SI5351_H__
> +#define __LINUX_PLATFORM_DATA_SI5351_H__
> +
> +struct clk;
> +
> +/**
> + * enum si5351_variant - SiLabs Si5351 chip variant
> + * @SI5351_VARIANT_A: Si5351A (8 output clocks, XTAL input)
> + * @SI5351_VARIANT_A3: Si5351A MSOP10 (3 output clocks, XTAL input)
> + * @SI5351_VARIANT_B: Si5351B (8 output clocks, XTAL/VXCO input)
> + * @SI5351_VARIANT_C: Si5351C (8 output clocks, XTAL/CLKIN input)
> + */
> +enum si5351_variant {
> +	SI5351_VARIANT_A = 1,
> +	SI5351_VARIANT_A3 = 2,
> +	SI5351_VARIANT_B = 3,
> +	SI5351_VARIANT_C = 4,
> +};
> +
> +/**
> + * enum si5351_pll_src - Si5351 pll clock source
> + * @SI5351_PLL_SRC_DEFAULT: default, do not change eeprom config
> + * @SI5351_PLL_SRC_XTAL: pll source clock is XTAL input
> + * @SI5351_PLL_SRC_CLKIN: pll source clock is CLKIN input (Si5351C only)
> + */
> +enum si5351_pll_src {
> +	SI5351_PLL_SRC_DEFAULT = 0,
> +	SI5351_PLL_SRC_XTAL = 1,
> +	SI5351_PLL_SRC_CLKIN = 2,
> +};
> +
> +/**
> + * enum si5351_multisynth_src - Si5351 multisynth clock source
> + * @SI5351_MULTISYNTH_SRC_DEFAULT: default, do not change eeprom config
> + * @SI5351_MULTISYNTH_SRC_VCO0: multisynth source clock is VCO0
> + * @SI5351_MULTISYNTH_SRC_VCO1: multisynth source clock is VCO1/VXCO
> + */
> +enum si5351_multisynth_src {
> +	SI5351_MULTISYNTH_SRC_DEFAULT = 0,
> +	SI5351_MULTISYNTH_SRC_VCO0 = 1,
> +	SI5351_MULTISYNTH_SRC_VCO1 = 2,
> +};
> +
> +/**
> + * enum si5351_clkout_src - Si5351 clock output clock source
> + * @SI5351_CLKOUT_SRC_DEFAULT: default, do not change eeprom config
> + * @SI5351_CLKOUT_SRC_MSYNTH_N: clkout N source clock is multisynth N
> + * @SI5351_CLKOUT_SRC_MSYNTH_0_4: clkout N source clock is multisynth 0
> (N<4) + *                                or 4 (N>=4)
> + * @SI5351_CLKOUT_SRC_XTAL: clkout N source clock is XTAL
> + * @SI5351_CLKOUT_SRC_CLKIN: clkout N source clock is CLKIN (Si5351C only)
> + */
> +enum si5351_clkout_src {
> +	SI5351_CLKOUT_SRC_DEFAULT = 0,
> +	SI5351_CLKOUT_SRC_MSYNTH_N = 1,
> +	SI5351_CLKOUT_SRC_MSYNTH_0_4 = 2,
> +	SI5351_CLKOUT_SRC_XTAL = 3,
> +	SI5351_CLKOUT_SRC_CLKIN = 4,
> +};
> +
> +/**
> + * enum si5351_drive_strength - Si5351 clock output drive strength
> + * @SI5351_DRIVE_DEFAULT: default, do not change eeprom config
> + * @SI5351_DRIVE_2MA: 2mA clock output drive strength
> + * @SI5351_DRIVE_4MA: 4mA clock output drive strength
> + * @SI5351_DRIVE_6MA: 6mA clock output drive strength
> + * @SI5351_DRIVE_8MA: 8mA clock output drive strength
> + */
> +enum si5351_drive_strength {
> +	SI5351_DRIVE_DEFAULT = 0,
> +	SI5351_DRIVE_2MA = 2,
> +	SI5351_DRIVE_4MA = 4,
> +	SI5351_DRIVE_6MA = 6,
> +	SI5351_DRIVE_8MA = 8,
> +};
> +
> +/**
> + * struct si5351_clkout_config - Si5351 clock output configuration
> + * @clkout: clkout number
> + * @multisynth_src: multisynth source clock
> + * @clkout_src: clkout source clock
> + * @pll_master: if true, clkout can also change pll rate
> + * @drive: output drive strength
> + * @rate: initial clkout rate, or default if 0
> + */
> +struct si5351_clkout_config {
> +	enum si5351_multisynth_src multisynth_src;
> +	enum si5351_clkout_src clkout_src;
> +	enum si5351_drive_strength drive;
> +	bool pll_master;
> +	unsigned long rate;
> +};
> +
> +/**
> + * struct si5351_platform_data - Platform data for the Si5351 clock driver
> + * @variant: Si5351 chip variant
> + * @clk_xtal: xtal input clock
> + * @clk_clkin: clkin input clock
> + * @pll_src: array of pll source clock setting
> + * @clkout: array of clkout configuration
> + */
> +struct si5351_platform_data {
> +	enum si5351_variant variant;
> +	struct clk *clk_xtal;
> +	struct clk *clk_clkin;
> +	enum si5351_pll_src pll_src[2];
> +	struct si5351_clkout_config clkout[8];
> +};
> +
> +#endif
Mike Turquette April 12, 2013, 6:19 p.m. UTC | #2
Quoting Sebastian Hesselbarth (2013-04-11 12:42:29)
> This patch adds a common clock driver for Silicon Labs Si5351a/b/c
> i2c programmable clock generators. Currently, the driver does not
> support VXCO feature of si5351b. Passing platform_data or DT bindings
> selectively allows to overwrite stored Si5351 configuration which is
> very helpful for clock generators with empty eeprom configuration.
> Corresponding device tree binding documentation is also added.
> 
> Signed-off-by: Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
> Tested-by: Daniel Mack <zonque@gmail.com>
> Acked-by: Guenter Roeck <linux@roeck-us.net>

Made it under the wire!  Thanks for cooking up v8 so quickly.  I've
taken this into clk-next.  It's cool to have a clk driver for a discrete
clock generator device.

Regards,
Mike

> ---
> Changes from v7:
> - readd clk powerup on clkout set_rate (Suggested by Michal Bachraty)
> 
> Changes from v6:
> - make invalid DT data parsing fatal (Suggested by Guenter Roeck)
> - add more variant checks and return errors
> - remove inline from _si5351_* functions
> - remove pll reset/clkout powerdown for gapless tuning
>   (Provided by Michal Backraty)
> 
> Changes from v5:
> - removed __clk_set_flags
> - remove CONFIG_OF dependency
> - introduce si5351_platform_data (non-DT untested)
> - parse DT into si5351_platform_data (tested)
> - minor cleanups
> 
> Changes from v4:
> - move from clk-private.h to clk-provider.h (Reported by Mike Turquette)
> - use __clk_set_flags() helper
> 
> Changes from v3:
> - add silabs prefix to custom DT properties (Reported by Lars-Peter Clausen)
> - use sizeof(*foo) instead of sizeof(struct bar) (Reported by Lars-Peter Clausen)
> - check return value of of_clk_add_provider (Reported by Lars-Peter Clausen)
> - clean up i2c client init (Reported by Lars-Peter Clausen)
> - silence successful probe (Suggested by Lars-Peter Clausen)
> - make CONFIG_CLK_SI5351 depend on CONFIG_OF
> 
> Changes from v2:
> - add curly brackets to if-else-statements (Reported by Daniel Mack)
> - fix div-by-zero for clk6/clk7 (Reported by Daniel Mack)
> - fix parameter address calculation for clk6/clk7
> 
> Changes from v1:
> - remove .is_enabled functions as they read from i2c
>   (Reported by Daniel Mack)
> - add CLK_SET_RATE_PARENT on clkout reparent if clkout uses
>   its own multisync
> 
> Cc: Grant Likely <grant.likely@secretlab.ca>
> Cc: Rob Herring <rob.herring@calxeda.com>
> Cc: Rob Landley <rob@landley.net>
> Cc: Mike Turquette <mturquette@linaro.org>
> Cc: Stephen Warren <swarren@nvidia.com>
> Cc: Thierry Reding <thierry.reding@avionic-design.de>
> Cc: Dom Cobley <popcornmix@gmail.com>
> Cc: Linus Walleij <linus.walleij@linaro.org>
> Cc: Arnd Bergmann <arnd@arndb.de>
> Cc: Andrew Morton <akpm@linux-foundation.org>
> Cc: Pawel Moll <pawel.moll@arm.com>
> Cc: Mark Brown <broonie@opensource.wolfsonmicro.com>
> Cc: Russell King - ARM Linux <linux@arm.linux.org.uk>
> Cc: Rabeeh Khoury <rabeeh@solid-run.com>
> Cc: Daniel Mack <zonque@gmail.com>
> Cc: Jean-Francois Moine <moinejf@free.fr>
> Cc: Lars-Peter Clausen <lars@metafoo.de>
> Cc: Guenter Roeck <linux@roeck-us.net>
> Cc: Michal Bachraty <michal.bachraty@streamunlimited.com>
> Cc: devicetree-discuss@lists.ozlabs.org
> Cc: linux-doc@vger.kernel.org
> Cc: linux-kernel@vger.kernel.org
> Cc: linux-arm-kernel@lists.infradead.org
> ---
>  .../devicetree/bindings/clock/silabs,si5351.txt    |  114 ++
>  .../devicetree/bindings/vendor-prefixes.txt        |    1 +
>  drivers/clk/Kconfig                                |    9 +
>  drivers/clk/Makefile                               |    1 +
>  drivers/clk/clk-si5351.c                           | 1510 ++++++++++++++++++++
>  drivers/clk/clk-si5351.h                           |  155 ++
>  include/linux/platform_data/si5351.h               |  114 ++
>  7 files changed, 1904 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/clock/silabs,si5351.txt
>  create mode 100644 drivers/clk/clk-si5351.c
>  create mode 100644 drivers/clk/clk-si5351.h
>  create mode 100644 include/linux/platform_data/si5351.h
> 
> diff --git a/Documentation/devicetree/bindings/clock/silabs,si5351.txt b/Documentation/devicetree/bindings/clock/silabs,si5351.txt
> new file mode 100644
> index 0000000..cc37465
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/clock/silabs,si5351.txt
> @@ -0,0 +1,114 @@
> +Binding for Silicon Labs Si5351a/b/c programmable i2c clock generator.
> +
> +Reference
> +[1] Si5351A/B/C Data Sheet
> +    http://www.silabs.com/Support%20Documents/TechnicalDocs/Si5351.pdf
> +
> +The Si5351a/b/c are programmable i2c clock generators with upto 8 output
> +clocks. Si5351a also has a reduced pin-count package (MSOP10) where only
> +3 output clocks are accessible. The internal structure of the clock
> +generators can be found in [1].
> +
> +==I2C device node==
> +
> +Required properties:
> +- compatible: shall be one of "silabs,si5351{a,a-msop,b,c}".
> +- reg: i2c device address, shall be 0x60 or 0x61.
> +- #clock-cells: from common clock binding; shall be set to 1.
> +- clocks: from common clock binding; list of parent clock
> +  handles, shall be xtal reference clock or xtal and clkin for
> +  si5351c only.
> +- #address-cells: shall be set to 1.
> +- #size-cells: shall be set to 0.
> +
> +Optional properties:
> +- silabs,pll-source: pair of (number, source) for each pll. Allows
> +  to overwrite clock source of pll A (number=0) or B (number=1).
> +
> +==Child nodes==
> +
> +Each of the clock outputs can be overwritten individually by
> +using a child node to the I2C device node. If a child node for a clock
> +output is not set, the eeprom configuration is not overwritten.
> +
> +Required child node properties:
> +- reg: number of clock output.
> +
> +Optional child node properties:
> +- silabs,clock-source: source clock of the output divider stage N, shall be
> +  0 = multisynth N
> +  1 = multisynth 0 for output clocks 0-3, else multisynth4
> +  2 = xtal
> +  3 = clkin (si5351c only)
> +- silabs,drive-strength: output drive strength in mA, shall be one of {2,4,6,8}.
> +- silabs,multisynth-source: source pll A(0) or B(1) of corresponding multisynth
> +  divider.
> +- silabs,pll-master: boolean, multisynth can change pll frequency.
> +
> +==Example==
> +
> +/* 25MHz reference crystal */
> +ref25: ref25M {
> +       compatible = "fixed-clock";
> +       #clock-cells = <0>;
> +       clock-frequency = <25000000>;
> +};
> +
> +i2c-master-node {
> +
> +       /* Si5351a msop10 i2c clock generator */
> +       si5351a: clock-generator@60 {
> +               compatible = "silabs,si5351a-msop";
> +               reg = <0x60>;
> +               #address-cells = <1>;
> +               #size-cells = <0>;
> +               #clock-cells = <1>;
> +
> +               /* connect xtal input to 25MHz reference */
> +               clocks = <&ref25>;
> +
> +               /* connect xtal input as source of pll0 and pll1 */
> +               silabs,pll-source = <0 0>, <1 0>;
> +
> +               /*
> +                * overwrite clkout0 configuration with:
> +                * - 8mA output drive strength
> +                * - pll0 as clock source of multisynth0
> +                * - multisynth0 as clock source of output divider
> +                * - multisynth0 can change pll0
> +                * - set initial clock frequency of 74.25MHz
> +                */
> +               clkout0 {
> +                       reg = <0>;
> +                       silabs,drive-strength = <8>;
> +                       silabs,multisynth-source = <0>;
> +                       silabs,clock-source = <0>;
> +                       silabs,pll-master;
> +                       clock-frequency = <74250000>;
> +               };
> +
> +               /*
> +                * overwrite clkout1 configuration with:
> +                * - 4mA output drive strength
> +                * - pll1 as clock source of multisynth1
> +                * - multisynth1 as clock source of output divider
> +                * - multisynth1 can change pll1
> +                */
> +               clkout1 {
> +                       reg = <1>;
> +                       silabs,drive-strength = <4>;
> +                       silabs,multisynth-source = <1>;
> +                       silabs,clock-source = <0>;
> +                       pll-master;
> +               };
> +
> +               /*
> +                * overwrite clkout2 configuration with:
> +                * - xtal as clock source of output divider
> +                */
> +               clkout2 {
> +                       reg = <2>;
> +                       silabs,clock-source = <2>;
> +               };
> +       };
> +};
> diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt
> index 19e1ef7..ca60849 100644
> --- a/Documentation/devicetree/bindings/vendor-prefixes.txt
> +++ b/Documentation/devicetree/bindings/vendor-prefixes.txt
> @@ -48,6 +48,7 @@ samsung       Samsung Semiconductor
>  sbs    Smart Battery System
>  schindler      Schindler
>  sil    Silicon Image
> +silabs Silicon Laboratories
>  simtek
>  sirf   SiRF Technology, Inc.
>  snps   Synopsys, Inc.
> diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig
> index a47e6ee..5039e41 100644
> --- a/drivers/clk/Kconfig
> +++ b/drivers/clk/Kconfig
> @@ -55,6 +55,15 @@ config COMMON_CLK_MAX77686
>         ---help---
>           This driver supports Maxim 77686 crystal oscillator clock. 
>  
> +config COMMON_CLK_SI5351
> +       tristate "Clock driver for SiLabs 5351A/B/C"
> +       depends on I2C
> +       select REGMAP_I2C
> +       select RATIONAL
> +       ---help---
> +         This driver supports Silicon Labs 5351A/B/C programmable clock
> +         generators.
> +
>  config CLK_TWL6040
>         tristate "External McPDM functional clock from twl6040"
>         depends on TWL6040_CORE
> diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
> index 300d477..92ca698 100644
> --- a/drivers/clk/Makefile
> +++ b/drivers/clk/Makefile
> @@ -33,4 +33,5 @@ obj-$(CONFIG_X86)             += x86/
>  # Chip specific
>  obj-$(CONFIG_COMMON_CLK_WM831X) += clk-wm831x.o
>  obj-$(CONFIG_COMMON_CLK_MAX77686) += clk-max77686.o
> +obj-$(CONFIG_COMMON_CLK_SI5351) += clk-si5351.o
>  obj-$(CONFIG_CLK_TWL6040)      += clk-twl6040.o
> diff --git a/drivers/clk/clk-si5351.c b/drivers/clk/clk-si5351.c
> new file mode 100644
> index 0000000..8927284
> --- /dev/null
> +++ b/drivers/clk/clk-si5351.c
> @@ -0,0 +1,1510 @@
> +/*
> + * clk-si5351.c: Silicon Laboratories Si5351A/B/C I2C Clock Generator
> + *
> + * Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
> + * Rabeeh Khoury <rabeeh@solid-run.com>
> + *
> + * References:
> + * [1] "Si5351A/B/C Data Sheet"
> + *     http://www.silabs.com/Support%20Documents/TechnicalDocs/Si5351.pdf
> + * [2] "Manually Generating an Si5351 Register Map"
> + *     http://www.silabs.com/Support%20Documents/TechnicalDocs/AN619.pdf
> + *
> + * This program is free software; you can redistribute  it and/or modify it
> + * under  the terms of  the GNU General  Public License as published by the
> + * Free Software Foundation;  either version 2 of the  License, or (at your
> + * option) any later version.
> + */
> +
> +#include <linux/module.h>
> +#include <linux/kernel.h>
> +#include <linux/clkdev.h>
> +#include <linux/clk-provider.h>
> +#include <linux/delay.h>
> +#include <linux/err.h>
> +#include <linux/errno.h>
> +#include <linux/rational.h>
> +#include <linux/i2c.h>
> +#include <linux/of_platform.h>
> +#include <linux/platform_data/si5351.h>
> +#include <linux/regmap.h>
> +#include <linux/slab.h>
> +#include <linux/string.h>
> +#include <asm/div64.h>
> +
> +#include "clk-si5351.h"
> +
> +struct si5351_driver_data;
> +
> +struct si5351_parameters {
> +       unsigned long   p1;
> +       unsigned long   p2;
> +       unsigned long   p3;
> +       int             valid;
> +};
> +
> +struct si5351_hw_data {
> +       struct clk_hw                   hw;
> +       struct si5351_driver_data       *drvdata;
> +       struct si5351_parameters        params;
> +       unsigned char                   num;
> +};
> +
> +struct si5351_driver_data {
> +       enum si5351_variant     variant;
> +       struct i2c_client       *client;
> +       struct regmap           *regmap;
> +       struct clk_onecell_data onecell;
> +
> +       struct clk              *pxtal;
> +       const char              *pxtal_name;
> +       struct clk_hw           xtal;
> +       struct clk              *pclkin;
> +       const char              *pclkin_name;
> +       struct clk_hw           clkin;
> +
> +       struct si5351_hw_data   pll[2];
> +       struct si5351_hw_data   *msynth;
> +       struct si5351_hw_data   *clkout;
> +};
> +
> +static const char const *si5351_input_names[] = {
> +       "xtal", "clkin"
> +};
> +static const char const *si5351_pll_names[] = {
> +       "plla", "pllb", "vxco"
> +};
> +static const char const *si5351_msynth_names[] = {
> +       "ms0", "ms1", "ms2", "ms3", "ms4", "ms5", "ms6", "ms7"
> +};
> +static const char const *si5351_clkout_names[] = {
> +       "clk0", "clk1", "clk2", "clk3", "clk4", "clk5", "clk6", "clk7"
> +};
> +
> +/*
> + * Si5351 i2c regmap
> + */
> +static inline u8 si5351_reg_read(struct si5351_driver_data *drvdata, u8 reg)
> +{
> +       u32 val;
> +       int ret;
> +
> +       ret = regmap_read(drvdata->regmap, reg, &val);
> +       if (ret) {
> +               dev_err(&drvdata->client->dev,
> +                       "unable to read from reg%02x\n", reg);
> +               return 0;
> +       }
> +
> +       return (u8)val;
> +}
> +
> +static inline int si5351_bulk_read(struct si5351_driver_data *drvdata,
> +                                  u8 reg, u8 count, u8 *buf)
> +{
> +       return regmap_bulk_read(drvdata->regmap, reg, buf, count);
> +}
> +
> +static inline int si5351_reg_write(struct si5351_driver_data *drvdata,
> +                                  u8 reg, u8 val)
> +{
> +       return regmap_write(drvdata->regmap, reg, val);
> +}
> +
> +static inline int si5351_bulk_write(struct si5351_driver_data *drvdata,
> +                                   u8 reg, u8 count, const u8 *buf)
> +{
> +       return regmap_raw_write(drvdata->regmap, reg, buf, count);
> +}
> +
> +static inline int si5351_set_bits(struct si5351_driver_data *drvdata,
> +                                 u8 reg, u8 mask, u8 val)
> +{
> +       return regmap_update_bits(drvdata->regmap, reg, mask, val);
> +}
> +
> +static inline u8 si5351_msynth_params_address(int num)
> +{
> +       if (num > 5)
> +               return SI5351_CLK6_PARAMETERS + (num - 6);
> +       return SI5351_CLK0_PARAMETERS + (SI5351_PARAMETERS_LENGTH * num);
> +}
> +
> +static void si5351_read_parameters(struct si5351_driver_data *drvdata,
> +                                  u8 reg, struct si5351_parameters *params)
> +{
> +       u8 buf[SI5351_PARAMETERS_LENGTH];
> +
> +       switch (reg) {
> +       case SI5351_CLK6_PARAMETERS:
> +       case SI5351_CLK7_PARAMETERS:
> +               buf[0] = si5351_reg_read(drvdata, reg);
> +               params->p1 = buf[0];
> +               params->p2 = 0;
> +               params->p3 = 1;
> +               break;
> +       default:
> +               si5351_bulk_read(drvdata, reg, SI5351_PARAMETERS_LENGTH, buf);
> +               params->p1 = ((buf[2] & 0x03) << 16) | (buf[3] << 8) | buf[4];
> +               params->p2 = ((buf[5] & 0x0f) << 16) | (buf[6] << 8) | buf[7];
> +               params->p3 = ((buf[5] & 0xf0) << 12) | (buf[0] << 8) | buf[1];
> +       }
> +       params->valid = 1;
> +}
> +
> +static void si5351_write_parameters(struct si5351_driver_data *drvdata,
> +                                   u8 reg, struct si5351_parameters *params)
> +{
> +       u8 buf[SI5351_PARAMETERS_LENGTH];
> +
> +       switch (reg) {
> +       case SI5351_CLK6_PARAMETERS:
> +       case SI5351_CLK7_PARAMETERS:
> +               buf[0] = params->p1 & 0xff;
> +               si5351_reg_write(drvdata, reg, buf[0]);
> +               break;
> +       default:
> +               buf[0] = ((params->p3 & 0x0ff00) >> 8) & 0xff;
> +               buf[1] = params->p3 & 0xff;
> +               /* save rdiv and divby4 */
> +               buf[2] = si5351_reg_read(drvdata, reg + 2) & ~0x03;
> +               buf[2] |= ((params->p1 & 0x30000) >> 16) & 0x03;
> +               buf[3] = ((params->p1 & 0x0ff00) >> 8) & 0xff;
> +               buf[4] = params->p1 & 0xff;
> +               buf[5] = ((params->p3 & 0xf0000) >> 12) |
> +                       ((params->p2 & 0xf0000) >> 16);
> +               buf[6] = ((params->p2 & 0x0ff00) >> 8) & 0xff;
> +               buf[7] = params->p2 & 0xff;
> +               si5351_bulk_write(drvdata, reg, SI5351_PARAMETERS_LENGTH, buf);
> +       }
> +}
> +
> +static bool si5351_regmap_is_volatile(struct device *dev, unsigned int reg)
> +{
> +       switch (reg) {
> +       case SI5351_DEVICE_STATUS:
> +       case SI5351_INTERRUPT_STATUS:
> +       case SI5351_PLL_RESET:
> +               return true;
> +       }
> +       return false;
> +}
> +
> +static bool si5351_regmap_is_writeable(struct device *dev, unsigned int reg)
> +{
> +       /* reserved registers */
> +       if (reg >= 4 && reg <= 8)
> +               return false;
> +       if (reg >= 10 && reg <= 14)
> +               return false;
> +       if (reg >= 173 && reg <= 176)
> +               return false;
> +       if (reg >= 178 && reg <= 182)
> +               return false;
> +       /* read-only */
> +       if (reg == SI5351_DEVICE_STATUS)
> +               return false;
> +       return true;
> +}
> +
> +static struct regmap_config si5351_regmap_config = {
> +       .reg_bits = 8,
> +       .val_bits = 8,
> +       .cache_type = REGCACHE_RBTREE,
> +       .max_register = 187,
> +       .writeable_reg = si5351_regmap_is_writeable,
> +       .volatile_reg = si5351_regmap_is_volatile,
> +};
> +
> +/*
> + * Si5351 xtal clock input
> + */
> +static int si5351_xtal_prepare(struct clk_hw *hw)
> +{
> +       struct si5351_driver_data *drvdata =
> +               container_of(hw, struct si5351_driver_data, xtal);
> +       si5351_set_bits(drvdata, SI5351_FANOUT_ENABLE,
> +                       SI5351_XTAL_ENABLE, SI5351_XTAL_ENABLE);
> +       return 0;
> +}
> +
> +static void si5351_xtal_unprepare(struct clk_hw *hw)
> +{
> +       struct si5351_driver_data *drvdata =
> +               container_of(hw, struct si5351_driver_data, xtal);
> +       si5351_set_bits(drvdata, SI5351_FANOUT_ENABLE,
> +                       SI5351_XTAL_ENABLE, 0);
> +}
> +
> +static const struct clk_ops si5351_xtal_ops = {
> +       .prepare = si5351_xtal_prepare,
> +       .unprepare = si5351_xtal_unprepare,
> +};
> +
> +/*
> + * Si5351 clkin clock input (Si5351C only)
> + */
> +static int si5351_clkin_prepare(struct clk_hw *hw)
> +{
> +       struct si5351_driver_data *drvdata =
> +               container_of(hw, struct si5351_driver_data, clkin);
> +       si5351_set_bits(drvdata, SI5351_FANOUT_ENABLE,
> +                       SI5351_CLKIN_ENABLE, SI5351_CLKIN_ENABLE);
> +       return 0;
> +}
> +
> +static void si5351_clkin_unprepare(struct clk_hw *hw)
> +{
> +       struct si5351_driver_data *drvdata =
> +               container_of(hw, struct si5351_driver_data, clkin);
> +       si5351_set_bits(drvdata, SI5351_FANOUT_ENABLE,
> +                       SI5351_CLKIN_ENABLE, 0);
> +}
> +
> +/*
> + * CMOS clock source constraints:
> + * The input frequency range of the PLL is 10Mhz to 40MHz.
> + * If CLKIN is >40MHz, the input divider must be used.
> + */
> +static unsigned long si5351_clkin_recalc_rate(struct clk_hw *hw,
> +                                             unsigned long parent_rate)
> +{
> +       struct si5351_driver_data *drvdata =
> +               container_of(hw, struct si5351_driver_data, clkin);
> +       unsigned long rate;
> +       unsigned char idiv;
> +
> +       rate = parent_rate;
> +       if (parent_rate > 160000000) {
> +               idiv = SI5351_CLKIN_DIV_8;
> +               rate /= 8;
> +       } else if (parent_rate > 80000000) {
> +               idiv = SI5351_CLKIN_DIV_4;
> +               rate /= 4;
> +       } else if (parent_rate > 40000000) {
> +               idiv = SI5351_CLKIN_DIV_2;
> +               rate /= 2;
> +       } else {
> +               idiv = SI5351_CLKIN_DIV_1;
> +       }
> +
> +       si5351_set_bits(drvdata, SI5351_PLL_INPUT_SOURCE,
> +                       SI5351_CLKIN_DIV_MASK, idiv);
> +
> +       dev_dbg(&drvdata->client->dev, "%s - clkin div = %d, rate = %lu\n",
> +               __func__, (1 << (idiv >> 6)), rate);
> +
> +       return rate;
> +}
> +
> +static const struct clk_ops si5351_clkin_ops = {
> +       .prepare = si5351_clkin_prepare,
> +       .unprepare = si5351_clkin_unprepare,
> +       .recalc_rate = si5351_clkin_recalc_rate,
> +};
> +
> +/*
> + * Si5351 vxco clock input (Si5351B only)
> + */
> +
> +static int si5351_vxco_prepare(struct clk_hw *hw)
> +{
> +       struct si5351_hw_data *hwdata =
> +               container_of(hw, struct si5351_hw_data, hw);
> +
> +       dev_warn(&hwdata->drvdata->client->dev, "VXCO currently unsupported\n");
> +
> +       return 0;
> +}
> +
> +static void si5351_vxco_unprepare(struct clk_hw *hw)
> +{
> +}
> +
> +static unsigned long si5351_vxco_recalc_rate(struct clk_hw *hw,
> +                                            unsigned long parent_rate)
> +{
> +       return 0;
> +}
> +
> +static int si5351_vxco_set_rate(struct clk_hw *hw, unsigned long rate,
> +                               unsigned long parent)
> +{
> +       return 0;
> +}
> +
> +static const struct clk_ops si5351_vxco_ops = {
> +       .prepare = si5351_vxco_prepare,
> +       .unprepare = si5351_vxco_unprepare,
> +       .recalc_rate = si5351_vxco_recalc_rate,
> +       .set_rate = si5351_vxco_set_rate,
> +};
> +
> +/*
> + * Si5351 pll a/b
> + *
> + * Feedback Multisynth Divider Equations [2]
> + *
> + * fVCO = fIN * (a + b/c)
> + *
> + * with 15 + 0/1048575 <= (a + b/c) <= 90 + 0/1048575 and
> + * fIN = fXTAL or fIN = fCLKIN/CLKIN_DIV
> + *
> + * Feedback Multisynth Register Equations
> + *
> + * (1) MSNx_P1[17:0] = 128 * a + floor(128 * b/c) - 512
> + * (2) MSNx_P2[19:0] = 128 * b - c * floor(128 * b/c) = (128*b) mod c
> + * (3) MSNx_P3[19:0] = c
> + *
> + * Transposing (2) yields: (4) floor(128 * b/c) = (128 * b / MSNx_P2)/c
> + *
> + * Using (4) on (1) yields:
> + * MSNx_P1 = 128 * a + (128 * b/MSNx_P2)/c - 512
> + * MSNx_P1 + 512 + MSNx_P2/c = 128 * a + 128 * b/c
> + *
> + * a + b/c = (MSNx_P1 + MSNx_P2/MSNx_P3 + 512)/128
> + *         = (MSNx_P1*MSNx_P3 + MSNx_P2 + 512*MSNx_P3)/(128*MSNx_P3)
> + *
> + */
> +static int _si5351_pll_reparent(struct si5351_driver_data *drvdata,
> +                               int num, enum si5351_pll_src parent)
> +{
> +       u8 mask = (num == 0) ? SI5351_PLLA_SOURCE : SI5351_PLLB_SOURCE;
> +
> +       if (parent == SI5351_PLL_SRC_DEFAULT)
> +               return 0;
> +
> +       if (num > 2)
> +               return -EINVAL;
> +
> +       if (drvdata->variant != SI5351_VARIANT_C &&
> +           parent != SI5351_PLL_SRC_XTAL)
> +               return -EINVAL;
> +
> +       si5351_set_bits(drvdata, SI5351_PLL_INPUT_SOURCE, mask,
> +                       (parent == SI5351_PLL_SRC_XTAL) ? 0 : mask);
> +       return 0;
> +}
> +
> +static unsigned char si5351_pll_get_parent(struct clk_hw *hw)
> +{
> +       struct si5351_hw_data *hwdata =
> +               container_of(hw, struct si5351_hw_data, hw);
> +       u8 mask = (hwdata->num == 0) ? SI5351_PLLA_SOURCE : SI5351_PLLB_SOURCE;
> +       u8 val;
> +
> +       val = si5351_reg_read(hwdata->drvdata, SI5351_PLL_INPUT_SOURCE);
> +
> +       return (val & mask) ? 1 : 0;
> +}
> +
> +static int si5351_pll_set_parent(struct clk_hw *hw, u8 index)
> +{
> +       struct si5351_hw_data *hwdata =
> +               container_of(hw, struct si5351_hw_data, hw);
> +
> +       if (hwdata->drvdata->variant != SI5351_VARIANT_C &&
> +           index > 0)
> +               return -EPERM;
> +
> +       if (index > 1)
> +               return -EINVAL;
> +
> +       return _si5351_pll_reparent(hwdata->drvdata, hwdata->num,
> +                            (index == 0) ? SI5351_PLL_SRC_XTAL :
> +                            SI5351_PLL_SRC_CLKIN);
> +}
> +
> +static unsigned long si5351_pll_recalc_rate(struct clk_hw *hw,
> +                                           unsigned long parent_rate)
> +{
> +       struct si5351_hw_data *hwdata =
> +               container_of(hw, struct si5351_hw_data, hw);
> +       u8 reg = (hwdata->num == 0) ? SI5351_PLLA_PARAMETERS :
> +               SI5351_PLLB_PARAMETERS;
> +       unsigned long long rate;
> +
> +       if (!hwdata->params.valid)
> +               si5351_read_parameters(hwdata->drvdata, reg, &hwdata->params);
> +
> +       if (hwdata->params.p3 == 0)
> +               return parent_rate;
> +
> +       /* fVCO = fIN * (P1*P3 + 512*P3 + P2)/(128*P3) */
> +       rate  = hwdata->params.p1 * hwdata->params.p3;
> +       rate += 512 * hwdata->params.p3;
> +       rate += hwdata->params.p2;
> +       rate *= parent_rate;
> +       do_div(rate, 128 * hwdata->params.p3);
> +
> +       dev_dbg(&hwdata->drvdata->client->dev,
> +               "%s - %s: p1 = %lu, p2 = %lu, p3 = %lu, parent_rate = %lu, rate = %lu\n",
> +               __func__, __clk_get_name(hwdata->hw.clk),
> +               hwdata->params.p1, hwdata->params.p2, hwdata->params.p3,
> +               parent_rate, (unsigned long)rate);
> +
> +       return (unsigned long)rate;
> +}
> +
> +static long si5351_pll_round_rate(struct clk_hw *hw, unsigned long rate,
> +                                 unsigned long *parent_rate)
> +{
> +       struct si5351_hw_data *hwdata =
> +               container_of(hw, struct si5351_hw_data, hw);
> +       unsigned long rfrac, denom, a, b, c;
> +       unsigned long long lltmp;
> +
> +       if (rate < SI5351_PLL_VCO_MIN)
> +               rate = SI5351_PLL_VCO_MIN;
> +       if (rate > SI5351_PLL_VCO_MAX)
> +               rate = SI5351_PLL_VCO_MAX;
> +
> +       /* determine integer part of feedback equation */
> +       a = rate / *parent_rate;
> +
> +       if (a < SI5351_PLL_A_MIN)
> +               rate = *parent_rate * SI5351_PLL_A_MIN;
> +       if (a > SI5351_PLL_A_MAX)
> +               rate = *parent_rate * SI5351_PLL_A_MAX;
> +
> +       /* find best approximation for b/c = fVCO mod fIN */
> +       denom = 1000 * 1000;
> +       lltmp = rate % (*parent_rate);
> +       lltmp *= denom;
> +       do_div(lltmp, *parent_rate);
> +       rfrac = (unsigned long)lltmp;
> +
> +       b = 0;
> +       c = 1;
> +       if (rfrac)
> +               rational_best_approximation(rfrac, denom,
> +                                   SI5351_PLL_B_MAX, SI5351_PLL_C_MAX, &b, &c);
> +
> +       /* calculate parameters */
> +       hwdata->params.p3  = c;
> +       hwdata->params.p2  = (128 * b) % c;
> +       hwdata->params.p1  = 128 * a;
> +       hwdata->params.p1 += (128 * b / c);
> +       hwdata->params.p1 -= 512;
> +
> +       /* recalculate rate by fIN * (a + b/c) */
> +       lltmp  = *parent_rate;
> +       lltmp *= b;
> +       do_div(lltmp, c);
> +
> +       rate  = (unsigned long)lltmp;
> +       rate += *parent_rate * a;
> +
> +       dev_dbg(&hwdata->drvdata->client->dev,
> +               "%s - %s: a = %lu, b = %lu, c = %lu, parent_rate = %lu, rate = %lu\n",
> +               __func__, __clk_get_name(hwdata->hw.clk), a, b, c,
> +               *parent_rate, rate);
> +
> +       return rate;
> +}
> +
> +static int si5351_pll_set_rate(struct clk_hw *hw, unsigned long rate,
> +                              unsigned long parent_rate)
> +{
> +       struct si5351_hw_data *hwdata =
> +               container_of(hw, struct si5351_hw_data, hw);
> +       u8 reg = (hwdata->num == 0) ? SI5351_PLLA_PARAMETERS :
> +               SI5351_PLLB_PARAMETERS;
> +
> +       /* write multisynth parameters */
> +       si5351_write_parameters(hwdata->drvdata, reg, &hwdata->params);
> +
> +       /* plla/pllb ctrl is in clk6/clk7 ctrl registers */
> +       si5351_set_bits(hwdata->drvdata, SI5351_CLK6_CTRL + hwdata->num,
> +               SI5351_CLK_INTEGER_MODE,
> +               (hwdata->params.p2 == 0) ? SI5351_CLK_INTEGER_MODE : 0);
> +
> +       dev_dbg(&hwdata->drvdata->client->dev,
> +               "%s - %s: p1 = %lu, p2 = %lu, p3 = %lu, parent_rate = %lu, rate = %lu\n",
> +               __func__, __clk_get_name(hwdata->hw.clk),
> +               hwdata->params.p1, hwdata->params.p2, hwdata->params.p3,
> +               parent_rate, rate);
> +
> +       return 0;
> +}
> +
> +static const struct clk_ops si5351_pll_ops = {
> +       .set_parent = si5351_pll_set_parent,
> +       .get_parent = si5351_pll_get_parent,
> +       .recalc_rate = si5351_pll_recalc_rate,
> +       .round_rate = si5351_pll_round_rate,
> +       .set_rate = si5351_pll_set_rate,
> +};
> +
> +/*
> + * Si5351 multisync divider
> + *
> + * for fOUT <= 150 MHz:
> + *
> + * fOUT = (fIN * (a + b/c)) / CLKOUTDIV
> + *
> + * with 6 + 0/1048575 <= (a + b/c) <= 1800 + 0/1048575 and
> + * fIN = fVCO0, fVCO1
> + *
> + * Output Clock Multisynth Register Equations
> + *
> + * MSx_P1[17:0] = 128 * a + floor(128 * b/c) - 512
> + * MSx_P2[19:0] = 128 * b - c * floor(128 * b/c) = (128*b) mod c
> + * MSx_P3[19:0] = c
> + *
> + * MS[6,7] are integer (P1) divide only, P2 = 0, P3 = 0
> + *
> + * for 150MHz < fOUT <= 160MHz:
> + *
> + * MSx_P1 = 0, MSx_P2 = 0, MSx_P3 = 1, MSx_INT = 1, MSx_DIVBY4 = 11b
> + */
> +static int _si5351_msynth_reparent(struct si5351_driver_data *drvdata,
> +                                  int num, enum si5351_multisynth_src parent)
> +{
> +       if (parent == SI5351_MULTISYNTH_SRC_DEFAULT)
> +               return 0;
> +
> +       if (num > 8)
> +               return -EINVAL;
> +
> +       si5351_set_bits(drvdata, SI5351_CLK0_CTRL + num, SI5351_CLK_PLL_SELECT,
> +                       (parent == SI5351_MULTISYNTH_SRC_VCO0) ? 0 :
> +                       SI5351_CLK_PLL_SELECT);
> +       return 0;
> +}
> +
> +static unsigned char si5351_msynth_get_parent(struct clk_hw *hw)
> +{
> +       struct si5351_hw_data *hwdata =
> +               container_of(hw, struct si5351_hw_data, hw);
> +       u8 val;
> +
> +       val = si5351_reg_read(hwdata->drvdata, SI5351_CLK0_CTRL + hwdata->num);
> +
> +       return (val & SI5351_CLK_PLL_SELECT) ? 1 : 0;
> +}
> +
> +static int si5351_msynth_set_parent(struct clk_hw *hw, u8 index)
> +{
> +       struct si5351_hw_data *hwdata =
> +               container_of(hw, struct si5351_hw_data, hw);
> +
> +       return _si5351_msynth_reparent(hwdata->drvdata, hwdata->num,
> +                              (index == 0) ? SI5351_MULTISYNTH_SRC_VCO0 :
> +                              SI5351_MULTISYNTH_SRC_VCO1);
> +}
> +
> +static unsigned long si5351_msynth_recalc_rate(struct clk_hw *hw,
> +                                              unsigned long parent_rate)
> +{
> +       struct si5351_hw_data *hwdata =
> +               container_of(hw, struct si5351_hw_data, hw);
> +       u8 reg = si5351_msynth_params_address(hwdata->num);
> +       unsigned long long rate;
> +       unsigned long m;
> +
> +       if (!hwdata->params.valid)
> +               si5351_read_parameters(hwdata->drvdata, reg, &hwdata->params);
> +
> +       if (hwdata->params.p3 == 0)
> +               return parent_rate;
> +
> +       /*
> +        * multisync0-5: fOUT = (128 * P3 * fIN) / (P1*P3 + P2 + 512*P3)
> +        * multisync6-7: fOUT = fIN / P1
> +        */
> +       rate = parent_rate;
> +       if (hwdata->num > 5) {
> +               m = hwdata->params.p1;
> +       } else if ((si5351_reg_read(hwdata->drvdata, reg + 2) &
> +                   SI5351_OUTPUT_CLK_DIVBY4) == SI5351_OUTPUT_CLK_DIVBY4) {
> +               m = 4;
> +       } else {
> +               rate *= 128 * hwdata->params.p3;
> +               m = hwdata->params.p1 * hwdata->params.p3;
> +               m += hwdata->params.p2;
> +               m += 512 * hwdata->params.p3;
> +       }
> +
> +       if (m == 0)
> +               return 0;
> +       do_div(rate, m);
> +
> +       dev_dbg(&hwdata->drvdata->client->dev,
> +               "%s - %s: p1 = %lu, p2 = %lu, p3 = %lu, m = %lu, parent_rate = %lu, rate = %lu\n",
> +               __func__, __clk_get_name(hwdata->hw.clk),
> +               hwdata->params.p1, hwdata->params.p2, hwdata->params.p3,
> +               m, parent_rate, (unsigned long)rate);
> +
> +       return (unsigned long)rate;
> +}
> +
> +static long si5351_msynth_round_rate(struct clk_hw *hw, unsigned long rate,
> +                                    unsigned long *parent_rate)
> +{
> +       struct si5351_hw_data *hwdata =
> +               container_of(hw, struct si5351_hw_data, hw);
> +       unsigned long long lltmp;
> +       unsigned long a, b, c;
> +       int divby4;
> +
> +       /* multisync6-7 can only handle freqencies < 150MHz */
> +       if (hwdata->num >= 6 && rate > SI5351_MULTISYNTH67_MAX_FREQ)
> +               rate = SI5351_MULTISYNTH67_MAX_FREQ;
> +
> +       /* multisync frequency is 1MHz .. 160MHz */
> +       if (rate > SI5351_MULTISYNTH_MAX_FREQ)
> +               rate = SI5351_MULTISYNTH_MAX_FREQ;
> +       if (rate < SI5351_MULTISYNTH_MIN_FREQ)
> +               rate = SI5351_MULTISYNTH_MIN_FREQ;
> +
> +       divby4 = 0;
> +       if (rate > SI5351_MULTISYNTH_DIVBY4_FREQ)
> +               divby4 = 1;
> +
> +       /* multisync can set pll */
> +       if (__clk_get_flags(hwdata->hw.clk) & CLK_SET_RATE_PARENT) {
> +               /*
> +                * find largest integer divider for max
> +                * vco frequency and given target rate
> +                */
> +               if (divby4 == 0) {
> +                       lltmp = SI5351_PLL_VCO_MAX;
> +                       do_div(lltmp, rate);
> +                       a = (unsigned long)lltmp;
> +               } else
> +                       a = 4;
> +
> +               b = 0;
> +               c = 1;
> +
> +               *parent_rate = a * rate;
> +       } else {
> +               unsigned long rfrac, denom;
> +
> +               /* disable divby4 */
> +               if (divby4) {
> +                       rate = SI5351_MULTISYNTH_DIVBY4_FREQ;
> +                       divby4 = 0;
> +               }
> +
> +               /* determine integer part of divider equation */
> +               a = *parent_rate / rate;
> +               if (a < SI5351_MULTISYNTH_A_MIN)
> +                       a = SI5351_MULTISYNTH_A_MIN;
> +               if (hwdata->num >= 6 && a > SI5351_MULTISYNTH67_A_MAX)
> +                       a = SI5351_MULTISYNTH67_A_MAX;
> +               else if (a > SI5351_MULTISYNTH_A_MAX)
> +                       a = SI5351_MULTISYNTH_A_MAX;
> +
> +               /* find best approximation for b/c = fVCO mod fOUT */
> +               denom = 1000 * 1000;
> +               lltmp = (*parent_rate) % rate;
> +               lltmp *= denom;
> +               do_div(lltmp, rate);
> +               rfrac = (unsigned long)lltmp;
> +
> +               b = 0;
> +               c = 1;
> +               if (rfrac)
> +                       rational_best_approximation(rfrac, denom,
> +                           SI5351_MULTISYNTH_B_MAX, SI5351_MULTISYNTH_C_MAX,
> +                           &b, &c);
> +       }
> +
> +       /* recalculate rate by fOUT = fIN / (a + b/c) */
> +       lltmp  = *parent_rate;
> +       lltmp *= c;
> +       do_div(lltmp, a * c + b);
> +       rate  = (unsigned long)lltmp;
> +
> +       /* calculate parameters */
> +       if (divby4) {
> +               hwdata->params.p3 = 1;
> +               hwdata->params.p2 = 0;
> +               hwdata->params.p1 = 0;
> +       } else {
> +               hwdata->params.p3  = c;
> +               hwdata->params.p2  = (128 * b) % c;
> +               hwdata->params.p1  = 128 * a;
> +               hwdata->params.p1 += (128 * b / c);
> +               hwdata->params.p1 -= 512;
> +       }
> +
> +       dev_dbg(&hwdata->drvdata->client->dev,
> +               "%s - %s: a = %lu, b = %lu, c = %lu, divby4 = %d, parent_rate = %lu, rate = %lu\n",
> +               __func__, __clk_get_name(hwdata->hw.clk), a, b, c, divby4,
> +               *parent_rate, rate);
> +
> +       return rate;
> +}
> +
> +static int si5351_msynth_set_rate(struct clk_hw *hw, unsigned long rate,
> +                                 unsigned long parent_rate)
> +{
> +       struct si5351_hw_data *hwdata =
> +               container_of(hw, struct si5351_hw_data, hw);
> +       u8 reg = si5351_msynth_params_address(hwdata->num);
> +       int divby4 = 0;
> +
> +       /* write multisynth parameters */
> +       si5351_write_parameters(hwdata->drvdata, reg, &hwdata->params);
> +
> +       if (rate > SI5351_MULTISYNTH_DIVBY4_FREQ)
> +               divby4 = 1;
> +
> +       /* enable/disable integer mode and divby4 on multisynth0-5 */
> +       if (hwdata->num < 6) {
> +               si5351_set_bits(hwdata->drvdata, reg + 2,
> +                               SI5351_OUTPUT_CLK_DIVBY4,
> +                               (divby4) ? SI5351_OUTPUT_CLK_DIVBY4 : 0);
> +               si5351_set_bits(hwdata->drvdata, SI5351_CLK0_CTRL + hwdata->num,
> +                       SI5351_CLK_INTEGER_MODE,
> +                       (hwdata->params.p2 == 0) ? SI5351_CLK_INTEGER_MODE : 0);
> +       }
> +
> +       dev_dbg(&hwdata->drvdata->client->dev,
> +               "%s - %s: p1 = %lu, p2 = %lu, p3 = %lu, divby4 = %d, parent_rate = %lu, rate = %lu\n",
> +               __func__, __clk_get_name(hwdata->hw.clk),
> +               hwdata->params.p1, hwdata->params.p2, hwdata->params.p3,
> +               divby4, parent_rate, rate);
> +
> +       return 0;
> +}
> +
> +static const struct clk_ops si5351_msynth_ops = {
> +       .set_parent = si5351_msynth_set_parent,
> +       .get_parent = si5351_msynth_get_parent,
> +       .recalc_rate = si5351_msynth_recalc_rate,
> +       .round_rate = si5351_msynth_round_rate,
> +       .set_rate = si5351_msynth_set_rate,
> +};
> +
> +/*
> + * Si5351 clkout divider
> + */
> +static int _si5351_clkout_reparent(struct si5351_driver_data *drvdata,
> +                                  int num, enum si5351_clkout_src parent)
> +{
> +       u8 val;
> +
> +       if (num > 8)
> +               return -EINVAL;
> +
> +       switch (parent) {
> +       case SI5351_CLKOUT_SRC_MSYNTH_N:
> +               val = SI5351_CLK_INPUT_MULTISYNTH_N;
> +               break;
> +       case SI5351_CLKOUT_SRC_MSYNTH_0_4:
> +               /* clk0/clk4 can only connect to its own multisync */
> +               if (num == 0 || num == 4)
> +                       val = SI5351_CLK_INPUT_MULTISYNTH_N;
> +               else
> +                       val = SI5351_CLK_INPUT_MULTISYNTH_0_4;
> +               break;
> +       case SI5351_CLKOUT_SRC_XTAL:
> +               val = SI5351_CLK_INPUT_XTAL;
> +               break;
> +       case SI5351_CLKOUT_SRC_CLKIN:
> +               if (drvdata->variant != SI5351_VARIANT_C)
> +                       return -EINVAL;
> +
> +               val = SI5351_CLK_INPUT_CLKIN;
> +               break;
> +       default:
> +               return 0;
> +       }
> +
> +       si5351_set_bits(drvdata, SI5351_CLK0_CTRL + num,
> +                       SI5351_CLK_INPUT_MASK, val);
> +       return 0;
> +}
> +
> +static int _si5351_clkout_set_drive_strength(
> +       struct si5351_driver_data *drvdata, int num,
> +       enum si5351_drive_strength drive)
> +{
> +       u8 mask;
> +
> +       if (num > 8)
> +               return -EINVAL;
> +
> +       switch (drive) {
> +       case SI5351_DRIVE_2MA:
> +               mask = SI5351_CLK_DRIVE_STRENGTH_2MA;
> +               break;
> +       case SI5351_DRIVE_4MA:
> +               mask = SI5351_CLK_DRIVE_STRENGTH_4MA;
> +               break;
> +       case SI5351_DRIVE_6MA:
> +               mask = SI5351_CLK_DRIVE_STRENGTH_6MA;
> +               break;
> +       case SI5351_DRIVE_8MA:
> +               mask = SI5351_CLK_DRIVE_STRENGTH_8MA;
> +               break;
> +       default:
> +               return 0;
> +       }
> +
> +       si5351_set_bits(drvdata, SI5351_CLK0_CTRL + num,
> +                       SI5351_CLK_DRIVE_STRENGTH_MASK, mask);
> +       return 0;
> +}
> +
> +static int si5351_clkout_prepare(struct clk_hw *hw)
> +{
> +       struct si5351_hw_data *hwdata =
> +               container_of(hw, struct si5351_hw_data, hw);
> +
> +       si5351_set_bits(hwdata->drvdata, SI5351_CLK0_CTRL + hwdata->num,
> +                       SI5351_CLK_POWERDOWN, 0);
> +       si5351_set_bits(hwdata->drvdata, SI5351_OUTPUT_ENABLE_CTRL,
> +                       (1 << hwdata->num), 0);
> +       return 0;
> +}
> +
> +static void si5351_clkout_unprepare(struct clk_hw *hw)
> +{
> +       struct si5351_hw_data *hwdata =
> +               container_of(hw, struct si5351_hw_data, hw);
> +
> +       si5351_set_bits(hwdata->drvdata, SI5351_CLK0_CTRL + hwdata->num,
> +                       SI5351_CLK_POWERDOWN, SI5351_CLK_POWERDOWN);
> +       si5351_set_bits(hwdata->drvdata, SI5351_OUTPUT_ENABLE_CTRL,
> +                       (1 << hwdata->num), (1 << hwdata->num));
> +}
> +
> +static u8 si5351_clkout_get_parent(struct clk_hw *hw)
> +{
> +       struct si5351_hw_data *hwdata =
> +               container_of(hw, struct si5351_hw_data, hw);
> +       int index = 0;
> +       unsigned char val;
> +
> +       val = si5351_reg_read(hwdata->drvdata, SI5351_CLK0_CTRL + hwdata->num);
> +       switch (val & SI5351_CLK_INPUT_MASK) {
> +       case SI5351_CLK_INPUT_MULTISYNTH_N:
> +               index = 0;
> +               break;
> +       case SI5351_CLK_INPUT_MULTISYNTH_0_4:
> +               index = 1;
> +               break;
> +       case SI5351_CLK_INPUT_XTAL:
> +               index = 2;
> +               break;
> +       case SI5351_CLK_INPUT_CLKIN:
> +               index = 3;
> +               break;
> +       }
> +
> +       return index;
> +}
> +
> +static int si5351_clkout_set_parent(struct clk_hw *hw, u8 index)
> +{
> +       struct si5351_hw_data *hwdata =
> +               container_of(hw, struct si5351_hw_data, hw);
> +       enum si5351_clkout_src parent = SI5351_CLKOUT_SRC_DEFAULT;
> +
> +       switch (index) {
> +       case 0:
> +               parent = SI5351_CLKOUT_SRC_MSYNTH_N;
> +               break;
> +       case 1:
> +               parent = SI5351_CLKOUT_SRC_MSYNTH_0_4;
> +               break;
> +       case 2:
> +               parent = SI5351_CLKOUT_SRC_XTAL;
> +               break;
> +       case 3:
> +               parent = SI5351_CLKOUT_SRC_CLKIN;
> +               break;
> +       }
> +
> +       return _si5351_clkout_reparent(hwdata->drvdata, hwdata->num, parent);
> +}
> +
> +static unsigned long si5351_clkout_recalc_rate(struct clk_hw *hw,
> +                                              unsigned long parent_rate)
> +{
> +       struct si5351_hw_data *hwdata =
> +               container_of(hw, struct si5351_hw_data, hw);
> +       unsigned char reg;
> +       unsigned char rdiv;
> +
> +       if (hwdata->num > 5)
> +               reg = si5351_msynth_params_address(hwdata->num) + 2;
> +       else
> +               reg = SI5351_CLK6_7_OUTPUT_DIVIDER;
> +
> +       rdiv = si5351_reg_read(hwdata->drvdata, reg);
> +       if (hwdata->num == 6) {
> +               rdiv &= SI5351_OUTPUT_CLK6_DIV_MASK;
> +       } else {
> +               rdiv &= SI5351_OUTPUT_CLK_DIV_MASK;
> +               rdiv >>= SI5351_OUTPUT_CLK_DIV_SHIFT;
> +       }
> +
> +       return parent_rate >> rdiv;
> +}
> +
> +static long si5351_clkout_round_rate(struct clk_hw *hw, unsigned long rate,
> +                                    unsigned long *parent_rate)
> +{
> +       struct si5351_hw_data *hwdata =
> +               container_of(hw, struct si5351_hw_data, hw);
> +       unsigned char rdiv;
> +
> +       /* clkout6/7 can only handle output freqencies < 150MHz */
> +       if (hwdata->num >= 6 && rate > SI5351_CLKOUT67_MAX_FREQ)
> +               rate = SI5351_CLKOUT67_MAX_FREQ;
> +
> +       /* clkout freqency is 8kHz - 160MHz */
> +       if (rate > SI5351_CLKOUT_MAX_FREQ)
> +               rate = SI5351_CLKOUT_MAX_FREQ;
> +       if (rate < SI5351_CLKOUT_MIN_FREQ)
> +               rate = SI5351_CLKOUT_MIN_FREQ;
> +
> +       /* request frequency if multisync master */
> +       if (__clk_get_flags(hwdata->hw.clk) & CLK_SET_RATE_PARENT) {
> +               /* use r divider for frequencies below 1MHz */
> +               rdiv = SI5351_OUTPUT_CLK_DIV_1;
> +               while (rate < SI5351_MULTISYNTH_MIN_FREQ &&
> +                      rdiv < SI5351_OUTPUT_CLK_DIV_128) {
> +                       rdiv += 1;
> +                       rate *= 2;
> +               }
> +               *parent_rate = rate;
> +       } else {
> +               unsigned long new_rate, new_err, err;
> +
> +               /* round to closed rdiv */
> +               rdiv = SI5351_OUTPUT_CLK_DIV_1;
> +               new_rate = *parent_rate;
> +               err = abs(new_rate - rate);
> +               do {
> +                       new_rate >>= 1;
> +                       new_err = abs(new_rate - rate);
> +                       if (new_err > err || rdiv == SI5351_OUTPUT_CLK_DIV_128)
> +                               break;
> +                       rdiv++;
> +                       err = new_err;
> +               } while (1);
> +       }
> +       rate = *parent_rate >> rdiv;
> +
> +       dev_dbg(&hwdata->drvdata->client->dev,
> +               "%s - %s: rdiv = %u, parent_rate = %lu, rate = %lu\n",
> +               __func__, __clk_get_name(hwdata->hw.clk), (1 << rdiv),
> +               *parent_rate, rate);
> +
> +       return rate;
> +}
> +
> +static int si5351_clkout_set_rate(struct clk_hw *hw, unsigned long rate,
> +                                 unsigned long parent_rate)
> +{
> +       struct si5351_hw_data *hwdata =
> +               container_of(hw, struct si5351_hw_data, hw);
> +       unsigned long new_rate, new_err, err;
> +       unsigned char rdiv;
> +
> +       /* round to closed rdiv */
> +       rdiv = SI5351_OUTPUT_CLK_DIV_1;
> +       new_rate = parent_rate;
> +       err = abs(new_rate - rate);
> +       do {
> +               new_rate >>= 1;
> +               new_err = abs(new_rate - rate);
> +               if (new_err > err || rdiv == SI5351_OUTPUT_CLK_DIV_128)
> +                       break;
> +               rdiv++;
> +               err = new_err;
> +       } while (1);
> +
> +       /* write output divider */
> +       switch (hwdata->num) {
> +       case 6:
> +               si5351_set_bits(hwdata->drvdata, SI5351_CLK6_7_OUTPUT_DIVIDER,
> +                               SI5351_OUTPUT_CLK6_DIV_MASK, rdiv);
> +               break;
> +       case 7:
> +               si5351_set_bits(hwdata->drvdata, SI5351_CLK6_7_OUTPUT_DIVIDER,
> +                               SI5351_OUTPUT_CLK_DIV_MASK,
> +                               rdiv << SI5351_OUTPUT_CLK_DIV_SHIFT);
> +               break;
> +       default:
> +               si5351_set_bits(hwdata->drvdata,
> +                               si5351_msynth_params_address(hwdata->num) + 2,
> +                               SI5351_OUTPUT_CLK_DIV_MASK,
> +                               rdiv << SI5351_OUTPUT_CLK_DIV_SHIFT);
> +       }
> +
> +       /* powerup clkout */
> +       si5351_set_bits(hwdata->drvdata, SI5351_CLK0_CTRL + hwdata->num,
> +                       SI5351_CLK_POWERDOWN, 0);
> +
> +       dev_dbg(&hwdata->drvdata->client->dev,
> +               "%s - %s: rdiv = %u, parent_rate = %lu, rate = %lu\n",
> +               __func__, __clk_get_name(hwdata->hw.clk), (1 << rdiv),
> +               parent_rate, rate);
> +
> +       return 0;
> +}
> +
> +static const struct clk_ops si5351_clkout_ops = {
> +       .prepare = si5351_clkout_prepare,
> +       .unprepare = si5351_clkout_unprepare,
> +       .set_parent = si5351_clkout_set_parent,
> +       .get_parent = si5351_clkout_get_parent,
> +       .recalc_rate = si5351_clkout_recalc_rate,
> +       .round_rate = si5351_clkout_round_rate,
> +       .set_rate = si5351_clkout_set_rate,
> +};
> +
> +/*
> + * Si5351 i2c probe and DT
> + */
> +#ifdef CONFIG_OF
> +static const struct of_device_id si5351_dt_ids[] = {
> +       { .compatible = "silabs,si5351a", .data = (void *)SI5351_VARIANT_A, },
> +       { .compatible = "silabs,si5351a-msop",
> +                                        .data = (void *)SI5351_VARIANT_A3, },
> +       { .compatible = "silabs,si5351b", .data = (void *)SI5351_VARIANT_B, },
> +       { .compatible = "silabs,si5351c", .data = (void *)SI5351_VARIANT_C, },
> +       { }
> +};
> +MODULE_DEVICE_TABLE(of, si5351_dt_ids);
> +
> +static int si5351_dt_parse(struct i2c_client *client)
> +{
> +       struct device_node *child, *np = client->dev.of_node;
> +       struct si5351_platform_data *pdata;
> +       const struct of_device_id *match;
> +       struct property *prop;
> +       const __be32 *p;
> +       int num = 0;
> +       u32 val;
> +
> +       if (np == NULL)
> +               return 0;
> +
> +       match = of_match_node(si5351_dt_ids, np);
> +       if (match == NULL)
> +               return -EINVAL;
> +
> +       pdata = devm_kzalloc(&client->dev, sizeof(*pdata), GFP_KERNEL);
> +       if (!pdata)
> +               return -ENOMEM;
> +
> +       pdata->variant = (enum si5351_variant)match->data;
> +       pdata->clk_xtal = of_clk_get(np, 0);
> +       if (!IS_ERR(pdata->clk_xtal))
> +               clk_put(pdata->clk_xtal);
> +       pdata->clk_clkin = of_clk_get(np, 1);
> +       if (!IS_ERR(pdata->clk_clkin))
> +               clk_put(pdata->clk_clkin);
> +
> +       /*
> +        * property silabs,pll-source : <num src>, [<..>]
> +        * allow to selectively set pll source
> +        */
> +       of_property_for_each_u32(np, "silabs,pll-source", prop, p, num) {
> +               if (num >= 2) {
> +                       dev_err(&client->dev,
> +                               "invalid pll %d on pll-source prop\n", num);
> +                       return -EINVAL;
> +               }
> +
> +               p = of_prop_next_u32(prop, p, &val);
> +               if (!p) {
> +                       dev_err(&client->dev,
> +                               "missing pll-source for pll %d\n", num);
> +                       return -EINVAL;
> +               }
> +
> +               switch (val) {
> +               case 0:
> +                       pdata->pll_src[num] = SI5351_PLL_SRC_XTAL;
> +                       break;
> +               case 1:
> +                       if (pdata->variant != SI5351_VARIANT_C) {
> +                               dev_err(&client->dev,
> +                                       "invalid parent %d for pll %d\n",
> +                                       val, num);
> +                               return -EINVAL;
> +                       }
> +                       pdata->pll_src[num] = SI5351_PLL_SRC_CLKIN;
> +                       break;
> +               default:
> +                       dev_err(&client->dev,
> +                                "invalid parent %d for pll %d\n", val, num);
> +                       return -EINVAL;
> +               }
> +       }
> +
> +       /* per clkout properties */
> +       for_each_child_of_node(np, child) {
> +               if (of_property_read_u32(child, "reg", &num)) {
> +                       dev_err(&client->dev, "missing reg property of %s\n",
> +                               child->name);
> +                       return -EINVAL;
> +               }
> +
> +               if (num >= 8 ||
> +                   (pdata->variant == SI5351_VARIANT_A3 && num >= 3)) {
> +                       dev_err(&client->dev, "invalid clkout %d\n", num);
> +                       return -EINVAL;
> +               }
> +
> +               if (!of_property_read_u32(child, "silabs,multisynth-source",
> +                                         &val)) {
> +                       switch (val) {
> +                       case 0:
> +                               pdata->clkout[num].multisynth_src =
> +                                       SI5351_MULTISYNTH_SRC_VCO0;
> +                               break;
> +                       case 1:
> +                               pdata->clkout[num].multisynth_src =
> +                                       SI5351_MULTISYNTH_SRC_VCO1;
> +                               break;
> +                       default:
> +                               dev_err(&client->dev,
> +                                       "invalid parent %d for multisynth %d\n",
> +                                       val, num);
> +                               return -EINVAL;
> +                       }
> +               }
> +
> +               if (!of_property_read_u32(child, "silabs,clock-source", &val)) {
> +                       switch (val) {
> +                       case 0:
> +                               pdata->clkout[num].clkout_src =
> +                                       SI5351_CLKOUT_SRC_MSYNTH_N;
> +                               break;
> +                       case 1:
> +                               pdata->clkout[num].clkout_src =
> +                                       SI5351_CLKOUT_SRC_MSYNTH_0_4;
> +                               break;
> +                       case 2:
> +                               pdata->clkout[num].clkout_src =
> +                                       SI5351_CLKOUT_SRC_XTAL;
> +                               break;
> +                       case 3:
> +                               if (pdata->variant != SI5351_VARIANT_C) {
> +                                       dev_err(&client->dev,
> +                                               "invalid parent %d for clkout %d\n",
> +                                               val, num);
> +                                       return -EINVAL;
> +                               }
> +                               pdata->clkout[num].clkout_src =
> +                                       SI5351_CLKOUT_SRC_CLKIN;
> +                               break;
> +                       default:
> +                               dev_err(&client->dev,
> +                                       "invalid parent %d for clkout %d\n",
> +                                       val, num);
> +                               return -EINVAL;
> +                       }
> +               }
> +
> +               if (!of_property_read_u32(child, "silabs,drive-strength",
> +                                         &val)) {
> +                       switch (val) {
> +                       case SI5351_DRIVE_2MA:
> +                       case SI5351_DRIVE_4MA:
> +                       case SI5351_DRIVE_6MA:
> +                       case SI5351_DRIVE_8MA:
> +                               pdata->clkout[num].drive = val;
> +                               break;
> +                       default:
> +                               dev_err(&client->dev,
> +                                       "invalid drive strength %d for clkout %d\n",
> +                                       val, num);
> +                               return -EINVAL;
> +                       }
> +               }
> +
> +               if (!of_property_read_u32(child, "clock-frequency", &val))
> +                       pdata->clkout[num].rate = val;
> +
> +               pdata->clkout[num].pll_master =
> +                       of_property_read_bool(child, "silabs,pll-master");
> +       }
> +       client->dev.platform_data = pdata;
> +
> +       return 0;
> +}
> +#else
> +static int si5351_dt_parse(struct i2c_client *client)
> +{
> +       return 0;
> +}
> +#endif /* CONFIG_OF */
> +
> +static int si5351_i2c_probe(struct i2c_client *client,
> +                           const struct i2c_device_id *id)
> +{
> +       struct si5351_platform_data *pdata;
> +       struct si5351_driver_data *drvdata;
> +       struct clk_init_data init;
> +       struct clk *clk;
> +       const char *parent_names[4];
> +       u8 num_parents, num_clocks;
> +       int ret, n;
> +
> +       ret = si5351_dt_parse(client);
> +       if (ret)
> +               return ret;
> +
> +       pdata = client->dev.platform_data;
> +       if (!pdata)
> +               return -EINVAL;
> +
> +       drvdata = devm_kzalloc(&client->dev, sizeof(*drvdata), GFP_KERNEL);
> +       if (drvdata == NULL) {
> +               dev_err(&client->dev, "unable to allocate driver data\n");
> +               return -ENOMEM;
> +       }
> +
> +       i2c_set_clientdata(client, drvdata);
> +       drvdata->client = client;
> +       drvdata->variant = pdata->variant;
> +       drvdata->pxtal = pdata->clk_xtal;
> +       drvdata->pclkin = pdata->clk_clkin;
> +
> +       drvdata->regmap = devm_regmap_init_i2c(client, &si5351_regmap_config);
> +       if (IS_ERR(drvdata->regmap)) {
> +               dev_err(&client->dev, "failed to allocate register map\n");
> +               return PTR_ERR(drvdata->regmap);
> +       }
> +
> +       /* Disable interrupts */
> +       si5351_reg_write(drvdata, SI5351_INTERRUPT_MASK, 0xf0);
> +       /* Set disabled output drivers to drive low */
> +       si5351_reg_write(drvdata, SI5351_CLK3_0_DISABLE_STATE, 0x00);
> +       si5351_reg_write(drvdata, SI5351_CLK7_4_DISABLE_STATE, 0x00);
> +       /* Ensure pll select is on XTAL for Si5351A/B */
> +       if (drvdata->variant != SI5351_VARIANT_C)
> +               si5351_set_bits(drvdata, SI5351_PLL_INPUT_SOURCE,
> +                               SI5351_PLLA_SOURCE | SI5351_PLLB_SOURCE, 0);
> +
> +       /* setup clock configuration */
> +       for (n = 0; n < 2; n++) {
> +               ret = _si5351_pll_reparent(drvdata, n, pdata->pll_src[n]);
> +               if (ret) {
> +                       dev_err(&client->dev,
> +                               "failed to reparent pll %d to %d\n",
> +                               n, pdata->pll_src[n]);
> +                       return ret;
> +               }
> +       }
> +
> +       for (n = 0; n < 8; n++) {
> +               ret = _si5351_msynth_reparent(drvdata, n,
> +                                             pdata->clkout[n].multisynth_src);
> +               if (ret) {
> +                       dev_err(&client->dev,
> +                               "failed to reparent multisynth %d to %d\n",
> +                               n, pdata->clkout[n].multisynth_src);
> +                       return ret;
> +               }
> +
> +               ret = _si5351_clkout_reparent(drvdata, n,
> +                                             pdata->clkout[n].clkout_src);
> +               if (ret) {
> +                       dev_err(&client->dev,
> +                               "failed to reparent clkout %d to %d\n",
> +                               n, pdata->clkout[n].clkout_src);
> +                       return ret;
> +               }
> +
> +               ret = _si5351_clkout_set_drive_strength(drvdata, n,
> +                                                       pdata->clkout[n].drive);
> +               if (ret) {
> +                       dev_err(&client->dev,
> +                               "failed set drive strength of clkout%d to %d\n",
> +                               n, pdata->clkout[n].drive);
> +                       return ret;
> +               }
> +       }
> +
> +       /* register xtal input clock gate */
> +       memset(&init, 0, sizeof(init));
> +       init.name = si5351_input_names[0];
> +       init.ops = &si5351_xtal_ops;
> +       init.flags = 0;
> +       if (!IS_ERR(drvdata->pxtal)) {
> +               drvdata->pxtal_name = __clk_get_name(drvdata->pxtal);
> +               init.parent_names = &drvdata->pxtal_name;
> +               init.num_parents = 1;
> +       }
> +       drvdata->xtal.init = &init;
> +       clk = devm_clk_register(&client->dev, &drvdata->xtal);
> +       if (IS_ERR(clk)) {
> +               dev_err(&client->dev, "unable to register %s\n", init.name);
> +               return PTR_ERR(clk);
> +       }
> +
> +       /* register clkin input clock gate */
> +       if (drvdata->variant == SI5351_VARIANT_C) {
> +               memset(&init, 0, sizeof(init));
> +               init.name = si5351_input_names[1];
> +               init.ops = &si5351_clkin_ops;
> +               if (!IS_ERR(drvdata->pclkin)) {
> +                       drvdata->pclkin_name = __clk_get_name(drvdata->pclkin);
> +                       init.parent_names = &drvdata->pclkin_name;
> +                       init.num_parents = 1;
> +               }
> +               drvdata->clkin.init = &init;
> +               clk = devm_clk_register(&client->dev, &drvdata->clkin);
> +               if (IS_ERR(clk)) {
> +                       dev_err(&client->dev, "unable to register %s\n",
> +                               init.name);
> +                       return PTR_ERR(clk);
> +               }
> +       }
> +
> +       /* Si5351C allows to mux either xtal or clkin to PLL input */
> +       num_parents = (drvdata->variant == SI5351_VARIANT_C) ? 2 : 1;
> +       parent_names[0] = si5351_input_names[0];
> +       parent_names[1] = si5351_input_names[1];
> +
> +       /* register PLLA */
> +       drvdata->pll[0].num = 0;
> +       drvdata->pll[0].drvdata = drvdata;
> +       drvdata->pll[0].hw.init = &init;
> +       memset(&init, 0, sizeof(init));
> +       init.name = si5351_pll_names[0];
> +       init.ops = &si5351_pll_ops;
> +       init.flags = 0;
> +       init.parent_names = parent_names;
> +       init.num_parents = num_parents;
> +       clk = devm_clk_register(&client->dev, &drvdata->pll[0].hw);
> +       if (IS_ERR(clk)) {
> +               dev_err(&client->dev, "unable to register %s\n", init.name);
> +               return -EINVAL;
> +       }
> +
> +       /* register PLLB or VXCO (Si5351B) */
> +       drvdata->pll[1].num = 1;
> +       drvdata->pll[1].drvdata = drvdata;
> +       drvdata->pll[1].hw.init = &init;
> +       memset(&init, 0, sizeof(init));
> +       if (drvdata->variant == SI5351_VARIANT_B) {
> +               init.name = si5351_pll_names[2];
> +               init.ops = &si5351_vxco_ops;
> +               init.flags = CLK_IS_ROOT;
> +               init.parent_names = NULL;
> +               init.num_parents = 0;
> +       } else {
> +               init.name = si5351_pll_names[1];
> +               init.ops = &si5351_pll_ops;
> +               init.flags = 0;
> +               init.parent_names = parent_names;
> +               init.num_parents = num_parents;
> +       }
> +       clk = devm_clk_register(&client->dev, &drvdata->pll[1].hw);
> +       if (IS_ERR(clk)) {
> +               dev_err(&client->dev, "unable to register %s\n", init.name);
> +               return -EINVAL;
> +       }
> +
> +       /* register clk multisync and clk out divider */
> +       num_clocks = (drvdata->variant == SI5351_VARIANT_A3) ? 3 : 8;
> +       parent_names[0] = si5351_pll_names[0];
> +       if (drvdata->variant == SI5351_VARIANT_B)
> +               parent_names[1] = si5351_pll_names[2];
> +       else
> +               parent_names[1] = si5351_pll_names[1];
> +
> +       drvdata->msynth = devm_kzalloc(&client->dev, num_clocks *
> +                                      sizeof(*drvdata->msynth), GFP_KERNEL);
> +       drvdata->clkout = devm_kzalloc(&client->dev, num_clocks *
> +                                      sizeof(*drvdata->clkout), GFP_KERNEL);
> +
> +       drvdata->onecell.clk_num = num_clocks;
> +       drvdata->onecell.clks = devm_kzalloc(&client->dev,
> +               num_clocks * sizeof(*drvdata->onecell.clks), GFP_KERNEL);
> +
> +       if (WARN_ON(!drvdata->msynth || !drvdata->clkout ||
> +                   !drvdata->onecell.clks))
> +               return -ENOMEM;
> +
> +       for (n = 0; n < num_clocks; n++) {
> +               drvdata->msynth[n].num = n;
> +               drvdata->msynth[n].drvdata = drvdata;
> +               drvdata->msynth[n].hw.init = &init;
> +               memset(&init, 0, sizeof(init));
> +               init.name = si5351_msynth_names[n];
> +               init.ops = &si5351_msynth_ops;
> +               init.flags = 0;
> +               if (pdata->clkout[n].pll_master)
> +                       init.flags |= CLK_SET_RATE_PARENT;
> +               init.parent_names = parent_names;
> +               init.num_parents = 2;
> +               clk = devm_clk_register(&client->dev, &drvdata->msynth[n].hw);
> +               if (IS_ERR(clk)) {
> +                       dev_err(&client->dev, "unable to register %s\n",
> +                               init.name);
> +                       return -EINVAL;
> +               }
> +       }
> +
> +       num_parents = (drvdata->variant == SI5351_VARIANT_C) ? 4 : 3;
> +       parent_names[2] = si5351_input_names[0];
> +       parent_names[3] = si5351_input_names[1];
> +       for (n = 0; n < num_clocks; n++) {
> +               parent_names[0] = si5351_msynth_names[n];
> +               parent_names[1] = (n < 4) ? si5351_msynth_names[0] :
> +                       si5351_msynth_names[4];
> +
> +               drvdata->clkout[n].num = n;
> +               drvdata->clkout[n].drvdata = drvdata;
> +               drvdata->clkout[n].hw.init = &init;
> +               memset(&init, 0, sizeof(init));
> +               init.name = si5351_clkout_names[n];
> +               init.ops = &si5351_clkout_ops;
> +               init.flags = 0;
> +               if (pdata->clkout[n].clkout_src == SI5351_CLKOUT_SRC_MSYNTH_N)
> +                       init.flags |= CLK_SET_RATE_PARENT;
> +               init.parent_names = parent_names;
> +               init.num_parents = num_parents;
> +               clk = devm_clk_register(&client->dev, &drvdata->clkout[n].hw);
> +               if (IS_ERR(clk)) {
> +                       dev_err(&client->dev, "unable to register %s\n",
> +                               init.name);
> +                       return -EINVAL;
> +               }
> +               drvdata->onecell.clks[n] = clk;
> +       }
> +
> +       ret = of_clk_add_provider(client->dev.of_node, of_clk_src_onecell_get,
> +                                 &drvdata->onecell);
> +       if (ret) {
> +               dev_err(&client->dev, "unable to add clk provider\n");
> +               return ret;
> +       }
> +
> +       return 0;
> +}
> +
> +static const struct i2c_device_id si5351_i2c_ids[] = {
> +       { "silabs,si5351", 0 },
> +       { }
> +};
> +MODULE_DEVICE_TABLE(i2c, si5351_i2c_ids);
> +
> +static struct i2c_driver si5351_driver = {
> +       .driver = {
> +               .name = "si5351",
> +               .of_match_table = of_match_ptr(si5351_dt_ids),
> +       },
> +       .probe = si5351_i2c_probe,
> +       .id_table = si5351_i2c_ids,
> +};
> +module_i2c_driver(si5351_driver);
> +
> +MODULE_AUTHOR("Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com");
> +MODULE_DESCRIPTION("Silicon Labs Si5351A/B/C clock generator driver");
> +MODULE_LICENSE("GPL");
> diff --git a/drivers/clk/clk-si5351.h b/drivers/clk/clk-si5351.h
> new file mode 100644
> index 0000000..af41b50
> --- /dev/null
> +++ b/drivers/clk/clk-si5351.h
> @@ -0,0 +1,155 @@
> +/*
> + * clk-si5351.h: Silicon Laboratories Si5351A/B/C I2C Clock Generator
> + *
> + * Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
> + * Rabeeh Khoury <rabeeh@solid-run.com>
> + *
> + * This program is free software; you can redistribute  it and/or modify it
> + * under  the terms of  the GNU General  Public License as published by the
> + * Free Software Foundation;  either version 2 of the  License, or (at your
> + * option) any later version.
> + */
> +
> +#ifndef _CLK_SI5351_H_
> +#define _CLK_SI5351_H_
> +
> +#define SI5351_BUS_BASE_ADDR                   0x60
> +
> +#define SI5351_PLL_VCO_MIN                     600000000
> +#define SI5351_PLL_VCO_MAX                     900000000
> +#define SI5351_MULTISYNTH_MIN_FREQ             1000000
> +#define SI5351_MULTISYNTH_DIVBY4_FREQ          150000000
> +#define SI5351_MULTISYNTH_MAX_FREQ             160000000
> +#define SI5351_MULTISYNTH67_MAX_FREQ           SI5351_MULTISYNTH_DIVBY4_FREQ
> +#define SI5351_CLKOUT_MIN_FREQ                 8000
> +#define SI5351_CLKOUT_MAX_FREQ                 SI5351_MULTISYNTH_MAX_FREQ
> +#define SI5351_CLKOUT67_MAX_FREQ               SI5351_MULTISYNTH67_MAX_FREQ
> +
> +#define SI5351_PLL_A_MIN                       15
> +#define SI5351_PLL_A_MAX                       90
> +#define SI5351_PLL_B_MAX                       (SI5351_PLL_C_MAX-1)
> +#define SI5351_PLL_C_MAX                       1048575
> +#define SI5351_MULTISYNTH_A_MIN                        6
> +#define SI5351_MULTISYNTH_A_MAX                        1800
> +#define SI5351_MULTISYNTH67_A_MAX              254
> +#define SI5351_MULTISYNTH_B_MAX                        (SI5351_MULTISYNTH_C_MAX-1)
> +#define SI5351_MULTISYNTH_C_MAX                        1048575
> +#define SI5351_MULTISYNTH_P1_MAX               ((1<<18)-1)
> +#define SI5351_MULTISYNTH_P2_MAX               ((1<<20)-1)
> +#define SI5351_MULTISYNTH_P3_MAX               ((1<<20)-1)
> +
> +#define SI5351_DEVICE_STATUS                   0
> +#define SI5351_INTERRUPT_STATUS                        1
> +#define SI5351_INTERRUPT_MASK                  2
> +#define  SI5351_STATUS_SYS_INIT                        (1<<7)
> +#define  SI5351_STATUS_LOL_B                   (1<<6)
> +#define  SI5351_STATUS_LOL_A                   (1<<5)
> +#define  SI5351_STATUS_LOS                     (1<<4)
> +#define SI5351_OUTPUT_ENABLE_CTRL              3
> +#define SI5351_OEB_PIN_ENABLE_CTRL             9
> +#define SI5351_PLL_INPUT_SOURCE                        15
> +#define  SI5351_CLKIN_DIV_MASK                 (3<<6)
> +#define  SI5351_CLKIN_DIV_1                    (0<<6)
> +#define  SI5351_CLKIN_DIV_2                    (1<<6)
> +#define  SI5351_CLKIN_DIV_4                    (2<<6)
> +#define  SI5351_CLKIN_DIV_8                    (3<<6)
> +#define  SI5351_PLLB_SOURCE                    (1<<3)
> +#define  SI5351_PLLA_SOURCE                    (1<<2)
> +
> +#define SI5351_CLK0_CTRL                       16
> +#define SI5351_CLK1_CTRL                       17
> +#define SI5351_CLK2_CTRL                       18
> +#define SI5351_CLK3_CTRL                       19
> +#define SI5351_CLK4_CTRL                       20
> +#define SI5351_CLK5_CTRL                       21
> +#define SI5351_CLK6_CTRL                       22
> +#define SI5351_CLK7_CTRL                       23
> +#define  SI5351_CLK_POWERDOWN                  (1<<7)
> +#define  SI5351_CLK_INTEGER_MODE               (1<<6)
> +#define  SI5351_CLK_PLL_SELECT                 (1<<5)
> +#define  SI5351_CLK_INVERT                     (1<<4)
> +#define  SI5351_CLK_INPUT_MASK                 (3<<2)
> +#define  SI5351_CLK_INPUT_XTAL                 (0<<2)
> +#define  SI5351_CLK_INPUT_CLKIN                        (1<<2)
> +#define  SI5351_CLK_INPUT_MULTISYNTH_0_4       (2<<2)
> +#define  SI5351_CLK_INPUT_MULTISYNTH_N         (3<<2)
> +#define  SI5351_CLK_DRIVE_STRENGTH_MASK                (3<<0)
> +#define  SI5351_CLK_DRIVE_STRENGTH_2MA         (0<<0)
> +#define  SI5351_CLK_DRIVE_STRENGTH_4MA         (1<<0)
> +#define  SI5351_CLK_DRIVE_STRENGTH_6MA         (2<<0)
> +#define  SI5351_CLK_DRIVE_STRENGTH_8MA         (3<<0)
> +
> +#define SI5351_CLK3_0_DISABLE_STATE            24
> +#define SI5351_CLK7_4_DISABLE_STATE            25
> +#define  SI5351_CLK_DISABLE_STATE_LOW          0
> +#define  SI5351_CLK_DISABLE_STATE_HIGH         1
> +#define  SI5351_CLK_DISABLE_STATE_FLOAT                2
> +#define  SI5351_CLK_DISABLE_STATE_NEVER                3
> +
> +#define SI5351_PARAMETERS_LENGTH               8
> +#define SI5351_PLLA_PARAMETERS                 26
> +#define SI5351_PLLB_PARAMETERS                 34
> +#define SI5351_CLK0_PARAMETERS                 42
> +#define SI5351_CLK1_PARAMETERS                 50
> +#define SI5351_CLK2_PARAMETERS                 58
> +#define SI5351_CLK3_PARAMETERS                 66
> +#define SI5351_CLK4_PARAMETERS                 74
> +#define SI5351_CLK5_PARAMETERS                 82
> +#define SI5351_CLK6_PARAMETERS                 90
> +#define SI5351_CLK7_PARAMETERS                 91
> +#define SI5351_CLK6_7_OUTPUT_DIVIDER           92
> +#define  SI5351_OUTPUT_CLK_DIV_MASK            (7 << 4)
> +#define  SI5351_OUTPUT_CLK6_DIV_MASK           (7 << 0)
> +#define  SI5351_OUTPUT_CLK_DIV_SHIFT           4
> +#define  SI5351_OUTPUT_CLK_DIV6_SHIFT          0
> +#define  SI5351_OUTPUT_CLK_DIV_1               0
> +#define  SI5351_OUTPUT_CLK_DIV_2               1
> +#define  SI5351_OUTPUT_CLK_DIV_4               2
> +#define  SI5351_OUTPUT_CLK_DIV_8               3
> +#define  SI5351_OUTPUT_CLK_DIV_16              4
> +#define  SI5351_OUTPUT_CLK_DIV_32              5
> +#define  SI5351_OUTPUT_CLK_DIV_64              6
> +#define  SI5351_OUTPUT_CLK_DIV_128             7
> +#define  SI5351_OUTPUT_CLK_DIVBY4              (3<<2)
> +
> +#define SI5351_SSC_PARAM0                      149
> +#define SI5351_SSC_PARAM1                      150
> +#define SI5351_SSC_PARAM2                      151
> +#define SI5351_SSC_PARAM3                      152
> +#define SI5351_SSC_PARAM4                      153
> +#define SI5351_SSC_PARAM5                      154
> +#define SI5351_SSC_PARAM6                      155
> +#define SI5351_SSC_PARAM7                      156
> +#define SI5351_SSC_PARAM8                      157
> +#define SI5351_SSC_PARAM9                      158
> +#define SI5351_SSC_PARAM10                     159
> +#define SI5351_SSC_PARAM11                     160
> +#define SI5351_SSC_PARAM12                     161
> +
> +#define SI5351_VXCO_PARAMETERS_LOW             162
> +#define SI5351_VXCO_PARAMETERS_MID             163
> +#define SI5351_VXCO_PARAMETERS_HIGH            164
> +
> +#define SI5351_CLK0_PHASE_OFFSET               165
> +#define SI5351_CLK1_PHASE_OFFSET               166
> +#define SI5351_CLK2_PHASE_OFFSET               167
> +#define SI5351_CLK3_PHASE_OFFSET               168
> +#define SI5351_CLK4_PHASE_OFFSET               169
> +#define SI5351_CLK5_PHASE_OFFSET               170
> +
> +#define SI5351_PLL_RESET                       177
> +#define  SI5351_PLL_RESET_B                    (1<<7)
> +#define  SI5351_PLL_RESET_A                    (1<<5)
> +
> +#define SI5351_CRYSTAL_LOAD                    183
> +#define  SI5351_CRYSTAL_LOAD_MASK              (3<<6)
> +#define  SI5351_CRYSTAL_LOAD_6PF               (1<<6)
> +#define  SI5351_CRYSTAL_LOAD_8PF               (2<<6)
> +#define  SI5351_CRYSTAL_LOAD_10PF              (3<<6)
> +
> +#define SI5351_FANOUT_ENABLE                   187
> +#define  SI5351_CLKIN_ENABLE                   (1<<7)
> +#define  SI5351_XTAL_ENABLE                    (1<<6)
> +#define  SI5351_MULTISYNTH_ENABLE              (1<<4)
> +
> +#endif
> diff --git a/include/linux/platform_data/si5351.h b/include/linux/platform_data/si5351.h
> new file mode 100644
> index 0000000..92dabca
> --- /dev/null
> +++ b/include/linux/platform_data/si5351.h
> @@ -0,0 +1,114 @@
> +/*
> + * Si5351A/B/C programmable clock generator platform_data.
> + */
> +
> +#ifndef __LINUX_PLATFORM_DATA_SI5351_H__
> +#define __LINUX_PLATFORM_DATA_SI5351_H__
> +
> +struct clk;
> +
> +/**
> + * enum si5351_variant - SiLabs Si5351 chip variant
> + * @SI5351_VARIANT_A: Si5351A (8 output clocks, XTAL input)
> + * @SI5351_VARIANT_A3: Si5351A MSOP10 (3 output clocks, XTAL input)
> + * @SI5351_VARIANT_B: Si5351B (8 output clocks, XTAL/VXCO input)
> + * @SI5351_VARIANT_C: Si5351C (8 output clocks, XTAL/CLKIN input)
> + */
> +enum si5351_variant {
> +       SI5351_VARIANT_A = 1,
> +       SI5351_VARIANT_A3 = 2,
> +       SI5351_VARIANT_B = 3,
> +       SI5351_VARIANT_C = 4,
> +};
> +
> +/**
> + * enum si5351_pll_src - Si5351 pll clock source
> + * @SI5351_PLL_SRC_DEFAULT: default, do not change eeprom config
> + * @SI5351_PLL_SRC_XTAL: pll source clock is XTAL input
> + * @SI5351_PLL_SRC_CLKIN: pll source clock is CLKIN input (Si5351C only)
> + */
> +enum si5351_pll_src {
> +       SI5351_PLL_SRC_DEFAULT = 0,
> +       SI5351_PLL_SRC_XTAL = 1,
> +       SI5351_PLL_SRC_CLKIN = 2,
> +};
> +
> +/**
> + * enum si5351_multisynth_src - Si5351 multisynth clock source
> + * @SI5351_MULTISYNTH_SRC_DEFAULT: default, do not change eeprom config
> + * @SI5351_MULTISYNTH_SRC_VCO0: multisynth source clock is VCO0
> + * @SI5351_MULTISYNTH_SRC_VCO1: multisynth source clock is VCO1/VXCO
> + */
> +enum si5351_multisynth_src {
> +       SI5351_MULTISYNTH_SRC_DEFAULT = 0,
> +       SI5351_MULTISYNTH_SRC_VCO0 = 1,
> +       SI5351_MULTISYNTH_SRC_VCO1 = 2,
> +};
> +
> +/**
> + * enum si5351_clkout_src - Si5351 clock output clock source
> + * @SI5351_CLKOUT_SRC_DEFAULT: default, do not change eeprom config
> + * @SI5351_CLKOUT_SRC_MSYNTH_N: clkout N source clock is multisynth N
> + * @SI5351_CLKOUT_SRC_MSYNTH_0_4: clkout N source clock is multisynth 0 (N<4)
> + *                                or 4 (N>=4)
> + * @SI5351_CLKOUT_SRC_XTAL: clkout N source clock is XTAL
> + * @SI5351_CLKOUT_SRC_CLKIN: clkout N source clock is CLKIN (Si5351C only)
> + */
> +enum si5351_clkout_src {
> +       SI5351_CLKOUT_SRC_DEFAULT = 0,
> +       SI5351_CLKOUT_SRC_MSYNTH_N = 1,
> +       SI5351_CLKOUT_SRC_MSYNTH_0_4 = 2,
> +       SI5351_CLKOUT_SRC_XTAL = 3,
> +       SI5351_CLKOUT_SRC_CLKIN = 4,
> +};
> +
> +/**
> + * enum si5351_drive_strength - Si5351 clock output drive strength
> + * @SI5351_DRIVE_DEFAULT: default, do not change eeprom config
> + * @SI5351_DRIVE_2MA: 2mA clock output drive strength
> + * @SI5351_DRIVE_4MA: 4mA clock output drive strength
> + * @SI5351_DRIVE_6MA: 6mA clock output drive strength
> + * @SI5351_DRIVE_8MA: 8mA clock output drive strength
> + */
> +enum si5351_drive_strength {
> +       SI5351_DRIVE_DEFAULT = 0,
> +       SI5351_DRIVE_2MA = 2,
> +       SI5351_DRIVE_4MA = 4,
> +       SI5351_DRIVE_6MA = 6,
> +       SI5351_DRIVE_8MA = 8,
> +};
> +
> +/**
> + * struct si5351_clkout_config - Si5351 clock output configuration
> + * @clkout: clkout number
> + * @multisynth_src: multisynth source clock
> + * @clkout_src: clkout source clock
> + * @pll_master: if true, clkout can also change pll rate
> + * @drive: output drive strength
> + * @rate: initial clkout rate, or default if 0
> + */
> +struct si5351_clkout_config {
> +       enum si5351_multisynth_src multisynth_src;
> +       enum si5351_clkout_src clkout_src;
> +       enum si5351_drive_strength drive;
> +       bool pll_master;
> +       unsigned long rate;
> +};
> +
> +/**
> + * struct si5351_platform_data - Platform data for the Si5351 clock driver
> + * @variant: Si5351 chip variant
> + * @clk_xtal: xtal input clock
> + * @clk_clkin: clkin input clock
> + * @pll_src: array of pll source clock setting
> + * @clkout: array of clkout configuration
> + */
> +struct si5351_platform_data {
> +       enum si5351_variant variant;
> +       struct clk *clk_xtal;
> +       struct clk *clk_clkin;
> +       enum si5351_pll_src pll_src[2];
> +       struct si5351_clkout_config clkout[8];
> +};
> +
> +#endif
> -- 
> 1.7.10.4
diff mbox

Patch

diff --git a/Documentation/devicetree/bindings/clock/silabs,si5351.txt b/Documentation/devicetree/bindings/clock/silabs,si5351.txt
new file mode 100644
index 0000000..cc37465
--- /dev/null
+++ b/Documentation/devicetree/bindings/clock/silabs,si5351.txt
@@ -0,0 +1,114 @@ 
+Binding for Silicon Labs Si5351a/b/c programmable i2c clock generator.
+
+Reference
+[1] Si5351A/B/C Data Sheet
+    http://www.silabs.com/Support%20Documents/TechnicalDocs/Si5351.pdf
+
+The Si5351a/b/c are programmable i2c clock generators with upto 8 output
+clocks. Si5351a also has a reduced pin-count package (MSOP10) where only
+3 output clocks are accessible. The internal structure of the clock
+generators can be found in [1].
+
+==I2C device node==
+
+Required properties:
+- compatible: shall be one of "silabs,si5351{a,a-msop,b,c}".
+- reg: i2c device address, shall be 0x60 or 0x61.
+- #clock-cells: from common clock binding; shall be set to 1.
+- clocks: from common clock binding; list of parent clock
+  handles, shall be xtal reference clock or xtal and clkin for
+  si5351c only.
+- #address-cells: shall be set to 1.
+- #size-cells: shall be set to 0.
+
+Optional properties:
+- silabs,pll-source: pair of (number, source) for each pll. Allows
+  to overwrite clock source of pll A (number=0) or B (number=1).
+
+==Child nodes==
+
+Each of the clock outputs can be overwritten individually by
+using a child node to the I2C device node. If a child node for a clock
+output is not set, the eeprom configuration is not overwritten.
+
+Required child node properties:
+- reg: number of clock output.
+
+Optional child node properties:
+- silabs,clock-source: source clock of the output divider stage N, shall be
+  0 = multisynth N
+  1 = multisynth 0 for output clocks 0-3, else multisynth4
+  2 = xtal
+  3 = clkin (si5351c only)
+- silabs,drive-strength: output drive strength in mA, shall be one of {2,4,6,8}.
+- silabs,multisynth-source: source pll A(0) or B(1) of corresponding multisynth
+  divider.
+- silabs,pll-master: boolean, multisynth can change pll frequency.
+
+==Example==
+
+/* 25MHz reference crystal */
+ref25: ref25M {
+	compatible = "fixed-clock";
+	#clock-cells = <0>;
+	clock-frequency = <25000000>;
+};
+
+i2c-master-node {
+
+	/* Si5351a msop10 i2c clock generator */
+	si5351a: clock-generator@60 {
+		compatible = "silabs,si5351a-msop";
+		reg = <0x60>;
+		#address-cells = <1>;
+		#size-cells = <0>;
+		#clock-cells = <1>;
+
+		/* connect xtal input to 25MHz reference */
+		clocks = <&ref25>;
+
+		/* connect xtal input as source of pll0 and pll1 */
+		silabs,pll-source = <0 0>, <1 0>;
+
+		/*
+		 * overwrite clkout0 configuration with:
+		 * - 8mA output drive strength
+		 * - pll0 as clock source of multisynth0
+		 * - multisynth0 as clock source of output divider
+		 * - multisynth0 can change pll0
+		 * - set initial clock frequency of 74.25MHz
+		 */
+		clkout0 {
+			reg = <0>;
+			silabs,drive-strength = <8>;
+			silabs,multisynth-source = <0>;
+			silabs,clock-source = <0>;
+			silabs,pll-master;
+			clock-frequency = <74250000>;
+		};
+
+		/*
+		 * overwrite clkout1 configuration with:
+		 * - 4mA output drive strength
+		 * - pll1 as clock source of multisynth1
+		 * - multisynth1 as clock source of output divider
+		 * - multisynth1 can change pll1
+		 */
+		clkout1 {
+			reg = <1>;
+			silabs,drive-strength = <4>;
+			silabs,multisynth-source = <1>;
+			silabs,clock-source = <0>;
+			pll-master;
+		};
+
+		/*
+		 * overwrite clkout2 configuration with:
+		 * - xtal as clock source of output divider
+		 */
+		clkout2 {
+			reg = <2>;
+			silabs,clock-source = <2>;
+		};
+	};
+};
diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt
index 19e1ef7..ca60849 100644
--- a/Documentation/devicetree/bindings/vendor-prefixes.txt
+++ b/Documentation/devicetree/bindings/vendor-prefixes.txt
@@ -48,6 +48,7 @@  samsung	Samsung Semiconductor
 sbs	Smart Battery System
 schindler	Schindler
 sil	Silicon Image
+silabs	Silicon Laboratories
 simtek
 sirf	SiRF Technology, Inc.
 snps 	Synopsys, Inc.
diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig
index a47e6ee..5039e41 100644
--- a/drivers/clk/Kconfig
+++ b/drivers/clk/Kconfig
@@ -55,6 +55,15 @@  config COMMON_CLK_MAX77686
 	---help---
 	  This driver supports Maxim 77686 crystal oscillator clock. 
 
+config COMMON_CLK_SI5351
+	tristate "Clock driver for SiLabs 5351A/B/C"
+	depends on I2C
+	select REGMAP_I2C
+	select RATIONAL
+	---help---
+	  This driver supports Silicon Labs 5351A/B/C programmable clock
+	  generators.
+
 config CLK_TWL6040
 	tristate "External McPDM functional clock from twl6040"
 	depends on TWL6040_CORE
diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
index 300d477..92ca698 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -33,4 +33,5 @@  obj-$(CONFIG_X86)		+= x86/
 # Chip specific
 obj-$(CONFIG_COMMON_CLK_WM831X) += clk-wm831x.o
 obj-$(CONFIG_COMMON_CLK_MAX77686) += clk-max77686.o
+obj-$(CONFIG_COMMON_CLK_SI5351) += clk-si5351.o
 obj-$(CONFIG_CLK_TWL6040)	+= clk-twl6040.o
diff --git a/drivers/clk/clk-si5351.c b/drivers/clk/clk-si5351.c
new file mode 100644
index 0000000..8927284
--- /dev/null
+++ b/drivers/clk/clk-si5351.c
@@ -0,0 +1,1510 @@ 
+/*
+ * clk-si5351.c: Silicon Laboratories Si5351A/B/C I2C Clock Generator
+ *
+ * Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
+ * Rabeeh Khoury <rabeeh@solid-run.com>
+ *
+ * References:
+ * [1] "Si5351A/B/C Data Sheet"
+ *     http://www.silabs.com/Support%20Documents/TechnicalDocs/Si5351.pdf
+ * [2] "Manually Generating an Si5351 Register Map"
+ *     http://www.silabs.com/Support%20Documents/TechnicalDocs/AN619.pdf
+ *
+ * This program is free software; you can redistribute  it and/or modify it
+ * under  the terms of  the GNU General  Public License as published by the
+ * Free Software Foundation;  either version 2 of the  License, or (at your
+ * option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/clkdev.h>
+#include <linux/clk-provider.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/errno.h>
+#include <linux/rational.h>
+#include <linux/i2c.h>
+#include <linux/of_platform.h>
+#include <linux/platform_data/si5351.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <asm/div64.h>
+
+#include "clk-si5351.h"
+
+struct si5351_driver_data;
+
+struct si5351_parameters {
+	unsigned long	p1;
+	unsigned long	p2;
+	unsigned long	p3;
+	int		valid;
+};
+
+struct si5351_hw_data {
+	struct clk_hw			hw;
+	struct si5351_driver_data	*drvdata;
+	struct si5351_parameters	params;
+	unsigned char			num;
+};
+
+struct si5351_driver_data {
+	enum si5351_variant	variant;
+	struct i2c_client	*client;
+	struct regmap		*regmap;
+	struct clk_onecell_data onecell;
+
+	struct clk		*pxtal;
+	const char		*pxtal_name;
+	struct clk_hw		xtal;
+	struct clk		*pclkin;
+	const char		*pclkin_name;
+	struct clk_hw		clkin;
+
+	struct si5351_hw_data	pll[2];
+	struct si5351_hw_data	*msynth;
+	struct si5351_hw_data	*clkout;
+};
+
+static const char const *si5351_input_names[] = {
+	"xtal", "clkin"
+};
+static const char const *si5351_pll_names[] = {
+	"plla", "pllb", "vxco"
+};
+static const char const *si5351_msynth_names[] = {
+	"ms0", "ms1", "ms2", "ms3", "ms4", "ms5", "ms6", "ms7"
+};
+static const char const *si5351_clkout_names[] = {
+	"clk0", "clk1", "clk2", "clk3", "clk4", "clk5", "clk6", "clk7"
+};
+
+/*
+ * Si5351 i2c regmap
+ */
+static inline u8 si5351_reg_read(struct si5351_driver_data *drvdata, u8 reg)
+{
+	u32 val;
+	int ret;
+
+	ret = regmap_read(drvdata->regmap, reg, &val);
+	if (ret) {
+		dev_err(&drvdata->client->dev,
+			"unable to read from reg%02x\n", reg);
+		return 0;
+	}
+
+	return (u8)val;
+}
+
+static inline int si5351_bulk_read(struct si5351_driver_data *drvdata,
+				   u8 reg, u8 count, u8 *buf)
+{
+	return regmap_bulk_read(drvdata->regmap, reg, buf, count);
+}
+
+static inline int si5351_reg_write(struct si5351_driver_data *drvdata,
+				   u8 reg, u8 val)
+{
+	return regmap_write(drvdata->regmap, reg, val);
+}
+
+static inline int si5351_bulk_write(struct si5351_driver_data *drvdata,
+				    u8 reg, u8 count, const u8 *buf)
+{
+	return regmap_raw_write(drvdata->regmap, reg, buf, count);
+}
+
+static inline int si5351_set_bits(struct si5351_driver_data *drvdata,
+				  u8 reg, u8 mask, u8 val)
+{
+	return regmap_update_bits(drvdata->regmap, reg, mask, val);
+}
+
+static inline u8 si5351_msynth_params_address(int num)
+{
+	if (num > 5)
+		return SI5351_CLK6_PARAMETERS + (num - 6);
+	return SI5351_CLK0_PARAMETERS + (SI5351_PARAMETERS_LENGTH * num);
+}
+
+static void si5351_read_parameters(struct si5351_driver_data *drvdata,
+				   u8 reg, struct si5351_parameters *params)
+{
+	u8 buf[SI5351_PARAMETERS_LENGTH];
+
+	switch (reg) {
+	case SI5351_CLK6_PARAMETERS:
+	case SI5351_CLK7_PARAMETERS:
+		buf[0] = si5351_reg_read(drvdata, reg);
+		params->p1 = buf[0];
+		params->p2 = 0;
+		params->p3 = 1;
+		break;
+	default:
+		si5351_bulk_read(drvdata, reg, SI5351_PARAMETERS_LENGTH, buf);
+		params->p1 = ((buf[2] & 0x03) << 16) | (buf[3] << 8) | buf[4];
+		params->p2 = ((buf[5] & 0x0f) << 16) | (buf[6] << 8) | buf[7];
+		params->p3 = ((buf[5] & 0xf0) << 12) | (buf[0] << 8) | buf[1];
+	}
+	params->valid = 1;
+}
+
+static void si5351_write_parameters(struct si5351_driver_data *drvdata,
+				    u8 reg, struct si5351_parameters *params)
+{
+	u8 buf[SI5351_PARAMETERS_LENGTH];
+
+	switch (reg) {
+	case SI5351_CLK6_PARAMETERS:
+	case SI5351_CLK7_PARAMETERS:
+		buf[0] = params->p1 & 0xff;
+		si5351_reg_write(drvdata, reg, buf[0]);
+		break;
+	default:
+		buf[0] = ((params->p3 & 0x0ff00) >> 8) & 0xff;
+		buf[1] = params->p3 & 0xff;
+		/* save rdiv and divby4 */
+		buf[2] = si5351_reg_read(drvdata, reg + 2) & ~0x03;
+		buf[2] |= ((params->p1 & 0x30000) >> 16) & 0x03;
+		buf[3] = ((params->p1 & 0x0ff00) >> 8) & 0xff;
+		buf[4] = params->p1 & 0xff;
+		buf[5] = ((params->p3 & 0xf0000) >> 12) |
+			((params->p2 & 0xf0000) >> 16);
+		buf[6] = ((params->p2 & 0x0ff00) >> 8) & 0xff;
+		buf[7] = params->p2 & 0xff;
+		si5351_bulk_write(drvdata, reg, SI5351_PARAMETERS_LENGTH, buf);
+	}
+}
+
+static bool si5351_regmap_is_volatile(struct device *dev, unsigned int reg)
+{
+	switch (reg) {
+	case SI5351_DEVICE_STATUS:
+	case SI5351_INTERRUPT_STATUS:
+	case SI5351_PLL_RESET:
+		return true;
+	}
+	return false;
+}
+
+static bool si5351_regmap_is_writeable(struct device *dev, unsigned int reg)
+{
+	/* reserved registers */
+	if (reg >= 4 && reg <= 8)
+		return false;
+	if (reg >= 10 && reg <= 14)
+		return false;
+	if (reg >= 173 && reg <= 176)
+		return false;
+	if (reg >= 178 && reg <= 182)
+		return false;
+	/* read-only */
+	if (reg == SI5351_DEVICE_STATUS)
+		return false;
+	return true;
+}
+
+static struct regmap_config si5351_regmap_config = {
+	.reg_bits = 8,
+	.val_bits = 8,
+	.cache_type = REGCACHE_RBTREE,
+	.max_register = 187,
+	.writeable_reg = si5351_regmap_is_writeable,
+	.volatile_reg = si5351_regmap_is_volatile,
+};
+
+/*
+ * Si5351 xtal clock input
+ */
+static int si5351_xtal_prepare(struct clk_hw *hw)
+{
+	struct si5351_driver_data *drvdata =
+		container_of(hw, struct si5351_driver_data, xtal);
+	si5351_set_bits(drvdata, SI5351_FANOUT_ENABLE,
+			SI5351_XTAL_ENABLE, SI5351_XTAL_ENABLE);
+	return 0;
+}
+
+static void si5351_xtal_unprepare(struct clk_hw *hw)
+{
+	struct si5351_driver_data *drvdata =
+		container_of(hw, struct si5351_driver_data, xtal);
+	si5351_set_bits(drvdata, SI5351_FANOUT_ENABLE,
+			SI5351_XTAL_ENABLE, 0);
+}
+
+static const struct clk_ops si5351_xtal_ops = {
+	.prepare = si5351_xtal_prepare,
+	.unprepare = si5351_xtal_unprepare,
+};
+
+/*
+ * Si5351 clkin clock input (Si5351C only)
+ */
+static int si5351_clkin_prepare(struct clk_hw *hw)
+{
+	struct si5351_driver_data *drvdata =
+		container_of(hw, struct si5351_driver_data, clkin);
+	si5351_set_bits(drvdata, SI5351_FANOUT_ENABLE,
+			SI5351_CLKIN_ENABLE, SI5351_CLKIN_ENABLE);
+	return 0;
+}
+
+static void si5351_clkin_unprepare(struct clk_hw *hw)
+{
+	struct si5351_driver_data *drvdata =
+		container_of(hw, struct si5351_driver_data, clkin);
+	si5351_set_bits(drvdata, SI5351_FANOUT_ENABLE,
+			SI5351_CLKIN_ENABLE, 0);
+}
+
+/*
+ * CMOS clock source constraints:
+ * The input frequency range of the PLL is 10Mhz to 40MHz.
+ * If CLKIN is >40MHz, the input divider must be used.
+ */
+static unsigned long si5351_clkin_recalc_rate(struct clk_hw *hw,
+					      unsigned long parent_rate)
+{
+	struct si5351_driver_data *drvdata =
+		container_of(hw, struct si5351_driver_data, clkin);
+	unsigned long rate;
+	unsigned char idiv;
+
+	rate = parent_rate;
+	if (parent_rate > 160000000) {
+		idiv = SI5351_CLKIN_DIV_8;
+		rate /= 8;
+	} else if (parent_rate > 80000000) {
+		idiv = SI5351_CLKIN_DIV_4;
+		rate /= 4;
+	} else if (parent_rate > 40000000) {
+		idiv = SI5351_CLKIN_DIV_2;
+		rate /= 2;
+	} else {
+		idiv = SI5351_CLKIN_DIV_1;
+	}
+
+	si5351_set_bits(drvdata, SI5351_PLL_INPUT_SOURCE,
+			SI5351_CLKIN_DIV_MASK, idiv);
+
+	dev_dbg(&drvdata->client->dev, "%s - clkin div = %d, rate = %lu\n",
+		__func__, (1 << (idiv >> 6)), rate);
+
+	return rate;
+}
+
+static const struct clk_ops si5351_clkin_ops = {
+	.prepare = si5351_clkin_prepare,
+	.unprepare = si5351_clkin_unprepare,
+	.recalc_rate = si5351_clkin_recalc_rate,
+};
+
+/*
+ * Si5351 vxco clock input (Si5351B only)
+ */
+
+static int si5351_vxco_prepare(struct clk_hw *hw)
+{
+	struct si5351_hw_data *hwdata =
+		container_of(hw, struct si5351_hw_data, hw);
+
+	dev_warn(&hwdata->drvdata->client->dev, "VXCO currently unsupported\n");
+
+	return 0;
+}
+
+static void si5351_vxco_unprepare(struct clk_hw *hw)
+{
+}
+
+static unsigned long si5351_vxco_recalc_rate(struct clk_hw *hw,
+					     unsigned long parent_rate)
+{
+	return 0;
+}
+
+static int si5351_vxco_set_rate(struct clk_hw *hw, unsigned long rate,
+				unsigned long parent)
+{
+	return 0;
+}
+
+static const struct clk_ops si5351_vxco_ops = {
+	.prepare = si5351_vxco_prepare,
+	.unprepare = si5351_vxco_unprepare,
+	.recalc_rate = si5351_vxco_recalc_rate,
+	.set_rate = si5351_vxco_set_rate,
+};
+
+/*
+ * Si5351 pll a/b
+ *
+ * Feedback Multisynth Divider Equations [2]
+ *
+ * fVCO = fIN * (a + b/c)
+ *
+ * with 15 + 0/1048575 <= (a + b/c) <= 90 + 0/1048575 and
+ * fIN = fXTAL or fIN = fCLKIN/CLKIN_DIV
+ *
+ * Feedback Multisynth Register Equations
+ *
+ * (1) MSNx_P1[17:0] = 128 * a + floor(128 * b/c) - 512
+ * (2) MSNx_P2[19:0] = 128 * b - c * floor(128 * b/c) = (128*b) mod c
+ * (3) MSNx_P3[19:0] = c
+ *
+ * Transposing (2) yields: (4) floor(128 * b/c) = (128 * b / MSNx_P2)/c
+ *
+ * Using (4) on (1) yields:
+ * MSNx_P1 = 128 * a + (128 * b/MSNx_P2)/c - 512
+ * MSNx_P1 + 512 + MSNx_P2/c = 128 * a + 128 * b/c
+ *
+ * a + b/c = (MSNx_P1 + MSNx_P2/MSNx_P3 + 512)/128
+ *         = (MSNx_P1*MSNx_P3 + MSNx_P2 + 512*MSNx_P3)/(128*MSNx_P3)
+ *
+ */
+static int _si5351_pll_reparent(struct si5351_driver_data *drvdata,
+				int num, enum si5351_pll_src parent)
+{
+	u8 mask = (num == 0) ? SI5351_PLLA_SOURCE : SI5351_PLLB_SOURCE;
+
+	if (parent == SI5351_PLL_SRC_DEFAULT)
+		return 0;
+
+	if (num > 2)
+		return -EINVAL;
+
+	if (drvdata->variant != SI5351_VARIANT_C &&
+	    parent != SI5351_PLL_SRC_XTAL)
+		return -EINVAL;
+
+	si5351_set_bits(drvdata, SI5351_PLL_INPUT_SOURCE, mask,
+			(parent == SI5351_PLL_SRC_XTAL) ? 0 : mask);
+	return 0;
+}
+
+static unsigned char si5351_pll_get_parent(struct clk_hw *hw)
+{
+	struct si5351_hw_data *hwdata =
+		container_of(hw, struct si5351_hw_data, hw);
+	u8 mask = (hwdata->num == 0) ? SI5351_PLLA_SOURCE : SI5351_PLLB_SOURCE;
+	u8 val;
+
+	val = si5351_reg_read(hwdata->drvdata, SI5351_PLL_INPUT_SOURCE);
+
+	return (val & mask) ? 1 : 0;
+}
+
+static int si5351_pll_set_parent(struct clk_hw *hw, u8 index)
+{
+	struct si5351_hw_data *hwdata =
+		container_of(hw, struct si5351_hw_data, hw);
+
+	if (hwdata->drvdata->variant != SI5351_VARIANT_C &&
+	    index > 0)
+		return -EPERM;
+
+	if (index > 1)
+		return -EINVAL;
+
+	return _si5351_pll_reparent(hwdata->drvdata, hwdata->num,
+			     (index == 0) ? SI5351_PLL_SRC_XTAL :
+			     SI5351_PLL_SRC_CLKIN);
+}
+
+static unsigned long si5351_pll_recalc_rate(struct clk_hw *hw,
+					    unsigned long parent_rate)
+{
+	struct si5351_hw_data *hwdata =
+		container_of(hw, struct si5351_hw_data, hw);
+	u8 reg = (hwdata->num == 0) ? SI5351_PLLA_PARAMETERS :
+		SI5351_PLLB_PARAMETERS;
+	unsigned long long rate;
+
+	if (!hwdata->params.valid)
+		si5351_read_parameters(hwdata->drvdata, reg, &hwdata->params);
+
+	if (hwdata->params.p3 == 0)
+		return parent_rate;
+
+	/* fVCO = fIN * (P1*P3 + 512*P3 + P2)/(128*P3) */
+	rate  = hwdata->params.p1 * hwdata->params.p3;
+	rate += 512 * hwdata->params.p3;
+	rate += hwdata->params.p2;
+	rate *= parent_rate;
+	do_div(rate, 128 * hwdata->params.p3);
+
+	dev_dbg(&hwdata->drvdata->client->dev,
+		"%s - %s: p1 = %lu, p2 = %lu, p3 = %lu, parent_rate = %lu, rate = %lu\n",
+		__func__, __clk_get_name(hwdata->hw.clk),
+		hwdata->params.p1, hwdata->params.p2, hwdata->params.p3,
+		parent_rate, (unsigned long)rate);
+
+	return (unsigned long)rate;
+}
+
+static long si5351_pll_round_rate(struct clk_hw *hw, unsigned long rate,
+				  unsigned long *parent_rate)
+{
+	struct si5351_hw_data *hwdata =
+		container_of(hw, struct si5351_hw_data, hw);
+	unsigned long rfrac, denom, a, b, c;
+	unsigned long long lltmp;
+
+	if (rate < SI5351_PLL_VCO_MIN)
+		rate = SI5351_PLL_VCO_MIN;
+	if (rate > SI5351_PLL_VCO_MAX)
+		rate = SI5351_PLL_VCO_MAX;
+
+	/* determine integer part of feedback equation */
+	a = rate / *parent_rate;
+
+	if (a < SI5351_PLL_A_MIN)
+		rate = *parent_rate * SI5351_PLL_A_MIN;
+	if (a > SI5351_PLL_A_MAX)
+		rate = *parent_rate * SI5351_PLL_A_MAX;
+
+	/* find best approximation for b/c = fVCO mod fIN */
+	denom = 1000 * 1000;
+	lltmp = rate % (*parent_rate);
+	lltmp *= denom;
+	do_div(lltmp, *parent_rate);
+	rfrac = (unsigned long)lltmp;
+
+	b = 0;
+	c = 1;
+	if (rfrac)
+		rational_best_approximation(rfrac, denom,
+				    SI5351_PLL_B_MAX, SI5351_PLL_C_MAX, &b, &c);
+
+	/* calculate parameters */
+	hwdata->params.p3  = c;
+	hwdata->params.p2  = (128 * b) % c;
+	hwdata->params.p1  = 128 * a;
+	hwdata->params.p1 += (128 * b / c);
+	hwdata->params.p1 -= 512;
+
+	/* recalculate rate by fIN * (a + b/c) */
+	lltmp  = *parent_rate;
+	lltmp *= b;
+	do_div(lltmp, c);
+
+	rate  = (unsigned long)lltmp;
+	rate += *parent_rate * a;
+
+	dev_dbg(&hwdata->drvdata->client->dev,
+		"%s - %s: a = %lu, b = %lu, c = %lu, parent_rate = %lu, rate = %lu\n",
+		__func__, __clk_get_name(hwdata->hw.clk), a, b, c,
+		*parent_rate, rate);
+
+	return rate;
+}
+
+static int si5351_pll_set_rate(struct clk_hw *hw, unsigned long rate,
+			       unsigned long parent_rate)
+{
+	struct si5351_hw_data *hwdata =
+		container_of(hw, struct si5351_hw_data, hw);
+	u8 reg = (hwdata->num == 0) ? SI5351_PLLA_PARAMETERS :
+		SI5351_PLLB_PARAMETERS;
+
+	/* write multisynth parameters */
+	si5351_write_parameters(hwdata->drvdata, reg, &hwdata->params);
+
+	/* plla/pllb ctrl is in clk6/clk7 ctrl registers */
+	si5351_set_bits(hwdata->drvdata, SI5351_CLK6_CTRL + hwdata->num,
+		SI5351_CLK_INTEGER_MODE,
+		(hwdata->params.p2 == 0) ? SI5351_CLK_INTEGER_MODE : 0);
+
+	dev_dbg(&hwdata->drvdata->client->dev,
+		"%s - %s: p1 = %lu, p2 = %lu, p3 = %lu, parent_rate = %lu, rate = %lu\n",
+		__func__, __clk_get_name(hwdata->hw.clk),
+		hwdata->params.p1, hwdata->params.p2, hwdata->params.p3,
+		parent_rate, rate);
+
+	return 0;
+}
+
+static const struct clk_ops si5351_pll_ops = {
+	.set_parent = si5351_pll_set_parent,
+	.get_parent = si5351_pll_get_parent,
+	.recalc_rate = si5351_pll_recalc_rate,
+	.round_rate = si5351_pll_round_rate,
+	.set_rate = si5351_pll_set_rate,
+};
+
+/*
+ * Si5351 multisync divider
+ *
+ * for fOUT <= 150 MHz:
+ *
+ * fOUT = (fIN * (a + b/c)) / CLKOUTDIV
+ *
+ * with 6 + 0/1048575 <= (a + b/c) <= 1800 + 0/1048575 and
+ * fIN = fVCO0, fVCO1
+ *
+ * Output Clock Multisynth Register Equations
+ *
+ * MSx_P1[17:0] = 128 * a + floor(128 * b/c) - 512
+ * MSx_P2[19:0] = 128 * b - c * floor(128 * b/c) = (128*b) mod c
+ * MSx_P3[19:0] = c
+ *
+ * MS[6,7] are integer (P1) divide only, P2 = 0, P3 = 0
+ *
+ * for 150MHz < fOUT <= 160MHz:
+ *
+ * MSx_P1 = 0, MSx_P2 = 0, MSx_P3 = 1, MSx_INT = 1, MSx_DIVBY4 = 11b
+ */
+static int _si5351_msynth_reparent(struct si5351_driver_data *drvdata,
+				   int num, enum si5351_multisynth_src parent)
+{
+	if (parent == SI5351_MULTISYNTH_SRC_DEFAULT)
+		return 0;
+
+	if (num > 8)
+		return -EINVAL;
+
+	si5351_set_bits(drvdata, SI5351_CLK0_CTRL + num, SI5351_CLK_PLL_SELECT,
+			(parent == SI5351_MULTISYNTH_SRC_VCO0) ? 0 :
+			SI5351_CLK_PLL_SELECT);
+	return 0;
+}
+
+static unsigned char si5351_msynth_get_parent(struct clk_hw *hw)
+{
+	struct si5351_hw_data *hwdata =
+		container_of(hw, struct si5351_hw_data, hw);
+	u8 val;
+
+	val = si5351_reg_read(hwdata->drvdata, SI5351_CLK0_CTRL + hwdata->num);
+
+	return (val & SI5351_CLK_PLL_SELECT) ? 1 : 0;
+}
+
+static int si5351_msynth_set_parent(struct clk_hw *hw, u8 index)
+{
+	struct si5351_hw_data *hwdata =
+		container_of(hw, struct si5351_hw_data, hw);
+
+	return _si5351_msynth_reparent(hwdata->drvdata, hwdata->num,
+			       (index == 0) ? SI5351_MULTISYNTH_SRC_VCO0 :
+			       SI5351_MULTISYNTH_SRC_VCO1);
+}
+
+static unsigned long si5351_msynth_recalc_rate(struct clk_hw *hw,
+					       unsigned long parent_rate)
+{
+	struct si5351_hw_data *hwdata =
+		container_of(hw, struct si5351_hw_data, hw);
+	u8 reg = si5351_msynth_params_address(hwdata->num);
+	unsigned long long rate;
+	unsigned long m;
+
+	if (!hwdata->params.valid)
+		si5351_read_parameters(hwdata->drvdata, reg, &hwdata->params);
+
+	if (hwdata->params.p3 == 0)
+		return parent_rate;
+
+	/*
+	 * multisync0-5: fOUT = (128 * P3 * fIN) / (P1*P3 + P2 + 512*P3)
+	 * multisync6-7: fOUT = fIN / P1
+	 */
+	rate = parent_rate;
+	if (hwdata->num > 5) {
+		m = hwdata->params.p1;
+	} else if ((si5351_reg_read(hwdata->drvdata, reg + 2) &
+		    SI5351_OUTPUT_CLK_DIVBY4) == SI5351_OUTPUT_CLK_DIVBY4) {
+		m = 4;
+	} else {
+		rate *= 128 * hwdata->params.p3;
+		m = hwdata->params.p1 * hwdata->params.p3;
+		m += hwdata->params.p2;
+		m += 512 * hwdata->params.p3;
+	}
+
+	if (m == 0)
+		return 0;
+	do_div(rate, m);
+
+	dev_dbg(&hwdata->drvdata->client->dev,
+		"%s - %s: p1 = %lu, p2 = %lu, p3 = %lu, m = %lu, parent_rate = %lu, rate = %lu\n",
+		__func__, __clk_get_name(hwdata->hw.clk),
+		hwdata->params.p1, hwdata->params.p2, hwdata->params.p3,
+		m, parent_rate, (unsigned long)rate);
+
+	return (unsigned long)rate;
+}
+
+static long si5351_msynth_round_rate(struct clk_hw *hw, unsigned long rate,
+				     unsigned long *parent_rate)
+{
+	struct si5351_hw_data *hwdata =
+		container_of(hw, struct si5351_hw_data, hw);
+	unsigned long long lltmp;
+	unsigned long a, b, c;
+	int divby4;
+
+	/* multisync6-7 can only handle freqencies < 150MHz */
+	if (hwdata->num >= 6 && rate > SI5351_MULTISYNTH67_MAX_FREQ)
+		rate = SI5351_MULTISYNTH67_MAX_FREQ;
+
+	/* multisync frequency is 1MHz .. 160MHz */
+	if (rate > SI5351_MULTISYNTH_MAX_FREQ)
+		rate = SI5351_MULTISYNTH_MAX_FREQ;
+	if (rate < SI5351_MULTISYNTH_MIN_FREQ)
+		rate = SI5351_MULTISYNTH_MIN_FREQ;
+
+	divby4 = 0;
+	if (rate > SI5351_MULTISYNTH_DIVBY4_FREQ)
+		divby4 = 1;
+
+	/* multisync can set pll */
+	if (__clk_get_flags(hwdata->hw.clk) & CLK_SET_RATE_PARENT) {
+		/*
+		 * find largest integer divider for max
+		 * vco frequency and given target rate
+		 */
+		if (divby4 == 0) {
+			lltmp = SI5351_PLL_VCO_MAX;
+			do_div(lltmp, rate);
+			a = (unsigned long)lltmp;
+		} else
+			a = 4;
+
+		b = 0;
+		c = 1;
+
+		*parent_rate = a * rate;
+	} else {
+		unsigned long rfrac, denom;
+
+		/* disable divby4 */
+		if (divby4) {
+			rate = SI5351_MULTISYNTH_DIVBY4_FREQ;
+			divby4 = 0;
+		}
+
+		/* determine integer part of divider equation */
+		a = *parent_rate / rate;
+		if (a < SI5351_MULTISYNTH_A_MIN)
+			a = SI5351_MULTISYNTH_A_MIN;
+		if (hwdata->num >= 6 && a > SI5351_MULTISYNTH67_A_MAX)
+			a = SI5351_MULTISYNTH67_A_MAX;
+		else if (a > SI5351_MULTISYNTH_A_MAX)
+			a = SI5351_MULTISYNTH_A_MAX;
+
+		/* find best approximation for b/c = fVCO mod fOUT */
+		denom = 1000 * 1000;
+		lltmp = (*parent_rate) % rate;
+		lltmp *= denom;
+		do_div(lltmp, rate);
+		rfrac = (unsigned long)lltmp;
+
+		b = 0;
+		c = 1;
+		if (rfrac)
+			rational_best_approximation(rfrac, denom,
+			    SI5351_MULTISYNTH_B_MAX, SI5351_MULTISYNTH_C_MAX,
+			    &b, &c);
+	}
+
+	/* recalculate rate by fOUT = fIN / (a + b/c) */
+	lltmp  = *parent_rate;
+	lltmp *= c;
+	do_div(lltmp, a * c + b);
+	rate  = (unsigned long)lltmp;
+
+	/* calculate parameters */
+	if (divby4) {
+		hwdata->params.p3 = 1;
+		hwdata->params.p2 = 0;
+		hwdata->params.p1 = 0;
+	} else {
+		hwdata->params.p3  = c;
+		hwdata->params.p2  = (128 * b) % c;
+		hwdata->params.p1  = 128 * a;
+		hwdata->params.p1 += (128 * b / c);
+		hwdata->params.p1 -= 512;
+	}
+
+	dev_dbg(&hwdata->drvdata->client->dev,
+		"%s - %s: a = %lu, b = %lu, c = %lu, divby4 = %d, parent_rate = %lu, rate = %lu\n",
+		__func__, __clk_get_name(hwdata->hw.clk), a, b, c, divby4,
+		*parent_rate, rate);
+
+	return rate;
+}
+
+static int si5351_msynth_set_rate(struct clk_hw *hw, unsigned long rate,
+				  unsigned long parent_rate)
+{
+	struct si5351_hw_data *hwdata =
+		container_of(hw, struct si5351_hw_data, hw);
+	u8 reg = si5351_msynth_params_address(hwdata->num);
+	int divby4 = 0;
+
+	/* write multisynth parameters */
+	si5351_write_parameters(hwdata->drvdata, reg, &hwdata->params);
+
+	if (rate > SI5351_MULTISYNTH_DIVBY4_FREQ)
+		divby4 = 1;
+
+	/* enable/disable integer mode and divby4 on multisynth0-5 */
+	if (hwdata->num < 6) {
+		si5351_set_bits(hwdata->drvdata, reg + 2,
+				SI5351_OUTPUT_CLK_DIVBY4,
+				(divby4) ? SI5351_OUTPUT_CLK_DIVBY4 : 0);
+		si5351_set_bits(hwdata->drvdata, SI5351_CLK0_CTRL + hwdata->num,
+			SI5351_CLK_INTEGER_MODE,
+			(hwdata->params.p2 == 0) ? SI5351_CLK_INTEGER_MODE : 0);
+	}
+
+	dev_dbg(&hwdata->drvdata->client->dev,
+		"%s - %s: p1 = %lu, p2 = %lu, p3 = %lu, divby4 = %d, parent_rate = %lu, rate = %lu\n",
+		__func__, __clk_get_name(hwdata->hw.clk),
+		hwdata->params.p1, hwdata->params.p2, hwdata->params.p3,
+		divby4, parent_rate, rate);
+
+	return 0;
+}
+
+static const struct clk_ops si5351_msynth_ops = {
+	.set_parent = si5351_msynth_set_parent,
+	.get_parent = si5351_msynth_get_parent,
+	.recalc_rate = si5351_msynth_recalc_rate,
+	.round_rate = si5351_msynth_round_rate,
+	.set_rate = si5351_msynth_set_rate,
+};
+
+/*
+ * Si5351 clkout divider
+ */
+static int _si5351_clkout_reparent(struct si5351_driver_data *drvdata,
+				   int num, enum si5351_clkout_src parent)
+{
+	u8 val;
+
+	if (num > 8)
+		return -EINVAL;
+
+	switch (parent) {
+	case SI5351_CLKOUT_SRC_MSYNTH_N:
+		val = SI5351_CLK_INPUT_MULTISYNTH_N;
+		break;
+	case SI5351_CLKOUT_SRC_MSYNTH_0_4:
+		/* clk0/clk4 can only connect to its own multisync */
+		if (num == 0 || num == 4)
+			val = SI5351_CLK_INPUT_MULTISYNTH_N;
+		else
+			val = SI5351_CLK_INPUT_MULTISYNTH_0_4;
+		break;
+	case SI5351_CLKOUT_SRC_XTAL:
+		val = SI5351_CLK_INPUT_XTAL;
+		break;
+	case SI5351_CLKOUT_SRC_CLKIN:
+		if (drvdata->variant != SI5351_VARIANT_C)
+			return -EINVAL;
+
+		val = SI5351_CLK_INPUT_CLKIN;
+		break;
+	default:
+		return 0;
+	}
+
+	si5351_set_bits(drvdata, SI5351_CLK0_CTRL + num,
+			SI5351_CLK_INPUT_MASK, val);
+	return 0;
+}
+
+static int _si5351_clkout_set_drive_strength(
+	struct si5351_driver_data *drvdata, int num,
+	enum si5351_drive_strength drive)
+{
+	u8 mask;
+
+	if (num > 8)
+		return -EINVAL;
+
+	switch (drive) {
+	case SI5351_DRIVE_2MA:
+		mask = SI5351_CLK_DRIVE_STRENGTH_2MA;
+		break;
+	case SI5351_DRIVE_4MA:
+		mask = SI5351_CLK_DRIVE_STRENGTH_4MA;
+		break;
+	case SI5351_DRIVE_6MA:
+		mask = SI5351_CLK_DRIVE_STRENGTH_6MA;
+		break;
+	case SI5351_DRIVE_8MA:
+		mask = SI5351_CLK_DRIVE_STRENGTH_8MA;
+		break;
+	default:
+		return 0;
+	}
+
+	si5351_set_bits(drvdata, SI5351_CLK0_CTRL + num,
+			SI5351_CLK_DRIVE_STRENGTH_MASK, mask);
+	return 0;
+}
+
+static int si5351_clkout_prepare(struct clk_hw *hw)
+{
+	struct si5351_hw_data *hwdata =
+		container_of(hw, struct si5351_hw_data, hw);
+
+	si5351_set_bits(hwdata->drvdata, SI5351_CLK0_CTRL + hwdata->num,
+			SI5351_CLK_POWERDOWN, 0);
+	si5351_set_bits(hwdata->drvdata, SI5351_OUTPUT_ENABLE_CTRL,
+			(1 << hwdata->num), 0);
+	return 0;
+}
+
+static void si5351_clkout_unprepare(struct clk_hw *hw)
+{
+	struct si5351_hw_data *hwdata =
+		container_of(hw, struct si5351_hw_data, hw);
+
+	si5351_set_bits(hwdata->drvdata, SI5351_CLK0_CTRL + hwdata->num,
+			SI5351_CLK_POWERDOWN, SI5351_CLK_POWERDOWN);
+	si5351_set_bits(hwdata->drvdata, SI5351_OUTPUT_ENABLE_CTRL,
+			(1 << hwdata->num), (1 << hwdata->num));
+}
+
+static u8 si5351_clkout_get_parent(struct clk_hw *hw)
+{
+	struct si5351_hw_data *hwdata =
+		container_of(hw, struct si5351_hw_data, hw);
+	int index = 0;
+	unsigned char val;
+
+	val = si5351_reg_read(hwdata->drvdata, SI5351_CLK0_CTRL + hwdata->num);
+	switch (val & SI5351_CLK_INPUT_MASK) {
+	case SI5351_CLK_INPUT_MULTISYNTH_N:
+		index = 0;
+		break;
+	case SI5351_CLK_INPUT_MULTISYNTH_0_4:
+		index = 1;
+		break;
+	case SI5351_CLK_INPUT_XTAL:
+		index = 2;
+		break;
+	case SI5351_CLK_INPUT_CLKIN:
+		index = 3;
+		break;
+	}
+
+	return index;
+}
+
+static int si5351_clkout_set_parent(struct clk_hw *hw, u8 index)
+{
+	struct si5351_hw_data *hwdata =
+		container_of(hw, struct si5351_hw_data, hw);
+	enum si5351_clkout_src parent = SI5351_CLKOUT_SRC_DEFAULT;
+
+	switch (index) {
+	case 0:
+		parent = SI5351_CLKOUT_SRC_MSYNTH_N;
+		break;
+	case 1:
+		parent = SI5351_CLKOUT_SRC_MSYNTH_0_4;
+		break;
+	case 2:
+		parent = SI5351_CLKOUT_SRC_XTAL;
+		break;
+	case 3:
+		parent = SI5351_CLKOUT_SRC_CLKIN;
+		break;
+	}
+
+	return _si5351_clkout_reparent(hwdata->drvdata, hwdata->num, parent);
+}
+
+static unsigned long si5351_clkout_recalc_rate(struct clk_hw *hw,
+					       unsigned long parent_rate)
+{
+	struct si5351_hw_data *hwdata =
+		container_of(hw, struct si5351_hw_data, hw);
+	unsigned char reg;
+	unsigned char rdiv;
+
+	if (hwdata->num > 5)
+		reg = si5351_msynth_params_address(hwdata->num) + 2;
+	else
+		reg = SI5351_CLK6_7_OUTPUT_DIVIDER;
+
+	rdiv = si5351_reg_read(hwdata->drvdata, reg);
+	if (hwdata->num == 6) {
+		rdiv &= SI5351_OUTPUT_CLK6_DIV_MASK;
+	} else {
+		rdiv &= SI5351_OUTPUT_CLK_DIV_MASK;
+		rdiv >>= SI5351_OUTPUT_CLK_DIV_SHIFT;
+	}
+
+	return parent_rate >> rdiv;
+}
+
+static long si5351_clkout_round_rate(struct clk_hw *hw, unsigned long rate,
+				     unsigned long *parent_rate)
+{
+	struct si5351_hw_data *hwdata =
+		container_of(hw, struct si5351_hw_data, hw);
+	unsigned char rdiv;
+
+	/* clkout6/7 can only handle output freqencies < 150MHz */
+	if (hwdata->num >= 6 && rate > SI5351_CLKOUT67_MAX_FREQ)
+		rate = SI5351_CLKOUT67_MAX_FREQ;
+
+	/* clkout freqency is 8kHz - 160MHz */
+	if (rate > SI5351_CLKOUT_MAX_FREQ)
+		rate = SI5351_CLKOUT_MAX_FREQ;
+	if (rate < SI5351_CLKOUT_MIN_FREQ)
+		rate = SI5351_CLKOUT_MIN_FREQ;
+
+	/* request frequency if multisync master */
+	if (__clk_get_flags(hwdata->hw.clk) & CLK_SET_RATE_PARENT) {
+		/* use r divider for frequencies below 1MHz */
+		rdiv = SI5351_OUTPUT_CLK_DIV_1;
+		while (rate < SI5351_MULTISYNTH_MIN_FREQ &&
+		       rdiv < SI5351_OUTPUT_CLK_DIV_128) {
+			rdiv += 1;
+			rate *= 2;
+		}
+		*parent_rate = rate;
+	} else {
+		unsigned long new_rate, new_err, err;
+
+		/* round to closed rdiv */
+		rdiv = SI5351_OUTPUT_CLK_DIV_1;
+		new_rate = *parent_rate;
+		err = abs(new_rate - rate);
+		do {
+			new_rate >>= 1;
+			new_err = abs(new_rate - rate);
+			if (new_err > err || rdiv == SI5351_OUTPUT_CLK_DIV_128)
+				break;
+			rdiv++;
+			err = new_err;
+		} while (1);
+	}
+	rate = *parent_rate >> rdiv;
+
+	dev_dbg(&hwdata->drvdata->client->dev,
+		"%s - %s: rdiv = %u, parent_rate = %lu, rate = %lu\n",
+		__func__, __clk_get_name(hwdata->hw.clk), (1 << rdiv),
+		*parent_rate, rate);
+
+	return rate;
+}
+
+static int si5351_clkout_set_rate(struct clk_hw *hw, unsigned long rate,
+				  unsigned long parent_rate)
+{
+	struct si5351_hw_data *hwdata =
+		container_of(hw, struct si5351_hw_data, hw);
+	unsigned long new_rate, new_err, err;
+	unsigned char rdiv;
+
+	/* round to closed rdiv */
+	rdiv = SI5351_OUTPUT_CLK_DIV_1;
+	new_rate = parent_rate;
+	err = abs(new_rate - rate);
+	do {
+		new_rate >>= 1;
+		new_err = abs(new_rate - rate);
+		if (new_err > err || rdiv == SI5351_OUTPUT_CLK_DIV_128)
+			break;
+		rdiv++;
+		err = new_err;
+	} while (1);
+
+	/* write output divider */
+	switch (hwdata->num) {
+	case 6:
+		si5351_set_bits(hwdata->drvdata, SI5351_CLK6_7_OUTPUT_DIVIDER,
+				SI5351_OUTPUT_CLK6_DIV_MASK, rdiv);
+		break;
+	case 7:
+		si5351_set_bits(hwdata->drvdata, SI5351_CLK6_7_OUTPUT_DIVIDER,
+				SI5351_OUTPUT_CLK_DIV_MASK,
+				rdiv << SI5351_OUTPUT_CLK_DIV_SHIFT);
+		break;
+	default:
+		si5351_set_bits(hwdata->drvdata,
+				si5351_msynth_params_address(hwdata->num) + 2,
+				SI5351_OUTPUT_CLK_DIV_MASK,
+				rdiv << SI5351_OUTPUT_CLK_DIV_SHIFT);
+	}
+
+	/* powerup clkout */
+	si5351_set_bits(hwdata->drvdata, SI5351_CLK0_CTRL + hwdata->num,
+			SI5351_CLK_POWERDOWN, 0);
+
+	dev_dbg(&hwdata->drvdata->client->dev,
+		"%s - %s: rdiv = %u, parent_rate = %lu, rate = %lu\n",
+		__func__, __clk_get_name(hwdata->hw.clk), (1 << rdiv),
+		parent_rate, rate);
+
+	return 0;
+}
+
+static const struct clk_ops si5351_clkout_ops = {
+	.prepare = si5351_clkout_prepare,
+	.unprepare = si5351_clkout_unprepare,
+	.set_parent = si5351_clkout_set_parent,
+	.get_parent = si5351_clkout_get_parent,
+	.recalc_rate = si5351_clkout_recalc_rate,
+	.round_rate = si5351_clkout_round_rate,
+	.set_rate = si5351_clkout_set_rate,
+};
+
+/*
+ * Si5351 i2c probe and DT
+ */
+#ifdef CONFIG_OF
+static const struct of_device_id si5351_dt_ids[] = {
+	{ .compatible = "silabs,si5351a", .data = (void *)SI5351_VARIANT_A, },
+	{ .compatible = "silabs,si5351a-msop",
+					 .data = (void *)SI5351_VARIANT_A3, },
+	{ .compatible = "silabs,si5351b", .data = (void *)SI5351_VARIANT_B, },
+	{ .compatible = "silabs,si5351c", .data = (void *)SI5351_VARIANT_C, },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, si5351_dt_ids);
+
+static int si5351_dt_parse(struct i2c_client *client)
+{
+	struct device_node *child, *np = client->dev.of_node;
+	struct si5351_platform_data *pdata;
+	const struct of_device_id *match;
+	struct property *prop;
+	const __be32 *p;
+	int num = 0;
+	u32 val;
+
+	if (np == NULL)
+		return 0;
+
+	match = of_match_node(si5351_dt_ids, np);
+	if (match == NULL)
+		return -EINVAL;
+
+	pdata = devm_kzalloc(&client->dev, sizeof(*pdata), GFP_KERNEL);
+	if (!pdata)
+		return -ENOMEM;
+
+	pdata->variant = (enum si5351_variant)match->data;
+	pdata->clk_xtal = of_clk_get(np, 0);
+	if (!IS_ERR(pdata->clk_xtal))
+		clk_put(pdata->clk_xtal);
+	pdata->clk_clkin = of_clk_get(np, 1);
+	if (!IS_ERR(pdata->clk_clkin))
+		clk_put(pdata->clk_clkin);
+
+	/*
+	 * property silabs,pll-source : <num src>, [<..>]
+	 * allow to selectively set pll source
+	 */
+	of_property_for_each_u32(np, "silabs,pll-source", prop, p, num) {
+		if (num >= 2) {
+			dev_err(&client->dev,
+				"invalid pll %d on pll-source prop\n", num);
+			return -EINVAL;
+		}
+
+		p = of_prop_next_u32(prop, p, &val);
+		if (!p) {
+			dev_err(&client->dev,
+				"missing pll-source for pll %d\n", num);
+			return -EINVAL;
+		}
+
+		switch (val) {
+		case 0:
+			pdata->pll_src[num] = SI5351_PLL_SRC_XTAL;
+			break;
+		case 1:
+			if (pdata->variant != SI5351_VARIANT_C) {
+				dev_err(&client->dev,
+					"invalid parent %d for pll %d\n",
+					val, num);
+				return -EINVAL;
+			}
+			pdata->pll_src[num] = SI5351_PLL_SRC_CLKIN;
+			break;
+		default:
+			dev_err(&client->dev,
+				 "invalid parent %d for pll %d\n", val, num);
+			return -EINVAL;
+		}
+	}
+
+	/* per clkout properties */
+	for_each_child_of_node(np, child) {
+		if (of_property_read_u32(child, "reg", &num)) {
+			dev_err(&client->dev, "missing reg property of %s\n",
+				child->name);
+			return -EINVAL;
+		}
+
+		if (num >= 8 ||
+		    (pdata->variant == SI5351_VARIANT_A3 && num >= 3)) {
+			dev_err(&client->dev, "invalid clkout %d\n", num);
+			return -EINVAL;
+		}
+
+		if (!of_property_read_u32(child, "silabs,multisynth-source",
+					  &val)) {
+			switch (val) {
+			case 0:
+				pdata->clkout[num].multisynth_src =
+					SI5351_MULTISYNTH_SRC_VCO0;
+				break;
+			case 1:
+				pdata->clkout[num].multisynth_src =
+					SI5351_MULTISYNTH_SRC_VCO1;
+				break;
+			default:
+				dev_err(&client->dev,
+					"invalid parent %d for multisynth %d\n",
+					val, num);
+				return -EINVAL;
+			}
+		}
+
+		if (!of_property_read_u32(child, "silabs,clock-source", &val)) {
+			switch (val) {
+			case 0:
+				pdata->clkout[num].clkout_src =
+					SI5351_CLKOUT_SRC_MSYNTH_N;
+				break;
+			case 1:
+				pdata->clkout[num].clkout_src =
+					SI5351_CLKOUT_SRC_MSYNTH_0_4;
+				break;
+			case 2:
+				pdata->clkout[num].clkout_src =
+					SI5351_CLKOUT_SRC_XTAL;
+				break;
+			case 3:
+				if (pdata->variant != SI5351_VARIANT_C) {
+					dev_err(&client->dev,
+						"invalid parent %d for clkout %d\n",
+						val, num);
+					return -EINVAL;
+				}
+				pdata->clkout[num].clkout_src =
+					SI5351_CLKOUT_SRC_CLKIN;
+				break;
+			default:
+				dev_err(&client->dev,
+					"invalid parent %d for clkout %d\n",
+					val, num);
+				return -EINVAL;
+			}
+		}
+
+		if (!of_property_read_u32(child, "silabs,drive-strength",
+					  &val)) {
+			switch (val) {
+			case SI5351_DRIVE_2MA:
+			case SI5351_DRIVE_4MA:
+			case SI5351_DRIVE_6MA:
+			case SI5351_DRIVE_8MA:
+				pdata->clkout[num].drive = val;
+				break;
+			default:
+				dev_err(&client->dev,
+					"invalid drive strength %d for clkout %d\n",
+					val, num);
+				return -EINVAL;
+			}
+		}
+
+		if (!of_property_read_u32(child, "clock-frequency", &val))
+			pdata->clkout[num].rate = val;
+
+		pdata->clkout[num].pll_master =
+			of_property_read_bool(child, "silabs,pll-master");
+	}
+	client->dev.platform_data = pdata;
+
+	return 0;
+}
+#else
+static int si5351_dt_parse(struct i2c_client *client)
+{
+	return 0;
+}
+#endif /* CONFIG_OF */
+
+static int si5351_i2c_probe(struct i2c_client *client,
+			    const struct i2c_device_id *id)
+{
+	struct si5351_platform_data *pdata;
+	struct si5351_driver_data *drvdata;
+	struct clk_init_data init;
+	struct clk *clk;
+	const char *parent_names[4];
+	u8 num_parents, num_clocks;
+	int ret, n;
+
+	ret = si5351_dt_parse(client);
+	if (ret)
+		return ret;
+
+	pdata = client->dev.platform_data;
+	if (!pdata)
+		return -EINVAL;
+
+	drvdata = devm_kzalloc(&client->dev, sizeof(*drvdata), GFP_KERNEL);
+	if (drvdata == NULL) {
+		dev_err(&client->dev, "unable to allocate driver data\n");
+		return -ENOMEM;
+	}
+
+	i2c_set_clientdata(client, drvdata);
+	drvdata->client = client;
+	drvdata->variant = pdata->variant;
+	drvdata->pxtal = pdata->clk_xtal;
+	drvdata->pclkin = pdata->clk_clkin;
+
+	drvdata->regmap = devm_regmap_init_i2c(client, &si5351_regmap_config);
+	if (IS_ERR(drvdata->regmap)) {
+		dev_err(&client->dev, "failed to allocate register map\n");
+		return PTR_ERR(drvdata->regmap);
+	}
+
+	/* Disable interrupts */
+	si5351_reg_write(drvdata, SI5351_INTERRUPT_MASK, 0xf0);
+	/* Set disabled output drivers to drive low */
+	si5351_reg_write(drvdata, SI5351_CLK3_0_DISABLE_STATE, 0x00);
+	si5351_reg_write(drvdata, SI5351_CLK7_4_DISABLE_STATE, 0x00);
+	/* Ensure pll select is on XTAL for Si5351A/B */
+	if (drvdata->variant != SI5351_VARIANT_C)
+		si5351_set_bits(drvdata, SI5351_PLL_INPUT_SOURCE,
+				SI5351_PLLA_SOURCE | SI5351_PLLB_SOURCE, 0);
+
+	/* setup clock configuration */
+	for (n = 0; n < 2; n++) {
+		ret = _si5351_pll_reparent(drvdata, n, pdata->pll_src[n]);
+		if (ret) {
+			dev_err(&client->dev,
+				"failed to reparent pll %d to %d\n",
+				n, pdata->pll_src[n]);
+			return ret;
+		}
+	}
+
+	for (n = 0; n < 8; n++) {
+		ret = _si5351_msynth_reparent(drvdata, n,
+					      pdata->clkout[n].multisynth_src);
+		if (ret) {
+			dev_err(&client->dev,
+				"failed to reparent multisynth %d to %d\n",
+				n, pdata->clkout[n].multisynth_src);
+			return ret;
+		}
+
+		ret = _si5351_clkout_reparent(drvdata, n,
+					      pdata->clkout[n].clkout_src);
+		if (ret) {
+			dev_err(&client->dev,
+				"failed to reparent clkout %d to %d\n",
+				n, pdata->clkout[n].clkout_src);
+			return ret;
+		}
+
+		ret = _si5351_clkout_set_drive_strength(drvdata, n,
+							pdata->clkout[n].drive);
+		if (ret) {
+			dev_err(&client->dev,
+				"failed set drive strength of clkout%d to %d\n",
+				n, pdata->clkout[n].drive);
+			return ret;
+		}
+	}
+
+	/* register xtal input clock gate */
+	memset(&init, 0, sizeof(init));
+	init.name = si5351_input_names[0];
+	init.ops = &si5351_xtal_ops;
+	init.flags = 0;
+	if (!IS_ERR(drvdata->pxtal)) {
+		drvdata->pxtal_name = __clk_get_name(drvdata->pxtal);
+		init.parent_names = &drvdata->pxtal_name;
+		init.num_parents = 1;
+	}
+	drvdata->xtal.init = &init;
+	clk = devm_clk_register(&client->dev, &drvdata->xtal);
+	if (IS_ERR(clk)) {
+		dev_err(&client->dev, "unable to register %s\n", init.name);
+		return PTR_ERR(clk);
+	}
+
+	/* register clkin input clock gate */
+	if (drvdata->variant == SI5351_VARIANT_C) {
+		memset(&init, 0, sizeof(init));
+		init.name = si5351_input_names[1];
+		init.ops = &si5351_clkin_ops;
+		if (!IS_ERR(drvdata->pclkin)) {
+			drvdata->pclkin_name = __clk_get_name(drvdata->pclkin);
+			init.parent_names = &drvdata->pclkin_name;
+			init.num_parents = 1;
+		}
+		drvdata->clkin.init = &init;
+		clk = devm_clk_register(&client->dev, &drvdata->clkin);
+		if (IS_ERR(clk)) {
+			dev_err(&client->dev, "unable to register %s\n",
+				init.name);
+			return PTR_ERR(clk);
+		}
+	}
+
+	/* Si5351C allows to mux either xtal or clkin to PLL input */
+	num_parents = (drvdata->variant == SI5351_VARIANT_C) ? 2 : 1;
+	parent_names[0] = si5351_input_names[0];
+	parent_names[1] = si5351_input_names[1];
+
+	/* register PLLA */
+	drvdata->pll[0].num = 0;
+	drvdata->pll[0].drvdata = drvdata;
+	drvdata->pll[0].hw.init = &init;
+	memset(&init, 0, sizeof(init));
+	init.name = si5351_pll_names[0];
+	init.ops = &si5351_pll_ops;
+	init.flags = 0;
+	init.parent_names = parent_names;
+	init.num_parents = num_parents;
+	clk = devm_clk_register(&client->dev, &drvdata->pll[0].hw);
+	if (IS_ERR(clk)) {
+		dev_err(&client->dev, "unable to register %s\n", init.name);
+		return -EINVAL;
+	}
+
+	/* register PLLB or VXCO (Si5351B) */
+	drvdata->pll[1].num = 1;
+	drvdata->pll[1].drvdata = drvdata;
+	drvdata->pll[1].hw.init = &init;
+	memset(&init, 0, sizeof(init));
+	if (drvdata->variant == SI5351_VARIANT_B) {
+		init.name = si5351_pll_names[2];
+		init.ops = &si5351_vxco_ops;
+		init.flags = CLK_IS_ROOT;
+		init.parent_names = NULL;
+		init.num_parents = 0;
+	} else {
+		init.name = si5351_pll_names[1];
+		init.ops = &si5351_pll_ops;
+		init.flags = 0;
+		init.parent_names = parent_names;
+		init.num_parents = num_parents;
+	}
+	clk = devm_clk_register(&client->dev, &drvdata->pll[1].hw);
+	if (IS_ERR(clk)) {
+		dev_err(&client->dev, "unable to register %s\n", init.name);
+		return -EINVAL;
+	}
+
+	/* register clk multisync and clk out divider */
+	num_clocks = (drvdata->variant == SI5351_VARIANT_A3) ? 3 : 8;
+	parent_names[0] = si5351_pll_names[0];
+	if (drvdata->variant == SI5351_VARIANT_B)
+		parent_names[1] = si5351_pll_names[2];
+	else
+		parent_names[1] = si5351_pll_names[1];
+
+	drvdata->msynth = devm_kzalloc(&client->dev, num_clocks *
+				       sizeof(*drvdata->msynth), GFP_KERNEL);
+	drvdata->clkout = devm_kzalloc(&client->dev, num_clocks *
+				       sizeof(*drvdata->clkout), GFP_KERNEL);
+
+	drvdata->onecell.clk_num = num_clocks;
+	drvdata->onecell.clks = devm_kzalloc(&client->dev,
+		num_clocks * sizeof(*drvdata->onecell.clks), GFP_KERNEL);
+
+	if (WARN_ON(!drvdata->msynth || !drvdata->clkout ||
+		    !drvdata->onecell.clks))
+		return -ENOMEM;
+
+	for (n = 0; n < num_clocks; n++) {
+		drvdata->msynth[n].num = n;
+		drvdata->msynth[n].drvdata = drvdata;
+		drvdata->msynth[n].hw.init = &init;
+		memset(&init, 0, sizeof(init));
+		init.name = si5351_msynth_names[n];
+		init.ops = &si5351_msynth_ops;
+		init.flags = 0;
+		if (pdata->clkout[n].pll_master)
+			init.flags |= CLK_SET_RATE_PARENT;
+		init.parent_names = parent_names;
+		init.num_parents = 2;
+		clk = devm_clk_register(&client->dev, &drvdata->msynth[n].hw);
+		if (IS_ERR(clk)) {
+			dev_err(&client->dev, "unable to register %s\n",
+				init.name);
+			return -EINVAL;
+		}
+	}
+
+	num_parents = (drvdata->variant == SI5351_VARIANT_C) ? 4 : 3;
+	parent_names[2] = si5351_input_names[0];
+	parent_names[3] = si5351_input_names[1];
+	for (n = 0; n < num_clocks; n++) {
+		parent_names[0] = si5351_msynth_names[n];
+		parent_names[1] = (n < 4) ? si5351_msynth_names[0] :
+			si5351_msynth_names[4];
+
+		drvdata->clkout[n].num = n;
+		drvdata->clkout[n].drvdata = drvdata;
+		drvdata->clkout[n].hw.init = &init;
+		memset(&init, 0, sizeof(init));
+		init.name = si5351_clkout_names[n];
+		init.ops = &si5351_clkout_ops;
+		init.flags = 0;
+		if (pdata->clkout[n].clkout_src == SI5351_CLKOUT_SRC_MSYNTH_N)
+			init.flags |= CLK_SET_RATE_PARENT;
+		init.parent_names = parent_names;
+		init.num_parents = num_parents;
+		clk = devm_clk_register(&client->dev, &drvdata->clkout[n].hw);
+		if (IS_ERR(clk)) {
+			dev_err(&client->dev, "unable to register %s\n",
+				init.name);
+			return -EINVAL;
+		}
+		drvdata->onecell.clks[n] = clk;
+	}
+
+	ret = of_clk_add_provider(client->dev.of_node, of_clk_src_onecell_get,
+				  &drvdata->onecell);
+	if (ret) {
+		dev_err(&client->dev, "unable to add clk provider\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+static const struct i2c_device_id si5351_i2c_ids[] = {
+	{ "silabs,si5351", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, si5351_i2c_ids);
+
+static struct i2c_driver si5351_driver = {
+	.driver = {
+		.name = "si5351",
+		.of_match_table = of_match_ptr(si5351_dt_ids),
+	},
+	.probe = si5351_i2c_probe,
+	.id_table = si5351_i2c_ids,
+};
+module_i2c_driver(si5351_driver);
+
+MODULE_AUTHOR("Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com");
+MODULE_DESCRIPTION("Silicon Labs Si5351A/B/C clock generator driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/clk-si5351.h b/drivers/clk/clk-si5351.h
new file mode 100644
index 0000000..af41b50
--- /dev/null
+++ b/drivers/clk/clk-si5351.h
@@ -0,0 +1,155 @@ 
+/*
+ * clk-si5351.h: Silicon Laboratories Si5351A/B/C I2C Clock Generator
+ *
+ * Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
+ * Rabeeh Khoury <rabeeh@solid-run.com>
+ *
+ * This program is free software; you can redistribute  it and/or modify it
+ * under  the terms of  the GNU General  Public License as published by the
+ * Free Software Foundation;  either version 2 of the  License, or (at your
+ * option) any later version.
+ */
+
+#ifndef _CLK_SI5351_H_
+#define _CLK_SI5351_H_
+
+#define SI5351_BUS_BASE_ADDR			0x60
+
+#define SI5351_PLL_VCO_MIN			600000000
+#define SI5351_PLL_VCO_MAX			900000000
+#define SI5351_MULTISYNTH_MIN_FREQ		1000000
+#define SI5351_MULTISYNTH_DIVBY4_FREQ		150000000
+#define SI5351_MULTISYNTH_MAX_FREQ		160000000
+#define SI5351_MULTISYNTH67_MAX_FREQ		SI5351_MULTISYNTH_DIVBY4_FREQ
+#define SI5351_CLKOUT_MIN_FREQ			8000
+#define SI5351_CLKOUT_MAX_FREQ			SI5351_MULTISYNTH_MAX_FREQ
+#define SI5351_CLKOUT67_MAX_FREQ		SI5351_MULTISYNTH67_MAX_FREQ
+
+#define SI5351_PLL_A_MIN			15
+#define SI5351_PLL_A_MAX			90
+#define SI5351_PLL_B_MAX			(SI5351_PLL_C_MAX-1)
+#define SI5351_PLL_C_MAX			1048575
+#define SI5351_MULTISYNTH_A_MIN			6
+#define SI5351_MULTISYNTH_A_MAX			1800
+#define SI5351_MULTISYNTH67_A_MAX		254
+#define SI5351_MULTISYNTH_B_MAX			(SI5351_MULTISYNTH_C_MAX-1)
+#define SI5351_MULTISYNTH_C_MAX			1048575
+#define SI5351_MULTISYNTH_P1_MAX		((1<<18)-1)
+#define SI5351_MULTISYNTH_P2_MAX		((1<<20)-1)
+#define SI5351_MULTISYNTH_P3_MAX		((1<<20)-1)
+
+#define SI5351_DEVICE_STATUS			0
+#define SI5351_INTERRUPT_STATUS			1
+#define SI5351_INTERRUPT_MASK			2
+#define  SI5351_STATUS_SYS_INIT			(1<<7)
+#define  SI5351_STATUS_LOL_B			(1<<6)
+#define  SI5351_STATUS_LOL_A			(1<<5)
+#define  SI5351_STATUS_LOS			(1<<4)
+#define SI5351_OUTPUT_ENABLE_CTRL		3
+#define SI5351_OEB_PIN_ENABLE_CTRL		9
+#define SI5351_PLL_INPUT_SOURCE			15
+#define  SI5351_CLKIN_DIV_MASK			(3<<6)
+#define  SI5351_CLKIN_DIV_1			(0<<6)
+#define  SI5351_CLKIN_DIV_2			(1<<6)
+#define  SI5351_CLKIN_DIV_4			(2<<6)
+#define  SI5351_CLKIN_DIV_8			(3<<6)
+#define  SI5351_PLLB_SOURCE			(1<<3)
+#define  SI5351_PLLA_SOURCE			(1<<2)
+
+#define SI5351_CLK0_CTRL			16
+#define SI5351_CLK1_CTRL			17
+#define SI5351_CLK2_CTRL			18
+#define SI5351_CLK3_CTRL			19
+#define SI5351_CLK4_CTRL			20
+#define SI5351_CLK5_CTRL			21
+#define SI5351_CLK6_CTRL			22
+#define SI5351_CLK7_CTRL			23
+#define  SI5351_CLK_POWERDOWN			(1<<7)
+#define  SI5351_CLK_INTEGER_MODE		(1<<6)
+#define  SI5351_CLK_PLL_SELECT			(1<<5)
+#define  SI5351_CLK_INVERT			(1<<4)
+#define  SI5351_CLK_INPUT_MASK			(3<<2)
+#define  SI5351_CLK_INPUT_XTAL			(0<<2)
+#define  SI5351_CLK_INPUT_CLKIN			(1<<2)
+#define  SI5351_CLK_INPUT_MULTISYNTH_0_4	(2<<2)
+#define  SI5351_CLK_INPUT_MULTISYNTH_N		(3<<2)
+#define  SI5351_CLK_DRIVE_STRENGTH_MASK		(3<<0)
+#define  SI5351_CLK_DRIVE_STRENGTH_2MA		(0<<0)
+#define  SI5351_CLK_DRIVE_STRENGTH_4MA		(1<<0)
+#define  SI5351_CLK_DRIVE_STRENGTH_6MA		(2<<0)
+#define  SI5351_CLK_DRIVE_STRENGTH_8MA		(3<<0)
+
+#define SI5351_CLK3_0_DISABLE_STATE		24
+#define SI5351_CLK7_4_DISABLE_STATE		25
+#define  SI5351_CLK_DISABLE_STATE_LOW		0
+#define  SI5351_CLK_DISABLE_STATE_HIGH		1
+#define  SI5351_CLK_DISABLE_STATE_FLOAT		2
+#define  SI5351_CLK_DISABLE_STATE_NEVER		3
+
+#define SI5351_PARAMETERS_LENGTH		8
+#define SI5351_PLLA_PARAMETERS			26
+#define SI5351_PLLB_PARAMETERS			34
+#define SI5351_CLK0_PARAMETERS			42
+#define SI5351_CLK1_PARAMETERS			50
+#define SI5351_CLK2_PARAMETERS			58
+#define SI5351_CLK3_PARAMETERS			66
+#define SI5351_CLK4_PARAMETERS			74
+#define SI5351_CLK5_PARAMETERS			82
+#define SI5351_CLK6_PARAMETERS			90
+#define SI5351_CLK7_PARAMETERS			91
+#define SI5351_CLK6_7_OUTPUT_DIVIDER		92
+#define  SI5351_OUTPUT_CLK_DIV_MASK		(7 << 4)
+#define  SI5351_OUTPUT_CLK6_DIV_MASK		(7 << 0)
+#define  SI5351_OUTPUT_CLK_DIV_SHIFT		4
+#define  SI5351_OUTPUT_CLK_DIV6_SHIFT		0
+#define  SI5351_OUTPUT_CLK_DIV_1		0
+#define  SI5351_OUTPUT_CLK_DIV_2		1
+#define  SI5351_OUTPUT_CLK_DIV_4		2
+#define  SI5351_OUTPUT_CLK_DIV_8		3
+#define  SI5351_OUTPUT_CLK_DIV_16		4
+#define  SI5351_OUTPUT_CLK_DIV_32		5
+#define  SI5351_OUTPUT_CLK_DIV_64		6
+#define  SI5351_OUTPUT_CLK_DIV_128		7
+#define  SI5351_OUTPUT_CLK_DIVBY4		(3<<2)
+
+#define SI5351_SSC_PARAM0			149
+#define SI5351_SSC_PARAM1			150
+#define SI5351_SSC_PARAM2			151
+#define SI5351_SSC_PARAM3			152
+#define SI5351_SSC_PARAM4			153
+#define SI5351_SSC_PARAM5			154
+#define SI5351_SSC_PARAM6			155
+#define SI5351_SSC_PARAM7			156
+#define SI5351_SSC_PARAM8			157
+#define SI5351_SSC_PARAM9			158
+#define SI5351_SSC_PARAM10			159
+#define SI5351_SSC_PARAM11			160
+#define SI5351_SSC_PARAM12			161
+
+#define SI5351_VXCO_PARAMETERS_LOW		162
+#define SI5351_VXCO_PARAMETERS_MID		163
+#define SI5351_VXCO_PARAMETERS_HIGH		164
+
+#define SI5351_CLK0_PHASE_OFFSET		165
+#define SI5351_CLK1_PHASE_OFFSET		166
+#define SI5351_CLK2_PHASE_OFFSET		167
+#define SI5351_CLK3_PHASE_OFFSET		168
+#define SI5351_CLK4_PHASE_OFFSET		169
+#define SI5351_CLK5_PHASE_OFFSET		170
+
+#define SI5351_PLL_RESET			177
+#define  SI5351_PLL_RESET_B			(1<<7)
+#define  SI5351_PLL_RESET_A			(1<<5)
+
+#define SI5351_CRYSTAL_LOAD			183
+#define  SI5351_CRYSTAL_LOAD_MASK		(3<<6)
+#define  SI5351_CRYSTAL_LOAD_6PF		(1<<6)
+#define  SI5351_CRYSTAL_LOAD_8PF		(2<<6)
+#define  SI5351_CRYSTAL_LOAD_10PF		(3<<6)
+
+#define SI5351_FANOUT_ENABLE			187
+#define  SI5351_CLKIN_ENABLE			(1<<7)
+#define  SI5351_XTAL_ENABLE			(1<<6)
+#define  SI5351_MULTISYNTH_ENABLE		(1<<4)
+
+#endif
diff --git a/include/linux/platform_data/si5351.h b/include/linux/platform_data/si5351.h
new file mode 100644
index 0000000..92dabca
--- /dev/null
+++ b/include/linux/platform_data/si5351.h
@@ -0,0 +1,114 @@ 
+/*
+ * Si5351A/B/C programmable clock generator platform_data.
+ */
+
+#ifndef __LINUX_PLATFORM_DATA_SI5351_H__
+#define __LINUX_PLATFORM_DATA_SI5351_H__
+
+struct clk;
+
+/**
+ * enum si5351_variant - SiLabs Si5351 chip variant
+ * @SI5351_VARIANT_A: Si5351A (8 output clocks, XTAL input)
+ * @SI5351_VARIANT_A3: Si5351A MSOP10 (3 output clocks, XTAL input)
+ * @SI5351_VARIANT_B: Si5351B (8 output clocks, XTAL/VXCO input)
+ * @SI5351_VARIANT_C: Si5351C (8 output clocks, XTAL/CLKIN input)
+ */
+enum si5351_variant {
+	SI5351_VARIANT_A = 1,
+	SI5351_VARIANT_A3 = 2,
+	SI5351_VARIANT_B = 3,
+	SI5351_VARIANT_C = 4,
+};
+
+/**
+ * enum si5351_pll_src - Si5351 pll clock source
+ * @SI5351_PLL_SRC_DEFAULT: default, do not change eeprom config
+ * @SI5351_PLL_SRC_XTAL: pll source clock is XTAL input
+ * @SI5351_PLL_SRC_CLKIN: pll source clock is CLKIN input (Si5351C only)
+ */
+enum si5351_pll_src {
+	SI5351_PLL_SRC_DEFAULT = 0,
+	SI5351_PLL_SRC_XTAL = 1,
+	SI5351_PLL_SRC_CLKIN = 2,
+};
+
+/**
+ * enum si5351_multisynth_src - Si5351 multisynth clock source
+ * @SI5351_MULTISYNTH_SRC_DEFAULT: default, do not change eeprom config
+ * @SI5351_MULTISYNTH_SRC_VCO0: multisynth source clock is VCO0
+ * @SI5351_MULTISYNTH_SRC_VCO1: multisynth source clock is VCO1/VXCO
+ */
+enum si5351_multisynth_src {
+	SI5351_MULTISYNTH_SRC_DEFAULT = 0,
+	SI5351_MULTISYNTH_SRC_VCO0 = 1,
+	SI5351_MULTISYNTH_SRC_VCO1 = 2,
+};
+
+/**
+ * enum si5351_clkout_src - Si5351 clock output clock source
+ * @SI5351_CLKOUT_SRC_DEFAULT: default, do not change eeprom config
+ * @SI5351_CLKOUT_SRC_MSYNTH_N: clkout N source clock is multisynth N
+ * @SI5351_CLKOUT_SRC_MSYNTH_0_4: clkout N source clock is multisynth 0 (N<4)
+ *                                or 4 (N>=4)
+ * @SI5351_CLKOUT_SRC_XTAL: clkout N source clock is XTAL
+ * @SI5351_CLKOUT_SRC_CLKIN: clkout N source clock is CLKIN (Si5351C only)
+ */
+enum si5351_clkout_src {
+	SI5351_CLKOUT_SRC_DEFAULT = 0,
+	SI5351_CLKOUT_SRC_MSYNTH_N = 1,
+	SI5351_CLKOUT_SRC_MSYNTH_0_4 = 2,
+	SI5351_CLKOUT_SRC_XTAL = 3,
+	SI5351_CLKOUT_SRC_CLKIN = 4,
+};
+
+/**
+ * enum si5351_drive_strength - Si5351 clock output drive strength
+ * @SI5351_DRIVE_DEFAULT: default, do not change eeprom config
+ * @SI5351_DRIVE_2MA: 2mA clock output drive strength
+ * @SI5351_DRIVE_4MA: 4mA clock output drive strength
+ * @SI5351_DRIVE_6MA: 6mA clock output drive strength
+ * @SI5351_DRIVE_8MA: 8mA clock output drive strength
+ */
+enum si5351_drive_strength {
+	SI5351_DRIVE_DEFAULT = 0,
+	SI5351_DRIVE_2MA = 2,
+	SI5351_DRIVE_4MA = 4,
+	SI5351_DRIVE_6MA = 6,
+	SI5351_DRIVE_8MA = 8,
+};
+
+/**
+ * struct si5351_clkout_config - Si5351 clock output configuration
+ * @clkout: clkout number
+ * @multisynth_src: multisynth source clock
+ * @clkout_src: clkout source clock
+ * @pll_master: if true, clkout can also change pll rate
+ * @drive: output drive strength
+ * @rate: initial clkout rate, or default if 0
+ */
+struct si5351_clkout_config {
+	enum si5351_multisynth_src multisynth_src;
+	enum si5351_clkout_src clkout_src;
+	enum si5351_drive_strength drive;
+	bool pll_master;
+	unsigned long rate;
+};
+
+/**
+ * struct si5351_platform_data - Platform data for the Si5351 clock driver
+ * @variant: Si5351 chip variant
+ * @clk_xtal: xtal input clock
+ * @clk_clkin: clkin input clock
+ * @pll_src: array of pll source clock setting
+ * @clkout: array of clkout configuration
+ */
+struct si5351_platform_data {
+	enum si5351_variant variant;
+	struct clk *clk_xtal;
+	struct clk *clk_clkin;
+	enum si5351_pll_src pll_src[2];
+	struct si5351_clkout_config clkout[8];
+};
+
+#endif