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
pgpyiJEGMt0oP.pgp
Description: OpenPGP digital signature
_______________________________________________ U-Boot mailing list U-Boot@lists.denx.de https://lists.denx.de/listinfo/u-boot