[U-Boot] [PATCH v3 11/11] clk: Port Linux common clock framework [CCF] for imx6q to U-boot (tag: 5.0-rc3)

Peng Fan peng.fan at nxp.com
Fri Apr 26 02:54:00 UTC 2019


> Subject: [PATCH v3 11/11] clk: Port Linux common clock framework [CCF] for
> imx6q to U-boot (tag: 5.0-rc3)
> 
> This commit brings the files from Linux kernel to provide clocks support as it is
> used on the Linux kernel with common clock framework [CCF] setup.
> 
> The directory structure has been preserved. The ported code only supports
> reading information from PLL, MUX, Divider, etc and enabling/disabling the
> clocks USDHCx/ECSPIx depending on used bus. Moreover, it is agnostic to the
> alias numbering as the information about the clock is read from device tree.
> 
> One needs to pay attention to the comments indicating necessary for U-boot's
> DM changes.
> 
> If needed the code can be extended to support the "set" part of the clock
> management.
> 
> 
> Signed-off-by: Lukasz Majewski <lukma at denx.de>
> ---
> 
> Changes in v3: None
> 
>  drivers/clk/Kconfig            |  14 ++++
>  drivers/clk/Makefile           |   2 +
>  drivers/clk/clk-divider.c      | 148
> ++++++++++++++++++++++++++++++++++
>  drivers/clk/clk-fixed-factor.c |  87 ++++++++++++++++++++
>  drivers/clk/clk-mux.c          | 164
> +++++++++++++++++++++++++++++++++++++
>  drivers/clk/clk.c              |  56 +++++++++++++
>  drivers/clk/imx/Kconfig        |   9 +++
>  drivers/clk/imx/Makefile       |   2 +
>  drivers/clk/imx/clk-gate2.c    | 113 ++++++++++++++++++++++++++
>  drivers/clk/imx/clk-imx6q.c    | 179
> +++++++++++++++++++++++++++++++++++++++++
>  drivers/clk/imx/clk-pfd.c      |  91 +++++++++++++++++++++
>  drivers/clk/imx/clk-pllv3.c    |  83 +++++++++++++++++++
>  drivers/clk/imx/clk.h          |  75 +++++++++++++++++
>  include/linux/clk-provider.h   |  94 ++++++++++++++++++++++
>  14 files changed, 1117 insertions(+)
>  create mode 100644 drivers/clk/clk-divider.c  create mode 100644
> drivers/clk/clk-fixed-factor.c  create mode 100644 drivers/clk/clk-mux.c
> create mode 100644 drivers/clk/clk.c  create mode 100644
> drivers/clk/imx/clk-gate2.c  create mode 100644
> drivers/clk/imx/clk-imx6q.c  create mode 100644 drivers/clk/imx/clk-pfd.c
> create mode 100644 drivers/clk/imx/clk-pllv3.c  create mode 100644
> drivers/clk/imx/clk.h  create mode 100644 include/linux/clk-provider.h
> 
> diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig index
> ff60fc5c45..9df3bc731a 100644
> --- a/drivers/clk/Kconfig
> +++ b/drivers/clk/Kconfig
> @@ -46,6 +46,20 @@ config CLK_BOSTON
>  	help
>  	  Enable this to support the clocks
> 
> +config SPL_CLK_CCF
> +	bool "SPL Common Clock Framework [CCF] support "
> +	depends on SPL_CLK
> +	help
> +	  Enable this option if you want to (re-)use the Linux kernel's Common
> +	  Clock Framework [CCF] code in U-Boot's SPL.
> +
> +config CLK_CCF
> +	bool "Common Clock Framework [CCF] support "
> +	depends on CLK
> +	help
> +	  Enable this option if you want to (re-)use the Linux kernel's Common
> +	  Clock Framework [CCF] code in U-Boot's clock driver.
> +
>  config CLK_STM32F
>  	bool "Enable clock driver support for STM32F family"
>  	depends on CLK && (STM32F7 || STM32F4) diff --git
> a/drivers/clk/Makefile b/drivers/clk/Makefile index 1d9d725cae..9fcc75e0ea
> 100644
> --- a/drivers/clk/Makefile
> +++ b/drivers/clk/Makefile
> @@ -7,6 +7,8 @@
>  obj-$(CONFIG_$(SPL_TPL_)CLK) += clk-uclass.o
>  obj-$(CONFIG_$(SPL_TPL_)CLK) += clk_fixed_rate.o
>  obj-$(CONFIG_$(SPL_TPL_)CLK) += clk_fixed_factor.o
> +obj-$(CONFIG_$(SPL_TPL_)CLK_CCF) += clk.o clk-divider.o clk-mux.o
> +obj-$(CONFIG_$(SPL_TPL_)CLK_CCF) += clk-fixed-factor.o
> 
>  obj-y += imx/
>  obj-y += tegra/
> diff --git a/drivers/clk/clk-divider.c b/drivers/clk/clk-divider.c new file mode
> 100644 index 0000000000..3841d8bfbb
> --- /dev/null
> +++ b/drivers/clk/clk-divider.c
> @@ -0,0 +1,148 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (C) 2019 DENX Software Engineering
> + * Lukasz Majewski, DENX Software Engineering, lukma at denx.de
> + *
> + * Copyright (C) 2011 Sascha Hauer, Pengutronix
> +<s.hauer at pengutronix.de>
> + * Copyright (C) 2011 Richard Zhao, Linaro <richard.zhao at linaro.org>
> + * Copyright (C) 2011-2012 Mike Turquette, Linaro Ltd
> +<mturquette at linaro.org>
> + *
> + */
> +
> +#include <common.h>
> +#include <asm/io.h>
> +#include <malloc.h>
> +#include <clk-uclass.h>
> +#include <dm/device.h>
> +#include <dm/uclass.h>
> +#include <dm/lists.h>
> +#include <dm/device-internal.h>
> +#include <linux/clk-provider.h>
> +#include <div64.h>
> +#include <clk.h>
> +#include "clk.h"
> +
> +#define UBOOT_DM_CLK_CCF_DIVIDER "ccf_clk_divider"
> +
> +static unsigned int _get_table_div(const struct clk_div_table *table,
> +				   unsigned int val)
> +{
> +	const struct clk_div_table *clkt;
> +
> +	for (clkt = table; clkt->div; clkt++)
> +		if (clkt->val == val)
> +			return clkt->div;
> +	return 0;
> +}
> +
> +static unsigned int _get_div(const struct clk_div_table *table,
> +			     unsigned int val, unsigned long flags, u8 width) {
> +	if (flags & CLK_DIVIDER_ONE_BASED)
> +		return val;
> +	if (flags & CLK_DIVIDER_POWER_OF_TWO)
> +		return 1 << val;
> +	if (flags & CLK_DIVIDER_MAX_AT_ZERO)
> +		return val ? val : clk_div_mask(width) + 1;
> +	if (table)
> +		return _get_table_div(table, val);
> +	return val + 1;
> +}
> +
> +unsigned long divider_recalc_rate(struct clk *hw, unsigned long parent_rate,
> +				  unsigned int val,
> +				  const struct clk_div_table *table,
> +				  unsigned long flags, unsigned long width) {
> +	unsigned int div;
> +
> +	div = _get_div(table, val, flags, width);
> +	if (!div) {
> +		WARN(!(flags & CLK_DIVIDER_ALLOW_ZERO),
> +		     "%s: Zero divisor and CLK_DIVIDER_ALLOW_ZERO not set\n",
> +		     clk_hw_get_name(hw));
> +		return parent_rate;
> +	}
> +
> +	return DIV_ROUND_UP_ULL((u64)parent_rate, div); }
> +
> +static ulong clk_divider_recalc_rate(struct clk *clk) {
> +	struct clk_divider *divider =
> +		(struct clk_divider *)dev_get_driver_data(clk->dev);
> +	unsigned long parent_rate = clk_get_parent_rate(clk);
> +	unsigned int val;
> +
> +	val = readl(divider->reg) >> divider->shift;
> +	val &= clk_div_mask(divider->width);
> +
> +	return divider_recalc_rate(clk, parent_rate, val, divider->table,
> +				   divider->flags, divider->width); }
> +
> +const struct clk_ops clk_divider_ops = {
> +	.get_rate = clk_divider_recalc_rate,
> +};
> +
> +static struct clk *_register_divider(struct device *dev, const char *name,
> +		const char *parent_name, unsigned long flags,
> +		void __iomem *reg, u8 shift, u8 width,
> +		u8 clk_divider_flags, const struct clk_div_table *table) {
> +	struct clk_divider *div;
> +	struct clk *clk;
> +	int ret;
> +
> +	if (clk_divider_flags & CLK_DIVIDER_HIWORD_MASK) {
> +		if (width + shift > 16) {
> +			pr_warn("divider value exceeds LOWORD field\n");
> +			return ERR_PTR(-EINVAL);
> +		}
> +	}
> +
> +	/* allocate the divider */
> +	div = kzalloc(sizeof(*div), GFP_KERNEL);
> +	if (!div)
> +		return ERR_PTR(-ENOMEM);
> +
> +	/* struct clk_divider assignments */
> +	div->reg = reg;
> +	div->shift = shift;
> +	div->width = width;
> +	div->flags = clk_divider_flags;
> +	div->table = table;
> +
> +	/* register the clock */
> +	clk = &div->clk;
> +
> +	ret = clk_register(clk, UBOOT_DM_CLK_CCF_DIVIDER, (ulong)clk,
> +			   name, parent_name);
> +	if (ret) {
> +		kfree(div);
> +		return ERR_PTR(ret);
> +	}
> +
> +	return clk;
> +}
> +
> +struct clk *clk_register_divider(struct device *dev, const char *name,
> +		const char *parent_name, unsigned long flags,
> +		void __iomem *reg, u8 shift, u8 width,
> +		u8 clk_divider_flags)
> +{
> +	struct clk *clk;
> +
> +	clk =  _register_divider(dev, name, parent_name, flags, reg, shift,
> +				 width, clk_divider_flags, NULL);
> +	if (IS_ERR(clk))
> +		return ERR_CAST(clk);
> +	return clk;
> +}
> +
> +U_BOOT_DRIVER(ccf_clk_divider) = {
> +	.name	= UBOOT_DM_CLK_CCF_DIVIDER,
> +	.id	= UCLASS_CLK,
> +	.ops	= &clk_divider_ops,
> +	.flags = DM_FLAG_PRE_RELOC,
> +};
> diff --git a/drivers/clk/clk-fixed-factor.c b/drivers/clk/clk-fixed-factor.c new
> file mode 100644 index 0000000000..acbc0909b4
> --- /dev/null
> +++ b/drivers/clk/clk-fixed-factor.c
> @@ -0,0 +1,87 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (C) 2019 DENX Software Engineering
> + * Lukasz Majewski, DENX Software Engineering, lukma at denx.de
> + *
> + * Copyright (C) 2011 Sascha Hauer, Pengutronix
> +<s.hauer at pengutronix.de>  */ #include <common.h> #include <malloc.h>
> +#include <clk-uclass.h> #include <dm/device.h> #include
> +<linux/clk-provider.h> #include <div64.h> #include <clk.h> #include
> +"clk.h"
> +
> +#define UBOOT_DM_CLK_IMX_FIXED_FACTOR "ccf_clk_fixed_factor"
> +
> +static ulong clk_factor_recalc_rate(struct clk *clk) {
> +	struct clk_fixed_factor *fix =
> +		(struct clk_fixed_factor *)dev_get_driver_data(clk->dev);
> +	unsigned long parent_rate = clk_get_parent_rate(clk);
> +	unsigned long long int rate;
> +
> +	rate = (unsigned long long int)parent_rate * fix->mult;
> +	do_div(rate, fix->div);
> +	return (ulong)rate;
> +}
> +
> +const struct clk_ops ccf_clk_fixed_factor_ops = {
> +	.get_rate = clk_factor_recalc_rate,
> +};
> +
> +struct clk *clk_hw_register_fixed_factor(struct device *dev,
> +		const char *name, const char *parent_name, unsigned long flags,
> +		unsigned int mult, unsigned int div)
> +{
> +	struct clk_fixed_factor *fix;
> +	struct clk *clk;
> +	int ret;
> +
> +	fix = kmalloc(sizeof(*fix), GFP_KERNEL);
> +	if (!fix)
> +		return ERR_PTR(-ENOMEM);
> +
> +	/* struct clk_fixed_factor assignments */
> +	fix->mult = mult;
> +	fix->div = div;
> +	clk = &fix->clk;
> +
> +	/*
> +	 * We pass the struct clk *clk pointer (which is the same as
> +	 * clk_fixed_factor *fix - by the struct elements alignment) to DM as a
> +	 * driver_data, so it can be easily accessible from the udevice level.
> +	 * Moreover, the struct clk is only a wrapper on udevice which
> +	 * corresponds to the "real" clock device.
> +	 */
> +	ret = clk_register(clk, UBOOT_DM_CLK_IMX_FIXED_FACTOR, (ulong)clk,
> +			   name, parent_name);
> +	if (ret) {
> +		kfree(fix);
> +		return ERR_PTR(ret);
> +	}
> +
> +	return clk;
> +}
> +
> +struct clk *clk_register_fixed_factor(struct device *dev, const char *name,
> +		const char *parent_name, unsigned long flags,
> +		unsigned int mult, unsigned int div)
> +{
> +	struct clk *clk;
> +
> +	clk = clk_hw_register_fixed_factor(dev, name, parent_name, flags, mult,
> +					  div);
> +	if (IS_ERR(clk))
> +		return ERR_CAST(clk);
> +	return clk;
> +}
> +
> +U_BOOT_DRIVER(imx_clk_fixed_factor) = {
> +	.name	= UBOOT_DM_CLK_IMX_FIXED_FACTOR,
> +	.id	= UCLASS_CLK,
> +	.ops	= &ccf_clk_fixed_factor_ops,
> +	.flags = DM_FLAG_PRE_RELOC,
> +};
> diff --git a/drivers/clk/clk-mux.c b/drivers/clk/clk-mux.c new file mode
> 100644 index 0000000000..2c85f2052c
> --- /dev/null
> +++ b/drivers/clk/clk-mux.c
> @@ -0,0 +1,164 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (C) 2019 DENX Software Engineering
> + * Lukasz Majewski, DENX Software Engineering, lukma at denx.de
> + *
> + * Copyright (C) 2011 Sascha Hauer, Pengutronix
> +<s.hauer at pengutronix.de>
> + * Copyright (C) 2011 Richard Zhao, Linaro <richard.zhao at linaro.org>
> + * Copyright (C) 2011-2012 Mike Turquette, Linaro Ltd
> +<mturquette at linaro.org>
> + *
> + * Simple multiplexer clock implementation  */
> +
> +/*
> + * U-Boot CCF porting node:
> + *
> + * The Linux kernel - as of tag: 5.0-rc3 is using also the
> +imx_clk_fixup_mux()
> + * version of CCF mux. It is used on e.g. imx6q to provide fixes (like
> + * imx_cscmr1_fixup) for broken HW.
> + *
> + * At least for IMX6Q (but NOT IMX6QP) it is important when we set the
> +parent
> + * clock.
> + */
> +
> +#include <common.h>
> +#include <asm/io.h>
> +#include <malloc.h>
> +#include <clk-uclass.h>
> +#include <dm/device.h>
> +#include <linux/clk-provider.h>
> +#include <clk.h>
> +#include "clk.h"
> +
> +#define UBOOT_DM_CLK_CCF_MUX "ccf_clk_mux"
> +
> +int clk_mux_val_to_index(struct clk *clk, u32 *table, unsigned int flags,
> +			 unsigned int val)
> +{
> +	struct clk_mux *mux = to_clk_mux(clk);
> +	int num_parents = mux->num_parents;
> +
> +	if (table) {
> +		int i;
> +
> +		for (i = 0; i < num_parents; i++)
> +			if (table[i] == val)
> +				return i;
> +		return -EINVAL;
> +	}
> +
> +	if (val && (flags & CLK_MUX_INDEX_BIT))
> +		val = ffs(val) - 1;
> +
> +	if (val && (flags & CLK_MUX_INDEX_ONE))
> +		val--;
> +
> +	if (val >= num_parents)
> +		return -EINVAL;
> +
> +	return val;
> +}
> +
> +static u8 clk_mux_get_parent(struct clk *clk) {
> +	struct clk_mux *mux = to_clk_mux(clk);
> +	u32 val;
> +
> +	val = readl(mux->reg) >> mux->shift;
> +	val &= mux->mask;
> +
> +	return clk_mux_val_to_index(clk, mux->table, mux->flags, val); }
> +
> +const struct clk_ops clk_mux_ops = {
> +		.get_rate = clk_generic_get_rate,
> +};
> +
> +struct clk *clk_hw_register_mux_table(struct device *dev, const char
> *name,
> +		const char * const *parent_names, u8 num_parents,
> +		unsigned long flags,
> +		void __iomem *reg, u8 shift, u32 mask,
> +		u8 clk_mux_flags, u32 *table)
> +{
> +	struct clk_mux *mux;
> +	struct clk *clk;
> +	u8 width = 0;
> +	int ret;
> +
> +	if (clk_mux_flags & CLK_MUX_HIWORD_MASK) {
> +		width = fls(mask) - ffs(mask) + 1;
> +		if (width + shift > 16) {
> +			pr_err("mux value exceeds LOWORD field\n");
> +			return ERR_PTR(-EINVAL);
> +		}
> +	}
> +
> +	/* allocate the mux */
> +	mux = kzalloc(sizeof(*mux), GFP_KERNEL);
> +	if (!mux)
> +		return ERR_PTR(-ENOMEM);
> +
> +	/* U-boot specific assignments */
> +	mux->parent_names = parent_names;
> +	mux->num_parents = num_parents;
> +
> +	/* struct clk_mux assignments */
> +	mux->reg = reg;
> +	mux->shift = shift;
> +	mux->mask = mask;
> +	mux->flags = clk_mux_flags;
> +	mux->table = table;
> +
> +	clk = &mux->clk;
> +
> +	/*
> +	 * Read the current mux setup - so we assign correct parent.
> +	 *
> +	 * Changing parent would require changing internals of udevice struct
> +	 * for the corresponding clock (to do that define .set_parent() method.
> +	 */
> +	ret = clk_register(clk, UBOOT_DM_CLK_CCF_MUX, (ulong)clk, name,
> +			   parent_names[clk_mux_get_parent(clk)]);
> +	if (ret) {
> +		kfree(mux);
> +		return ERR_PTR(ret);
> +	}
> +
> +	return clk;
> +}
> +
> +struct clk *clk_register_mux_table(struct device *dev, const char *name,
> +		const char * const *parent_names, u8 num_parents,
> +		unsigned long flags,
> +		void __iomem *reg, u8 shift, u32 mask,
> +		u8 clk_mux_flags, u32 *table)
> +{
> +	struct clk *clk;
> +
> +	clk = clk_hw_register_mux_table(dev, name, parent_names,
> num_parents,
> +				       flags, reg, shift, mask, clk_mux_flags,
> +				       table);
> +	if (IS_ERR(clk))
> +		return ERR_CAST(clk);
> +	return clk;
> +}
> +
> +struct clk *clk_register_mux(struct device *dev, const char *name,
> +		const char * const *parent_names, u8 num_parents,
> +		unsigned long flags,
> +		void __iomem *reg, u8 shift, u8 width,
> +		u8 clk_mux_flags)
> +{
> +	u32 mask = BIT(width) - 1;
> +
> +	return clk_register_mux_table(dev, name, parent_names, num_parents,
> +				      flags, reg, shift, mask, clk_mux_flags,
> +				      NULL);
> +}
> +
> +U_BOOT_DRIVER(ccf_clk_mux) = {
> +	.name	= UBOOT_DM_CLK_CCF_MUX,
> +	.id	= UCLASS_CLK,
> +	.ops	= &clk_mux_ops,
> +	.flags = DM_FLAG_PRE_RELOC,
> +};
> diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c new file mode 100644 index
> 0000000000..0a0fffb50b
> --- /dev/null
> +++ b/drivers/clk/clk.c
> @@ -0,0 +1,56 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * Copyright (C) 2019 DENX Software Engineering
> + * Lukasz Majewski, DENX Software Engineering, lukma at denx.de  */
> +
> +#include <common.h>
> +#include <clk-uclass.h>
> +#include <dm/device.h>
> +#include <dm/uclass.h>
> +#include <dm/lists.h>
> +#include <dm/device-internal.h>
> +#include <clk.h>
> +
> +int clk_register(struct clk *clk, const char *drv_name,
> +		 ulong drv_data, const char *name,
> +		 const char *parent_name)
> +{
> +	struct udevice *parent;
> +	struct driver *drv;
> +	int ret;
> +
> +	ret = uclass_get_device_by_name(UCLASS_CLK, parent_name,
> &parent);
> +	if (ret)
> +		printf("%s: UCLASS parent: 0x%p\n", __func__, parent);
> +
> +	debug("%s: name: %s parent: %s [0x%p]\n", __func__, name,
> parent->name,
> +	      parent);
> +
> +	drv = lists_driver_lookup_name(drv_name);
> +	if (!drv) {
> +		printf("%s: %s is not a valid driver name\n",
> +		       __func__, drv_name);
> +		return -ENOENT;
> +	}
> +
> +	ret = device_bind_with_driver_data(parent, drv, name, drv_data,
> +					   ofnode_null(), &clk->dev);
> +	if (ret) {
> +		printf("%s: CLK: %s driver bind error [%d]!\n", __func__, name,
> +		       ret);
> +		return ret;
> +	}
> +
> +	return 0;
> +}
> +
> +ulong clk_generic_get_rate(struct clk *clk) {
> +	return clk_get_parent_rate(clk);
> +}
> +
> +const char *clk_hw_get_name(const struct clk *hw) {
> +	return hw->dev->name;
> +}
> diff --git a/drivers/clk/imx/Kconfig b/drivers/clk/imx/Kconfig index
> a6fb58d6cf..469768b5c3 100644
> --- a/drivers/clk/imx/Kconfig
> +++ b/drivers/clk/imx/Kconfig
> @@ -1,3 +1,12 @@
> +config CLK_IMX6Q
> +	bool "Clock support for i.MX6Q"
> +	depends on ARCH_MX6
> +	select CLK
> +	select CLK_CCF
> +	select SPL_CLK_CCF
> +	help
> +	  This enables DM/DTS support for clock driver in i.MX6Q platforms.
> +
>  config CLK_IMX8
>  	bool "Clock support for i.MX8"
>  	depends on ARCH_IMX8
> diff --git a/drivers/clk/imx/Makefile b/drivers/clk/imx/Makefile index
> 5505ae52e2..beba3bff39 100644
> --- a/drivers/clk/imx/Makefile
> +++ b/drivers/clk/imx/Makefile
> @@ -2,4 +2,6 @@
>  #
>  # SPDX-License-Identifier: GPL-2.0
> 
> +obj-$(CONFIG_$(SPL_TPL_)CLK_CCF) += clk-gate2.o clk-pllv3.o clk-pfd.o
> +obj-$(CONFIG_CLK_IMX6Q) += clk-imx6q.o
>  obj-$(CONFIG_CLK_IMX8) += clk-imx8.o
> diff --git a/drivers/clk/imx/clk-gate2.c b/drivers/clk/imx/clk-gate2.c new file
> mode 100644 index 0000000000..1e53e4f9db
> --- /dev/null
> +++ b/drivers/clk/imx/clk-gate2.c
> @@ -0,0 +1,113 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * Copyright (C) 2019 DENX Software Engineering
> + * Lukasz Majewski, DENX Software Engineering, lukma at denx.de
> + *
> + * Copyright (C) 2010-2011 Canonical Ltd <jeremy.kerr at canonical.com>
> + * Copyright (C) 2011-2012 Mike Turquette, Linaro Ltd
> +<mturquette at linaro.org>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + *
> + * Gated clock implementation
> + *
> + */
> +
> +#include <common.h>
> +#include <asm/io.h>
> +#include <malloc.h>
> +#include <clk-uclass.h>
> +#include <dm/device.h>
> +#include <linux/clk-provider.h>
> +#include <clk.h>
> +#include "clk.h"
> +
> +#define UBOOT_DM_CLK_IMX_GATE2 "imx_clk_gate2"
> +
> +struct clk_gate2 {
> +	struct clk clk;
> +	void __iomem	*reg;
> +	u8		bit_idx;
> +	u8		cgr_val;
> +	u8		flags;
> +};
> +
> +#define to_clk_gate2(_clk) container_of(_clk, struct clk_gate2, clk)
> +
> +static int clk_gate2_enable(struct clk *clk) {
> +	struct clk_gate2 *gate =
> +		(struct clk_gate2 *)dev_get_driver_data(clk->dev);
> +	u32 reg;
> +
> +	reg = readl(gate->reg);
> +	reg &= ~(3 << gate->bit_idx);
> +	reg |= gate->cgr_val << gate->bit_idx;
> +	writel(reg, gate->reg);
> +
> +	return 0;
> +}
> +
> +static int clk_gate2_disable(struct clk *clk) {
> +	struct clk_gate2 *gate =
> +		(struct clk_gate2 *)dev_get_driver_data(clk->dev);
> +	u32 reg;
> +
> +	reg = readl(gate->reg);
> +	reg &= ~(3 << gate->bit_idx);
> +	writel(reg, gate->reg);
> +
> +	return 0;
> +}
> +
> +static const struct clk_ops clk_gate2_ops = {
> +	.enable = clk_gate2_enable,
> +	.disable = clk_gate2_disable,
> +	.get_rate = clk_generic_get_rate,
> +};
> +
> +struct clk *clk_register_gate2(struct device *dev, const char *name,
> +		const char *parent_name, unsigned long flags,
> +		void __iomem *reg, u8 bit_idx, u8 cgr_val,
> +		u8 clk_gate2_flags)
> +{
> +	struct clk_gate2 *gate;
> +	struct clk *clk;
> +	int ret;
> +
> +	gate = kzalloc(sizeof(*gate), GFP_KERNEL);
> +	if (!gate)
> +		return ERR_PTR(-ENOMEM);
> +
> +	/* struct clk_gate2 assignments */
> +	gate->reg = reg;
> +	gate->bit_idx = bit_idx;
> +	gate->cgr_val = cgr_val;
> +	gate->flags = clk_gate2_flags;
> +
> +	/*
> +	 * U-boot DM adjustments:
> +	 *
> +	 * clk and gate reslove to the same address - lets pass clock
> +	 * for better readability.
> +	 */
> +	clk = &gate->clk;
> +
> +	ret = clk_register(clk, UBOOT_DM_CLK_IMX_GATE2, (ulong)clk,
> +			   name, parent_name);
> +	if (ret) {
> +		kfree(gate);
> +		return ERR_PTR(ret);
> +	}
> +
> +	return clk;
> +}
> +
> +U_BOOT_DRIVER(clk_gate2) = {
> +	.name	= UBOOT_DM_CLK_IMX_GATE2,
> +	.id	= UCLASS_CLK,
> +	.ops	= &clk_gate2_ops,
> +	.flags = DM_FLAG_PRE_RELOC,
> +};
> diff --git a/drivers/clk/imx/clk-imx6q.c b/drivers/clk/imx/clk-imx6q.c new file
> mode 100644 index 0000000000..92e9337d44
> --- /dev/null
> +++ b/drivers/clk/imx/clk-imx6q.c
> @@ -0,0 +1,179 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (C) 2019 DENX Software Engineering
> + * Lukasz Majewski, DENX Software Engineering, lukma at denx.de  */
> +
> +#include <common.h>
> +#include <clk-uclass.h>
> +#include <dm.h>
> +#include <asm/arch/clock.h>
> +#include <asm/arch/imx-regs.h>
> +#include <dt-bindings/clock/imx6qdl-clock.h>
> +
> +#include "clk.h"
> +
> +static int imx6q_check_id(ulong id)
> +{
> +	if (id < IMX6QDL_CLK_DUMMY || id >= IMX6QDL_CLK_END) {
> +		printf("%s: Invalid clk ID #%lu\n", __func__, id);
> +		return -EINVAL;
> +	}
> +
> +	return 0;
> +}
> +
> +static ulong imx6q_clk_get_rate(struct clk *clk) {
> +	struct clk *c;
> +	int ret;
> +
> +	debug("%s(#%lu)\n", __func__, clk->id);
> +
> +	ret = imx6q_check_id(clk->id);
> +	if (ret)
> +		return ret;
> +
> +	ret = clk_get_by_id(clk->id, &c);
> +	if (ret)
> +		return ret;
> +
> +	return clk_get_rate(c);
> +}
> +
> +static ulong imx6q_clk_set_rate(struct clk *clk, unsigned long rate) {
> +	debug("%s(#%lu), rate: %lu\n", __func__, clk->id, rate);
> +
> +	return rate;
> +}
> +
> +static int __imx6q_clk_enable(struct clk *clk, bool enable) {
> +	struct clk *c;
> +	int ret = 0;
> +
> +	debug("%s(#%lu) en: %d\n", __func__, clk->id, enable);
> +
> +	ret = imx6q_check_id(clk->id);
> +	if (ret)
> +		return ret;
> +
> +	ret = clk_get_by_id(clk->id, &c);
> +	if (ret)
> +		return ret;
> +
> +	if (enable)
> +		ret = clk_enable(c);
> +	else
> +		ret = clk_disable(c);
> +
> +	return ret;
> +}
> +
> +static int imx6q_clk_disable(struct clk *clk) {
> +	return __imx6q_clk_enable(clk, 0);
> +}
> +
> +static int imx6q_clk_enable(struct clk *clk) {
> +	return __imx6q_clk_enable(clk, 1);
> +}
> +
> +static struct clk_ops imx6q_clk_ops = {
> +	.set_rate = imx6q_clk_set_rate,
> +	.get_rate = imx6q_clk_get_rate,
> +	.enable = imx6q_clk_enable,
> +	.disable = imx6q_clk_disable,
> +};
> +
> +static const char *const usdhc_sels[] = { "pll2_pfd2_396m",
> +"pll2_pfd0_352m", };
> +
> +static int imx6q_clk_probe(struct udevice *dev) {
> +	void *base;
> +
> +	/* Anatop clocks */
> +	base = (void *)ANATOP_BASE_ADDR;
> +
> +	clk_dm(IMX6QDL_CLK_PLL2,
> +	       imx_clk_pllv3(IMX_PLLV3_GENERIC, "pll2_bus", "osc",
> +			     base + 0x30, 0x1));
> +	clk_dm(IMX6QDL_CLK_PLL3_USB_OTG,
> +	       imx_clk_pllv3(IMX_PLLV3_USB, "pll3_usb_otg", "osc",
> +			     base + 0x10, 0x3));
> +	clk_dm(IMX6QDL_CLK_PLL3_60M,
> +	       imx_clk_fixed_factor("pll3_60m",  "pll3_usb_otg",   1, 8));
> +	clk_dm(IMX6QDL_CLK_PLL2_PFD0_352M,
> +	       imx_clk_pfd("pll2_pfd0_352m", "pll2_bus", base + 0x100, 0));
> +	clk_dm(IMX6QDL_CLK_PLL2_PFD2_396M,
> +	       imx_clk_pfd("pll2_pfd2_396m", "pll2_bus", base + 0x100, 2));
> +
> +	/* CCM clocks */
> +	base = dev_read_addr_ptr(dev);
> +	if (base == (void *)FDT_ADDR_T_NONE)
> +		return -EINVAL;
> +
> +	clk_dm(IMX6QDL_CLK_USDHC1_SEL,
> +	       imx_clk_mux("usdhc1_sel", base + 0x1c, 16, 1,
> +			   usdhc_sels, ARRAY_SIZE(usdhc_sels)));
> +	clk_dm(IMX6QDL_CLK_USDHC2_SEL,
> +	       imx_clk_mux("usdhc2_sel", base + 0x1c, 17, 1,
> +			   usdhc_sels, ARRAY_SIZE(usdhc_sels)));
> +	clk_dm(IMX6QDL_CLK_USDHC3_SEL,
> +	       imx_clk_mux("usdhc3_sel", base + 0x1c, 18, 1,
> +			   usdhc_sels, ARRAY_SIZE(usdhc_sels)));
> +	clk_dm(IMX6QDL_CLK_USDHC4_SEL,
> +	       imx_clk_mux("usdhc4_sel", base + 0x1c, 19, 1,
> +			   usdhc_sels, ARRAY_SIZE(usdhc_sels)));
> +
> +	clk_dm(IMX6QDL_CLK_USDHC1_PODF,
> +	       imx_clk_divider("usdhc1_podf", "usdhc1_sel",
> +			       base + 0x24, 11, 3));
> +	clk_dm(IMX6QDL_CLK_USDHC2_PODF,
> +	       imx_clk_divider("usdhc2_podf", "usdhc2_sel",
> +			       base + 0x24, 16, 3));
> +	clk_dm(IMX6QDL_CLK_USDHC3_PODF,
> +	       imx_clk_divider("usdhc3_podf", "usdhc3_sel",
> +			       base + 0x24, 19, 3));
> +	clk_dm(IMX6QDL_CLK_USDHC4_PODF,
> +	       imx_clk_divider("usdhc4_podf", "usdhc4_sel",
> +			       base + 0x24, 22, 3));
> +
> +	clk_dm(IMX6QDL_CLK_ECSPI_ROOT,
> +	       imx_clk_divider("ecspi_root", "pll3_60m", base + 0x38, 19, 6));
> +
> +	clk_dm(IMX6QDL_CLK_ECSPI1,
> +	       imx_clk_gate2("ecspi1", "ecspi_root", base + 0x6c, 0));
> +	clk_dm(IMX6QDL_CLK_ECSPI2,
> +	       imx_clk_gate2("ecspi2", "ecspi_root", base + 0x6c, 2));
> +	clk_dm(IMX6QDL_CLK_ECSPI3,
> +	       imx_clk_gate2("ecspi3", "ecspi_root", base + 0x6c, 4));
> +	clk_dm(IMX6QDL_CLK_ECSPI4,
> +	       imx_clk_gate2("ecspi4", "ecspi_root", base + 0x6c, 6));
> +	clk_dm(IMX6QDL_CLK_USDHC1,
> +	       imx_clk_gate2("usdhc1", "usdhc1_podf", base + 0x80, 2));
> +	clk_dm(IMX6QDL_CLK_USDHC2,
> +	       imx_clk_gate2("usdhc2", "usdhc2_podf", base + 0x80, 4));
> +	clk_dm(IMX6QDL_CLK_USDHC3,
> +	       imx_clk_gate2("usdhc3", "usdhc3_podf", base + 0x80, 6));
> +	clk_dm(IMX6QDL_CLK_USDHC4,
> +	       imx_clk_gate2("usdhc4", "usdhc4_podf", base + 0x80, 8));
> +
> +	return 0;
> +}
> +
> +static const struct udevice_id imx6q_clk_ids[] = {
> +	{ .compatible = "fsl,imx6q-ccm" },
> +	{ },
> +};
> +
> +U_BOOT_DRIVER(imx6q_clk) = {
> +	.name = "clk_imx6q",
> +	.id = UCLASS_CLK,
> +	.of_match = imx6q_clk_ids,
> +	.ops = &imx6q_clk_ops,
> +	.probe = imx6q_clk_probe,
> +	.flags = DM_FLAG_PRE_RELOC,
> +};
> diff --git a/drivers/clk/imx/clk-pfd.c b/drivers/clk/imx/clk-pfd.c new file mode
> 100644 index 0000000000..2293d481d4
> --- /dev/null
> +++ b/drivers/clk/imx/clk-pfd.c
> @@ -0,0 +1,91 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * Copyright (C) 2019 DENX Software Engineering
> + * Lukasz Majewski, DENX Software Engineering, lukma at denx.de
> + *
> + * Copyright 2012 Freescale Semiconductor, Inc.
> + * Copyright 2012 Linaro Ltd.
> + *
> + * The code contained herein is licensed under the GNU General Public
> + * License. You may obtain a copy of the GNU General Public License
> + * Version 2 or later at the following locations:
> + *
> + *
> +https://eur01.safelinks.protection.outlook.com/?url=http%3A%2F%2Fwww.
> op
> +ensource.org%2Flicenses%2Fgpl-license.html&data=02%7C01%7Cpeng
> .fan%
> +40nxp.com%7C92b89511186641d8ecf508d6c969131f%7C686ea1d3bc2b4c
> 6fa92cd99c
> +5c301635%7C0%7C0%7C636917850484120681&sdata=4wXyWrLW9k
> NgQ7YfTNYDH1d
> +tO4AEHOS50suxlzBBugY%3D&reserved=0
> + *
> +https://eur01.safelinks.protection.outlook.com/?url=http%3A%2F%2Fwww.
> gn
> +u.org%2Fcopyleft%2Fgpl.html&data=02%7C01%7Cpeng.fan%40nxp.co
> m%7C92b
> +89511186641d8ecf508d6c969131f%7C686ea1d3bc2b4c6fa92cd99c5c3016
> 35%7C0%7C
> +0%7C636917850484120681&sdata=EV6piw9NY0H71%2BjCCVOwnKDz
> a%2FF33189dT
> +oqBsgUKn8%3D&reserved=0
> + */
> +
> +#include <common.h>
> +#include <asm/io.h>
> +#include <malloc.h>
> +#include <clk-uclass.h>
> +#include <dm/device.h>
> +#include <linux/clk-provider.h>
> +#include <div64.h>
> +#include <clk.h>
> +#include "clk.h"
> +
> +#define UBOOT_DM_CLK_IMX_PFD "imx_clk_pfd"
> +
> +struct clk_pfd {
> +	struct clk	clk;
> +	void __iomem	*reg;
> +	u8		idx;
> +};
> +
> +#define to_clk_pfd(_clk) container_of(_clk, struct clk_pfd, clk)
> +
> +#define SET	0x4
> +#define CLR	0x8
> +#define OTG	0xc
> +
> +static unsigned long clk_pfd_recalc_rate(struct clk *clk) {
> +	struct clk_pfd *pfd =
> +		(struct clk_pfd *)dev_get_driver_data(clk->dev);
> +	unsigned long parent_rate = clk_get_parent_rate(clk);
> +	u64 tmp = parent_rate;
> +	u8 frac = (readl(pfd->reg) >> (pfd->idx * 8)) & 0x3f;
> +
> +	tmp *= 18;
> +	do_div(tmp, frac);
> +
> +	return tmp;
> +}
> +
> +static const struct clk_ops clk_pfd_ops = {
> +	.get_rate	= clk_pfd_recalc_rate,
> +};
> +
> +struct clk *imx_clk_pfd(const char *name, const char *parent_name,
> +			void __iomem *reg, u8 idx)
> +{
> +	struct clk_pfd *pfd;
> +	struct clk *clk;
> +	int ret;
> +
> +	pfd = kzalloc(sizeof(*pfd), GFP_KERNEL);
> +	if (!pfd)
> +		return ERR_PTR(-ENOMEM);
> +
> +	pfd->reg = reg;
> +	pfd->idx = idx;
> +
> +	/* register the clock */
> +	clk = &pfd->clk;
> +
> +	ret = clk_register(clk, UBOOT_DM_CLK_IMX_PFD, (ulong)clk,
> +			   name, parent_name);
> +	if (ret) {
> +		kfree(pfd);
> +		return ERR_PTR(ret);
> +	}
> +
> +	return clk;
> +}
> +
> +U_BOOT_DRIVER(clk_pfd) = {
> +	.name	= UBOOT_DM_CLK_IMX_PFD,
> +	.id	= UCLASS_CLK,
> +	.ops	= &clk_pfd_ops,
> +	.flags = DM_FLAG_PRE_RELOC,
> +};
> diff --git a/drivers/clk/imx/clk-pllv3.c b/drivers/clk/imx/clk-pllv3.c new file
> mode 100644 index 0000000000..3fe9b7c03d
> --- /dev/null
> +++ b/drivers/clk/imx/clk-pllv3.c
> @@ -0,0 +1,83 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * Copyright (C) 2019 DENX Software Engineering
> + * Lukasz Majewski, DENX Software Engineering, lukma at denx.de  */
> +
> +#include <common.h>
> +#include <asm/io.h>
> +#include <malloc.h>
> +#include <clk-uclass.h>
> +#include <dm/device.h>
> +#include <dm/uclass.h>
> +#include <clk.h>
> +#include "clk.h"
> +
> +#define UBOOT_DM_CLK_IMX_PLLV3 "imx_clk_pllv3"
> +
> +struct clk_pllv3 {
> +	struct clk	clk;
> +	void __iomem	*base;
> +	u32		div_mask;
> +	u32		div_shift;
> +};
> +
> +#define to_clk_pllv3(_clk) container_of(_clk, struct clk_pllv3, clk)
> +
> +static ulong clk_pllv3_get_rate(struct clk *clk) {
> +	struct clk_pllv3 *pll =
> +		(struct clk_pllv3 *)dev_get_driver_data(clk->dev);
> +	unsigned long parent_rate = clk_get_parent_rate(clk);
> +
> +	u32 div = (readl(pll->base) >> pll->div_shift) & pll->div_mask;
> +
> +	return (div == 1) ? parent_rate * 22 : parent_rate * 20; }
> +
> +static const struct clk_ops clk_pllv3_generic_ops = {
> +	.get_rate       = clk_pllv3_get_rate,
> +};
> +
> +struct clk *imx_clk_pllv3(enum imx_pllv3_type type, const char *name,
> +			  const char *parent_name, void __iomem *base,
> +			  u32 div_mask)
> +{
> +	struct clk_pllv3 *pll;
> +	struct clk *clk;
> +	char *drv_name;
> +	int ret;
> +
> +	pll = kzalloc(sizeof(*pll), GFP_KERNEL);
> +	if (!pll)
> +		return ERR_PTR(-ENOMEM);
> +
> +	switch (type) {
> +	case IMX_PLLV3_GENERIC:
> +	case IMX_PLLV3_USB:
> +		drv_name = UBOOT_DM_CLK_IMX_PLLV3;
> +		break;
> +	default:
> +		kfree(pll);
> +		return ERR_PTR(-ENOTSUPP);
> +	}
> +
> +	pll->base = base;
> +	pll->div_mask = div_mask;
> +	clk = &pll->clk;
> +
> +	ret = clk_register(clk, drv_name, (ulong)clk, name, parent_name);
> +	if (ret) {
> +		kfree(pll);
> +		return ERR_PTR(ret);
> +	}
> +
> +	return clk;
> +}
> +
> +U_BOOT_DRIVER(clk_pllv3_generic) = {
> +	.name	= UBOOT_DM_CLK_IMX_PLLV3,
> +	.id	= UCLASS_CLK,
> +	.ops	= &clk_pllv3_generic_ops,
> +	.flags = DM_FLAG_PRE_RELOC,
> +};
> diff --git a/drivers/clk/imx/clk.h b/drivers/clk/imx/clk.h new file mode 100644
> index 0000000000..864a215a22
> --- /dev/null
> +++ b/drivers/clk/imx/clk.h
> @@ -0,0 +1,75 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * Copyright (C) 2019 DENX Software Engineering
> + * Lukasz Majewski, DENX Software Engineering, lukma at denx.de  */
> +#ifndef __MACH_IMX_CLK_H #define __MACH_IMX_CLK_H
> +
> +#include <linux/clk-provider.h>
> +
> +static inline void clk_dm(ulong id, struct clk *clk) {
> +	if (!IS_ERR(clk))
> +		clk->id = id;
> +}
> +
> +enum imx_pllv3_type {
> +	IMX_PLLV3_GENERIC,
> +	IMX_PLLV3_SYS,
> +	IMX_PLLV3_USB,
> +	IMX_PLLV3_USB_VF610,
> +	IMX_PLLV3_AV,
> +	IMX_PLLV3_ENET,
> +	IMX_PLLV3_ENET_IMX7,
> +	IMX_PLLV3_SYS_VF610,
> +	IMX_PLLV3_DDR_IMX7,
> +};
> +
> +struct clk *clk_register_gate2(struct device *dev, const char *name,
> +		const char *parent_name, unsigned long flags,
> +		void __iomem *reg, u8 bit_idx, u8 cgr_val,
> +		u8 clk_gate_flags);
> +
> +struct clk *imx_clk_pllv3(enum imx_pllv3_type type, const char *name,
> +			  const char *parent_name, void __iomem *base,
> +			  u32 div_mask);
> +
> +static inline struct clk *imx_clk_gate2(const char *name, const char *parent,
> +					void __iomem *reg, u8 shift)
> +{
> +	return clk_register_gate2(NULL, name, parent, CLK_SET_RATE_PARENT,
> reg,
> +			shift, 0x3, 0);
> +}
> +
> +static inline struct clk *imx_clk_fixed_factor(const char *name,
> +		const char *parent, unsigned int mult, unsigned int div) {
> +	return clk_register_fixed_factor(NULL, name, parent,
> +			CLK_SET_RATE_PARENT, mult, div);
> +}
> +
> +static inline struct clk *imx_clk_divider(const char *name, const char
> *parent,
> +		void __iomem *reg, u8 shift, u8 width) {
> +	return clk_register_divider(NULL, name, parent, CLK_SET_RATE_PARENT,
> +			reg, shift, width, 0);
> +}
> +
> +struct clk *imx_clk_pfd(const char *name, const char *parent_name,
> +			void __iomem *reg, u8 idx);
> +
> +struct clk *imx_clk_fixup_mux(const char *name, void __iomem *reg,
> +			      u8 shift, u8 width, const char * const *parents,
> +			      int num_parents, void (*fixup)(u32 *val));
> +
> +static inline struct clk *imx_clk_mux(const char *name, void __iomem *reg,
> +			u8 shift, u8 width, const char * const *parents,
> +			int num_parents)
> +{
> +	return clk_register_mux(NULL, name, parents, num_parents,
> +			CLK_SET_RATE_NO_REPARENT, reg, shift,
> +			width, 0);
> +}
> +
> +#endif /* __MACH_IMX_CLK_H */
> diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h new file
> mode 100644 index 0000000000..eac045c5f8
> --- /dev/null
> +++ b/include/linux/clk-provider.h
> @@ -0,0 +1,94 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * Copyright (C) 2019 DENX Software Engineering
> + * Lukasz Majewski, DENX Software Engineering, lukma at denx.de
> + *
> + * Copyright (c) 2010-2011 Jeremy Kerr <jeremy.kerr at canonical.com>
> + * Copyright (C) 2011-2012 Linaro Ltd <mturquette at linaro.org>  */
> +#ifndef __LINUX_CLK_PROVIDER_H #define __LINUX_CLK_PROVIDER_H
> +
> +#define CLK_SET_RATE_PARENT	BIT(2) /* propagate rate change up one
> level */
> +#define CLK_SET_RATE_NO_REPARENT BIT(7) /* don't re-parent on rate
> +change */
> +
> +#define CLK_MUX_INDEX_ONE		BIT(0)
> +#define CLK_MUX_INDEX_BIT		BIT(1)
> +#define CLK_MUX_HIWORD_MASK		BIT(2)
> +#define CLK_MUX_READ_ONLY		BIT(3) /* mux can't be changed */
> +#define CLK_MUX_ROUND_CLOSEST		BIT(4)
> +
> +struct clk_mux {
> +	struct clk	clk;
> +	void __iomem	*reg;
> +	u32		*table;
> +	u32		mask;
> +	u8		shift;
> +	u8		flags;
> +
> +	/*
> +	 * Fields from struct clk_init_data - this struct has been
> +	 * omitted to avoid too deep level of CCF for bootloader
> +	 */
> +	const char	* const *parent_names;
> +	u8		num_parents;
> +};
> +
> +#define to_clk_mux(_clk) container_of(_clk, struct clk_mux, clk)
> +
> +struct clk_div_table {
> +	unsigned int	val;
> +	unsigned int	div;
> +};
> +
> +struct clk_divider {
> +	struct clk	clk;
> +	void __iomem	*reg;
> +	u8		shift;
> +	u8		width;
> +	u8		flags;
> +	const struct clk_div_table	*table;
> +};
> +
> +#define clk_div_mask(width)	((1 << (width)) - 1)
> +#define to_clk_divider(_clk) container_of(_clk, struct clk_divider,
> +clk)
> +
> +#define CLK_DIVIDER_ONE_BASED		BIT(0)
> +#define CLK_DIVIDER_POWER_OF_TWO	BIT(1)
> +#define CLK_DIVIDER_ALLOW_ZERO		BIT(2)
> +#define CLK_DIVIDER_HIWORD_MASK		BIT(3)
> +#define CLK_DIVIDER_ROUND_CLOSEST	BIT(4)
> +#define CLK_DIVIDER_READ_ONLY		BIT(5)
> +#define CLK_DIVIDER_MAX_AT_ZERO		BIT(6)
> +
> +struct clk_fixed_factor {
> +	struct clk	clk;
> +	unsigned int	mult;
> +	unsigned int	div;
> +};
> +
> +#define to_clk_fixed_factor(_clk) container_of(_clk, struct clk_fixed_factor,\
> +					       clk)
> +
> +int clk_register(struct clk *clk, const char *drv_name,
> +		 ulong drv_data, const char *name,
> +		 const char *parent_name);
> +
> +struct clk *clk_register_fixed_factor(struct device *dev, const char *name,
> +		const char *parent_name, unsigned long flags,
> +		unsigned int mult, unsigned int div);
> +
> +struct clk *clk_register_divider(struct device *dev, const char *name,
> +		const char *parent_name, unsigned long flags,
> +		void __iomem *reg, u8 shift, u8 width,
> +		u8 clk_divider_flags);
> +
> +struct clk *clk_register_mux(struct device *dev, const char *name,
> +		const char * const *parent_names, u8 num_parents,
> +		unsigned long flags,
> +		void __iomem *reg, u8 shift, u8 width,
> +		u8 clk_mux_flags);
> +
> +const char *clk_hw_get_name(const struct clk *hw); ulong
> +clk_generic_get_rate(struct clk *clk); #endif /* __LINUX_CLK_PROVIDER_H
> +*/

I think it might be better to add new Kconfig for the clk files, such as PFD/GATE2
There are mixed usage hw and clk, do we need to provide hw, or just clk?

Set rate is not added, I could consider to add it in my patches.
Also I am thinking to add round_rate, thoughts?

Regards,
Peng.

> --
> 2.11.0



More information about the U-Boot mailing list