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[] = {
