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 <ja...@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: lu...@denx.de

Attachment: pgpyiJEGMt0oP.pgp
Description: OpenPGP digital signature

_______________________________________________
U-Boot mailing list
U-Boot@lists.denx.de
https://lists.denx.de/listinfo/u-boot

Reply via email to