[U-Boot] [PATCH v2 05/10] clk: imx: Add imx6q clock tree support

Lukasz Majewski lukma at denx.de
Fri Apr 19 07:54:34 UTC 2019


Hi Jagan,

> i.MX6 clock control module comprise of parent clocks, gates,
> multiplexers, dividers, PODF, PLL, fixed rate and etc.
> 
> So, the U-Boot implementation of ccm has divided into gates and tree.
> 
> 1) gate clocks are generic clock configuration of enable/disable bit
> management which can be handle via imx6_clock_gate.
> 2) tree clocks are handle via tree clock management where it link the
> clocks based on the parent clock which usually required to get and
> set the clock rates.

If I understood the patch series:

The clock hierarchy has been flattened to only two kinds of elements:
gates and "tree".

I'm not sure if we will not find this not enough in the future when one
wants to implement/use video/USB/other clocks.

For example only pllv3 type has several variants:

+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,

USB, USB_VF610 (vybrid), Generic, ENET (which is not implemented by
this series).

With porting Linux approach I'm pretty sure that I can add support for
vybrid in the future when e.g. gadget is converted to DM.

Is this also so predictable with your approach ?

> 
> This patch add tree clock management for imx6q USDHC clocks, so the
> mmc driver from imx6 can eventually use this so getting the USDHC
> clock rates.

But for USDHC you only need gate and read the clock value (which is not
so deepen hidden in the hierarchy).

> 
> Unlike Linux, U-Boot implementation may not require to maintain exact
> clock tree due to various constrains and use cases.

Another use case - ECSPI / ENET.

> So here is how
> the clock tree differs between them.
> 
> usdhc clock tree in Linux:
> -------------------------
> USDHC1 => USDHC1_PODF => USDHC1_SEL => PLL2_PFD2 => PLL2_BUS =>
> PLL2_BYPASS => PLL2 => OSC
> 
> usdhc clock tree in U-Boot:
> ---------------------------
> USDHC1 => USDHC1_PODF => USDHC1_SEL => PLL2_PFD2 => PLL2_BUS => OSC
> 
> Signed-off-by: Jagan Teki <jagan at amarulasolutions.com>
> ---
>  arch/arm/include/asm/arch-mx6/clock.h |  65 ++++++++++++++++
>  drivers/clk/imx/clk-imx6-common.c     | 103
> ++++++++++++++++++++++++++ drivers/clk/imx/clk-imx6q.c           |
> 70 +++++++++++++++++ 3 files changed, 238 insertions(+)
> 
> diff --git a/arch/arm/include/asm/arch-mx6/clock.h
> b/arch/arm/include/asm/arch-mx6/clock.h index fa921a9f08..424231c691
> 100644 --- a/arch/arm/include/asm/arch-mx6/clock.h
> +++ b/arch/arm/include/asm/arch-mx6/clock.h
> @@ -21,6 +21,67 @@
>  #define MXC_CLK32	32768
>  #endif
>  
> +#define OSC_24M_ULL	24000000ULL
> +
> +enum imx6_clk_type {
> +	IMX6_CLK_TYPE_SIMPLE	= 0,
> +	IMX6_CLK_TYPE_FIXED,
> +	IMX6_CLK_TYPE_DIV,
> +	IMX6_CLK_TYPE_MUX,
> +	IMX6_CLK_TYPE_PLL_PFD,
> +	IMX6_CLK_TYPE_PLL_DIV,
> +};
> +
> +/**
> + * struct imx6_clk_tree - imx6 ccm clock tree
> + *
> + * @parent:		parent clock tree
> + * @type:		clock type
> + * @off:		register offset of the specified clock
> + * @shift:		number of bits to shift the bitfield
> + * @width:		width of the bitfield
> + * @idx:		index of the specified clock
> + * @fixed_rate:		fixed clock rate
> + */
> +struct imx6_clk_tree {
> +	const unsigned long *parent;
> +	enum imx6_clk_type type;
> +	u16 off;
> +
> +	u8 shift;
> +	u8 width;
> +	u8 idx;
> +	ulong fixed_rate;
> +};
> +
> +#define TREE(_parent, _type, _off, _shift, _width, _idx,
> _fixed_rate) {	\
> +	.parent =
> _parent,						\
> +	.type =
> _type,							\
> +	.off =
> _off,							\
> +	.shift =
> _shift,						\
> +	.width =
> _width,						\
> +	.idx =
> _idx,							\
> +	.fixed_rate =
> _fixed_rate,					\ +}
> +
> +#define
> SIMPLE(_parent)
> \
> +	TREE(_parent, IMX6_CLK_TYPE_SIMPLE, 0, 0, 0, 0, 0)
> +
> +#define
> FIXED(_fixed_rate)						\
> +	TREE(NULL, IMX6_CLK_TYPE_FIXED, 0, 0, 0, 0, _fixed_rate)
> +
> +#define DIV(_parent, _off, _shift,
> _width)				\
> +	TREE(_parent, IMX6_CLK_TYPE_DIV, _off, _shift, _width, 0, 0)
> +
> +#define MUX(_parent, _off, _shift,
> _width)				\
> +	TREE(_parent, IMX6_CLK_TYPE_MUX, _off, _shift, _width, 0, 0)
> +
> +#define PLL_PFD(_parent, _off, _width,
> _idx)				\
> +	TREE(_parent, IMX6_CLK_TYPE_PLL_PFD, _off, 0, _width, _idx,
> 0) +
> +#define PLL_DIV(_parent, _off, _shift,
> _width)				\
> +	TREE(_parent, IMX6_CLK_TYPE_PLL_DIV, _off, _shift, _width,
> 0, 0) +
>  /**
>   * struct imx6_clk_gate - imx6 ccm clock gate
>   *
> @@ -41,19 +102,23 @@ struct imx6_clk_gate {
>   * struct imx6_clk_desc - imx6 clock control module descriptor
>   *
>   * @gates:	ccm clock gates
> + * @tree:	ccm clock tree
>   */
>  struct imx6_clk_desc {
>  	const struct imx6_clk_gate *gates;
> +	const struct imx6_clk_tree *tree;
>  };
>  
>  /**
>   * struct imx6_clk_priv - imx6 clock control module
>   *
>   * @base:	ccm base address
> + * @anatop:	anatop base address
>   * @desc:	ccm descriptor
>   */
>  struct imx6_clk_priv {
>  	void *base;
> +	void *anatop;
>  	const struct imx6_clk_desc *desc;
>  };
>  
> diff --git a/drivers/clk/imx/clk-imx6-common.c
> b/drivers/clk/imx/clk-imx6-common.c index 1d38f51f7e..d21facf2e5
> 100644 --- a/drivers/clk/imx/clk-imx6-common.c
> +++ b/drivers/clk/imx/clk-imx6-common.c
> @@ -6,18 +6,117 @@
>  
>  #include <common.h>
>  #include <clk-uclass.h>
> +#include <div64.h>
>  #include <dm.h>
>  #include <errno.h>
>  #include <asm/io.h>
>  #include <asm/arch/clock.h>
>  #include <linux/log2.h>
>  
> +#include <dt-bindings/clock/imx6qdl-clock.h>
> +
>  static const struct imx6_clk_gate *priv_to_gate(struct imx6_clk_priv
> *priv, unsigned long id)
>  {
>  	return &priv->desc->gates[id];
>  }
>  
> +static const struct imx6_clk_tree *priv_to_tree(struct imx6_clk_priv
> *priv,
> +						unsigned long id)
> +{
> +	return &priv->desc->tree[id];
> +}
> +
> +static u8 get_bitfield(void *base, u8 shift, u8 width)
> +{
> +	return (readl(base) >> shift) & clk_div_mask(width);
> +}
> +
> +static u8 get_mux_parent(const struct imx6_clk_tree *tree, void
> *base) +{
> +	u8 idx = get_bitfield(base + tree->off, tree->shift,
> tree->width); +
> +	return tree->parent[idx];
> +}
> +
> +static ulong get_pll2_bus_rate(struct imx6_clk_priv *priv, unsigned
> long id,
> +			       ulong parent_rate)
> +{
> +	const struct imx6_clk_tree *tree = priv_to_tree(priv, id);
> +	u8 div;
> +
> +	div = get_bitfield(priv->anatop + tree->off, tree->shift,
> tree->width);
> +	return (div == 1) ? parent_rate * 22 : parent_rate * 20;
> +}
> +
> +static ulong get_pfd_rate(struct imx6_clk_priv *priv, unsigned long
> id,
> +			  ulong parent_rate)
> +{
> +	const struct imx6_clk_tree *tree = priv_to_tree(priv, id);
> +	u64 tmp = parent_rate;
> +	u8 frac;
> +
> +	frac = get_bitfield(priv->anatop + tree->off,
> +			    tree->idx * 8, tree->width);
> +	tmp *= 18;
> +	do_div(tmp, frac);
> +
> +	return tmp;
> +}
> +
> +static ulong get_mux_rate(ulong parent_rate)
> +{
> +	/* derive clock from respective parent */
> +	return parent_rate;
> +}
> +
> +static ulong get_div_rate(struct imx6_clk_priv *priv, unsigned long
> id,
> +			  ulong parent_rate)
> +{
> +	const struct imx6_clk_tree *tree = priv_to_tree(priv, id);
> +	u8 podf;
> +
> +	podf = get_bitfield(priv->base + tree->off, tree->shift,
> tree->width);
> +	return parent_rate / (podf + 1);
> +}
> +
> +static ulong imx6_calc_clk_rate(struct imx6_clk_priv *priv, unsigned
> long id) +{
> +	const struct imx6_clk_tree *tree = priv_to_tree(priv, id);
> +	ulong rate = 0;
> +
> +	switch (tree->type) {
> +	case IMX6_CLK_TYPE_FIXED:
> +		return tree->fixed_rate;
> +	case IMX6_CLK_TYPE_PLL_DIV:
> +		rate = imx6_calc_clk_rate(priv, tree->parent[0]);
> +		return get_pll2_bus_rate(priv, id, rate);
> +	case IMX6_CLK_TYPE_PLL_PFD:
> +		rate = imx6_calc_clk_rate(priv, tree->parent[0]);
> +		return get_pfd_rate(priv, id, rate);
> +	case IMX6_CLK_TYPE_MUX:
> +		rate = imx6_calc_clk_rate(priv,
> +					  get_mux_parent(tree,
> priv->base));
> +		return get_mux_rate(rate);
> +	case IMX6_CLK_TYPE_DIV:
> +		rate = imx6_calc_clk_rate(priv, tree->parent[0]);
> +		return get_div_rate(priv, id, rate);
> +	case IMX6_CLK_TYPE_SIMPLE:
> +		return imx6_calc_clk_rate(priv, tree->parent[0]);
> +	default:
> +		printf("%s: (TYPE#%d) unhandled\n", __func__,
> tree->type);
> +	}
> +
> +	return rate;
> +}
> +
> +static ulong imx6_clk_get_rate(struct clk *clk)
> +{
> +	struct imx6_clk_priv *priv = dev_get_priv(clk->dev);
> +
> +	return imx6_calc_clk_rate(priv, clk->id);
> +}
> +
>  static int imx6_set_gate(struct clk *clk, bool on)
>  {
>  	struct imx6_clk_priv *priv = dev_get_priv(clk->dev);
> @@ -51,6 +150,7 @@ static int imx6_clk_disable(struct clk *clk)
>  struct clk_ops imx6_clk_ops = {
>  	.enable = imx6_clk_enable,
>  	.disable = imx6_clk_disable,
> +	.get_rate = imx6_clk_get_rate,
>  };
>  
>  int imx6_clk_probe(struct udevice *dev)
> @@ -61,6 +161,9 @@ int imx6_clk_probe(struct udevice *dev)
>  	if (!priv->base)
>  		return -ENOMEM;
>  
> +	/* FIXME get the anatop base via OF_LIVE */
> +	priv->anatop = priv->base + 0x4000;
> +
>  	priv->desc = (const struct imx6_clk_desc
> *)dev_get_driver_data(dev); if (!priv->desc)
>  		return -EINVAL;
> diff --git a/drivers/clk/imx/clk-imx6q.c b/drivers/clk/imx/clk-imx6q.c
> index 8ec713298d..a46d2f6f88 100644
> --- a/drivers/clk/imx/clk-imx6q.c
> +++ b/drivers/clk/imx/clk-imx6q.c
> @@ -10,6 +10,75 @@
>  #include <asm/arch/clock.h>
>  #include <dt-bindings/clock/imx6qdl-clock.h>
>  
> +static const unsigned long pll2_bus[] = {
> +	IMX6QDL_CLK_OSC,
> +};
> +
> +static const unsigned long pfd_352m[] = {
> +	IMX6QDL_CLK_PLL2_BUS,
> +};
> +
> +static const unsigned long usdhc_sel[] = {
> +	IMX6QDL_CLK_PLL2_PFD2_396M,
> +	IMX6QDL_CLK_PLL2_PFD0_352M,
> +};
> +
> +static const unsigned long usdhc1_podf[] = {
> +	IMX6QDL_CLK_USDHC1_SEL,
> +};
> +
> +static const unsigned long usdhc2_podf[] = {
> +	IMX6QDL_CLK_USDHC2_SEL,
> +};
> +
> +static const unsigned long usdhc3_podf[] = {
> +	IMX6QDL_CLK_USDHC3_SEL,
> +};
> +
> +static const unsigned long usdhc4_podf[] = {
> +	IMX6QDL_CLK_USDHC4_SEL,
> +};
> +
> +static const unsigned long usdhc1[] = {
> +	IMX6QDL_CLK_USDHC1_PODF,
> +};
> +
> +static const unsigned long usdhc2[] = {
> +	IMX6QDL_CLK_USDHC2_PODF,
> +};
> +
> +static const unsigned long usdhc3[] = {
> +	IMX6QDL_CLK_USDHC3_PODF,
> +};
> +
> +static const unsigned long usdhc4[] = {
> +	IMX6QDL_CLK_USDHC4_PODF,
> +};
> +
> +static const struct imx6_clk_tree imx6q_tree[] = {
> +	[IMX6QDL_CLK_OSC]		= FIXED(OSC_24M_ULL),
> +
> +	[IMX6QDL_CLK_PLL2_BUS]		= PLL_DIV(pll2_bus,
> 0x30, 13, 1), +
> +	[IMX6QDL_CLK_PLL2_PFD0_352M]	= PLL_PFD(pfd_352m,
> 0x100, 6, 0),
> +	[IMX6QDL_CLK_PLL2_PFD2_396M]	= PLL_PFD(pfd_352m,
> 0x100, 6, 2), +
> +	[IMX6QDL_CLK_USDHC1_SEL]	= MUX(usdhc_sel, 0x01c, 16,
> 1),
> +	[IMX6QDL_CLK_USDHC2_SEL]	= MUX(usdhc_sel, 0x01c, 17,
> 1),
> +	[IMX6QDL_CLK_USDHC3_SEL]	= MUX(usdhc_sel, 0x01c, 18,
> 1),
> +	[IMX6QDL_CLK_USDHC4_SEL]	= MUX(usdhc_sel, 0x01c, 19,
> 1), +
> +	[IMX6QDL_CLK_USDHC1_PODF]	= DIV(usdhc1_podf, 0x024,
> 11, 3),
> +	[IMX6QDL_CLK_USDHC2_PODF]	= DIV(usdhc2_podf, 0x024,
> 16, 3),
> +	[IMX6QDL_CLK_USDHC3_PODF]	= DIV(usdhc3_podf, 0x024,
> 19, 3),
> +	[IMX6QDL_CLK_USDHC4_PODF]	= DIV(usdhc4_podf, 0x024,
> 22, 3), +
> +	[IMX6QDL_CLK_USDHC1]		= SIMPLE(usdhc1),
> +	[IMX6QDL_CLK_USDHC2]		= SIMPLE(usdhc2),
> +	[IMX6QDL_CLK_USDHC3]		= SIMPLE(usdhc3),
> +	[IMX6QDL_CLK_USDHC4]		= SIMPLE(usdhc4),
> +};
> +
>  static const struct imx6_clk_gate imx6q_gates[] = {
>  	[IMX6QDL_CLK_USDHC1]		= GATE(0x080, GENMASK(3,
> 2)), [IMX6QDL_CLK_USDHC2]		= GATE(0x080, GENMASK(5, 4)),
> @@ -19,6 +88,7 @@ static const struct imx6_clk_gate imx6q_gates[] = {
>  
>  static const struct imx6_clk_desc imx6q_clk_desc = {
>  	.gates = imx6q_gates,
> +	.tree = imx6q_tree,
>  };
>  
>  static const struct udevice_id clk_imx6q_ids[] = {




Best regards,

Lukasz Majewski

--

DENX Software Engineering GmbH,      Managing Director: Wolfgang Denk
HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany
Phone: (+49)-8142-66989-59 Fax: (+49)-8142-66989-80 Email: lukma at denx.de
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 488 bytes
Desc: OpenPGP digital signature
URL: <http://lists.denx.de/pipermail/u-boot/attachments/20190419/fbfc5259/attachment.sig>


More information about the U-Boot mailing list