[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